出于教育目的,我使用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 "\000\001\002\003\004\005"
但是为什么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
,所以$hi $lo
显然也可以是“有些变量”?
也许我不明白这一点的原因是因为我真的不明白为什么lui a0, %hi(i)
这个东西首先是必要的。为什么我们要做lbu a0, %lo(i)(a0)
,lbu a0, 0(i)
而不是{{1}}?
答案 0 :(得分:2)
不仅仅是lbu a0,a0(a1)吗?
RISC V(和MIPS)没有基址寄存器+基址寄存器寻址模式-它们都只有一个,即基址寄存器+立即数。
因此,A + i所需的寄存器+寄存器操作必须使用单独的加法指令来完成。
而不只是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 "\000\001\002\003\004\005"
如果尝试其他方法,您会发现其中一些消失了,因此您可以直接查看数组引用:
int myfunction(char A [], int i) {
return A[i];
}
myfunction(char*, int): # @myfunction(char*, int)
add a0, a0, a1
lbu a0, 0(a0)
ret
答案 1 :(得分:1)
正如Erik Eidt所说,i
是全局变量,即位于32/64位可寻址内存中的某个位置,并且可以随时更改。
i
的32/64位地址分为两部分,因为不能立即对32/64位进行编码。 %hi(i)
和%lo(i)
是i
的地址的上部和下部。 i
已从内存中加载,因为在两次调用myfunction()之间可能已更改。