2011-12-12 47 views
9

我正在尝试使用GCC的内联汇编器来熟悉x86汇编。我试图添加两个数字(ab)并将结果存储在c中。我有四个稍微不同的尝试,其中三个工作;最后不会产生预期的结果。添加两个数字

前两个示例使用中间寄存器,并且这两个示例都正常工作。第三个和第四个示例尝试直接将这两个值相加,而不使用中间寄存器,但结果因优化级别和添加输入值的顺序而异。我错了什么?

环境是:

i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3) 

首先,变量的声明如下:

int a = 4; 
int b = 7; 
int c; 

实施例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 

实施例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 

实施例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 

实施例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 

解决。Matthew Slattery's answer是正确的。之前,试图重用eax两个bc

movl -4(%rbp), %edx 
movl -8(%rbp), %eax 
movl %edx, %eax 
addl %eax, %eax 

有了马修的修复建议,它现在使用ecx分别持有c

movl -4(%rbp), %edx 
movl -8(%rbp), %eax 
movl %edx, %ecx 
addl %eax, %ecx 
+2

适用于我,无论是否启用优化。尝试使用-S编译,以获取汇编语言列表。然后您可以看到正在使用哪些寄存器。 – TonyK

+0

只是注意到我根据优化级别得到不同的结果。代码示例使用新输出更新。 –

+0

那么程序集清单告诉你什么? – TonyK

回答

7

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

但是,这并不一定是在实施例3和壳体4

例如例3:

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

...你已经阅读a%1)之前更新c%0)。如果gcc恰好分配同一个寄存器既%0%1,然后将计算c = b; c += c,因此将在完全相同的方式失败,你观察:告诉gcc

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 

您可以修复它的输出操作数可以所使用的输入被消耗之前,通过添加“&”改性剂对操作数,这样的:

asm(" movl %2,%0;" 
    " addl %1,%0;" 
    : "=&r" (c) 
    : "r" (a), "r" (b) 
    ); 

(参见在gcc文档"Constraint Modifier Characters"

+0

就是这样,谢谢。修复之前和之后更新了汇编列表中的问题。 –

+0

感谢您的支持!这是我需要了解的东西,但文档是相当可怕的。像这样的提示是非常有价值的。 – TonyK

0

Hoi,我没有看到有问题,它编译和工作正常。然而,一个小提示:我很快与未命名的变量/寄存器混淆,所以我决定使用命名的变量/寄存器。该插件啄你可以例如实现这样的:

static inline void atomicAdd32(volInt32 *dest, int32_t source) { 
// IMPLEMENTS: add m32, r32 
__asm__ __volatile__(
     "lock; addl %[in], %[out]" 
     : [out] "+m"(*dest) 
     : [in] "ir"(source)//, "[out]" "m"(*dest) 
     ); 
return; 
    } 

(你可以忽略现在的原子/锁的东西),这使得清楚发生了什么:

1)什么寄存器是可写的,可读或者两者皆为

2)什么是使用(内存,寄存器),这在性能和时钟周期中可能很重要,因为寄存器操作比访问内存的操作更快。

干杯, G.

P.S:你检查你的编译器是否重新排列代码?