在内联汇编中使用-masm=intel
和不使用任何.att_syntax
指令。 这适用于 GCC,我认为是 ICC,以及您使用的任何约束。其他方法没有。(请参阅 Can I use Intel syntax of x86 assembly with GCC?以获得一个简单的答案;这个答案探讨了究竟出了什么问题,包括 clang 13 和更早版本。)
这也适用于clang 14及更高版本。(尚未发布,但补丁是当前主干的一部分;请参阅https://reviews.llvm.org/D113707)。
Clang 13 和更早版本总是将 AT&T 语法用于内联 asm,无论是在替换操作数还是在组装 as 时op src, dst
。但更糟糕的clang -masm=intel
是,即使在使用诸如asm ("add {att | intel}
“: ... )` 之类的方言替代方案的 asm 模板的 Intel 方面也会这样做!
clang -masm=intel
在其内置汇编程序将语句转换为指令的某种内部表示之后,它仍然控制了它如何打印asm。asm()
例如Godbolt显示 clang13-masm=intel
转动add %0, 1
为add dword ptr [1], eax
,但 clang 主干产生add eax, 1
。
这个答案的其余部分谈论铿锵声尚未针对这个新的铿锵补丁进行更新。
Clang 确实支持 MSVC 样式的 asm 块中的 Intel 语法,但这很糟糕(没有限制,因此输入/输出必须通过内存。
如果您使用 clang 对寄存器名称进行硬编码,-masm=intel
则可以使用(或等效的-mllvm --x86-asm-syntax=intel
)。但它mov %eax, 5
在 Intel 语法模式下会阻塞,因此您不能让%0
扩展为 AT&T 语法寄存器名称。
-masm=intel
使编译器.intel_syntax noprefix
在其 asm 输出文件的顶部使用,并在 inline-asm 语句之外从 C 生成 asm 时使用 Intel-syntax。 在 asm 模板的底部使用.att_syntax
会破坏编译器的 asm ,因此错误消息PTR [rbp-4]
看起来像汇编器的垃圾(需要 AT&T 语法)。
“mov 的操作数太多”是因为在 AT&T 语法中,mov eax, ebx
是mov
从内存操作数(带有符号名称eax
)到内存操作数(带有符号名称ebx
)
有些人建议使用.intel_syntax noprefix
和.att_syntax prefix
围绕您的 asm 模板。这有时可以工作,但它是有问题的。并且与首选方法不兼容-masm=intel
。
“三明治”方法的问题:
当编译器将操作数替换为您的 asm 模板时,它将根据-masm=
. 这对于内存操作数总是会中断(寻址模式语法完全不同)。
即使是寄存器,它也会因clang而中断。 Clang 的内置汇编器%eax
在 Intel 语法模式下不接受作为寄存器名称,并且不接受.intel_syntax prefix
(noprefix
与 Intel 语法通常使用的相反)。
考虑这个函数:
int foo(int x) {
asm(".intel_syntax noprefix \n\t"
"add %0, 1 \n\t"
".att_syntax"
: "+r"(x)
);
return x;
}
它与 GCC ( Godbolt )组装如下:
movl %edi, %eax
.intel_syntax noprefix
add %eax, 1 # AT&T register name in Intel syntax
.att_syntax
三明治方法依赖于 GAS 接受%eax
作为寄存器名称,即使在 Intel 语法模式下也是如此。来自 GNU Binutils 的 GAS 可以,但 clang 的内置汇编器没有。
在 Mac 上,即使使用真正的 GCC,asm 输出也必须使用as
基于 clang 而不是 GNU Binutils 的汇编。
在该源代码上使用 clang 会抱怨:
<source>:2:35: error: unknown token in expression
asm(".intel_syntax noprefix \n\t"
^
<inline asm>:2:6: note: instantiated into assembly here
add %eax, 1
^
(错误消息的第一行没有很好地处理多行字符串文字。如果您使用;
代替\n\t
并将所有内容放在一行上,则 clang 错误消息效果更好,但源代码一团糟。)
"ri"
当编译器选择立即数时,我没有检查约束会发生什么;如果 GAS 在 Intel 语法模式下也默默地忽略它,它仍然会用$
IDK 装饰它。
PS:您的 asm 语句有一个错误:您在输出操作数上忘记了一个 early-clobber,因此没有什么能阻止编译器为%0
输出和%2
输入选择相同的寄存器,直到第二条指令才读取。然后mov
会破坏一个输入。
但是使用mov
asm 模板的第一条或最后一条指令通常也是一个错过优化的错误。在这种情况下,您可以并且应该只使用lea %0, [%1 + %2]
让编译器将结果添加到第三个寄存器中,非破坏性的。或者只是包装add
指令(使用"+r"
操作数和"r"
,让编译器担心数据移动。)如果无论如何它必须从内存中加载值,它可以将它放在正确的寄存器中,因此mov
不需要。
PS:可以使用GNU C inline asm dialect alternatives编写与-masm=intel
or一起使用的内联 asm 。例如att
void atomic_inc(int *p) {
asm( "lock add{l $1, %0 | %0, 1}"
: "+m" (*p)
:: "memory"
);
}
gcc -O2
用(-masm=att
是默认值)编译为
atomic_inc(int*):
lock addl $1, (%rdi)
ret
或与-masm=intel
:
atomic_inc(int*):
lock add DWORD PTR [rdi], 1
ret
请注意,l
AT&T 需要后缀dword ptr
,intel 需要后缀,因为 memory, immediate 并不意味着操作数大小。并且编译器为这两种情况填写了有效的寻址模式语法。
这适用于 clang,但只有 AT&T 版本被使用过。