为什么 "long long" 参数在 RISC-V 中需要 "aligned even-odd register pair"

Why "long long" arguments need to "aligned even-odd register pair" in RISC-V

正如RISC-V calling convention documentation所说:

When primitive arguments twice the size of a pointer-word are passed on the stack, they are naturally aligned. When they are passed in the integer registers, they reside in an aligned even-odd register pair, with the even register holding the least-significant bits. In RV32, for example, the function void foo(int, long long) is passed its first argument in a0 and its second in a2 and a3. Nothing is passed in a1.

为什么不直接使用 a1a2 而不是 a2a3?所以我们可以通过寄存器传递更多的参数。

通常,这样做是为了让简单的代码可以将所有寄存器刷新到内存中,并获得您期望的结果,就好像首先使用内存来传递对齐良好的参数一样——这是通常对可变参数函数很重要。

但是,我们应该注意,对于非可变参数函数,clang 和 gcc 都不会直接遵循这一点。他们都使用 a0 作为 inta1a2 作为 long long (其中 a1 是低阶, a2 long long).

的高阶
long long square(int num, long long foo) {
    return foo + 100;
}

结果

square:
    addi    a0, a1, 100
    sltu    a1, a0, a1
    add     a1, a1, a2
    ret

叮当声:https://godbolt.org/z/9Pez4r

gcc: https://godbolt.org/z/b4dMsr


只有当我们使用可变参数时,我们才会看到编译器跳过 a1:

long long square(int num, ...);

int test () {
    square ( 100, (long long) 200 );
}

结果

test:
    addi    a0, zero, 100
    addi    a2, zero, 200
    mv      a3, zero
    tail    square

https://godbolt.org/z/4xWc1E