https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121936
Bug ID: 121936
Summary: Invalid optimisation (at O3) based on bodies of
weak-defined functions
Product: gcc
Version: 16.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: ipa
Assignee: unassigned at gcc dot gnu.org
Reporter: iains at gcc dot gnu.org
CC: jamborm at gcc dot gnu.org, jason at gcc dot gnu.org
Target Milestone: ---
At optimisation level 3, the bodies of referenced weak defined functions can be
inspected and evaluation of that used to guide optimisation of callers (but
without actually inlining the callee).
This is not valid if the evaluation depends on any content of the callee that
is "unspecified" per the language standard(s). This is because the actual
callee that prevails at dynamic load time could be produced by an
implementation that makes a different choice for that "unspecified" content.
In general, it would seem quite challenging to determine cases where the
dependent analysis was reasonable and therefore, unless this can be done, we
should disable this optimisation. Note that at least one other platform has
already taken this step.
In the example below the weak definition `depends_on_unspecified_content()` has
a result that depends on the order of call argument evaluation.
(On x86_64 in this example) we conclude that `check_arg_1_value ()` will always
return true and that therefore `depends_on_unspecified_content()` will always
abort if x == 0.
We then use this information to elide the `if (f(0) == 0)` in `main()` and
unconditionally return 20.
This is wrong code because a different implementation can choose to evaluate
the arguments in a different order, which can mean that
`depends_on_unspecified_content()` is no longer guaranteed to abort for x == 0.
This is not hypothetical, current clang trunk indeed evaluates in the opposite
order.
Since we have no mechanism to guarantee the implementation that will produce
the edition of `depends_on_unspecified_content()` that is linked at dynamic
load time, this can break at runtime.
With thanks to WG21 colleagues for the suggested test-case basis.
======
class Counter {
int count = 0;
public:
int nextCount() { return ++count; }
};
// Will return true if args are evaluated R => L.
[[gnu::used, gnu::noinline]]
inline bool
check_arg_1_value (int a, int b)
{
return a == 2;
}
// Will abort if x == 0 and args are evaluated R => L.
[[gnu::used, gnu::noinline]]
inline int
depends_on_unpsecified_content (int x)
{
Counter c;
if (check_arg_1_value (c.nextCount(), c.nextCount())
&& x == 0)
__builtin_abort();
return x;
}
int
main ()
{
if (f(0) == 0)
return 5;
return 20;
}
=====
https://godbolt.org/z/bj487x16K