Yes, it's fine on gcc and clang, as long as the false
is definitely a compile-time constant. It won't compile on compilers like MSVC that don't support asm("");
at all.
I wouldn't recommend it, though. It's much more normal to use the preprocessor for arch-specific and implementation-specific code selection.
ISO C++ has basically nothing to say about it. asm
is an implementation-specific extension, so yes an implementation would be free to reject asm that didn't assemble, even in dead code. But the question is whether the compilers we actually use are like that.
The only meaningful question is what happens on actual compilers you care about.
On MSVC for example, asm("");
is not supported syntax, regardless of the contents of the string literal. So it's a syntax error at compile time everywhere. You can't use if(false){}
to protect code that doesn't even compile, just like you can't do so in template metaprogramming (unfortunately).
On compilers that support GNU extensions (the most widely used extensions to ISO C or C++ that include an asm("")
style of syntax), let's have a look on the Godbolt compiler explorer.
I picked mfence
because it's a valid instruction that doesn't need any input or output operands to make sense, because it doesn't modify any registers. And unlike nop
or something, it won't assemble on non-x86. (volatile
is implicit for a GNU extended-asm statement with no output operands.)
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 and clang don't even try to assemble the asm()
statement inside the if(false)
, even with optimization disabled.
Of course, -O0
stops them from treating int cond=0;
if(cond){}
as always false, because for consistent debugging they make asm that assumes every variable was modified in memory (by a debugger) between every statement. (All the stores/reloads is partly why un-optimized code is so slow).
With ARM or AArch64 gcc, nobarrier()
compiles just fine, to asm that will assemble just fine. e.g.
@ gcc7.2 -O3 for ARM32. Similar output with -O0
nobarrier():
bx lr
GCC doesn't even try to parse the contents of the asm
template, so it will compile barrier()
for AArch64, but it includes an instruction that won't assemble.
@ gcc6.3 -O0 -DUNCONDITIONAL
barrier():
mfence
nop
ret
If you care about the distinction between compiling and assembling, clang is different.
clang6.0 -O0 -target mips -DUNCONDITIONAL
says
<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
This doesn't matter if you only care about C++ -> object file, not making .s
asm source output. For that, it behaves the same as gcc. if(false)
does protect an asm
statement with non-MIPS instructions from being parsed:
@ 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