https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126567

            Bug ID: 126567
           Summary: [17 Regression] Wrong code with
                    factor_out_conditional_load
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: tree-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ktkachov at gcc dot gnu.org
  Target Milestone: ---

/* Wrong code: factor_out_conditional_load () in gcc/tree-ssa-phiopt.cc moves a
   conditional load past a store that follows it in the same arm.

   When get_virtual_phi (merge) is non-NULL the transform demands
        gimple_vuse (loadN) == the virtual PHI argument on edge eN
   which proves there is no VDEF between the load and the merge.  When the
   merge block has no virtual PHI the only check left is
        gimple_vuse (load0) == gimple_vuse (load1)
   (tree-ssa-phiopt.cc:4170), which says nothing about statements AFTER the
   load.  is_factor_profitable () tolerates up to
   --param=phiopt-factor-max-stmts-live further GIMPLE_ASSIGNs in the arm, and
   a store is a GIMPLE_ASSIGN, so the factored load is inserted at
   gsi_after_labels (merge), physically behind that store, and reads the
   post-store value.

   Every sibling in the file (cond_store_replacement_limited,
   cselim_candidate, cond_if_else_store_replacement_limited,
   cond_if_else_store_replacement) bails out when get_virtual_phi is NULL.

   To reach the no-virtual-PHI path .MEM must be dead at the merge, so nothing
   after the merge may touch memory.  That rules out abort(), a return, or any
   volatile access as the reporting channel: each of them keeps .MEM live and
   makes the virtual PHI reappear.  The result is therefore reported through
   the process exit status with a register-only exit syscall.

   Correct behaviour: exit status 0.  Miscompiled: exit status 1.
   -O1 and above; needs the late phiopt, so -fno-ssa-phiopt makes it pass.  */

int a, b;

#if defined(__aarch64__)
#define EXIT_WITH(x) \
  __asm__ __volatile__ ("mov x8, 94\n\tmov w0, %w0\n\tsvc 0" \
                        :: "r" (x) : "x0", "x8")
#elif defined(__x86_64__)
#define EXIT_WITH(x) \
  __asm__ __volatile__ ("syscall" :: "a" (60), "D" (x) : "rcx", "r11")
#else
#define EXIT_WITH(x) __builtin_exit (x) /* has a VDEF, will not trigger */
#endif

__attribute__((noipa, noreturn))
static void f (int c, int *p, int *r, int want)
{
  int v;

  if (c)
    {
      v = *p;                   /* must read the value BEFORE the store */
      *p = 7;
    }
  else
    v = *r;

  /* No memory operand from here on: .MEM is dead at the merge block, so
     into-SSA never puts a virtual PHI there.  */
  for (;;)
    EXIT_WITH (v != want);
}

int main (void)
{
  volatile int five = 5, nine = 9;

  a = five;
  b = nine;
  f (1, &a, &b, five);
}

This exits on aarch64 with status code 1 at -O1 and above and status code 0 at
-O0

Reply via email to