我写了一个包含内联汇编代码的简单程序。 我的代码只是添加变量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;
答案 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)
表示%0
为b
并且用于输入和输出,{{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
如果你想要,你可以叫我爱因斯坦......
这让我想起了一个“欺骗”的采访
阅读和哭泣