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 中的名称。
z*
、s*
和x*
前缀扩展名称的顺序在用户指定的 ISA 命名字符串(例如-march
)中未强制执行。
我们目前积极决定不支持多个规范修订版本。我们承认未来可能需要这样做,但积极推迟围绕处理此问题的决策制定,直到我们有真实硬件已发货的具体示例以及之后对规范进行的不兼容更改。
基础 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
将除了 rva20u64
配置文件中的扩展外,还启用 zicond
扩展。
除非指定了 -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¶
在撰写本文时,为 RISC-V 定义了三种原子操作映射 (ABI)。截至 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 未标准化的扩展,而是由硬件供应商定义的。术语供应商扩展大致与 RISC-V 非特权 ISA 规范第 I 卷第 1.3 节中 非标准 扩展的定义相对应。特别是,我们期望最终接受 自定义 扩展和 不符合标准 的扩展。
是否包含供应商扩展将根据具体情况考虑。所有提案都应提交给每两周一次的 RISC-V 同步电话会议进行讨论。有关可能考虑的因素的一般概念,请参阅 Clang 文档。
我们的目的是遵循 riscv-non-isa/riscv-toolchain-conventions 中描述的命名约定。对此命名的例外情况需要有充分的理由。
当前支持的供应商扩展是
XTHeadBa
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadBa(地址生成)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadBb
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadBb(基本位操作)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadBs
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadBs(单比特操作)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadCondMov
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadCondMov(条件移动)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadCmo
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadCmo(缓存管理操作)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadFMemIdx
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadFMemIdx(浮点索引内存操作)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTheadMac
LLVM 实现了阿里巴巴平头哥半导体 中指定的 XTheadMac(乘法累加指令)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadMemIdx
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadMemIdx(索引内存操作)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadMemPair
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadMemPair(双 GPR 内存操作)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadSync
LLVM 实现了阿里巴巴平头哥半导体 中指定的 THeadSync(多核同步指令)供应商定义的指令。指令以 th. 为前缀,如规范中所述。
XTHeadVdot
LLVM 实现了阿里巴巴平头哥半导体 发布的 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 发布的 SiFive 向量协处理器接口 (VCIX) 软件规范的 1.1.0 版本。所有指令都以 sf.vc. 为前缀,如规范和上面链接的 riscv-toolchain-convention 文档中所述。
XSfvqmaccdod
,XSfvqmaccqoq
LLVM 实现了 SiFive 发布的 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 发布的 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 中指定的 SiFive sf.cdiscard.d.l1 指令。
XSiFivecflushdlone
LLVM 实现了 SiFive 中指定的 SiFive sf.cflush.d.l1 指令。
XSfcease
LLVM 实现了 SiFive 中指定的 SiFive sf.cease 指令。
Xwchc
LLVM 实现了 WCH / 南京沁恒微电子在某些青稞内核中提供的自定义压缩操作码。供应商将这些操作码称为 “XW”。
experimental-Xqccmp
LLVM 实现了 Qualcomm 发布的 16 位 Push/Pop 指令和双重移动扩展规范的 0.1 版本。所有指令都以 qc. 为前缀,如规范中所述。
experimental-Xqcia
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 算术扩展规范的 0.4 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqciac
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 加载-存储地址计算扩展规范的 0.3 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcibm
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 位操作扩展规范的 0.4 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcicli
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 条件加载立即数扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcicm
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 条件移动扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcics
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 条件选择扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcicsr
LLVM 实现了 Qualcomm 发布的 Qualcomm uC CSR 扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqciint
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 中断扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcilia
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 大型立即数算术扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcilo
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 大型偏移加载存储扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcilsm
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 加载存储多重扩展规范的 0.2 版本。所有指令都以 qc. 为前缀,如规范中所述。这些指令仅适用于 riscv32。
experimental-Xqcisls
LLVM 实现了 Qualcomm 发布的 Qualcomm uC 缩放加载存储扩展规范的 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 向量整数小型新指令扩展规范的 0.1 版本。
experimental-XRivosVizip
LLVM 实现了 Rivos 向量寄存器压缩扩展规范的 0.1 版本。
实验性 C 内联函数¶
在某些情况下,扩展本身是非实验性的,但该扩展的 C 内联函数仍然是实验性的。要从 clang 中使用此类扩展的 C 内联函数,您必须在命令行中添加 -menable-experimental-extensions。 这目前适用于以下扩展:
没有扩展具有实验性内联函数。
长(>32 位)指令支持¶
RISC-V 是一种可变长度 ISA,但标准目前仅定义了 16 位和 32 位指令。 该规范描述了更长的指令编码,但这些编码尚未批准。
LLVM 反汇编器 llvm-objdump 确实使用了规范中描述的更长指令编码来猜测指令长度(最多 176 位),并将相应地对编码字节的反汇编视图进行分组。
用于 RISC-V 的 LLVM 集成汇编器支持两种不同的 .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 字节范围内的地址(如果使用且尚不存在,则将定义该地址),而是使用 gp
和正确的(有符号)12 位立即数生成地址。 与具体化完整的 32 位地址值相比,这通常至少节省一条指令。
一个进程中只能有一个 gp
值(因为当调用共享库中的函数时,gp
不会更改),因此该符号仅被定义,并且此放松仅针对可执行文件执行,而不针对共享库执行。 链接器期望可执行文件的启动代码在运行任何用户代码之前,将 __global_pointer$
(来自可执行文件)的值放入 gp
中。
可以说,此寻址模式最有效的用途是用于较小的全局变量,因为较大的全局变量在被访问时可能需要更多的加载或存储,因此可以分摊具体化高位的成本。
因此,编译器可以将较小的全局变量放入名称以 .sdata
或 .sbss
开头的 section 中(与名称以 .data
和 .bss
开头的 section 匹配)。 LLD 知道将 global_pointer$
符号定义在这些 section 附近,并将这些 section 布局在与 .data
section 相邻的位置。
Clang 的 -msmall-data-limit=
选项控制全局变量被视为小的阈值大小(以字节为单位)。 -msmall-data-limit=0
禁用以 .sdata
和 .sbss
开头的 section 的使用。 -msmall-data-limit=
选项不会移动具有显式数据 section 的全局变量,如果您使用 -fdata-sections
,则会将全局变量保留在单独的 section 中。
小数据限制阈值也用于将小的常量分隔到名称以 .srodata
开头的 section 中。 LLD 不会将这些 section 与 .sdata
和 .sbss
section 放在一起,因为 .srodata
section 是只读的,而其他两个是可写的。 相反,.srodata
section 放置在与 .rodata
相邻的位置。
数据表明,这些选项可以在一系列基准测试中产生显着改进。