https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109112
--- Comment #8 from Jakub Jelinek <jakub at gcc dot gnu.org> --- (In reply to Jason Merrill from comment #7) > Why don't the existing optimizations work on the artificial function the > same as any other function? i.e. like > > struct S { bool x; }; > void do_something(); > inline void assumption_1 (const S& s) noexcept { > if (s.x) __builtin_unreachable (); > } > void fn(S s) { > assumption_1 (s); > if (s.x) do_something(); > } > > which is also optimized as expected. Because the assumptions have different representation in the IL. While normal calls look like: ret = foo (arg1, arg2, arg3); and we can inline those etc., because the assumptions potentially contain side-effects which shouldn't be evaluated and therefore should e.g. not be inlined nor assumed that they are actually called, the representation is like: .ASSUME (&foo, arg1, arg2, arg3); where foo is that artificial assumption function which takes arg1, arg2, arg3. The above behaves like if (!foo (arg1, arg2, arg3)) __builtin_unreachable (); except that the function actually isn't called (nor emitted into assembly etc.). The assumption is if this function would be called and returned false at this spot, it would be UB. So, VRP walks the assumption function (after optimizations are performed on it, e.g. inlining into those and various other optimizations) backwards from the return value starting with [1, 1] and from that derives ranges for the arguments. Similarly to how for functions which aren't inlined but can be e.g. cloned it is essential to get IPA SRA and IPA CP etc. optimizations to tweak the arguments of functions (scalarize them, remove unneeded ones, replace others), it is needed that we optimize the assumptions similarly. The assumption functions should be always static and often will have a single reference (unless inlined multiple times / loop unrolled), so it is just fine to tweak them, just those optimizations will need to special case the IFN_ASSUME internal calls and treat .ASSUME (&foo, arg1, arg2, arg3); more like foo (arg1, arg2, arg3); (i.e. off by one argument and treat it as if there was a call edge from the .ASSUME caller to foo.