此 C 源代码:
typedef struct {
unsigned long one;
unsigned long two;
} twin;
twin function( twin t ) {
return (twin){ 0,0 };
}
生成此程序集:
.file "p.c"
.option nopic
.attribute arch, "rv64i2p1_m2p0_a2p1_f2p2_d2p2_c2p0_zicsr2p0_zifencei2p0"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 1
.globl function
.type function, @function
function:
addi sp,sp,-32 # <<< WHY?
li a0,0
li a1,0
addi sp,sp,32 # <<< WHY?
jr ra
.size function, .-function
.ident "GCC: (g04696df09) 14.2.0"
.section .note.GNU-stack,"",@progbits
riscv64-unknown-elf-gcc (g04696df09) 14.2.0
当通过或-O3
或-O2
或-O1
甚至运行时-Os
。
那么为什么代码要在堆栈上为保存在寄存器中的内容(32 字节)创建空间呢a0
?a1
这是一个错误,还是我遗漏了什么?
是的,看起来像是一个错过优化的错误,你可以在 GCC 的 bugzilla 上报告。https ://gcc.gnu.org/bugzilla
即使在 中,它也针对使用 RV32 GCC ( Godbolt )的 Linux 执行相同的操作
-fomit-frame-pointer
。浪费的堆栈指针指令在 GCC8 ( Godbolt 上最早的版本) 的主干中存在。我非常确信没有 ABI 需要它,并且 Clang 不会发出它们。
其中一个
mv
是不可避免的,因为我们需要将a0
和替换a1
为分别依赖于两个原始输入的值。所以我们不能在第一条指令中写入 overwritea0
或a1
。但它肯定不需要溢出任何内容,而且它是一个叶函数,所以不需要保存返回地址。而且我们没有使用帧指针,所以也不需要保存调用者的 FP。重现此问题的关键因素是 struct local;
int r = u1^u2;
例如,这种情况不会发生。因此,GCC 可能未能优化该结构的堆栈空间,而是将其优化到寄存器中。ret r = { 0, 0 };
仍然重现了此问题。