意外的GCC内联ASM行为(被覆盖的被破坏的变量)

时间:2014-10-25 22:08:02

标签: gcc assembly inline-assembly i386

在我的电脑上,编译后的可执行文件忽略了在循环顶部执行“mov%2,%% ax”

当“添加%1,%% ax”取消注释时。

是否有人进行双重检查或评论?

#include <stdio.h>

int main() {

short unsigned result, low ,high;

    low  = 0;
    high = 1;

    __asm__ (   
        "movl $10, %%ecx \n\t"

        "loop: mov  %2, %%ax \n\t"

//      "add    %1, %%ax \n\t"      // uncomment and result = 10
        "mov    %%ax, %0     \n\t"

        "subl   $1, %%ecx \n\t"                 
        "jnz loop"                              
        : "=r" (result)
        : "r" (low) , "r" (high)
        : "%ecx" ,"%eax" );        

    printf("%d\n", result);  
    return 0;
}

遵循生成的程序集

movl $1, %esi
xorl %edx, %edx
/APP
movl $10 ,%ecx 

loop: mov %si, %ax 
mov  %dx, %bx 
add %bx, %ax 
mov %ax, %dx     
subl $1, %ecx 
jnz loop  
/NO_APP

感谢Jester的解决方案:

    : "=&r" (result)        // early clober modifier

1 个答案:

答案 0 :(得分:6)

GCC内联汇编是高级编程,存在很多陷阱。确保您确实需要它,并且不能使用独立组装模块或使用内在函数的C代码替换它。或矢量支持。

如果您坚持内联汇编,您应该准备好至少查看生成的汇编代码并尝试从中找出任何错误。显然,编译器不会省略您写入asm块的任何内容,它只是替换参数。如果查看生成的代码,您可能会看到如下内容:

    add    %dx, %ax
    mov    %ax, %dx

显然,编译器选择dx作为参数01。允许这样做,因为默认情况下它假定在写入任何输出之前消耗输入参数。要表明情况并非如此,您必须使用early clobber modifier作为输出操作数,因此它看起来像"=&r"

PS:即使内联汇编似乎有效,它也可能有隐藏的问题,当编译器碰巧做出其他选择时,这些问题会让你感到痛苦。你应该真的避免它。