我是 RISC-V 的新手。
我无法理解何时编写 PC(程序计数器)相对指令以及何时编写 PC 绝对指令。
例如,带有lui后跟jalr指令的指令被认为是PC-absolute,而带有 auipc后跟jalr指令的指令被认为是PC-relative。
据我了解,所有指令都将由 PC 执行,因此执行此类 PC 绝对指令似乎是隐藏的(即在 PC 不知情的情况下)。
对我来说,那些 PC 绝对指令不会被执行。
有人可以提供一些基本的例子来帮助我理解这一点吗?
小智 9
我认为您遇到的问题是“PC-absolute”的概念,这实际上不是一回事。您的选择是“PC 相对”和“绝对”。RISC-V 定义了两个寻址指令,可以有效地实现这些模式:
lui
(Load Upper Immediate):设置rd
为 32 位值,低 12 位为 0,高 20 位来自 U 型立即数。auipc
(Add Upper Immediate to Program Counter):这将设置rd
为当前 PC 和一个 32 位值的总和,其中低 12 位为 0,高 20 位来自 U 型立即数。这些指令本质上是相同的:它们都采用 U 类型的立即数(即 32 位数量的高 20 位),将其添加到某物上,并在 中产生结果rd
。区别在于,lui
将立即添加到0
,而auipc
将立即添加到 PC。有时更容易将两种寻址模式视为“PC 相对”和“0 相对”,因为这使区别更加明确。
虽然auipc
and 和lui
and 都被设计为双指令对中的第一条指令,但第二条指令并不是特别相关。二者auipc
并lui
填写一个32位地址的高20位,留下他们与配对的指令来填写低12位。I 和 S 格式的指令被设计成在这里很好地配对,并且基本 ISA 中的每条指令都有一个 I 或 S 变体,对于这种格式是有意义的。
作为一个具体的例子,下面的 C 代码执行了一个非常简单的
int global;
int func(void) { return global; }
Run Code Online (Sandbox Code Playgroud)
例如,假设 global 位于 0x20000004,func 中第一条指令的 PC 为 0x10000008。
当用-mcmodel=medlow
(0-相对寻址模式)编译时,你会得到
func:
lui a0, 0x20000
lw a0, 0x008(a0)
Run Code Online (Sandbox Code Playgroud)
如您所见,全局(0x2000004)的完整绝对地址填充到指令对中。另一方面,当用-mcmodel=medany
(相对于 PC 的寻址模式)编译时,你会得到
func:
auipc a0, 0x10000
lw a0, 0x004(a0)
Run Code Online (Sandbox Code Playgroud)
这一次,auipc
指令对中只出现了和目标符号的 PC 之间的偏移量。发生这种情况是因为 PC 明确(通过使用auipc
指令)包含在寻址计算中。在这种情况下,auipc
被设置a0
到0x2000004
:计算进行时a0 = PC + (imm20 << 12)
,在这里,我们有0x10000004
PC和0x10000
进行imm20
。
这些与 PC 相关的寻址序列还允许一定程度的位置独立性:如果您非常小心地限制您正在执行的操作,则可以生成链接的二进制文件,当加载位置与链接位置不同时,它们仍然可以工作。在实践中,这对于 POSIX 风格系统中完全与位置无关的寻址是不够的(这就是为什么我们也有一个-fPIC
争论,就像其他人一样),但如果你在一个严格约束的嵌入式系统中,你可能能够摆脱它。
对于最后的皱纹,就像 RISC-V ISA 中的几乎所有其他东西一样,由使用的立即数auipc
和lui
符号扩展到 XLEN。在 32 位系统上,这些寻址模式可以在系统中生成任何地址,但对于 64 位系统则不是这种情况。这就是我们称这些寻址模式为“medany”和“medlow”的原因:“med”代表“medium”,这意味着所有全局符号必须适合的 4GiB 窗口。“低”部分表示此窗口以绝对地址 0 为中心,而“任何”部分表示此窗口以链接到的任何 PC 为中心。