RISC-V 加载指令中的地址偏移是否硬编码?
Address offset in RISC-V load instructions hardcoded or not?
出于教育目的,我使用 https://godbolt.org/z/7F-Lhm 进行翻译
// C++ code
char i = 3;
char A[] = {0,1,2,3,4,5};
int myfunction() {
return A[i];
}
进入
# RISC-V instructions
myfunction(): # @myfunction()
lui a0, %hi(i)
lbu a0, %lo(i)(a0)
lui a1, %hi(A)
addi a1, a1, %lo(A)
add a0, a0, a1
lbu a0, 0(a0)
ret
i:
.byte 3 # 0x3
A:
.ascii "[=11=]0[=11=]1[=11=]2[=11=]3[=11=]4[=11=]5"
但为什么 A[i]
加载了 add a0, a0, a1
、lbu a0, 0(a0)
而不仅仅是 lbu a0, a0(a1)
?
如果对于 lbu dest, offset(baseAdress)
只有 dest
和 baseAdresse
允许作为寄存器地址,那么 offset
是指令字本身中的硬编码数字.但是在上面的相同代码中,我看到 lbu a0, %lo(i)(a0)
所以 offset
显然也可以是 "somewhat variable"?
也许我不明白这一点的原因是因为我真的不明白为什么这个 $hi $lo
首先是必要的。为什么我们要 lui a0, %hi(i)
、lbu a0, %lo(i)(a0)
而不是 lbu a0, 0(i)
?
and not just with lbu a0, a0(a1)?
RISC V(和MIPS)没有基址寄存器+基址寄存器的寻址方式——它们都只有一个,那就是基址寄存器+立即数。
所以A+i需要的寄存器+寄存器的操作要单独用一条add指令来完成
instead of just lbu a0, 0(i)?
32 位指令中没有足够的空间来保存全局地址,因此使用了多条指令。
您已将变量声明为全局变量,因此部分代码是关于访问全局变量的。
myfunction(): # @myfunction()
lui a0, %hi(i) <--- 1st part of 2 instructions for the char i global
lbu a0, %lo(i)(a0) <--- 2nd instruction for fetching the char i global
lui a1, %hi(A) <--- 1st part of 2 instructions for A global array
addi a1, a1, %lo(A) <--- 2nd instruction for fetching addr of A global
add a0, a0, a1 <--- the array indexing A + i
lbu a0, 0(a0) <--- the dereference *(A+i)
ret
i:
.byte 3 # 0x3
A:
.ascii "[=10=]0[=10=]1[=10=]2[=10=]3[=10=]4[=10=]5"
如果您尝试不同的方法,您会看到其中一些消失,这样您就可以更直接地看到数组引用:
int myfunction(char A [], int i) {
return A[i];
}
myfunction(char*, int): # @myfunction(char*, int)
add a0, a0, a1
lbu a0, 0(a0)
ret
正如 Erik Eidt 所说,i
是全局变量,即驻留在 32/64 位可寻址内存中的某个位置,并且可以随时更改。
i
的32/64位地址分两部分加载,因为32/64位不能用立即数编码。 %hi(i)
和%lo(i)
是i
的地址的高位和低位。 i
是从内存加载的,因为它可能在对 myfunction() 的调用之间发生了变化。
出于教育目的,我使用 https://godbolt.org/z/7F-Lhm 进行翻译
// C++ code
char i = 3;
char A[] = {0,1,2,3,4,5};
int myfunction() {
return A[i];
}
进入
# RISC-V instructions
myfunction(): # @myfunction()
lui a0, %hi(i)
lbu a0, %lo(i)(a0)
lui a1, %hi(A)
addi a1, a1, %lo(A)
add a0, a0, a1
lbu a0, 0(a0)
ret
i:
.byte 3 # 0x3
A:
.ascii "[=11=]0[=11=]1[=11=]2[=11=]3[=11=]4[=11=]5"
但为什么 A[i]
加载了 add a0, a0, a1
、lbu a0, 0(a0)
而不仅仅是 lbu a0, a0(a1)
?
如果对于 lbu dest, offset(baseAdress)
只有 dest
和 baseAdresse
允许作为寄存器地址,那么 offset
是指令字本身中的硬编码数字.但是在上面的相同代码中,我看到 lbu a0, %lo(i)(a0)
所以 offset
显然也可以是 "somewhat variable"?
也许我不明白这一点的原因是因为我真的不明白为什么这个 $hi $lo
首先是必要的。为什么我们要 lui a0, %hi(i)
、lbu a0, %lo(i)(a0)
而不是 lbu a0, 0(i)
?
and not just with lbu a0, a0(a1)?
RISC V(和MIPS)没有基址寄存器+基址寄存器的寻址方式——它们都只有一个,那就是基址寄存器+立即数。
所以A+i需要的寄存器+寄存器的操作要单独用一条add指令来完成
instead of just lbu a0, 0(i)?
32 位指令中没有足够的空间来保存全局地址,因此使用了多条指令。
您已将变量声明为全局变量,因此部分代码是关于访问全局变量的。
myfunction(): # @myfunction()
lui a0, %hi(i) <--- 1st part of 2 instructions for the char i global
lbu a0, %lo(i)(a0) <--- 2nd instruction for fetching the char i global
lui a1, %hi(A) <--- 1st part of 2 instructions for A global array
addi a1, a1, %lo(A) <--- 2nd instruction for fetching addr of A global
add a0, a0, a1 <--- the array indexing A + i
lbu a0, 0(a0) <--- the dereference *(A+i)
ret
i:
.byte 3 # 0x3
A:
.ascii "[=10=]0[=10=]1[=10=]2[=10=]3[=10=]4[=10=]5"
如果您尝试不同的方法,您会看到其中一些消失,这样您就可以更直接地看到数组引用:
int myfunction(char A [], int i) {
return A[i];
}
myfunction(char*, int): # @myfunction(char*, int)
add a0, a0, a1
lbu a0, 0(a0)
ret
正如 Erik Eidt 所说,i
是全局变量,即驻留在 32/64 位可寻址内存中的某个位置,并且可以随时更改。
i
的32/64位地址分两部分加载,因为32/64位不能用立即数编码。 %hi(i)
和%lo(i)
是i
的地址的高位和低位。 i
是从内存加载的,因为它可能在对 myfunction() 的调用之间发生了变化。