内联程序集约束修饰符 = 和 +
Inline assembly constraint modifiers = and +
我写了一个简单的程序,其中包含内联汇编代码。我的代码只是添加变量 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
(分别用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));
对%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
- 内联程序集printf将整数解释为地址
- 正在解码MSVC 32位版本的程序集(作业).没有手术做什么
- 具有外部"c"和程序集的未定义函数
- 用于将C++代码转换为 Web 程序集的脚本未终止
- 为什么我的C++程序的程序集输出充满了 .ascii,没有汇编代码?
- CoreCLR 中的检测探查器 - 将帮助程序程序集加载到 dotnet 进程的方法
- 不同于按值传递和常量引用传递的程序集
- 为什么在堆栈和堆上创建变量会产生相同的程序集代码?
- C++变量在调用 x64 程序集函数后重置为 0
- 如何将C++子例程链接到 x86 程序集程序?
- Qt 网页程序集缓存
- 测试操作系统时执行程序集"sti"时虚拟框崩溃
- 为什么从 constexpr 引用生成的程序集代码与从 constexpr 指针生成的程序集代码不同?
- 将内联程序集尾调用函数尾声替换为用于x86/x64 msvc的Intrinsics
- 解析 C# 中的C++程序集
- 64 位进程中的 AnyCPU C# DLL 无法引用 64 位C++ DLL(给出错误:无法加载文件或程序集)
- 使用CLANG内联程序集创建C++预增量操作
- 是否可以在C++中基于程序集输出(.dll或.exe)定义变量
- 用于比较基元类型的std::可选的有趣程序集
- 内联程序集约束修饰符 = 和 +