简体   繁体   中英

How to specify a specific register to assign the result of a C expression in inline assembly in GCC?

I would like to store the result of a C expression into register a0 in a RISC-V program using GCC (for the sake of the examples below, suppose there is a volatile int val = 42 or similar). I am able to accomplish this with inline assembly when explicitly invoking a mv instruction:

asm ("mv a0, %0\n\t" :: "r" ((val == 0) ? 1 : val) : "a0");

However, the move would be unnecessary if the result would already be calculated "in" the right register. I have tried various things using register variable as described in the documentation but failed. For example, this following gets optimized away completely:

register volatile int res asm("a0") = (val == 0) ? 1 : val;

The same with an "empty" asm statement:

register int res asm("a0") = (val == 0) ? 1 : val;
asm volatile ( "" /* just store */ : "=r" (res) );

Is there a better way to do this? Do I overlook something?

You were close, but "=r"(res) told the compiler that the old value of res was dead, so it didn't materialize it anywhere.


register... asm("regname") local vars are only guaranteed to do anything when used as operands of an Extended asm statement. If the asm statement itself is asm volatile , then it can't be optimized away even if its inputs are unused. (Don't make the register... asm variable volatile ; that's not useful.)

So, to force the compiler to materialize the result of your expression in a0 (eg for a microbenchmark where you want to partially defeat optimization for some reason, otherwise https://gcc.gnu.org/wiki/DontUseInlineAsm ):

   register int res asm("a0") = (val == 0) ? 1 : val;
 // then read that reg var, forcing the compiler to actually materialize it
   asm volatile("# just a comment, input picked %0" : : "r"(res));

This is portable to other compilers that support GNU C inline asm, including clang.

Demo on Godbolt (I tweaked the asm template to use a nop in front of the comment so the compiler-explorer comment filter wouldn't remove it. An empty template string wouldn't change anything; that's only there to confirm that the compiler "knows" what register it put res in.)

void foo(int dummy, int val) {
    register int res asm("a3") = (val == 0) ? 1 : val;
    asm volatile("nop # just a comment, input picked %0" : : "r"(res));
}

I picked a3 and a void function to make sure GCC would have no reason to happen to pick that register by chance, or even to materialize res at all; I'm not returning it.

# RISC-V gcc 10.2  -O3
foo:
        mv      a3,a1
        bne     a1,zero,.L2
        li      a3,1
.L2:
        nop # just a comment, input picked a3
  # if we had returned res, we'd have   mv  a0,a3   here 
        ret

Note that this asm statement has no output operands and no clobbers, so the compiler knows it has no effect on any registers or memory.

If I'd used "=r"(res) , that would tell the compiler the original value of res was dead, so there was no need to materialize it in that register before the asm statement executed and produced whatever the new value was going to be. Just like if you'd written res = rand(); or something, the previous value of res wouldn't need to even be calculated if nothing ever read it.

"+r"(res) would tell the compiler that the asm instructions read that register and then write a new value there. You could use this to hide a constant from the optimizer, for example, so the compiler would have to assume that uses of res after this were a totally unknown runtime variable, even if you'd used register int res asm("a0") = 1;


Getting it wrong, without asm()

int bar(int dummy, int val) {
    register int res asm("a3") = (val == 0) ? 1 : val;
    return res;
}

With a0 , happens to "work" only because a0 is the return-value register, and we're returning res (to stop it optimizing away).

bar:                   # same computation, but without using a3
        mv      a0,a1
        bne     a1,zero,.L5
        li      a0,1
.L5:
        ret

And if I didn't return res , it would fully optimize away the calculation of an unused variable. volatile register int res asm("a3") can't change that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM