I think that in this case the undefined behaviour is pretty common between the most popular compilers. If you disassemble your foo
function, you will find these instructions in almost every version of GCC:
push rbp
mov rbp, rsp
nop
pop rbp
ret
This code is doing nothing, exactly what you want. However, it is not even following the calling convention rules whenever a result from a function is expected. The result should be passed to the caller by the eax/rax
register. In foo
, this register is not even modified and this is the reason why you get undefined behaviour.
Omitting a return is like saying: "I do not want to follow the rules you gave me". Clang produces similar code and I guess other compilers (unless further options or optimizations are applied) reason in the same way. Nothing stops you from doing that, neither a compiler. Undefined behaviour is the price for this freedom.
The proof that the compiler puts its faith on the code written is that the eax/rax
register is really considered as the store for the function's result. In your case eax
is never used before in that code, and its last value is zero. But try with this:
int i = 12345;
int m = 12345;
m *= 3;
i = foo();
printf("i is: %d", i);
The output is probably going to be 37035 even if that's the m
's value.