I’m working on a project that involves writing some embedded RISC-V code in Zig. I’ve got this code snippet:
export fn main() noreturn {
const x = fib(15);
asm volatile ("ecall"
:
: [arg0] "{s1}" (x),
);
unreachable;
}
fn fib(x: u32) u32 {
if (x <= 2) {
return 1;
}
return fib(x - 1) +% fib(x - 2);
}
If I compile this with -O small -target riscv32-freestanding-none (Godbolt link), the main function produces this assembly code:
example.main:
addi sp, sp, -16
sw ra, 12(sp)
sw s1, 8(sp)
li a0, 15
call example.fib
mv s1, a0
ecall
Is there a way I can get Zig not to generate the addi and sw instructions that push ra and s1 onto the stack? Since this function is noreturn, we never pop them off the stack, so it seems like we’re just wasting 16 bytes of memory. (In today’s economy, that’s like five bucks’ worth!) Maybe this is useful for certain debuggers or control-flow integrity features or something, but I’m not using any of those.
callconv(.naked) generates the kind of code I want, but you’re not allowed to call other runtime functions from inside a callconv(.naked) function.