- Notifications
You must be signed in to change notification settings - Fork 15.3k
Description
Currently, when optimizations are enabled, flowing off the end of a function in C++ mode emits unreachable. This means that a function such as:
int get(int x) { if (x == 0) { } else std::abort(); }... is equivalent to simply calling abort(), which is caused by emitting unreachable at the end of the function, and having SimplifyCFG eliminate the x == 0 branch. This behavior does not reflect developer intent, and it's possible to fall through into other code sections as a result of simply treating this as optimizable UB.
On debug builds, @llvm.trap() is emitted at the end of the function, which generates a ud2 pseudo-instruction on x86-64. This should also be done with optimizations enabled.
Even with no warning flags enabled, clang emits
<source>:6:1: warning: non-void function does not return a value in all control paths [-Wreturn-type] 6 | } | ^
Flowing off the end of a function is a common developer mistake and basically never intentional. It should not be hidden by the optimizer because it may result in security vulnerabilities such as CVE-2014-9296.
If a developer actually wants the current behavior, they can simply write:
int get(int x) { if (x == 0) { __builtin_unreachable(); // or, since C++23 std::unreachable(); // or, alternatively [[assume(false)]]; } else std::abort(); }There is zero motivation for violating programmer intent and emitting security vulnerabilities here. This offers no optimization opportunities that could not be trivially obtained with more explicit code.