gcc inline assembly error "operand type mismatch for mov"

20,142

Solution 1

unsigned char t;
asm("movl %1, %%eax;"
     "movl %%eax, %0;"
     :"=r"(t)  /* <--here */
     :"r"(*x)  /* <-- and here */
     :"%eax");

You can not move a value from a 32-bit register to a single-byte memory location. t is on the stack and x is somewhere else, but both are accessed in the same way. Problems on the other lines are similar. You should move only a byte.

Try something like this, but there are more ways to do it (I haven't tried that, read below):

unsigned char t;
asm("movb %1, %%al\n"
     "movb %%al, %0\n"
     :"=r"(t)
     :"r"(*x)
     :"%al");
asm("movb %1, %%al\n"
     "movb %%al, %0\n"
     :"=r"(*x)
     :"r"(*y)
     :"%al");
asm("movb %1, %%al\n"
     "movb %%al, %0\n"
     :"=r"(*y)
     :"r"(t)
     :"%al"); 

The whole procedure can be simplified into this:

asm("movb (%1), %%al\n"
    "movb (%2), %%ah\n"
    "movb %%ah, (%1)\n"
    "movb %%al, (%2)\n"
    : /* no outputs for compiler to know about */
    : "r" (x), "r" (y)
    : "%ax", "memory");

Solution 2

To simplify it even more (than user35443):

asm("" : "=r" (*x), "=r" (*y) : "1" (*x), "0" (*y));

Look ma! No code! And yes, this really works.

To explain how this works:

When the compiler is building the code, it keeps track of what value is in each register. So if had these for inputs to asm:

"r" (*x), "r" (*y)

The compiler will pick a register and put *x in it, then pick a register and put *y in it, then call your asm. But it also keeps track of what variable is in which register. If there were just some way to tell the compiler that all it had to do was start treating the two registers as the opposite variables, then we'd be set. And that's what this code does:

  1. Saying "=r" (*x) means that we are going to be overwriting the value in *x, that that we will be putting the value into a register.
  2. Saying "0" (*y) means that on input to the asm, the compiler must put the value of *y into the same register as is being used by output parameter #0.

So, without using any actually assembly instructions, we have told the compiler to swap these two values.

We don't get this quite "for free" since the compiler must load the values into registers before calling the asm. But since that has to happen anyway...

What about actually updating memory? The compiler will (if necessary) write these values from the registers back to memory. And since it knows what variable is in which register, all works as expected.

Share:
20,142
user3799906
Author by

user3799906

Updated on December 14, 2020

Comments

  • user3799906
    user3799906 over 3 years
    //quick inline asm statements performing the swap_byte for key_scheduling
    inline void swap_byte(unsigned char *x, unsigned char *y)
    {
     unsigned char t;
     asm("movl %1, %%eax;"
         "movl %%eax, %0;"
         :"=r"(t)
         :"r"(*x)
         :"%eax");
     asm("movl %1, %%eax;"
         "movl %%eax, %0;"
         :"=r"(*x)
         :"r"(*y)
         :"%eax");
     asm("movl %1, %%eax;"
         "movl %%eax, %0;"
         :"=r"(*y)
         :"r"(t)
         :"%eax");       
    }
    

    Here I am trying to swap the char from x and store in y, and the same for y to x. I have compiled these instructions by changing movl to mov but with no success. Where is the problem in compiling/linking?

    Here is the output from compiling in cygwin:

    $ gcc rc4_main.c -o rc4ex
    /tmp/ccy0wo6H.s: Assembler messages:
    /tmp/ccy0wo6H.s:18: Error: operand type mismatch for `mov'
    /tmp/ccy0wo6H.s:18: Error: operand type mismatch for `mov'
    /tmp/ccy0wo6H.s:26: Error: operand type mismatch for `mov'
    /tmp/ccy0wo6H.s:26: Error: operand type mismatch for `mov'
    /tmp/ccy0wo6H.s:34: Error: operand type mismatch for `mov'
    /tmp/ccy0wo6H.s:34: Error: operand type mismatch for `mov'
    
  • David Wohlferd
    David Wohlferd almost 10 years
    Actually, this 'simplified' code isn't right. You haven't told the compiler that you are changing anything. It has no way of knowing that *x and *y have changed.
  • user35443
    user35443 almost 10 years
    Yes, I forgot to add "memory" at the and. BTW that's a nice code you have there :)
  • David Wohlferd
    David Wohlferd almost 10 years
    Still not right. Should be %0/%1. And while adding the "memory" qualifier will make this work, it isn't the best solution. In simple code like this, it probably doesn't matter, but in anything much larger, this has performance implications. Using "memory" (may) cause the compiler to take any other values it has stored in registers and flush them back to memory before running the asm and they may need to get re-loaded from memory back into registers after the asm. Contrast this additional overhead from "memory" plus 4 lines of assembler with the zero lines of assembly in my alternate answer.
  • David Wohlferd
    David Wohlferd almost 10 years
    Also, when my asm exits, the compiler knows that the specified values are in known registers. If (as seems likely) it needs to immediately do something with the values, it doesn't have to read them back from memory into registers again. Depending on how the values are used, the compiler may decide it NEVER needs to flush them back to memory, again improving performance. And if the values were ALREADY in registers before calling swap_byte (or were constants), my code can use those values directly, where your code would always force memory writes/reads.
  • user35443
    user35443 almost 10 years
    What do you expect from me? I come here and I see a guy using caps and making that effort to write two comments just to... what?
  • David Wohlferd
    David Wohlferd almost 10 years
    Sorry, didn't mean to be so critical. My goal isn't just to answer this guy (who is probably long gone), but to answer all the other guys that visit SO in the future and find this answer. I find gcc's inline asm stuff to be interesting and cool, but it can also be tricky. There are some non-obvious issues with it (like the impact of "memory") that people aren't aware of, so I try to pass on what I have learned.
  • Peter Cordes
    Peter Cordes over 7 years
    Hah, I wrote exactly the same zero-instruction inline-asm swap implementation to answer a similar question about swapping C vars.
  • Michael Petch
    Michael Petch over 6 years
    movl %%eax, %0; actually doesn't change a constant. %0 uses the operand 0 supplied by the extended asm constraints. I think you are confusing %0 and $0 , they are totally different.