简体   繁体   中英

Return values and constraints for asm (assembler code) using gnu Extended Asm

As a (beginners) exercise I want to implement a swap operation in C using GNU asm.

I'm confused about the restraints. My code is:

    int c = 1;
    int b = 2;
    asm ("xchg %2,%3"
            : "=r" (c), "=r" (b)
            : "<X>" (c), "<Y>" (b)
            );
    return c+10*b;

Where $<X>$ and $<Y>$ are replaced by ("0", "0"), ("0", "r"), ("r", "r") and ("r", 0").

("0", "0") does not compile. (Why?)

("0", "r") returns 12, the expected result.

("r", "r") and ("r", "0") return 21, as if nothing happened. (Why?)

So what is wrong in those cases? Why does it fail?

You can use : "+r" (c), "+r" (b) to declare read/write operands 1 ( https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html ) In/out instructions go in the first (output) group of constraints.

And of course you should use xchg %0, %1 to make sure the asm template only references operands you actually declared.

asm ("xchg %0,%1"
            : "+r" (c), "+r" (b)
  );

Footnote 1: Fun fact: GCC internally implements read/write operand by inventing matching input constraints for these output. So if you didn't fix the %2,%3 in the template, it would still compile. But you wouldn't have any guarantee of which order GCC chose to do the matching constraints in.


To find out what happened with your wrong constraints, make your asm template print the unused operand names as an asm comment, eg xchg %2,%3 # inputs= %0,%1 so you can look at the compiler's asm output ( gcc -S or on https://godbolt.org ), and see which registers it picked. If it picked opposite constraints you'd see no swap.

Or with "r" if it picked 2 registers that are different from the output operands then you're stepping on the compilers toes by modifying registers it expected to remain unchanged (because you told it those were inputs, and could be in separate registers from the outputs.) So no, "r" can't be safe.


If you did want to use matching constraints manually, you'd of course use "0"(c), "1"(b) to force it to pick the same register for c as an input that it picked for c as the output #0, and same for b as input as output #1.

Of course there's no need for a runtime instruction at all to just tell the compiler that C variables have each other's values.

asm ("nop  # c input in %2, output in %0.  b input in %3, output in %1"
     :  "=r" (c), "=r" (b)
     :  "1"(c),   "0"  (b)   // opposite matching constraints
  );

Even the nop is unnecessary, but https://godbolt.org/ by default filters comments so it's convenient to put a comment on a NOP.

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