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, a1lbu a0, 0(a0) 而不仅仅是 lbu a0, a0(a1)

如果对于 lbu dest, offset(baseAdress) 只有 destbaseAdresse 允许作为寄存器地址,那么 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() 的调用之间发生了变化。