RVI : AUIPC
Content
上篇研究了A64指令中的ADR/ADRP,当时就在想,蓬勃发展的RISC-V中有没有相应的PC-relative addressing的指令?!还真有,它就是AUIPC(Add Upper Immediate to PC),它的功能是否如ADR/ADRP一样呢?下面来看它的encoding和usage。
Encoding of AUIPC
RISC-V得益于后发优势,它的指令集编码是非常规整的。它定义了指令编码的基本语法和规则,以此生成一套基本指令集(如下图所示六种type的基本指令集,均为32bit),开发者还可以在规则范围内自己扩展自己的指令,包括指令的长度从16bit到更多,非常的友好。下面这张图应该也会在解读其他指令的时候反复用到。
回到AUIPC,它是U-type类型的指令。整体组成和ADR/ADRP类似,但,是不是规整很多!!!把ADR/ADRP的编码图片放上来做个对比。
至少不需在重新组合imm了吧。
AUIPC的bit[6:0]也就是opcode为0b0010111,其实所有base instruction的bit[1:0]都为0b11,因为非0b11是给16位指令留的,而bit[4:2]不能为0b111,因为这是给更长的指令预留的编码格式。跑题了,继续AUIPC。
1
2
3
AUIPC (add upper immediate to pc) is used to build pc-relative addresses and uses the U-type format.
AUIPC forms a 32-bit offset from the U-immediate, filling in the lowest 12 bits with zeros, adds this
offset to the address of the AUIPC instruction, then places the result in register rd.
同样rd是目标寄存器的index,imm是一个32位立即数的高20位(所以寻址范围在[$ -2^{31} - 2^{11},2^{31} - 2^{11}-1 $])。指令用法为:
1
auipc Xd, imm
第二个操作数为imm!?这貌似和ADR/ADRP不一样啊。来看伪代码:
1
X[rd] = pc + sign_extended(immediate[31:12] << 12)
看起来和ADRP有点像,但人家ADRP第二个操作数是label啊,这里imm难道要开发者自己去算offset?
Usage
关于上诉疑问,查阅spec,发现了下面这张表:
原来RISC-V还有pseudo instruction,开发的时候使用pseudo instruction,编译器会把它变成真正的指令,高级!!!
我们来看其中一个:
1
2
3
4
5
lla rd, symbol
------>
auipc rd, delta[31 : 12] + delta[11]
addi rd, rd, delta[11:0]
// Load local address, where delta = symbol − pc
这下合理了,编译器把lla rd, symbol中symobl的和PC的相对地址求出来变成delta,然后用了两条指令auipc和addi生成两条编码,X[rd]中的结果就是实际所在位置。这个不就是ADRP吗。
找一个例子来看看:
1
2
3
4
5
000000000002f000 g .rela.dyn 0000000000000000 __rel_dyn_start
lla t0, __rel_dyn_start
4a: 0002f297 auipc t0,0x2f
4e: fb628293 addi t0,t0,-74 # 2f000 <__rel_dyn_start>
上诉反汇编片段中,__rel_dyn_start的地址为0x2f000,auipc指令地址为0x4a,两者地址4K向下对齐后差值为0x2f000,所以auipc中imm值为0x2f。整个编码为U-type格式:
1
2
3
1. opcode [6:0] : 0b0010111 //auipc的操作码编码
2. rd [11:7] : 0b00101 //X5
3. imm [31:12]: 0b00101111 //0x2f
第二条指令用addi把低12位的offset补回来,这里正好是-74(-0x4a)。
如前所诉,RISC-V里auipc和ARM里的ADRP很像,都是只算4K对齐地址差值,需要另外一个指令补齐低位。ADRP的imm为21位,寻址空间会更大一些。RISC-V没有类似ADR的指令在小范围内寻址,所有的PC-relative addressing都要两条指令完成。
Example in OpenSource
- opensbi https://github.com/riscv-software-src/opensbi/blob/master/firmware/fw_base.S#L56
- optee https://github.com/OP-TEE/optee_os/blob/4.0.0/ldelf/start_rv64.S#L23
由于auipc隐藏在la/lla等pseudo instruction之后,了解他们之间的关系对阅读RISC-V的boot code会有一定的帮助。