適用於原生映像檔的 LLVM 後端
原生映像檔提供一個替代後端,其使用 LLVM 中繼表示法和 LLVM 編譯器來產生原生可執行檔。此 LLVM 後端讓使用者能夠鎖定 GraalVM 原生映像檔不直接支援的替代架構。然而,此方法會引入一些效能成本。
安裝與使用 #
LLVM 後端預設未包含在原生映像檔中。若要使用它,您需要使用以下 mx 命令從原始碼建置 GraalVM
mx --dynamicimports /substratevm build
export JAVA_HOME=$(mx --dynamicimports /substratevm graalvm-home)
若要啟用 LLVM 後端,請將 -H:CompilerBackend=llvm
選項傳遞至 native-image
命令。
程式碼產生選項 #
-H:+BitcodeOptimizations
:在 LLVM 位元碼層級啟用積極最佳化。這是實驗性的,可能會導致錯誤。
偵錯選項 #
-H:TempDirectory=
:指定儲存原生映像檔產生的檔案位置。LLVM 檔案會儲存在此目錄中的SVM-<timestamp>/llvm
下。-H:LLVMMaxFunctionsPerBatch=
:指定編譯批次的最大大小*。設為 1 會分別編譯每個函式,設為 0 會將所有內容編譯為單一批次。-H:DumpLLVMStackMap=
:指定要傾印偵錯資訊的檔案,包括已編譯函式與對應位元碼檔案名稱之間的對應。
關於批次:LLVM 編譯分四個階段進行
- 為每個函式建立 LLVM 位元碼檔案 (命名為
f0.bc
、f1.bc
等)。 - 位元碼檔案會連結成批次 (命名為
b0.bc
、b1.bc
等)。指定-H:LLVMMaxFunctionsPerBatch=1
時,會略過此階段。 - 批次會進行最佳化 (為
b0o.bc
、b1o.bc
等),然後編譯 (為b0.o
、b1.o
等)。 - 已編譯的批次會連結成單一物件檔 (
llvm.o
),然後連結至最終可執行檔。
如何使用 LLVM 後端將目標架構新增至 GraalVM #
LLVM 後端的一個有趣的使用案例是在不必為原生映像檔實作全新的後端的情況下鎖定新的架構。以下是目前達成此目的的必要步驟。
目標特定 LLVM 設定 #
在某些情況下,GraalVM 程式碼必須比 LLVM 的目標獨立特性更深入。這些最值得注意的是內嵌組件片段,用來實作直接暫存器存取和直接暫存器跳躍 (用於跳板),以及有關 LLVM 發出的程式碼堆疊框架結構的精確度。這表示每個新目標需要設定少於十幾個簡單的值。
LLVM Statepoint 支援 #
雖然 LLVM 後端大多使用常見、良好支援的 LLVM 功能,但垃圾收集支援表示需要使用 statepoint 內建函式,這是 LLVM 的實驗性功能。這表示支援新的架構需要實作 LLVM 中所請求目標的 statepoint。由於大部分 statepoint 邏輯在位元碼層級處理 (也就是在與目標無關的階段),這主要是發出正確類型的呼叫來降低 statepoint 內建函式的問題。
物件檔支援 #
使用 Graal 編譯器的 LLVM 後端建立的程式資料區段是獨立於程式碼發出的,程式碼由 LLVM 處理。這表示 Graal 編譯器需要瞭解目標架構的物件檔案重新配置,才能夠將 LLVM 編譯的程式碼與 GraalVM 產生的資料區段連結。
(如需範例,請參閱 ELFMachine$ELFAArch64Relocations
)