RISC-V 目標使用者指南¶
簡介¶
RISC-V 目標為實作 RISC-V 規格受支援變體的處理器提供程式碼產生功能。它位於 llvm/lib/Target/RISCV
目錄中。
規格文件¶
RISC-V 規格已經過多次修訂。LLVM 旨在實作最新批准的標準 RISC-V 基礎 ISA 和 ISA 擴充功能版本,並具有務實的差異。最新的規格可以在以下網址找到:https://github.com/riscv/riscv-isa-manual/releases/。
RISC-V International 官方規格頁面。也值得查看,但往往顯著落後於上面連結的規格。請務必查看 wiki 中尚未整合的擴充功能,並注意,此外,我們有時會支援尚未批准的擴充功能(這些將標記為實驗性 - 見下文)並支援各種供應商特定的擴充功能(見下文)。
目前已知的規格差異如下
無條件允許來自 zifencei、zicsr、zicntr 和 zihpm 的指令,而無需閘控它們是否啟用擴充功能。規格的先前版本將這些指令包含在基礎 ISA 中,我們保留此行為以避免破壞現有程式碼。如果規格的未來版本將這些運算碼重新用於其他擴充功能,我們可能需要重新評估此選擇,因此建議使用者遷移建置系統,使其不依賴於此。
允許在沒有閘控特定擴充功能的情況下命名 CSR。這適用於所有 CSR 名稱,而不僅僅是 zicsr、zicntr 和 zihpm 中的名稱。
使用者指定的 ISA 命名字串(例如
-march
)中不強制執行z*
、s*
和x*
字首擴充功能名稱的順序。
目前我們積極決定不支援多個規格修訂版本。我們承認未來可能需要,但積極延遲圍繞處理此問題的決策制定,直到我們有一個實際硬體已出貨並且規格在之後進行不相容更改的具體範例。
基礎 ISA¶
規格定義了五個基礎指令集:RV32I、RV32E、RV64I、RV64E 和 RV128I。目前,LLVM 完全支援 RV32I 和 RV64I。RV32E 和 RV64E 僅由基於組譯器的工具支援。RV128I 不受支援。
指定目標三元組
表 118 RISC-V 架構¶ 架構
描述
riscv32
XLEN=32 的 RISC-V (即 RV32I 或 RV32E)
riscv64
XLEN=64 的 RISC-V (即 RV64I 或 RV64E)
若要選擇 E 變體 ISA (例如 RV32E 而非 RV32I),請使用基礎架構字串 (例如 riscv32
) 和擴充功能 e
。
設定檔¶
可以使用 -march
而非標準 ISA 命名字串傳遞支援的設定檔名稱。目前支援的設定檔
rvi20u32
rvi20u64
rva20u64
rva20s64
rva22u64
rva22s64
rva23u64
rva23s64
rvb23u64
rvb23s64
請注意,您也可以附加要啟用的其他擴充功能名稱,例如 rva20u64_zicond
將啟用 zicond
擴充功能以及 rva20u64
設定檔中的擴充功能。
除非指定 -menable-experimental-extensions
(或其他工具的等效項),否則無法使用尚未批准的設定檔。這適用於以下設定檔
rvm23u32
擴充功能¶
下表提供了已批准且因此具有最終規格的擴充功能狀態摘要。如果相關,則會提供有關支援的詳細說明。
表 119 依狀態劃分的已批准擴充功能¶ 擴充功能
狀態
A
已支援
B
已支援
C
已支援
D
已支援
F
已支援
E
已支援 (請參閱註解)
H
組譯器支援
M
已支援
Sha
已支援
Shcounterenw
組譯器支援 (請參閱註解)
Shgatpa
組譯器支援 (請參閱註解)
Shtvala
組譯器支援 (請參閱註解)
Shvsatpa
組譯器支援 (請參閱註解)
Shvstvala
組譯器支援 (請參閱註解)
Shvstvecd
組譯器支援 (請參閱註解)
Smaia
已支援
Smcdeleg
已支援
Smcsrind
已支援
Smdbltrp
已支援
Smepmp
已支援
Smmpm
已支援
Smnpm
已支援
Smrnmi
組譯器支援
Smstateen
組譯器支援
Ssaia
已支援
Ssccfg
已支援
Ssccptr
組譯器支援 (請參閱註解)
Sscofpmf
組譯器支援
Sscounterenw
組譯器支援 (請參閱註解)
Sscsrind
已支援
Ssdbltrp
已支援
Ssnpm
已支援
Sspm
已支援
Ssqosid
組譯器支援
Ssstateen
組譯器支援 (請參閱註解)
Ssstrict
組譯器支援 (請參閱註解)
Sstc
組譯器支援
Sstvala
組譯器支援 (請參閱註解)
Sstvecd
組譯器支援 (請參閱註解)
Ssu64xl
組譯器支援 (請參閱註解)
Supm
已支援
Svade
組譯器支援 (請參閱註解)
Svadu
組譯器支援
Svbare
組譯器支援 (請參閱註解)
Svinval
組譯器支援
Svnapot
組譯器支援
Svpbmt
已支援
Svvptc
已支援
V
已支援
Za128rs
已支援 (請參閱註解)
Za64rs
已支援 (請參閱註解)
Zaamo
組譯器支援
Zabha
已支援
Zacas
已支援 (請參閱註解)
Zalrsc
組譯器支援
Zama16b
已支援 (請參閱註解)
Zawrs
組譯器支援
Zba
已支援
Zbb
已支援
Zbc
已支援
Zbkb
已支援 (請參閱註解)
Zbkc
已支援
Zbkx
已支援 (請參閱註解)
Zbs
已支援
Zca
已支援
Zcb
已支援
Zcd
已支援
Zcf
已支援
Zcmop
已支援
Zcmp
已支援
Zcmt
組譯器支援
Zdinx
已支援
Zfa
已支援
Zfbfmin
已支援
Zfh
已支援
Zfhmin
已支援
Zfinx
已支援
Zhinx
已支援
Zhinxmin
已支援
Zic64b
已支援 (請參閱註解)
Zicbom
組譯器支援
Zicbop
已支援
Zicboz
組譯器支援
Ziccamoa
已支援 (請參閱註解)
Ziccif
已支援 (請參閱註解)
Zicclsm
已支援 (請參閱註解)
Ziccrse
已支援 (請參閱註解)
Zicntr
(請參閱註解)
Zicond
已支援
Zicsr
(請參閱註解)
Zifencei
(請參閱註解)
Zihintntl
已支援
Zihintpause
組譯器支援
Zihpm
(請參閱註解)
Zimop
已支援
Zkn
已支援
Zknd
已支援 (請參閱註解)
Zkne
已支援 (請參閱註解)
Zknh
已支援 (請參閱註解)
Zksed
已支援 (請參閱註解)
Zksh
已支援 (請參閱註解)
Zk
已支援
Zkr
已支援
Zks
已支援
Zkt
已支援
Zmmul
已支援
Ztso
已支援
Zvbb
已支援
Zvbc
已支援 (請參閱註解)
Zve32x
(部分) 支援
Zve32f
(部分) 支援
Zve64x
已支援
Zve64f
已支援
Zve64d
已支援
Zvfbfmin
已支援
Zvfbfwma
已支援
Zvfh
已支援
Zvfhmin
已支援
Zvkb
已支援
Zvkg
已支援 (請參閱註解)
Zvkn
已支援 (請參閱註解)
Zvknc
已支援 (請參閱註解)
Zvkned
已支援 (請參閱註解)
Zvkng
已支援 (請參閱註解)
Zvknha
已支援 (請參閱註解)
Zvknhb
已支援 (請參閱註解)
Zvks
已支援 (請參閱註解)
Zvksc
已支援 (請參閱註解)
Zvksed
已支援 (請參閱註解)
Zvksg
已支援 (請參閱註解)
Zvksh
已支援 (請參閱註解)
Zvkt
已支援
Zvl32b
(部分) 支援
Zvl64b
已支援
Zvl128b
已支援
Zvl256b
已支援
Zvl512b
已支援
Zvl1024b
已支援
Zvl2048b
已支援
Zvl4096b
已支援
Zvl8192b
已支援
Zvl16384b
已支援
Zvl32768b
已支援
Zvl65536b
已支援
- 組譯器支援
LLVM 支援組譯器中的相關指令。支援所有組譯器相關工具(例如組譯器、反組譯器、llvm-objdump 等)。編譯器和連結器將接受擴充功能名稱,並且連結的二進位檔案將包含適當的 ELF 旗標和屬性,以反映已命名的擴充功能的使用。
- 已支援
編譯器完全支援。這包括組譯器支援中的所有內容,以及 - 如果相關 - 指令的 C 語言內建函數以及編譯器的模式比對,以識別可以降低為相關指令的慣用模式。
E
實驗性支援 RV32E/RV64E 和 ilp32e/lp64e ABI。為了與 GCC 中 ilp32e 的實作相容,我們不使用對齊的暫存器來傳遞可變參數。此外,我們將長度為 2*XLEN 的類型的堆疊對齊設定為 4 個位元組。
Zbkb
、Zbkx
對這些指令的模式比對支援不完整。
Zknd
、Zkne
、Zknh
、Zksed
、Zksh
不存在模式比對。因此,這些指令只能從組譯器或透過內建函數呼叫使用。
Zvbc
、Zvkg
、Zvkn
、Zvknc
、Zvkned
、Zvkng
、Zvknha
、Zvknhb
、Zvks
、Zvks
、Zvks
、Zvksc
、Zvksed
、Zvksg
、Zvksh
。不存在模式比對。因此,這些指令只能從組譯器或透過內建函數呼叫使用。
Zve32x
、Zve32f
、Zvl32b
LLVM 目前在編譯期間假設最小 VLEN(向量暫存器寬度)為 64 位元,因此
Zve32x
和Zve32f
僅在 VLEN>=64 時受到支援。組譯器支援沒有此限制。
Zicntr
、Zicsr
、Zifencei
、Zihpm
在基礎 I 規格的 2.0 版和 2.1 版之間,進行了向後不相容的更改,以從基礎 ISA 中移除選定的指令和 CSR。這些指令被分組為一組新的擴充功能,但不再是基礎 ISA 所必需的。規格文件中的「Document Version 20190608-Base-Ratified 序言」部分描述了此更改(未提及
zicntr
和zihpm
位元)。LLVM 目前實作基礎規格的 2.1 版。為了保持相容性,接受來自這些擴充功能的指令,而無需在-march
字串中。LLVM 也允許在-march
字串中明確指定擴充功能。
Za128rs
、Za64rs
、Zama16b
、Zic64b
、Ziccamoa
、Ziccif
、Zicclsm
、Ziccrse
、Shcounterenvw
、Shgatpa
、Shtvala
、Shvsatpa
、Shvstvala
、Shvstvecd
、Ssccptr
、Sscounterenw
、Ssstateen
、Ssstrict
、Sstvala
、Sstvecd
、Ssu64xl
、Svade
、Svbare
這些擴充功能定義為 RISC-V 設定檔規格的一部分。它們本身不引入任何新功能,而是描述現有的硬體功能。
Zacas
由於 ABI 相容性,編譯器不會在 RV32 上產生 amocas.d 或在 RV64 上產生 amocas.q。這些只能在組譯器中使用。
原子操作 ABI¶
在撰寫本文時,有三個原子操作對應 (ABI) 為 RISC-V 定義。截至 LLVM 19,LLVM 預設為「A6S」,它與原始「A6」和未來的「A7」ABI 相容。請參閱 psABI 原子操作文件,以取得有關這些對應的更多資訊。
請注意,雖然使用了「A6S」對應,但由於在處理包含此屬性的檔案時,舊版本 binutils 中存在導致崩潰的錯誤,因此目前預設情況下不會發出記錄對應的 ELF 屬性。
實驗性擴充功能¶
LLVM 支援(在不同程度上)許多實驗性擴充功能。所有實驗性擴充功能都以 experimental-
作為字首。明確承諾工具鏈版本之間沒有相容性,並且強烈建議一般使用者在實驗性擴充功能達到批准狀態之前不要使用它們。
實驗性支援的主要目標是透過提供實作的存在證明,並簡化驗證針對大型程式碼庫提出的擴充功能價值的努力,來協助批准過程。實驗性擴充功能預計會轉換為批准狀態,或最終被移除。目前完全根據具體情況做出是否接受實驗性擴充功能的決定;如果您想提出一個,強烈建議參加每週兩次的 RISC-V 同步電話會議。
experimental-zalasr
LLVM 實作 0.0.5 草稿規格。
experimental-zicfilp
、experimental-zicfiss
LLVM 實作 1.0 發行規格。
experimental-zvbc32e
、experimental-zvkgs
LLVM 實作 0.7 發行規格。
experimental-sdext
、experimental-sdtrig
LLVM 實作 1.0-rc4 規格。
experimental-smctr
、experimental-ssctr
LLVM 實作 1.0-rc3 規格。
experimental-svukte
LLVM 實作 0.3 草稿規格。
若要從 clang 使用實驗性擴充功能,您必須將 -menable-experimental-extensions 新增至命令列,並指定您使用的實驗性擴充功能的確切版本。若要將實驗性擴充功能與 LLVM 的內部開發人員工具(例如 llc、llvm-objdump、llvm-mc)搭配使用,您必須在擴充功能名稱前面加上 experimental-。請注意,您不需要使用內部工具指定版本,並且不應在使用 clang 時包含 experimental- 字首。
供應商擴充功能¶
供應商擴充功能是不由 RISC-V International 標準化的擴充功能,而是由硬體供應商定義。供應商擴充功能術語大致與 Volume I: RISC-V Unprivileged ISA 規格第 1.3 節中 非標準 擴充功能的定義平行。特別是,我們期望最終接受 自訂 擴充功能和 不符合標準 的擴充功能。
將根據具體情況考慮是否包含供應商擴充功能。所有提案都應提交給每週兩次的 RISC-V 同步電話會議進行討論。如需可能考慮的因素的一般概念,請參閱 Clang 文件。
我們打算遵循 riscv-non-isa/riscv-toolchain-conventions 中描述的命名慣例。對此命名的例外情況需要有強烈的動機。
目前支援的供應商擴充功能包括
XTHeadBa
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadBa(位址產生)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadBb
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadBb(基本位元操作)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadBs
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadBs(單位元操作)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadCondMov
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadCondMov(條件移動)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadCmo
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadCmo(快取管理操作)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadFMemIdx
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadFMemIdx(浮點索引記憶體操作)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTheadMac
LLVM 實作 T-HEAD of Alibaba 在其中指定的 XTheadMac(乘法累加指令)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadMemIdx
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadMemIdx(索引記憶體操作)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadMemPair
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadMemPair(雙 GPR 記憶體操作)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadSync
LLVM 實作 T-HEAD of Alibaba 在其中指定的 THeadSync(多核心同步指令)供應商定義指令。指令以 th. 為字首,如規格中所述。
XTHeadVdot
LLVM 實作 T-HEAD of Alibaba THeadV 系列自訂指令規格的 1.0.0 版。所有指令都以 th. 為字首,如規格和上面連結的 riscv-toolchain-convention 文件中所述。
XVentanaCondOps
LLVM 實作 Ventana Micro Systems VTx 系列自訂指令規格的 1.0.0 版。所有指令都以 vt. 為字首,如規格和上面連結的 riscv-toolchain-convention 文件中所述。這些指令目前僅適用於 riscv64。
XSfvcp
LLVM 實作 SiFive 向量協處理器介面 (VCIX) 軟體規格的 1.1.0 版。所有指令都以 sf.vc. 為字首,如規格和上面連結的 riscv-toolchain-convention 文件中所述。
XSfvqmaccdod
、XSfvqmaccqoq
LLVM 實作 SiFive Int8 矩陣乘法擴充功能規格的 1.1.0 版。所有指令都以 sf. 為字首,如上面連結的規格中所述。
Xsfvfnrclipxfqf
LLVM 實作 SiFive FP32 到 int8 範圍剪輯指令擴充功能規格的 1.0.0 版。所有指令都以 sf. 為字首,如上面連結的規格中所述。
Xsfvfwmaccqqq
LLVM 實作 SiFive 矩陣乘法累加指令擴充功能規格的 1.0.0 版。所有指令都以 sf. 為字首,如上面連結的規格中所述。
XCVbitmanip
LLVM 實作 OpenHW Group CORE-V 位元操作自訂指令規格的 1.0.0 版。所有指令都以 cv. 為字首,如規格中所述。
XCVelw
LLVM 實作 OpenHW Group CORE-V 事件載入自訂指令規格的 1.0.0 版。所有指令都以 cv. 為字首,如規格中所述。這些指令目前僅適用於 riscv32。
XCVmac
LLVM 實作 OpenHW Group CORE-V 乘法累加 (MAC) 自訂指令規格的 1.0.0 版。所有指令都以 cv.mac 為字首,如規格中所述。這些指令目前僅適用於 riscv32。
XCVmem
LLVM 實作 OpenHW Group CORE-V 後增量載入和儲存自訂指令規格的 1.0.0 版。所有指令都以 cv. 為字首,如規格中所述。這些指令目前僅適用於 riscv32。
XCValu
LLVM 實作 Core-V ALU 自訂指令規格的 1.0.0 版。所有指令都以 cv. 為字首,如規格中所述。這些指令目前僅適用於 riscv32。
XCVsimd
LLVM 實作 OpenHW Group CORE-V SIMD 自訂指令規格的 1.0.0 版。所有指令都以 cv. 為字首,如規格中所述。
XCVbi
LLVM 實作 OpenHW Group CORE-V 立即分支自訂指令規格的 1.0.0 版。所有指令都以 cv. 為字首,如規格中所述。這些指令目前僅適用於 riscv32。
XSiFivecdiscarddlone
LLVM 實作 SiFive sf.cdiscard.d.l1 指令,在其中指定。
XSiFivecflushdlone
LLVM 實作 SiFive sf.cflush.d.l1 指令,在其中指定。
XSfcease
LLVM 實作 SiFive sf.cease 指令,在其中指定。
Xwchc
LLVM 實作 某些青稞核心中存在的自訂壓縮運算碼,由 WCH / 南京沁恆微電子提供。供應商將這些運算碼稱為「XW」。
experimental-Xqccmp
LLVM 實作 Qualcomm 16 位元推入/彈出指令和雙移動擴充功能規格的 0.1 版。所有指令都以 qc. 為字首,如規格中所述。
experimental-Xqcia
LLVM 實作 Qualcomm uC 算術擴充功能規格的 0.4 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqciac
LLVM 實作 Qualcomm uC 載入-儲存位址計算擴充功能規格的 0.3 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqcibm
LLVM 實作 Qualcomm uC 位元操作擴充功能規格的 0.4 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqcicli
LLVM 實作 Qualcomm uC 條件載入立即值擴充功能規格的 0.2 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqcicm
LLVM 實作 Qualcomm uC 條件移動擴充功能規格的 0.2 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqcics
LLVM 實作 Qualcomm uC 條件選擇擴充功能規格的 0.2 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqcicsr
LLVM 實作 Qualcomm uC CSR 擴充功能規格的 0.2 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqciint
LLVM 實作 Qualcomm uC 中斷擴充功能規格的 0.2 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqcilia
LLVM 實作 Qualcomm uC 大型立即值算術擴充功能規格的 0.2 版。所有指令都以 qc. 為字首,如規格中所述。這些指令僅適用於 riscv32。
experimental-Xqcilo
LLVM 實作了 Qualcomm 公司的 Qualcomm uC Large Offset Load Store 擴充規格 0.2 版。所有指令都以前綴詞 qc. 作為開頭,如規格所述。這些指令僅適用於 riscv32 架構。
experimental-Xqcilsm
LLVM 實作了 Qualcomm 公司的 Qualcomm uC Load Store Multiple 擴充規格 0.2 版。所有指令都以前綴詞 qc. 作為開頭,如規格所述。這些指令僅適用於 riscv32 架構。
experimental-Xqcisls
LLVM 實作了 Qualcomm 公司的 Qualcomm uC Scaled Load Store 擴充規格 0.2 版。所有指令都以前綴詞 qc. 作為開頭,如規格所述。這些指令僅適用於 riscv32 架構。
Xmipscmove
LLVM 實作了 MIPS 公司的 p8700 處理器 <https://mips.com/products/hardware/p8700/> 的條件移動指令。
Xmipslsp
LLVM 實作了 MIPS 公司的 p8700 處理器 <https://mips.com/products/hardware/p8700/> 的載入/儲存配對指令。
experimental-XRivosVisni
LLVM 實作了 Rivos Vector Integer Small New Instructions 擴充規格 0.1 版。
experimental-XRivosVizip
LLVM 實作了 Rivos Vector Register Zips 擴充規格 0.1 版。
實驗性 C 內建函數¶
在某些情況下,擴充功能可能不是實驗性的,但該擴充功能的 C 內建函數仍然是實驗性的。若要從 clang 使用此類擴充功能的 C 內建函數,您必須在命令列中加入 -menable-experimental-extensions。這目前適用於以下擴充功能:
沒有擴充功能具有實驗性內建函數。
長型(>32 位元)指令支援¶
RISC-V 是一種可變長度 ISA,但標準目前僅定義 16 位元和 32 位元指令。規格描述了更長的指令編碼,但這些尚未獲得批准。
LLVM 反組譯器 llvm-objdump 確實使用了規格中描述的更長指令編碼來猜測指令長度(最多 176 位元),並將相應地對編碼位元組的反組譯檢視進行分組。
LLVM 針對 RISC-V 的整合組譯器支援兩種不同的 .insn
指令,用於組譯 LLVM 尚不支援的指令
.insn type, args*
接受已知的指令類型和欄位列表。如果您的指令符合現有的指令類型,強烈建議您使用此變體的指令。.insn [ length , ] encoding
接受(可選的)明確長度(以位元組為單位)和指令的原始編碼。當給定明確長度時,此變體可以編碼長達 64 位元的指令。指令的編碼部分必須給出指令的所有位元,沒有任何位元會為使用者填入。當在沒有可選長度的情況下使用時,此變體的指令將使用原始編碼的 LSB 來判斷指令是 16 位元還是 32 位元。LLVM 不會推斷指令可能超過 32 位元 - 在這種情況下,使用者必須明確給出長度。
強烈建議使用 .insn
指令來組譯不受支援的指令,而不是 .word
或 .hword
,因為它會產生正確的映射符號,將該字組標記為指令,而不是資料。
全域指標 (GP) 放鬆和小型資料限制¶
一些 RISC-V psABI 變體保留了 gp
(x3
)作為「全域指標」使用,以提高產生資料位址的效率。
若要使用此功能,您需要執行以下所有操作:
使用
medlow
(又名small
)程式碼模型;不要將
gp
暫存器用於任何其他用途(某些平台將其用於陰影堆疊,另一些平台則將其用作臨時暫存器 – 如Tag_RISCV_x3_reg_usage
建置屬性所指示);使用 Clang 的
-mrelax
選項編譯您的物件,以在可重定位物件上啟用放鬆註解(這是預設值,但-mno-relax
會停用這些放鬆註解);為與位置相關的靜態可執行檔編譯(不是共享程式庫,且使用
-fno-PIC
/-fno-pic
/-fno-pie
);以及使用 LLD 的
--relax-gp
選項。
LLD 將放鬆(重寫)任何程式碼序列,這些序列會將位址實體化在 __global_pointer$
的 2048 位元組範圍內(如果使用了 __global_pointer$
且它尚不存在,則將會定義 __global_pointer$
),而是使用 gp
和正確的(帶符號)12 位元立即值來產生位址。與實體化完整的 32 位元位址值相比,這通常至少可以節省一條指令。
一個進程中只能有一個 gp
值(因為在呼叫共享程式庫中的函數時,gp
不會變更),因此該符號僅在可執行檔中定義,並且此放鬆僅針對可執行檔完成,而不針對共享程式庫。連結器期望可執行檔啟動程式碼在執行任何使用者程式碼之前,將 __global_pointer$
的值(來自可執行檔)放入 gp
中。
可以說,這種定址模式最有效率的用途是用於較小的全域變數,因為較大的全域變數在被存取時可能需要更多的載入或儲存,因此實體化高位元的成本可以分攤。
因此,編譯器可以將較小的全域變數放入名稱以 .sdata
或 .sbss
開頭的區段中(與名稱以 .data
和 .bss
開頭的區段相符)。LLD 知道要將 global_pointer$
符號定義在靠近這些區段的位置,並將這些區段佈局在與 .data
區段相鄰的位置。
Clang 的 -msmall-data-limit=
選項控制將全域變數視為小的閾值大小(以位元組為單位)。-msmall-data-limit=0
會停用以 .sdata
和 .sbss
開頭的區段的使用。-msmall-data-limit=
選項不會移動具有明確資料區段的全域變數,並且如果您使用 -fdata-sections
,則會將全域變數保留在單獨的區段中。
小型資料限制閾值也用於將小型常數分隔到名稱以 .srodata
開頭的區段中。LLD 不會將這些區段與 .sdata
和 .sbss
區段放在一起,因為 .srodata
區段是唯讀的,而其他兩個是可寫入的。相反,.srodata
區段會放置在與 .rodata
相鄰的位置。
資料表明,這些選項可以在各種基準測試中產生顯著的改進。