https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78812
Bug ID: 78812 Summary: Wrong code generation due to hoisting memory load across function call Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: rtl-optimization Assignee: unassigned at gcc dot gnu.org Reporter: uweigand at gcc dot gnu.org Target Milestone: --- Compiling the following test case with g++ -Os -fpic on s390x-ibm-linux results in abort() being called unexpectedly: extern "C" void abort (void) __attribute__ ((__noreturn__)); class Transaction { public: bool Aborted; Transaction () : Aborted (false) { } ~Transaction () { if (!Aborted) abort (); } }; void test (Transaction &Trans) __attribute__ ((noinline)); void test (Transaction &Trans) { Trans.Aborted = true; } int main (void) { Transaction T; test (T); } What happens is that the destructor for Transaction is inlined into main (twice, once in the regular exit and once in the exception path). The load of the Aborted member is then moved by the code hoisting pass (pass_rtl_hoist) in gcse.c to a single location in basic block 2. However, that basic block ends with the call to the "test" routine, and for some reason, code hoisting thinks it therefore needs to move the hoisted instruction to *before* that call (see the CALL_P case of insert_insn_end_basic_block). This causes wrong code generation since that call actually modifies the memory that is being loaded. There seems to be other code that appears intended to recognize that case and avoid hoisting then (see prune_expressions), but that apparently only looks for abnormal edges, which we don't have here. Not sure how this is supposed to work correctly ...