我是RISC-V的新手。
我在理解何时编写PC(Program Counter)相对指令以及何时编写PC绝对指令方面遇到了困难。
例如,使用lui指令后跟jalr指令被认为是PC-absolute,而使用auipc指令后跟jalr指令被认为是PC-relative。
据我所知,所有指令都将由PC执行,因此进行此类PC绝对指令似乎是隐藏的(即没有PC的知识)。
对我来说,那些PC-absolute指令将不会被执行。
有人能提供一些基本示例来帮助我理解吗?
我是RISC-V的新手。
我在理解何时编写PC(Program Counter)相对指令以及何时编写PC绝对指令方面遇到了困难。
例如,使用lui指令后跟jalr指令被认为是PC-absolute,而使用auipc指令后跟jalr指令被认为是PC-relative。
据我所知,所有指令都将由PC执行,因此进行此类PC绝对指令似乎是隐藏的(即没有PC的知识)。
对我来说,那些PC-absolute指令将不会被执行。
有人能提供一些基本示例来帮助我理解吗?
lui
(Load Upper Immediate):将rd
设置为32位值,其中低12位为0,高20位来自U类型立即数。auipc
(Add Upper Immediate to Program Counter):将rd
设置为当前PC与32位值之和,低12位为0,高20位来自U类型立即数。rd
中。区别在于lui
将该立即数添加到0
,而auipc
将该立即数添加到PC。有时更容易将这两种寻址模式视为“PC-relative”和“0-relative”,因为这使得区别更加明显。
虽然auipc
和lui
都被设计为两个指令对中的第一条指令,但第二条指令并不特别相关。 auipc
和lui
都填充了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。在本例中,auipc
将a0
设置为0x2000004
:所执行的计算是a0 = PC + (imm20 << 12)
,这里的PC为0x10000004
,而imm20
为0x10000
。
这些基于PC的寻址序列还允许适度的位置独立性:如果你非常小心地限制你要做的事情,就可以生成链接二进制文件,在加载到与链接位置不同的偏移量时仍然能够正常工作。但实践中,这对于POSIX样式系统的完全位置无关寻址来说不足(这也是我们像其他人一样有一个-fPIC
参数的原因),但如果你位于严格受限的嵌入式系统中,则可能能够得逞。
最后,就像RISC-V ISA中的几乎所有其他内容一样,auipc
和lui
使用的即时数被扩展到XLEN位。在32位系统上,这些寻址模式可以生成系统中的任何地址,但对于64位系统而言情况并非如此。这就是为什么我们称这些寻址模式为“medany”和“medlow”的原因:“med”代表“medium”,意味着4GiB窗口,其中必须适配所有全局符号。 “low”表示该窗口以绝对地址0为中心,而“any”则表示该窗口以链接到的任何PC为中心。
global
在0x20000008处,func
在0x10000004处有效,但周围的文本说“global在0x20000004处,func第一条指令的PC是0x10000008”,以及“全局变量global的绝对地址(0x2000004)”。 - ottomeisterlui
的立即数需要符号扩展?它不只是将所有内容左移12位吗?我指的是32位riscv。 - Plasty Grove相对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 加载到寄存器中 - 指令本身的地址并不重要。
这被称为绝对寻址。