My code is
unsigned long user_stack_pointer;
__asm__(
".global exception_handling_entry\n"
"exception_handling_entry:\n"
"add %0, sp, x0\n"
: "=r" (user_stack_pointer)
);
gcc shows a error ')' before ':' token
when using riscv64-unknown-elf-gcc test.c -o test
. But it works well when I put the inline asm into a function such as
unsigned long user_stack_pointer;
int main() {
__asm__(
".global exception_handling_entry\n"
"exception_handling_entry:\n"
"addi %0, sp, 0\n"
: "=r" (user_stack_pointer)
);
return 0;
}
This confuses me a lot.
I can not find a good solution for it.
At global scope, the code only runs if something jumps inside the asm statement itself. There's no way for the path of execution in C terms to enter and leave the asm
statement, so there's nowhere for the compiler to put code that sets up input operands or that stores output operands to associated C variables. If you want that to happen, you have to write code for it yourself, exactly like writing a function in a separate .s
file. (Use .text
inside your global-scope asm statement in case the compiler left the current section something else.)
e.g. sd sp, user_stack_pointer
as a pseudo-instruction, or auipc
with the %hi
part of the address and use %lo(user_stack_pointer)
as the offset in a store instruction. Look at compiler-generated asm to see how it accesses global vars (https://godbolt.org/); I'm just going from memory and that might not be quite right for RV64; I might be remembering MIPS.
Or if you want to have the compiler do the .global
and label for you, use __attribute__((naked))
. You still have to write the entire function body with Basic asm
statements (no operands); it's not officially supported to use Extended asm
(with operands) in naked functions: https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Function-Attributes.html - but it's only at most a warning not a compile-time error, so the compiler won't stop you from relying on happens-to-work code.
Also, the compiler doesn't emit a ret
at the end of a naked
function, so can't let execution come out the bottom of your asm statement anyway, making an output operand useless. You need to ret
or jump somewhere.
It would actually be undefined behaviour if execution came out the bottom of the asm statement after jumping into the middle (e.g. like this exception handler rather than execution starting with the C __asm__
statement itself) no matter where it is, even inside a function. As the GCC manual documents, asm statements may not perform jumps into other asm statements, only to the listed GotoLabels. GCC’s optimizers do not know about other jumps; therefore they cannot take account of them when deciding how to optimize.
If that happens to work, you got lucky. The code for the "=r"(global_var)
operand could rely on some registers set up by instructions the compiler put ahead of the asm
statement, which wouldn't have executed if you jump into the middle of it.