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 序言”中部分描述(未提及 zicntrzihpm 位)。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 的内部开发人员工具(例如 llcllvm-objdumpllvm-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 相邻的位置。

数据表明,这些选项可以在一系列基准测试中产生显着改进。