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 ...

Reply via email to