未使用的 asm() 在不受支持的体系结构上的行为



在未使用但不支持的体系结构中,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

最新更新