Esta fonte C:
typedef struct {
unsigned long one;
unsigned long two;
} twin;
twin function( twin t ) {
return (twin){ 0,0 };
}
gera esta montagem:
.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
quando executado riscv64-unknown-elf-gcc (g04696df09) 14.2.0
com ou -O3
ou -O2
ou -O1
mesmo -Os
.
Então por que o código está criando espaço na pilha para coisas (32 bytes) que são e serão mantidas em registradores a0
e a1
?
Isso é um bug ou estou esquecendo de alguma coisa?
Sim, parece um bug de otimização perdida que você pode relatar no Bugzilla do GCC. https://gcc.gnu.org/bugzilla
Ele faz a mesma coisa visando o Linux com RV32 GCC ( Godbolt ), mesmo com
-fomit-frame-pointer
. As instruções de ponteiro de pilha desperdiçadas estão presentes com GCC8 (o mais antigo em Godbolt) através do trunk.Estou bastante confiante de que nenhuma ABI exige isso, e o Clang não os emite.
Uma
mv
é inevitável, pois precisamos substituir ambosa0
ea1
por valores que dependem de ambas as entradas originais. Portanto, não podemos sobrescrever nema0
oua1
com a primeira instrução. Mas certamente não precisa derramar nada, e é uma função folha, então salvar o endereço de retorno não é necessário. E não estamos usando um ponteiro de quadro, então salvar o FP do chamador também não é necessário.O ingrediente-chave para reproduzir isso é uma struct local; isso não acontece com,
int r = u1^u2;
por exemplo. Então, talvez o GCC não esteja conseguindo otimizar o espaço de pilha para essa struct, que ele otimiza em registradores.ret r = { 0, 0 };
Ainda a reproduz.