内联汇编约束修饰符=和+

时间:2013-05-22 15:09:48

标签: c++ c gcc assembly inline-assembly

我写了一个包含内联汇编代码的简单程序。 我的代码只是添加变量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)

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

好的,现在我从+ 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 个答案:

答案 0 :(得分:5)

  

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

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

组装应该是:

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

我不完全承担修饰符+和=在输入和输出列表中扮演的角色。

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

有关详细信息,请参阅documentation

  

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

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

如上所述,在第一个(输出)列表中使用"+r"(b)表示%0b并且用于输入和输出,{{1} }在第二个(输入)列表中指示"r"(a)%1并且仅用于输入。不要在第三个注册表中注册,因为程序集中没有a

答案 1 :(得分:3)

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

asm("addl %1,%0;" : "+r"(b) : "r"(a), "r"(b));
你有三个操作数。一个输入/输出寄存器(%0)初始化为b并将其输出置于b,两个输入寄存器(%1%2)初始化为{分别为{1}}和a。现在你永远不会使用b,但无论如何它都在那里。您可以在生成的代码中看到它:

%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)); 上使用0约束意味着编译器必须将其放在同一位置 %2因此它最终产生:

%0

movl 24(%esp), %edx movl 28(%esp), %eax addl %edx,%eax movl %eax, 28(%esp) %eax使用%0%2使用%edx

答案 2 :(得分:1)

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

答案 3 :(得分:-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