Sure, that optimization interacts badly with the optimization that removes the infinite loop. But half the point of UB is to avoid needing to deal with such interactions, because they are defined out of existence.
If I think about asm:
function1:
(do stuff)
jp function1
ret
function2: (other stuff)
ret
main: call function1
call function2
the 2nd call might happen internally due to branch prediction but in practice it shouldn't and the processor fixes thisOh yeah and TFA also goes with:
> The funny bit is that C got this right.(...) but C included one more rule: loops whose controlling expression is a constant expression may not be assumed to terminate.
Well, duh! A broken clock is right twice a day it seems
This is literally the opposite behaviour compared to what is written in the source code, even when you "assume the infinite loop terminates".
main:
unreachable():
push rbx
...
Due to the undefined behavior, it decides calling main must be impossible, so the easiest thing to do is just give up, don't bother defining the rest of it. You can also do the same with std::unreachable(). But the label for the function still sticks around for some reason, so when you jump to it, it falls through. Which leads to the really stupid fact that reordering the functions changes the behavior.I assume there are good reasons they can't just completely delete the label. Maybe it would screw linking, or with cases where you deliberately have multiple labels for the same function. And if the effect is only visible due to undefined behavior, it's not technically wrong. But I have always thought this is such a stupid case, surely it can't be that complex to add a trap instruction, even in an optimized build you shouldn't really care if it slows down a function that's "never called".
Probably the process was one optimization pass saw that the function will never return due to an infinite loop, and removed the function return from the IR of the function, then a later pass saw that the infinite loop was a no-op and undefined so removed that as well, leaving a function that basically did nothing, not even return.
Not really true, most instructions set have instructions specifically to implement functions as found in normal programming languages. x86 has CALL and RET for example.
https://en.wikipedia.org/wiki/X86_calling_conventions
Of course the compiler can stil optimize by inlining etc., but functions still mostly exist at the assembly level.