是否可以將不同語言編譯到LLVM IR層面鏈接?如果可以,與傳統的編譯為目標代碼鏈接有什麼不同?
完整的LLVM工具鏈是可以拆成這樣一系列步驟:
源代碼編譯為IR,IR之間互相鏈接,鏈接後IR編譯為目標文件,目標文件鏈接為可執行文件。傳統上的跨語言鏈接是在目標文件層面進行的,那麼是否可以在IR層面跨語言鏈接(即不同語言的IR鏈接為同一個IR,並編譯至同一個目標文件)?SO已有的問題:LLVM as base compiler for different languages
stackoverflow的解釋已經很清楚了,他說的是Rust和C,其實SWIFT和C++也是可以的。也感謝樓主邀請我回答這個問題,因為這個問題,我想把SWIFT和C++ / Rust 在LLVM IR混合,所以查了一下SWIFT和Rust的語法,以及如何與C/C++代碼混合,了解了SWIFT的橋接機制。
那麼,我接下來展示一下如何把SWIFT和C++ / Rust 的LLVM IR融合。
首先我有一段簡單的SWIFT代碼,我命名為main.swift
print("The integer from C++ is (magic())")
即列印來自magic函數的返回結果,而這個magic()將會來自C++。不過我們這裡不會如傳統的利用SWIFT編譯器來橋接編譯,而是利用LLVM,把C++吐出的LLVM IR拿過來。
那麼,C++代碼我命名為magic.cpp,代碼如下所示:
class Magic
{
public:
Magic(int num) : number_(num)
{
}
int getMagicNumber() const
{
return number_;
}
private:
int number_;
};
extern "C" int magic()
{
return Magic(42).getMagicNumber();
}
也就是magic函數會返回數字42,同時我利用extern "C",採用C鏈接,防止C++ Name Mangling。
同時,為了讓SWIFT代碼知道有magic函數,我利用一個橋接的頭文件,我命名為bridge.h
int magic();
準備工作差不多了,接下來我們分別吐出LLVM IR。
首先是C++部分,我們利用clang++來完成,命令是:
clang++ -c -emit-llvm magic.cpp -o magic.bc
swiftc -emit-bc main.swift -import-objc-header bridge.h -o main.bc
這裡的 -import-objc-header bridge.h則是橋接使用,讓SWIFT識別出來magic函數。
隨後,我們利用llvm-link,把產生的main.bc, magic.bc合併稱為一個LLVM IR的bitcode(bc)文件。
llvm-link main.bc magic.bc -o swiftcxx.bc
這個時候我們已經得到了SWIFT和C++混合的LLVM IR文件,名字叫做swiftcxx.bc.
然後,我們利用llc編譯這個混合LLVM IR文件,產生一個目標文件,我命名為swiftcxx.o
llc swiftcxx.bc -filetype=obj -o swiftcxx.o
隨後,我們利用鏈接器產生可執行文件, 我命名為swiftcxx
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld swiftcxx.o -force_load /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_macosx.a -framework CoreFoundation -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -lobjc -lSystem -arch x86_64 -L /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -rpath /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -macosx_version_min 10.9.0 -no_objc_category_merging -o swiftcxx
隨後,我們執行swiftcxx.
./swiftcxx
或許有同學會問為什麼我怎麼知道這麼複雜的鏈接器參數,其實我是不知道的,你可以使用swiftc swiftcxx.o -v 來查看,只是我想演示如何單獨使用工具來達到這一切。
那麼,我們再次嘗試一下Rust,也就是SWIFT調用Rust的代碼,我們main.swift 和 bridge.h 都不變,但是我們用Rust來實現magic函數。
我命名為rust.rs,返回值為47,區分於C++的42,不過SWIFT的print字元串內容The integer from C++ 我並沒有修改,這一點希望不要感到驚訝。
#![crate_type = "dylib"]
#[no_mangle]
pub extern fn magic() -&> i32 {
return 47;
}
隨後,使用rustc命令產生LLVM IR文件:
rustc --emit llvm-bc rust.rs -o rust.bc
接下來,我們利用llvm-link把SWIFT的main.bc 與 rust.bc進行融合,我命名為 swiftrust.bc.
llvm-link main.bc rust.bc -o swiftrust.bc
然後利用llc把產生的SWIFT與Rust混合swiftrust.bc編譯成目標文件swiftrust.o
llc swiftrust.bc -filetype=obj -o swiftrust.o
接下來,我們使用鏈接器,把swiftrust.o編譯成可執行文件swiftrust
這結果如我們所想。雖然技術上可以這麼做。實際上應該不比直接鏈接目標文件容易多少
推薦閱讀:
※libgccjit和LLVM相比,有哪些優點?
※LLVM相比於JVM,有哪些技術優勢?
※過程間和過程內的數據流分析演算法在類似LLVM的IR或HotSpot C1的HIR中,是如何實現的?