[fpc-devel] Dangerous optimization in CASE..OF
Martok
listbox at martoks-place.de
Tue Jul 25 19:31:08 CEST 2017
As has just been pointed out to me, we all misdiagnosed that example.
TL;DR: you did test undefined behaviour, only a different one. The example
actually proves that clang and gcc agree with the MSDN article in that (even
simple) C++ enums are low-level.
I have verified that Clang/LLVM generates the same code as GCC for the switch
statement itself. It does correctly check for the maximum jmptable index (via
[sub 0x7;ja]) and only then jumps.
LLVM however does generate an UD2(0F0B) trap instruction for several programmer
errors, such as the control flow reaching the end of a non-void function without
return. *That* is why we get a SIGILL when no switch label matches.
In -O1 and -Os, these debug instructions are removed again.
As predicted, there does indeed appear to be a Clang bug: GCC correctly warns
"control reaches end of non-void function", while Clang only emits the UD2
instruction (so it detected it) and does not print the warning.
Completing the function works as expected:
> *snip*
> return 2;
> case e8:
> printf("Hello\n");
> return 3;
> }
return -1;
> }
>
> int main()
> *snip*
Another equivalent solution is to have the return in the switch statement's
default label. No UD2 is emitted then.
It follows that the compiler concludes that the default can be matched, even
when all named elements are listed. Therefore, enum variables may contain
unnamed values. Therefore, C++ enums must be Low-Level. QED.
> "Ungültiger Maschinenbefehl (Speicherabzug geschrieben)" = Invalid opcode (memory dump written).
> Why? Because it does not range check before entering the jump table.
I really should have noticed that. A jump into nonexecutable memory would be
SIGSEGV, not SIGILL.
--
Martok
More information about the fpc-devel
mailing list