在未使用但不支持的体系结构中,asm()
代码块的行为是什么? 例如,如果我在 ARM 上编译以下代码会发生什么
if (false) {
asm volatile("x86specificinstruction" ...);
}
这是否有效C++? 代码会编译吗? 标准对这种情况有什么规定吗?
这取决于如何处理这个问题的实现。 来自 [dcl.asm]
asm
声明具有以下形式asm-definition: attribute-specifier-seq opt asm ( string-literal ) ;
asm
声明是有条件支持的;它的含义是实现定义的。asm 定义中的可选属性说明符 seq 适用于 asm 声明。[注意:通常它用于通过实现将信息传递给汇编程序。
强调我的
也就是说,您可以将代码放在#if #endif
中,使用SFINAE或使用if constexpr
。 如果不满足条件,所有这些都将从编译中删除代码。
是的,在 gcc 和 clang 上很好,只要false
绝对是编译时常量。 它不会在像 MSVC 这样根本不支持asm("");
的编译器上编译。
不过,我不会推荐它。 使用预处理器进行特定于架构和特定于实现的代码选择要正常得多。
ISO C++基本上没什么好说的。asm
是一个特定于实现的扩展,所以是的,实现可以自由地拒绝未组装的 ASM,即使是在死代码中。 但问题是我们实际使用的编译器是否是这样的。
唯一有意义的问题是,在你关心的实际编译器上会发生什么。
例如,在 MSVC 上,无论字符串文本的内容如何,都不支持asm("");
语法。 所以在编译时到处都是语法错误。你不能使用if(false){}
来保护甚至无法编译的代码,就像你不能在模板元编程中这样做一样(不幸的是(。
在支持 GNU扩展(包含asm("")
语法风格的 ISO C 或 C++ 的最广泛使用的扩展(的编译器上,让我们来看看 Godbolt 编译器资源管理器。
我选择mfence
是因为它是一个有效的指令,不需要任何输入或输出操作数即可有意义,因为它不会修改任何寄存器。 与nop
或其他东西不同,它不会在非x86上组装。 (volatile
对于没有输出操作数的 GNU 扩展 asm 语句是隐式的。
void nobarrier() {
//int condition = 0; if(condition) // wouldn't work at -O0
if(false) {
// only an error with MSVC
asm("mfence" ::: "memory");
}
}
#ifdef UNCONDITIONAL
void barrier() {
// gcc compiles separately from assembling
// clang/LLVM's built-in assembler chokes at compile time
asm("mfence" ::: "memory");
}
#endif
GCC 和 clang 甚至不尝试在if(false)
中组装asm()
语句,即使禁用了优化。
当然,-O0
阻止它们将int cond=0;
if(cond){}
视为始终为假,因为为了保持一致的调试,它们会制作 asm,假设每个变量在每个语句之间的内存中(由调试器(修改。 (所有的存储/重新加载是未优化的代码如此缓慢的部分原因(。
使用 ARM 或 AArch64 gcc,nobarrier()
编译得很好,到 asm 可以组装得很好。 例如
@ gcc7.2 -O3 for ARM32. Similar output with -O0
nobarrier():
bx lr
GCC 甚至不会尝试解析asm
模板的内容,因此它将为 AArch64 编译barrier()
,但它包含一个不会汇编的指令。
@ gcc6.3 -O0 -DUNCONDITIONAL
barrier():
mfence
nop
ret
如果你关心编译和汇编之间的区别,Clang 是不同的。
叮叮当当6.0-O0 -target mips -DUNCONDITIONAL
说
<source>:13:9: error: unknown instruction
asm("mfence" ::: "memory");
^
<inline asm>:1:2: note: instantiated into assembly here
mfence
^
1 error generated.
Compiler returned: 1
如果您只关心C++ -> 对象文件,而不进行 asm 源输出.s
这并不重要。 为此,它的行为与 gcc 相同。if(false)
确实可以保护具有非 MIPS 指令的asm
语句不被解析:
@ clang6.0 -O0 -target mips
nobarrier(): # @nobarrier()
addiu $sp, $sp, -8
sw $fp, 4($sp) # 4-byte Folded Spill
move $fp, $sp
move $sp, $fp
lw $fp, 4($sp) # 4-byte Folded Reload
addiu $sp, $sp, 8
jr $ra
nop