RISC-V: PC绝对地址 vs PC相对地址

8

我是RISC-V的新手。

我在理解何时编写PC(Program Counter)相对指令以及何时编写PC绝对指令方面遇到了困难。

例如,使用lui指令后跟jalr指令被认为是PC-absolute,而使用auipc指令后跟jalr指令被认为是PC-relative

据我所知,所有指令都将由PC执行,因此进行此类PC绝对指令似乎是隐藏的(即没有PC的知识)。

对我来说,那些PC-absolute指令将不会被执行。

有人能提供一些基本示例来帮助我理解吗?


2
通常不会说“PC-absolute”,只说“absolute”(不相对于任何东西)。 - Peter Cordes
2个回答

31
我认为你遇到的问题是“PC-absolute”这个概念实际上不存在。你的选项是“PC relative”和“absolute”。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-relative”和“0-relative”,因为这使得区别更加明显。 虽然auipclui都被设计为两个指令对中的第一条指令,但第二条指令并不特别相关。 auipclui都填充了32位地址的高20位,使它们配对的指令填充了低12位。格式为I和S的指令在这里很好地配对,而基本ISA中的每个指令都有一个I或S变体,适合使用这种格式。 以下C代码是一个具体的例子,执行非常简单:
int global;
int func(void) { return global; }
作为例子,假设全局变量global的地址是0x20000004,func函数中第一条指令的程序计数器(PC)为0x10000008。

当使用-mcmodel=medlow进行编译(一种0相对地址模式),你会得到:

func:
    lui a0, 0x20000
    lw  a0, 0x004(a0)

如您所见,全局变量的完整绝对地址(0x2000004)填充到指令对中。另一方面,在使用-mcmodel=medany时(采用PC相对寻址模式),您将得到

func:
    auipc a0, 0x10000
    lw    a0, 0x004(a0)

这一次,指令对之间仅出现了auipc的PC和目标符号之间的偏移量。这是因为在寻址计算中,通过auipc指令明确地包含了PC。在本例中,auipca0设置为0x2000004:所执行的计算是a0 = PC + (imm20 << 12),这里的PC为0x10000004,而imm200x10000

这些基于PC的寻址序列还允许适度的位置独立性:如果你非常小心地限制你要做的事情,就可以生成链接二进制文件,在加载到与链接位置不同的偏移量时仍然能够正常工作。但实践中,这对于POSIX样式系统的完全位置无关寻址来说不足(这也是我们像其他人一样有一个-fPIC参数的原因),但如果你位于严格受限的嵌入式系统中,则可能能够得逞。

最后,就像RISC-V ISA中的几乎所有其他内容一样,auipclui使用的即时数被扩展到XLEN位。在32位系统上,这些寻址模式可以生成系统中的任何地址,但对于64位系统而言情况并非如此。这就是为什么我们称这些寻址模式为“medany”和“medlow”的原因:“med”代表“medium”,意味着4GiB窗口,其中必须适配所有全局符号。 “low”表示该窗口以绝对地址0为中心,而“any”则表示该窗口以链接到的任何PC为中心。


4
我认为这里有错别字。例子中global在0x20000008处,func在0x10000004处有效,但周围的文本说“global在0x20000004处,func第一条指令的PC是0x10000008”,以及“全局变量global的绝对地址(0x2000004)”。 - ottomeister
抱歉,我知道这是一个老问题。为什么lui的立即数需要符号扩展?它不只是将所有内容左移12位吗?我指的是32位riscv。 - Plasty Grove

2

相对PC
绝对

如果地址是相对于代码本身的地址计算,您将某些指令(或代码)称为“相对PC”。

当地址不是相对于指令本身的地址计算时,您将指令称为“绝对”。

不幸的是,我不了解RISC V CPU,但以下示例适用于(旧的)68000 CPU,以说明其含义:

x:
    lea.l (PC+y-x-2), a0
    lea.l (y).l, a0
  ...
y:

这两条指令都会将地址 y 加载到寄存器 a0 中。

然而,它们之间有一个区别:

假设代码位于地址 0x1000,地址 y 位于地址 0x2000。

现在我们将代码移动到地址 0x1200 并在那里执行代码。会发生什么?

第一条指令将地址 0x2200 加载到寄存器中:

这个地址是相对于指令地址计算的:它被计算为 (指令地址)+0x1000。由于指令现在位于地址 0x1000 而不是 0x1200,因此要写入寄存器的值将是 0x2200 而不是 0x2000。

这被称为(PC)相对寻址

第二条指令将地址 0x2000 加载到寄存器中。它总是将值 0x2000 加载到寄存器中 - 指令本身的地址并不重要。

这被称为绝对寻址


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接