編譯為 LLVM 位元碼

GraalVM 可以執行 C/C++、Rust 和其他可以編譯為 LLVM 位元碼的語言。第一步,您必須使用一些 LLVM 編譯器前端將程式編譯為 LLVM 位元碼,例如,C 和 C++ 使用 clang,Rust 程式語言使用 rust 等。

檔案格式 #

雖然 GraalVM LLVM 執行階段可以執行純位元碼檔案,但首選格式是具有嵌入式位元碼原生可執行檔。可執行檔案格式在 Linux 和 macOS 上有所不同。Linux 預設使用 ELF 檔案。位元碼儲存在名為 .llvmbc 的區段中。macOS 平台使用 Mach-O 檔案。位元碼位於 __LLVM 段的 __bundle 區段中。

使用具有嵌入式位元碼的原生可執行檔比使用純位元碼檔案有兩個優勢。首先,原生專案的建置系統(例如 Makefile)預期結果是可執行檔。嵌入位元碼而不是變更輸出格式可提高與現有專案的相容性。其次,可執行檔允許指定程式庫相依性,這在 LLVM 位元碼中是不可能的。GraalVM LLVM 執行階段會利用此資訊來尋找和載入相依性。

用於編譯 C/C++ 的 LLVM 工具鏈 #

為了簡化將 C/C++ 編譯為具有嵌入式位元碼的可執行檔,LLVM 執行階段附帶一個預先建置的 LLVM 工具鏈。該工具鏈包含諸如 C 的 clang 或 C++ 的 clang++ 等編譯器,以及建置原生專案所需的其他工具,例如連結器 (ld) 或用於建立靜態程式庫的歸檔器 (ar)。

  1. 使用 lli--print-toolchain-path 引數來取得工具鏈的位置
     ./path/to/bin/lli --print-toolchain-path
    
  2. 設定 LLVM_TOOLCHAIN 環境變數
     export LLVM_TOOLCHAIN=$(./path/to/bin/lli --print-toolchain-path)
    
  3. 然後檢視工具鏈路徑的內容,以取得可用工具的清單
     ls $LLVM_TOOLCHAIN
    

像您對原生編譯所做的那樣使用這些工具。例如,將以下 C 程式碼儲存到名為 hello.c 的檔案中

#include <stdio.h>

int main() {
    printf("Hello from GraalVM!\n");
    return 0;
}

然後您可以將 hello.c 編譯為具有嵌入式 LLVM 位元碼的可執行檔,如下所示

$LLVM_TOOLCHAIN/clang hello.c -o hello

可以使用 lli 在 GraalVM 上執行產生的可執行檔 hello

$JAVA_HOME/bin/lli hello

外部程式庫相依性 #

如果位元碼檔案依賴外部程式庫,GraalVM 將會自動從二進位標頭中挑選相依性。例如

#include <unistd.h>
#include <ncurses.h>

int main() {
    initscr();
    printw("Hello, Curses!");
    refresh();
    sleep(1);
    endwin();
    return 0;
}

然後可以使用以下方式編譯和執行此 hello-curses.c 檔案

$LLVM_TOOLCHAIN/clang hello-curses.c -lncurses -o hello-curses
lli hello-curses

執行 C++ #

若要執行 C++ 程式碼,GraalVM LLVM 執行階段需要來自 LLVM 專案的 libc++ 標準程式庫。GraalVM 隨附的 LLVM 工具鏈會自動連結到 libc++。例如,將以下程式碼儲存為 hello-c++.cpp 檔案

#include <iostream>

int main() {
    std::cout << "Hello, C++ World!" << std::endl;
}

使用 GraalVM 隨附的 clang++ 編譯並執行

$LLVM_TOOLCHAIN/clang++ hello-c++.cpp -o hello-c++
lli hello-c++
Hello, C++ World!

執行 Rust #

與 GraalVM 捆綁的 LLVM 工具鏈不包含 Rust 編譯器。若要安裝 Rust,請在命令提示字元中執行以下命令,然後按照螢幕上的指示操作

curl https://sh.rustup.rs -sSf | sh

將以下 Rust 程式碼範例儲存到 hello-rust.rs 檔案中

fn main() {
    println!("Hello Rust!");
}

然後可以使用 --emit=llvm-bc 旗標將其編譯為位元碼

rustc --emit=llvm-bc hello-rust.rs

若要執行 Rust 程式,我們必須告知 GraalVM 在哪裡尋找 Rust 標準程式庫

lli --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
Hello Rust!

由於 Rust 編譯器未使用 GraalVM 隨附的 LLVM 工具鏈,因此根據本機 Rust 安裝,可能會發生類似於以下錯誤的錯誤

Mismatching target triple (expected x86_64-unknown-linux-gnu, got x86_64-pc-linux-gnu)
Mismatching target triple (expected x86_64-apple-macosx10.11.0, got x86_64-apple-darwin)

這表示 Rust 編譯器使用的目標三元組與 GraalVM 隨附的 LLVM 工具鏈不同。在這種情況下,差異僅在於 Linux 發行版本或 MacOS 版本之間的不同命名慣例,沒有真正的差異。在這種情況下,可以安全地忽略錯誤

lli --experimental-options --llvm.verifyBitcode=false --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc

只有在手動驗證目標三元組確實相容(即,架構、作業系統和 C 程式庫都符合)之後,才應該使用此選項。例如,x86_64-unknown-linux-muslx86_64-unknown-linux-gnu 確實不同,該位元碼是為不同的 C 程式庫編譯的。--llvm.verifyBitcode=false 選項會停用所有檢查,然後 GraalVM 將嘗試執行該位元碼,無論如何,這可能會以意想不到的方式隨機失敗。

與我們聯絡