添加两个数字

Gra*_*and 11 x86 gcc inline-assembly

我正在尝试使用GCC的内联汇编程序熟悉x86程序集.我正在尝试添加两个数字(ab)并存储结果c.我有四个略有不同的尝试,其中三个有效; 最后一个不会产生预期的结果.

前两个示例使用中间寄存器,这些都可以正常工作.第三个和第四个示例尝试直接添加两个值而不使用中间寄存器,但结果会根据优化级别和添加输入值的顺序而有所不同.我错了什么?

环境是:

i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
Run Code Online (Sandbox Code Playgroud)

首先,变量声明如下:

int a = 4;
int b = 7;
int c;
Run Code Online (Sandbox Code Playgroud)

例1:

asm("   movl    %1,%%eax;"
    "   addl    %2,%%eax;"
    "   movl    %%eax,%0;"
    : "=r" (c)
    : "r" (a), "r" (b)
    : "%eax"
   );
printf("a=%d, b=%d, c=%d\n", a, b, c);
// output: a=4, b=7, c=11
Run Code Online (Sandbox Code Playgroud)

例2:

asm("   movl    %2,%%eax;"
    "   addl    %1,%%eax;"
    "   movl    %%eax,%0;"
    : "=r" (c)
    : "r" (a), "r" (b)
    : "%eax"
   );
printf("a=%d, b=%d, c=%d\n", a, b, c);
// output: a=4, b=7, c=11
Run Code Online (Sandbox Code Playgroud)

例3:

asm("   movl    %2,%0;"
    "   addl    %1,%0;"
    : "=r" (c)
    : "r" (a), "r" (b)
   );
printf("a=%d, b=%d, c=%d\n", a, b, c);
// output with -O0: a=4, b=7, c=11
// output with -O3: a=4, b=7, c=14
Run Code Online (Sandbox Code Playgroud)

例4:

// this one appears to calculate a+a instead of a+b
asm("   movl    %1,%0;"
    "   addl    %2,%0;"
    : "=r" (c)
    : "r" (a), "r" (b)
   );
printf("a=%d, b=%d, c=%d\n", a, b, c);
// output with -O0: a=4, b=7, c=8
// output with -O3: a=4, b=7, c=11
Run Code Online (Sandbox Code Playgroud)

解决了. Matthew Slattery的回答是正确的.以前,它试图重用eax两者bc:

movl    -4(%rbp), %edx
movl    -8(%rbp), %eax
movl    %edx, %eax
addl    %eax, %eax
Run Code Online (Sandbox Code Playgroud)

有了Matthew建议的修复,它现在用于单独ecx保存c.

movl    -4(%rbp), %edx
movl    -8(%rbp), %eax
movl    %edx, %ecx
addl    %eax, %ecx
Run Code Online (Sandbox Code Playgroud)

Mat*_*ery 8

默认情况下,gcc假设asm在更新输出操作数之前,内联块将使用输入操作数完成.这意味着输入和输出都可以分配给同一个寄存器.

但是,在您的示例3和4中并不一定如此.

例如在例3中:

asm("   movl    %2,%0;"
    "   addl    %1,%0;"
    : "=r" (c)
    : "r" (a), "r" (b)
   );
Run Code Online (Sandbox Code Playgroud)

...你已经更新c(%0阅读前)a(%1).如果gcc碰巧分配相同的注册都%0%1,然后将计算c = b; c += c,因此会正是你观察的方式失败:

printf("a=%d, b=%d, c=%d\n", a, b, c);
// output with -O0: a=4, b=7, c=11
// output with -O3: a=4, b=7, c=14
Run Code Online (Sandbox Code Playgroud)

您可以通过gcc在输入被消耗之前告知可以使用输出操作数来修复它,方法是在操作数中添加" &"修饰符,如下所示:

asm("   movl    %2,%0;"
    "   addl    %1,%0;"
    : "=&r" (c)
    : "r" (a), "r" (b)
   );
Run Code Online (Sandbox Code Playgroud)

(请参阅文档中的"约束修饰符字符"gcc.)