2
votes

I wrote a simple program containing inline assembly code. My code simply adds the variables a and b and returns the result in b.

What is confusing me is why the code below generates this instruction movl 28(%esp), %ecx.

I don't fully undertand the roles the modifiers + and = play in the input and output lists. So it would be appreciated if you could shed some light on this.

#include <cstdio>

int main( int argc , char ** argv )
{
    int a = 2, b = 7;

    __asm__
    (
     "addl %1,%0;"      
    :"+r"(b)           
    :"r"(a), "r"(b)     
    );

    printf("b = %d\n",b);

    return 0;
}
    movl    $2, 24(%esp)
    movl    $7, 28(%esp)

    movl    24(%esp), %edx
    movl    28(%esp), %ecx
    movl    28(%esp), %eax

    addl %edx,%eax

    movl    %eax, 28(%esp)

What I am going to show next is wrong. But it is for the sake of my better understanding about what is going on in GCC.

Ok, now I changed from +r to =r. And this is the assembly code GCC generates.

#include <cstdio>

int main( int argc , char ** argv )
{
    int a = 2, b = 7;

    __asm__
    (
     "addl %1,%0;"      
    :"=r"(b)           
    :"r"(a), "r"(b)     
    );

    printf("b = %d\n",b);

    return 0;
}
    movl    $2, 24(%esp)
    movl    $7, 28(%esp)
    movl    24(%esp), %eax
    movl    28(%esp), %edx

    addl %eax,%eax;

    movl    %eax, 28(%esp)

Now the output is 4, which is wrong. My question is why with "=r" GCC decided to reuse the register eax for b as shown in this instruction addl %eax,%eax;

4

4 Answers

5
votes

What is confusing me is why the code below generates this instruction movl 28(%esp), %ecx

Because you've listed b as being in two separate input registers; the + in the first section means that the assembly reads and modifies the register. So it's loaded into two registers, even though the assembly doesn't use the second.

The assembly should just be:

"addl %1,%0;" : "+r"(b) : "r"(a)

I don't fully undertand the roles the modifiers + and = play in the input and output lists.

+ means that the register is read and written to, so that it must have the variable's value before the assembly begins. = means that it is just written to, and can have any value before the assembly.

See the documentation for full details.

My question is why with "=r" GCC decided to use the register eax for b as shown in this instruction addl %eax,%eax;

Because now your constraints are wrong. You're telling the compiler that you only write to the second operand of the addl instruction (%0), so it assumes that it can use the same register as one of the inputs. In fact that operand is also an input to addl. Then you're still telling it that you need a second copy of b in a separate register that the assembly doesn't use.

As I said above, use "+r"(b) in the first (output) list to indicate that that %0 is b and is used for both input and output, and "r"(a) in the second (input) list to indicate that %1 is a and is only used for input. Don't put a third register in, since there's no %2 in the assembly.

3
votes

Its pretty simple -- + means input and output while = means output only. So when you say:

asm("addl %1,%0;" : "+r"(b) : "r"(a), "r"(b));

you have THREE operands. One input/output register (%0) initialized with b and putting its output in b, and two input registers (%1 and %2) initialized with a and b respectively. Now you never use %2, but its there anyways. You can see it in the code produced:

movl    24(%esp), %edx
movl    28(%esp), %ecx
movl    28(%esp), %eax

addl %edx,%eax

movl    %eax, 28(%esp)

So the compiler has used %eax for %0, %edx for %1 and %ecx for %2. All three are loaded before the inline code, and %0 is written back afterwards.

When you do:

asm("addl %1,%0;" : "=r"(b) : "r"(a), "r"(b));

now %0 is output only (not input). So the compiler can produce:

movl    24(%esp), %eax
movl    28(%esp), %edx

addl %eax,%eax;

movl    %eax, 28(%esp)

using %eax for %0 and %1 and %edx for %2.

Another way of getting what you want is:

asm("addl %1,%0;" : "=r"(b) : "r"(a), "0"(b));

using a 0 constraint on %2 means that the compiler must put it in the same place as %0 So it ends up generating:

movl    24(%esp), %edx
movl    28(%esp), %eax

addl %edx,%eax

movl    %eax, 28(%esp)

using %eax for %0 and %2, and %edx for %1

1
votes

You shouldn't use "r" for the repeat of b - use "0" (the output argument number). Now the compiler is loading b into %ecx which isn't being used.

-2
votes

"I wrote a simple program containing inline assembly code. "

"My code simply adds the variables a and b and returns the result in b. blah blah blah"

Where?

mov ax,2
mov bx,3 

add bx,ax

You can call me Einstein if you want...

Which reminds me of a "spoof" interview

Read and weep

http://www-users.cs.york.ac.uk/susan/joke/cpp.htm