# Bootstrapped&tested on x86_64-unknown-linux-gnu at r193394.
# Bootstrapped&tested on x86_64-unknown-linux-gnu at r193924.
# Bootstrapped&tested on powerpc64-unknown-linux-gnu at r193924.

gcc/
	PR rtl-optimization/55006
	* ira.c (build_insn_chain): Look at the rtx code to determine if
	the use is actually a SUBREG.
	* df.h (enum df_ref_flags): Add new flags DF_REF_IN_EQUAL_NOTE and
	DF_REF_IN_EQUIV_NOTE, make DF_REF_IN_NOTE a combination of the two,
	and remove DF_REF_SUBREG to keep the number of flags at 16.  Renumber.
	* df-core.c (df_ref_dump): Print 'e' for a REG_EQUAL use,
	and 'q' for a REG_EQUIV use.
	* df-problems.c (df_chain_dump): Likewise.
	(df_remove_dead_eq_notes): Simplify condition.  Look only at refs
	in the kind of note we're processing.  Add sanity check to make
	sure that the SCAN problem is up-to-date, i.e. the EQ_USE reg is
	really mentioned in the note.
	* df-scan.c (df_uses_create): Adjust assert.
	(df_notes_rescan): Split handling of REG_EQUAL and REG_EQUIV notes.
	(df_insn_refs_collect): Likewise.
	(df_def_record_1): Do not set DF_REF_SUBREG.
	(df_uses_record): Likewise.
	* fwprop.c (update_df): Pass DF_REF_IN_EQUAL_NOTE.
	(forward_propagate_and_simplify): Only look in REG_EQUAL notes.
	(forward_propagate_into): Only look in REG_EQUAL notes.
	* gcse.c (gcse_emit_move_after): Remove.
	(pre_delete): Use emit_insn_after.
	(hoist_code): Likewise.
	* cprop.c (try_replace_reg): Only attach notes if the source was
	folded to a CPROP constant.
	(cprop_jump): Likewise.
	* cse.c (cse_main): Add the DF_NOTE problem.

testsuite/
	PR rtl-optimization/55006
	* testsuite/gcc.dg/pr55006.c: New test.

Index: ira.c
===================================================================
--- ira.c	(revision 193924)
+++ ira.c	(working copy)
@@ -3669,7 +3669,7 @@ build_insn_chain (void)
 		       fabricated use. */
 		    if (DF_REF_FLAGS_IS_SET (use, DF_REF_READ_WRITE)
 			&& !DF_REF_FLAGS_IS_SET (use, DF_REF_ZERO_EXTRACT)
-			&& DF_REF_FLAGS_IS_SET (use, DF_REF_SUBREG))
+			&& GET_CODE (DF_REF_REG (use)) == SUBREG)
 		      continue;
 
 		    /* Add the last use of each var to dead_or_set.  */
Index: df.h
===================================================================
--- df.h	(revision 193924)
+++ df.h	(working copy)
@@ -87,30 +87,31 @@ enum df_ref_flags
        bottom of the block.  This is never set for regular refs.  */
     DF_REF_AT_TOP = 1 << 1,
 
-    /* This flag is set if the use is inside a REG_EQUAL or REG_EQUIV
-       note.  */
-    DF_REF_IN_NOTE = 1 << 2,
+    /* This flag is set if the use is inside a REG_EQUAL note,
+       or a REG_EQUIV note, or any one of the two types of note.  */
+    DF_REF_IN_EQUAL_NOTE = 1 << 2,
+    DF_REF_IN_EQUIV_NOTE = 1 << 3,
+    DF_REF_IN_NOTE = DF_REF_IN_EQUAL_NOTE | DF_REF_IN_EQUIV_NOTE,
 
     /* This bit is true if this ref can make regs_ever_live true for
        this regno.  */
-    DF_HARD_REG_LIVE = 1 << 3,
-
+    DF_HARD_REG_LIVE = 1 << 4,
 
     /* This flag is set if this ref is a partial use or def of the
        associated register.  */
-    DF_REF_PARTIAL = 1 << 4,
+    DF_REF_PARTIAL = 1 << 5,
 
     /* Read-modify-write refs generate both a use and a def and
        these are marked with this flag to show that they are not
        independent.  */
-    DF_REF_READ_WRITE = 1 << 5,
+    DF_REF_READ_WRITE = 1 << 6,
 
     /* This flag is set if this ref, generally a def, may clobber the
        referenced register.  This is generally only set for hard
        registers that cross a call site.  With better information
        about calls, some of these could be changed in the future to
        DF_REF_MUST_CLOBBER.  */
-    DF_REF_MAY_CLOBBER = 1 << 6,
+    DF_REF_MAY_CLOBBER = 1 << 7,
 
     /* This flag is set if this ref, generally a def, is a real
        clobber. This is not currently set for registers live across a
@@ -121,25 +122,20 @@ enum df_ref_flags
        clobber is to a subreg.  So in order to tell if the clobber
        wipes out the entire register, it is necessary to also check
        the DF_REF_PARTIAL flag.  */
-    DF_REF_MUST_CLOBBER = 1 << 7,
-
+    DF_REF_MUST_CLOBBER = 1 << 8,
 
     /* If the ref has one of the following two flags set, then the
        struct df_ref can be cast to struct df_ref_extract to access
        the width and offset fields.  */
 
     /* This flag is set if the ref contains a SIGN_EXTRACT.  */
-    DF_REF_SIGN_EXTRACT = 1 << 8,
+    DF_REF_SIGN_EXTRACT = 1 << 9,
 
     /* This flag is set if the ref contains a ZERO_EXTRACT.  */
-    DF_REF_ZERO_EXTRACT = 1 << 9,
+    DF_REF_ZERO_EXTRACT = 1 << 10,
 
     /* This flag is set if the ref contains a STRICT_LOW_PART.  */
-    DF_REF_STRICT_LOW_PART = 1 << 10,
-
-    /* This flag is set if the ref contains a SUBREG.  */
-    DF_REF_SUBREG = 1 << 11,
-
+    DF_REF_STRICT_LOW_PART = 1 << 11,
 
     /* This bit is true if this ref is part of a multiword hardreg.  */
     DF_REF_MW_HARDREG = 1 << 12,
Index: df-core.c
===================================================================
--- df-core.c	(revision 193924)
+++ df-core.c	(working copy)
@@ -2127,9 +2127,10 @@ static void
 df_ref_dump (df_ref ref, FILE *file)
 {
   fprintf (file, "%c%d(%d)",
-	   DF_REF_REG_DEF_P (ref)
-	   ? 'd'
-	   : (DF_REF_FLAGS (ref) & DF_REF_IN_NOTE) ? 'e' : 'u',
+	   DF_REF_REG_DEF_P (ref) ? 'd'
+	   : (DF_REF_FLAGS (ref) & DF_REF_IN_EQUAL_NOTE) ? 'e'
+	   : (DF_REF_FLAGS (ref) & DF_REF_IN_EQUIV_NOTE) ? 'q'
+	   : 'u',
 	   DF_REF_ID (ref),
 	   DF_REF_REGNO (ref));
 }
Index: df-problems.c
===================================================================
--- df-problems.c	(revision 193924)
+++ df-problems.c	(working copy)
@@ -72,9 +72,10 @@ df_chain_dump (struct df_link *link, FIL
   for (; link; link = link->next)
     {
       fprintf (file, "%c%d(bb %d insn %d) ",
-	       DF_REF_REG_DEF_P (link->ref)
-	       ? 'd'
-	       : (DF_REF_FLAGS (link->ref) & DF_REF_IN_NOTE) ? 'e' : 'u',
+	       DF_REF_REG_DEF_P (link->ref) ? 'd'
+	       : (DF_REF_FLAGS (link->ref) & DF_REF_IN_EQUAL_NOTE) ? 'e'
+	       : (DF_REF_FLAGS (link->ref) & DF_REF_IN_EQUIV_NOTE) ? 'q'
+	       : 'u',
 	       DF_REF_ID (link->ref),
 	       DF_REF_BBNO (link->ref),
 	       DF_REF_IS_ARTIFICIAL (link->ref)
@@ -2893,27 +2894,39 @@ df_remove_dead_eq_notes (rtx insn, bitma
 
   while (link)
     {
+      /* A mask for the kind of note we should be looking for.  Because
+	 there is always at most one REG_EQUAL and/or at most one REG_EQUIV
+	 note, and we visit the notes one at a time, we can tell with this
+	 flag whether the EQ_USE we're looking at is in the note we are
+	 processing.  */
+      int note_kind_flag = DF_REF_IN_EQUAL_NOTE;
       switch (REG_NOTE_KIND (link))
 	{
-	case REG_EQUAL:
 	case REG_EQUIV:
+	  note_kind_flag = DF_REF_IN_EQUIV_NOTE;
+	  /* fallthru */
+	case REG_EQUAL:
 	  {
-	    /* Remove the notes that refer to dead registers.  As we have at most
-	       one REG_EQUAL/EQUIV note, all of EQ_USES will refer to this note
-	       so we need to purge the complete EQ_USES vector when removing
-	       the note using df_notes_rescan.  */
 	    df_ref *use_rec;
 	    bool deleted = false;
 
 	    for (use_rec = DF_INSN_EQ_USES (insn); *use_rec; use_rec++)
 	      {
 		df_ref use = *use_rec;
-		if (DF_REF_REGNO (use) > FIRST_PSEUDO_REGISTER
-		    && DF_REF_LOC (use)
-		    && (DF_REF_FLAGS (use) & DF_REF_IN_NOTE)
-		    && ! bitmap_bit_p (live, DF_REF_REGNO (use))
-		    && loc_mentioned_in_p (DF_REF_LOC (use), XEXP (link, 0)))
+
+		if (! (DF_REF_FLAGS (use) & note_kind_flag))
+		  continue;
+
+		/* A REG_EQUAL/REG_EQUIV note may not refer registers that
+		   are not live at the point of the EQ_USE.  Notes referencing
+		   registers without a definition are also dead.  (And wrong,
+		   but that's another story...)  */
+		if ((DF_REF_REGNO (use) > FIRST_PSEUDO_REGISTER
+		     && ! bitmap_bit_p (live, DF_REF_REGNO (use)))
+		    || DF_REG_DEF_COUNT (DF_REF_REGNO (use)) == 0)
 		  {
+		    gcc_checking_assert (reg_mentioned_p (DF_REF_REAL_REG (use),
+							  XEXP (link, 0)));
 		    deleted = true;
 		    break;
 		  }
Index: df-scan.c
===================================================================
--- df-scan.c	(revision 193924)
+++ df-scan.c	(working copy)
@@ -673,13 +673,16 @@ df_scan_blocks (void)
 }
 
 /* Create new refs under address LOC within INSN.  This function is
-   only used externally.  REF_FLAGS must be either 0 or DF_REF_IN_NOTE,
-   depending on whether LOC is inside PATTERN (INSN) or a note.  */
+   only used externally.  REF_FLAGS must be either 0 or one of the
+   flavors of DF_REF_IN_NOTE flags, depending on whether LOC is
+   inside PATTERN (INSN) or a note.  */
 
 void
 df_uses_create (rtx *loc, rtx insn, int ref_flags)
 {
-  gcc_assert (!(ref_flags & ~DF_REF_IN_NOTE));
+  gcc_checking_assert (ref_flags == 0
+		       || ref_flags == DF_REF_IN_EQUAL_NOTE
+		       || ref_flags == DF_REF_IN_EQUIV_NOTE);
   df_uses_record (NULL, loc, DF_REF_REG_USE,
                   BLOCK_FOR_INSN (insn),
                   DF_INSN_INFO_GET (insn),
@@ -2203,10 +2206,17 @@ df_notes_rescan (rtx insn)
 	  switch (REG_NOTE_KIND (note))
 	    {
 	    case REG_EQUIV:
+	      df_uses_record (&collection_rec,
+			      &XEXP (note, 0), DF_REF_REG_USE,
+			      bb, insn_info, DF_REF_IN_EQUIV_NOTE);
+	      break;
+
 	    case REG_EQUAL:
 	      df_uses_record (&collection_rec,
 			      &XEXP (note, 0), DF_REF_REG_USE,
-			      bb, insn_info, DF_REF_IN_NOTE);
+			      bb, insn_info, DF_REF_IN_EQUAL_NOTE);
+	      break;
+
 	    default:
 	      break;
 	    }
@@ -2959,8 +2969,6 @@ df_def_record_1 (struct df_collection_re
       if (df_read_modify_subreg_p (dst))
 	flags |= DF_REF_READ_WRITE | DF_REF_PARTIAL;
 
-      flags |= DF_REF_SUBREG;
-
       df_ref_record (DF_REF_REGULAR, collection_rec,
 		     dst, loc, bb, insn_info, DF_REF_REG_DEF, flags);
     }
@@ -3174,7 +3182,7 @@ df_uses_record (struct df_collection_rec
 		{
 		  df_uses_record (collection_rec, &SUBREG_REG (dst),
 				  DF_REF_REG_USE, bb, insn_info,
-				  flags | DF_REF_READ_WRITE | DF_REF_SUBREG);
+				  flags | DF_REF_READ_WRITE);
 		  break;
 		}
 	      /* Fall through.  */
@@ -3447,17 +3455,21 @@ df_insn_refs_collect (struct df_collecti
   collection_rec->eq_use_vec.truncate (0);
   collection_rec->mw_vec.truncate (0);
 
-  /* Process REG_EQUIV/REG_EQUAL notes.  */
+  /* Process REG_EQUIV/REG_EQUAL/REG_NON_LOCAL_GOTO notes.  */
   for (note = REG_NOTES (insn_info->insn); note;
        note = XEXP (note, 1))
     {
       switch (REG_NOTE_KIND (note))
         {
         case REG_EQUIV:
+          df_uses_record (collection_rec,
+                          &XEXP (note, 0), DF_REF_REG_USE,
+                          bb, insn_info, DF_REF_IN_EQUIV_NOTE);
+          break;
         case REG_EQUAL:
           df_uses_record (collection_rec,
                           &XEXP (note, 0), DF_REF_REG_USE,
-                          bb, insn_info, DF_REF_IN_NOTE);
+                          bb, insn_info, DF_REF_IN_EQUAL_NOTE);
           break;
         case REG_NON_LOCAL_GOTO:
           /* The frame ptr is used by a non-local goto.  */
Index: fwprop.c
===================================================================
--- fwprop.c	(revision 193924)
+++ fwprop.c	(working copy)
@@ -930,7 +930,7 @@ update_df (rtx insn, rtx note)
 
   if (note)
     {
-      df_uses_create (&XEXP (note, 0), insn, DF_REF_IN_NOTE);
+      df_uses_create (&XEXP (note, 0), insn, DF_REF_IN_EQUAL_NOTE);
       df_notes_rescan (insn);
     }
   else
@@ -1307,7 +1307,7 @@ forward_propagate_and_simplify (df_ref u
   else
     {
       rtx note = find_reg_note (use_insn, REG_EQUAL, NULL_RTX);
-      if (DF_REF_FLAGS (use) & DF_REF_IN_NOTE)
+      if (DF_REF_FLAGS (use) & DF_REF_IN_EQUAL_NOTE)
 	loc = &XEXP (note, 0);
       else
 	loc = &SET_SRC (use_set);
@@ -1369,7 +1369,7 @@ forward_propagate_into (df_ref use)
 
   /* Check if the use is still present in the insn!  */
   use_insn = DF_REF_INSN (use);
-  if (DF_REF_FLAGS (use) & DF_REF_IN_NOTE)
+  if (DF_REF_FLAGS (use) & DF_REF_IN_EQUAL_NOTE)
     parent = find_reg_note (use_insn, REG_EQUAL, NULL_RTX);
   else
     parent = PATTERN (use_insn);
Index: gcse.c
===================================================================
--- gcse.c	(revision 193924)
+++ gcse.c	(working copy)
@@ -255,8 +255,6 @@ int flag_rerun_cse_after_global_opts;
 /* An obstack for our working variables.  */
 static struct obstack gcse_obstack;
 
-struct reg_use {rtx reg_rtx; };
-
 /* Hash table of expressions.  */
 
 struct expr
@@ -504,7 +502,6 @@ static void trim_ld_motion_mems (void);
 static void update_ld_motion_stores (struct expr *);
 static void clear_modify_mem_tables (void);
 static void free_modify_mem_tables (void);
-static rtx gcse_emit_move_after (rtx, rtx, rtx);
 static bool is_too_expensive (const char *);
 
 #define GNEW(T)			((T *) gmalloc (sizeof (T)))
@@ -2482,36 +2479,6 @@ pre_insert_copies (void)
       }
 }
 
-/* Emit move from SRC to DEST noting the equivalence with expression computed
-   in INSN.  */
-
-static rtx
-gcse_emit_move_after (rtx dest, rtx src, rtx insn)
-{
-  rtx new_rtx;
-  rtx set = single_set (insn), set2;
-  rtx note;
-  rtx eqv;
-
-  /* This should never fail since we're creating a reg->reg copy
-     we've verified to be valid.  */
-
-  new_rtx = emit_insn_after (gen_move_insn (dest, src), insn);
-
-  /* Note the equivalence for local CSE pass.  */
-  set2 = single_set (new_rtx);
-  if (!set2 || !rtx_equal_p (SET_DEST (set2), dest))
-    return new_rtx;
-  if ((note = find_reg_equal_equiv_note (insn)))
-    eqv = XEXP (note, 0);
-  else
-    eqv = SET_SRC (set);
-
-  set_unique_reg_note (new_rtx, REG_EQUAL, copy_insn_1 (eqv));
-
-  return new_rtx;
-}
-
 /* Delete redundant computations.
    Deletion is done by changing the insn to copy the `reaching_reg' of
    the expression into the result of the SET.  It is left to later passes
@@ -2551,7 +2518,8 @@ pre_delete (void)
 		if (expr->reaching_reg == NULL)
 		  expr->reaching_reg = gen_reg_rtx_and_attrs (SET_DEST (set));
 
-		gcse_emit_move_after (SET_DEST (set), expr->reaching_reg, insn);
+		emit_insn_after (gen_move_insn (SET_DEST (set),
+						expr->reaching_reg), insn);
 		delete_insn (insn);
 		occr->deleted_p = 1;
 		changed = 1;
@@ -3363,8 +3331,8 @@ hoist_code (void)
 		    expr->reaching_reg
 		      = gen_reg_rtx_and_attrs (SET_DEST (set));
 
-		  gcse_emit_move_after (SET_DEST (set), expr->reaching_reg,
-					insn);
+		  emit_insn_after (gen_move_insn (SET_DEST (set),
+						  expr->reaching_reg), insn);
 		  delete_insn (insn);
 		  occr->deleted_p = 1;
 		  changed = 1;
Index: cprop.c
===================================================================
--- cprop.c	(revision 193924)
+++ cprop.c	(working copy)
@@ -769,10 +769,11 @@ try_replace_reg (rtx from, rtx to, rtx i
 	success = 1;
 
       /* If we've failed perform the replacement, have a single SET to
-	 a REG destination and don't yet have a note, add a REG_EQUAL note
-	 to not lose information.  */
-      if (!success && note == 0 && set != 0 && REG_P (SET_DEST (set)))
-	note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (src));
+	 a REG destination, don't yet have a note, and have simplified
+	 SRC to a constant, add a REG_EQUAL note to not lose information.  */
+      if (!success && note == 0 && set != 0 && REG_P (SET_DEST (set))
+	  && cprop_constant_p (src))
+	note = set_unique_reg_note (insn, REG_EQUAL, src);
     }
 
   if (set && MEM_P (SET_DEST (set)) && reg_mentioned_p (from, SET_DEST (set)))
@@ -931,8 +932,9 @@ cprop_jump (basic_block bb, rtx setcc, r
 	     we need to attach a note to the branch itself to make this
 	     optimization work.  */
 
-	  if (!rtx_equal_p (new_rtx, note_src))
-	    set_unique_reg_note (jump, REG_EQUAL, copy_rtx (new_rtx));
+	  if (!rtx_equal_p (new_rtx, note_src)
+	      && cprop_constant_p (new_rtx))
+	    set_unique_reg_note (jump, REG_EQUAL, new_rtx);
 	  return 0;
 	}
 
Index: cse.c
===================================================================
--- cse.c	(revision 193924)
+++ cse.c	(working copy)
@@ -6520,6 +6520,7 @@ cse_main (rtx f ATTRIBUTE_UNUSED, int nr
   int i, n_blocks;
 
   df_set_flags (DF_LR_RUN_DCE);
+  df_note_add_problem ();
   df_analyze ();
   df_set_flags (DF_DEFER_INSN_RESCAN);
 
Index: testsuite/gcc.dg/pr55006.c
===================================================================
--- testsuite/gcc.dg/pr55006.c	(revision 0)
+++ testsuite/gcc.dg/pr55006.c	(revision 0)
@@ -0,0 +1,88 @@
+/* PR rtl-optimization/55006 */
+/* { dg-do run } */
+/* { dg-options "-O3 -fomit-frame-pointer -funroll-loops" } */
+
+extern void abort (void) __attribute__ ((__noreturn__));
+extern void *memcpy (void *__restrict, const void *__restrict, __SIZE_TYPE__);
+
+static void __attribute__ ((__noinline__, __noclone__)) ga4076 (void)
+{
+  int D833;
+  float D834;
+  int D837;
+  int D840;
+  float D841;
+  int D844;
+  float dda[100];
+  int ids;
+  float limit3;
+  int offset2;
+  int pos1;
+  int S4;
+
+  static float A0[100] =
+  { 10e+0, 20e+0, 30e+0, 40e+0, 50e+0, 60e+0, 70e+0, 80e+0, 90e+0,
+    10e+1, 11e+1, 12e+1, 13e+1, 14e+1, 15e+1, 16e+1, 17e+1, 18e+1,
+    19e+1, 20e+1, 21e+1, 22e+1, 23e+1, 24e+1, 25e+1, 26e+1, 27e+1,
+    28e+1, 29e+1, 30e+1, 31e+1, 32e+1, 33e+1, 34e+1, 35e+1, 36e+1,
+    37e+1, 38e+1, 39e+1, 40e+1, 41e+1, 42e+1, 43e+1, 44e+1, 45e+1,
+    46e+1, 47e+1, 48e+1, 49e+1, 50e+1, 51e+1, 52e+1, 53e+1, 54e+1,
+    55e+1, 56e+1, 57e+1, 58e+1, 59e+1, 60e+1, 61e+1, 62e+1, 63e+1,
+    64e+1, 65e+1, 66e+1, 67e+1, 68e+1, 69e+1, 70e+1, 71e+1, 72e+1,
+    73e+1, 74e+1, 75e+1, 76e+1, 77e+1, 78e+1, 79e+1, 80e+1, 81e+1,
+    82e+1, 83e+1, 84e+1, 85e+1, 86e+1, 87e+1, 88e+1, 89e+1, 90e+1,
+    91e+1, 92e+1, 93e+1, 94e+1, 95e+1, 96e+1, 97e+1, 98e+1, 99e+1,
+    10e+2
+  };
+
+  memcpy (dda, A0, sizeof (dda));
+
+  limit3 = -1. * __builtin_inf();
+  pos1 = 0;
+  offset2 = 0;
+  S4 = 1;
+
+D831:
+  if (S4 > 100) goto L3; else goto D832;
+D832:
+  D833 = S4 - 1;
+  D834 = dda[D833];
+  if (D834 >= limit3) goto D835; else goto D836;
+D835:
+  D837 = S4 - 1;
+  limit3 = dda[D837];
+  pos1 = S4 + offset2;
+  goto L1;
+D836:
+  S4 = S4 + 1;
+  goto D831;
+L3:
+  pos1 = 1;
+  goto L2;
+L1:
+  if (S4 > 100) goto L2; else goto D839;
+D839:
+  D840 = S4 - 1;
+  D841 = dda[D840];
+  if (D841 > limit3) goto D842; else goto D843;
+D842:
+  D844 = S4 - 1;
+  limit3 = dda[D844];
+  pos1 = S4 + offset2;
+D843:
+  S4 = S4 + 1;
+  goto L1;
+L2:
+  ids = pos1;
+  if (ids != 100)
+    abort ();
+}
+
+
+int
+main (void)
+{
+  ga4076 ();
+  return 0;
+}
+
