2

我写了一个包含内联汇编代码的简单程序。我的代码只是添加变量 a 和 b 并在 b 中返回结果。

令我困惑的是为什么下面的代码会生成这条指令 movl 28(%esp), %ecx。

我不完全理解修饰符 + 和 = 在输入和输出列表中的作用。因此,如果您能对此有所了解,我们将不胜感激。

#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)

我接下来要展示的内容是错误的。但这是为了让我更好地了解 GCC 中发生的事情。

好的,现在我从 +r 更改为 =r。这是 GCC 生成的汇编代码。

#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)

现在输出为 4,这是错误的。我的问题是为什么使用 "=r" GCC 决定为 b 重用寄存器 eax,如本指令所示 addl %eax,%eax;

4

4 回答 4

5

令我困惑的是为什么下面的代码会生成此指令movl 28(%esp), %ecx

因为您已将其列为b两个单独的输入寄存器;第一+部分中的 表示程序集读取和修改寄存器。所以它被加载到两个寄存器中,即使程序集不使用第二个。

程序集应该只是:

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

我不完全理解修饰符 + 和 = 在输入和输出列表中的作用。

+意味着寄存器被读取和写入,因此在汇编开始之前它必须具有变量的值。=意味着它只是被写入,并且在汇编之前可以具有任何值。

有关完整详细信息,请参阅文档

我的问题是为什么使用 "=r" GCC 决定将寄存器 eax 用于 b,如本指令所示 addl %eax,%eax;

因为现在你的约束是错误的。您告诉编译器您只写入addl指令的第二个操作数 ( %0),因此它假定它可以使用与输入之一相同的寄存器。事实上,该操作数也是addl. 然后您仍然告诉它您需要b在程序集不使用的单独寄存器中的第二个副本。

正如我上面所说的,"+r"(b)在第一个(输出)列表中使用来表示那个%0b并且用于输入和输出,"r"(a)在第二个(输入)列表中表示那个%1a并且只用于输入。不要放入第三个寄存器,因为%2程序集中没有。

于 2013-05-22T15:24:51.877 回答
4

它非常简单——+表示输入和输出,而=仅表示输出。所以当你说:

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

你有三个操作数。一个输入/输出寄存器 ( %0) 用 初始化b并将其输出放入b,两个输入寄存器 (%1和) 分别用和%2初始化。现在您从不使用,但无论如何它都在那里。您可以在生成的代码中看到它:ab%2

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

addl %edx,%eax

movl    %eax, 28(%esp)

所以编译器使用%eax了 for %0%edxfor%1%ecxfor %2。所有这三个都在内联代码之前加载,然后再写%0回来。

当你这样做时:

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

现在%0只是输出(不是输入)。所以编译器可以产生:

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

addl %eax,%eax;

movl    %eax, 28(%esp)

使用%eaxfor%0和for 。%1_%edx%2

获得你想要的东西的另一种方法是:

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

使用0约束%2意味着编译器必须将它放在与 %0 所以它最终生成的位置相同的位置:

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

addl %edx,%eax

movl    %eax, 28(%esp)

使用%eaxfor%0%2, 和%edxfor%1

于 2013-05-23T02:24:07.443 回答
1

您不应该"r"重复b使用 - 使用 "0"(输出参数编号)。现在编译器正在加载b%ecx哪个没有被使用。

于 2013-05-22T15:14:12.403 回答
-2

“我写了一个包含内联汇编代码的简单程序。”

“我的代码只是添加变量 a 和 b 并以 b.blah blah blah 的形式返回结果”

在哪里?

mov ax,2
mov bx,3 

add bx,ax

如果你愿意,你可以叫我爱因斯坦...

这让我想起了“恶搞”采访

阅读和哭泣

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

于 2013-05-22T23:55:42.803 回答