内联程序集约束修饰符 = 和 +

Inline assembly constraint modifiers = and +

本文关键字:程序集 约束      更新时间:2023-10-16

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

让我感到困惑的是为什么下面的代码会生成此指令移动 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 = %dn",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)

我接下来要展示的是错误的。但这是为了我更好地了解海湾合作委员会正在发生的事情。

好的,现在我从 +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 = %dn",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,如此指令添加 %eax,%eax;

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

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

程序集应该只是:

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

我没有完全理解修饰符 + 和 = 在输入和输出列表中扮演的角色。

+表示寄存器被读取和写入,因此在程序集开始之前,寄存器必须具有变量的值。 = 表示它只是写入程序集,并且可以在程序集之前具有任何值。

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

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

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

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

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

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 %0%edx %1%ecx %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)

%eax用于%0%1%edx用于%2

获得所需内容的另一种方法是:

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

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

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

%eax用于%0%2%edx用于%1

你不应该使用 "r" 来重复b - 使用 "0"(输出参数号(。现在,编译器正在b加载到未使用的%ecx中。

"我写了一个简单的程序,其中包含内联汇编代码。

"我的代码只是将变量 a 和 b 相加,并以 b 返回结果。等等等等">

哪里?

mov ax,2
mov bx,3 
add bx,ax

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

这让我想起了一次"恶搞"采访

阅读和哭泣

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