diff mbox series

[pushed,PR117248,LRA] : Rewriting reg notes update and fix calculation of conflict hard regs of pseudo.

Message ID e4922f0e-3720-42cb-a742-2fb34fd1721c@redhat.com
State New
Headers show
Series [pushed,PR117248,LRA] : Rewriting reg notes update and fix calculation of conflict hard regs of pseudo. | expand

Commit Message

Vladimir Makarov Dec. 6, 2024, 9:25 p.m. UTC
The following patch solves

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

I tested the patch extensively on x86-64, aarch64, ppc64le as the patch 
contains big changes in live analysis and reg notes update.  I hope it 
will not result in new PRs.
diff mbox series

Patch

commit 75e7d1600f47859df40b2ac0feff5a71e0dbb040
Author: Vladimir N. Makarov <vmakarov@redhat.com>
Date:   Fri Dec 6 16:16:28 2024 -0500

    [PR117248][LRA]: Rewriting reg notes update and fix calculation of conflict hard regs of pseudo.
    
      LRA updates conflict hard regs of pseudo when some hard reg dies.  A
    complicated PA div/mod insns reference for clobbered explicit hard regs and
    hard reg as operands.  It prevents some hard reg dying although they
    still conflict with pseudos living through.  Although on such insns LRA
    updates wrongly reg notes (REG_DEAD, REG_UNUSED) which are used later in
    rematerialization subpass.  The patch fixes the problems.
    
    gcc/ChangeLog:
    
            PR rtl-optimization/117248
            * lra-lives.cc (start_living, start_dying): Remove.
            (insn_regnos, out_insn_regnos, insn_regnos_live_after): New.
            (sparseset_contains_pseudos_p): Remove.
            (make_hard_regno_live, make_hard_regno_dead): Return true if
            something in liveness is changed.
            (mark_pseudo_live,  mark_pseudo_dead): Ditto.
            (mark_regno_live, mark_regno_dead): Ditto.
            (clear_sparseset_regnos, regnos_in_sparseset_p): Use set instead
            of dead_set.
            (process_bb_lives): Rewrite dealing with reg notes.  Update
            conflict hard regs even when clobber hard reg is not marked as
            dead.
            (lra_create_live_ranges_1): Add initialization/finalization of
            insn_regnos, out_insn_regnos, insn_regnos_live_after.

diff --git a/gcc/lra-lives.cc b/gcc/lra-lives.cc
index 49134ade713..f1bb5701bc4 100644
--- a/gcc/lra-lives.cc
+++ b/gcc/lra-lives.cc
@@ -83,10 +83,10 @@  static sparseset pseudos_live_through_setjumps;
 /* Set of hard regs (except eliminable ones) currently live.  */
 static HARD_REG_SET hard_regs_live;
 
-/* Set of pseudos and hard registers start living/dying in the current
-   insn.  These sets are used to update REG_DEAD and REG_UNUSED notes
-   in the insn.  */
-static sparseset start_living, start_dying;
+/* Set of pseudos and hard registers in the current insn, only out/inout ones,
+   and the current insn pseudos and hard registers living right after the
+   insn.  */
+static sparseset insn_regnos, out_insn_regnos, insn_regnos_live_after;
 
 /* Set of pseudos and hard regs dead and unused in the current
    insn.  */
@@ -227,17 +227,6 @@  enum point_type {
   USE_POINT
 };
 
-/* Return TRUE if set A contains a pseudo register, otherwise, return FALSE.  */
-static bool
-sparseset_contains_pseudos_p (sparseset a)
-{
-  int regno;
-  EXECUTE_IF_SET_IN_SPARSESET (a, regno)
-    if (!HARD_REGISTER_NUM_P (regno))
-      return true;
-  return false;
-}
-
 /* Mark pseudo REGNO as living or dying at program point POINT, depending on
    whether TYPE is a definition or a use.  If this is the first reference to
    REGNO that we've encountered, then create a new live range for it.  */
@@ -276,29 +265,29 @@  update_pseudo_point (int regno, int point, enum point_type type)
 /* The corresponding bitmaps of BB currently being processed.  */
 static bitmap bb_killed_pseudos, bb_gen_pseudos;
 
-/* Record hard register REGNO as now being live.  It updates
-   living hard regs and START_LIVING.  */
-static void
+/* Record hard register REGNO as now being live.  Return true if REGNO liveness
+   changes.  */
+static bool
 make_hard_regno_live (int regno)
 {
   lra_assert (HARD_REGISTER_NUM_P (regno));
   if (TEST_HARD_REG_BIT (hard_regs_live, regno)
       || TEST_HARD_REG_BIT (eliminable_regset, regno))
-    return;
+    return false;
   SET_HARD_REG_BIT (hard_regs_live, regno);
-  sparseset_set_bit (start_living, regno);
   if (fixed_regs[regno] || TEST_HARD_REG_BIT (hard_regs_spilled_into, regno))
     bitmap_set_bit (bb_gen_pseudos, regno);
+  return true;
 }
 
-/* Process the definition of hard register REGNO.  This updates
-   hard_regs_live, START_DYING and conflict hard regs for living
-   pseudos.  */
-static void
+/* Process the definition of hard register REGNO.  This updates hard_regs_live
+   and conflict hard regs for living pseudos.  Return true if REGNO liveness
+   changes.  */
+static bool
 make_hard_regno_dead (int regno)
 {
   if (TEST_HARD_REG_BIT (eliminable_regset, regno))
-    return;
+    return false;
 
   lra_assert (HARD_REGISTER_NUM_P (regno));
   unsigned int i;
@@ -306,79 +295,89 @@  make_hard_regno_dead (int regno)
     SET_HARD_REG_BIT (lra_reg_info[i].conflict_hard_regs, regno);
 
   if (! TEST_HARD_REG_BIT (hard_regs_live, regno))
-    return;
+    return false;
   CLEAR_HARD_REG_BIT (hard_regs_live, regno);
-  sparseset_set_bit (start_dying, regno);
   if (fixed_regs[regno] || TEST_HARD_REG_BIT (hard_regs_spilled_into, regno))
     {
       bitmap_clear_bit (bb_gen_pseudos, regno);
       bitmap_set_bit (bb_killed_pseudos, regno);
     }
+  return true;
 }
 
-/* Mark pseudo REGNO as now being live and update START_LIVING.  */
-static void
+/* Mark pseudo REGNO as now being live.  Return true if REGNO liveness
+   changes.  */
+static bool
 mark_pseudo_live (int regno)
 {
   lra_assert (!HARD_REGISTER_NUM_P (regno));
   if (sparseset_bit_p (pseudos_live, regno))
-    return;
-
+    return false;
   sparseset_set_bit (pseudos_live, regno);
-  sparseset_set_bit (start_living, regno);
+  return true;
 }
 
-/* Mark pseudo REGNO as now being dead and update START_DYING.  */
-static void
+/* Mark pseudo REGNO as now being dead.  Return true if REGNO liveness
+   changes.  */
+static bool
 mark_pseudo_dead (int regno)
 {
   lra_assert (!HARD_REGISTER_NUM_P (regno));
   lra_reg_info[regno].conflict_hard_regs |= hard_regs_live;
   if (!sparseset_bit_p (pseudos_live, regno))
-    return;
+    return false;
 
   sparseset_clear_bit (pseudos_live, regno);
-  sparseset_set_bit (start_dying, regno);
+  return true;
 }
 
-/* Mark register REGNO (pseudo or hard register) in MODE as being live
-   and update BB_GEN_PSEUDOS.  */
-static void
+/* Mark register REGNO (pseudo or hard register) in MODE as being live and
+   update BB_GEN_PSEUDOS.  Return true if REGNO liveness changes.  */
+static bool
 mark_regno_live (int regno, machine_mode mode)
 {
   int last;
+  bool change_p = false;
 
   if (HARD_REGISTER_NUM_P (regno))
     {
       for (last = end_hard_regno (mode, regno); regno < last; regno++)
-	make_hard_regno_live (regno);
+	if (make_hard_regno_live (regno))
+	  change_p = true;
     }
   else
     {
-      mark_pseudo_live (regno);
+      if (mark_pseudo_live (regno))
+	change_p = true;
       bitmap_set_bit (bb_gen_pseudos, regno);
     }
+  return change_p;
 }
 
 
-/* Mark register REGNO (pseudo or hard register) in MODE as being dead
-   and update BB_GEN_PSEUDOS and BB_KILLED_PSEUDOS.  */
-static void
+/* Mark register REGNO (pseudo or hard register) in MODE as being dead and
+   update BB_GEN_PSEUDOS and BB_KILLED_PSEUDOS.  Return true if REGNO liveness
+   changes.  */
+static bool
 mark_regno_dead (int regno, machine_mode mode)
 {
   int last;
+  bool change_p = false;
 
   if (HARD_REGISTER_NUM_P (regno))
     {
       for (last = end_hard_regno (mode, regno); regno < last; regno++)
-	make_hard_regno_dead (regno);
+	if (make_hard_regno_dead (regno))
+	  change_p = true;
     }
   else
     {
-      mark_pseudo_dead (regno);
+      if (mark_pseudo_dead (regno))
+	change_p = true;
       bitmap_clear_bit (bb_gen_pseudos, regno);
       bitmap_set_bit (bb_killed_pseudos, regno);
     }
+  return change_p;
 }
 
 
@@ -622,7 +621,7 @@  clear_sparseset_regnos (sparseset set, int regno, enum machine_mode mode)
 {
   if (regno >= FIRST_PSEUDO_REGISTER)
     {
-      sparseset_clear_bit (dead_set, regno);
+      sparseset_clear_bit (set, regno);
       return;
     }
   for (int last = end_hard_regno (mode, regno); regno < last; regno++)
@@ -635,7 +634,7 @@  static bool
 regnos_in_sparseset_p (sparseset set, int regno, enum machine_mode mode)
 {
   if (regno >= FIRST_PSEUDO_REGISTER)
-    return sparseset_bit_p (dead_set, regno);
+    return sparseset_bit_p (set, regno);
   for (int last = end_hard_regno (mode, regno); regno < last; regno++)
     if (!sparseset_bit_p (set, regno))
       return false;
@@ -852,8 +851,46 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	      (regno, hard_regno, freq);
 	}
 
-      sparseset_clear (start_living);
+      sparseset_clear (insn_regnos_live_after);
+      sparseset_clear (insn_regnos);
+      sparseset_clear (out_insn_regnos);
+
+      for (reg = curr_id->regs; reg != NULL; reg = reg->next)
+	{
+	  if (!HARD_REGISTER_NUM_P (reg->regno))
+	    {
+	      sparseset_set_bit (insn_regnos, reg->regno);
+	      if (reg->type != OP_IN)
+		sparseset_set_bit (out_insn_regnos, reg->regno);
+	      if (sparseset_bit_p (pseudos_live, reg->regno))
+		sparseset_set_bit (insn_regnos_live_after, reg->regno);
+	    }
+	  else
+	    {
+	      for (int regno = reg->regno,
+		     last = end_hard_regno (reg->biggest_mode, regno);
+		   regno < last;
+		   regno++)
+		{
+		  sparseset_set_bit (insn_regnos, regno);
+		  if (reg->type != OP_IN)
+		    sparseset_set_bit (out_insn_regnos, regno);
+		  if (TEST_HARD_REG_BIT (hard_regs_live, regno))
+		    sparseset_set_bit (insn_regnos_live_after, regno);
+		}
+	    }
+	}
+
+      for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next)
+	{
+	  sparseset_set_bit (insn_regnos, reg->regno);
+	  if (reg->type != OP_IN)
+	    sparseset_set_bit (out_insn_regnos, reg->regno);
+	  if (TEST_HARD_REG_BIT (hard_regs_live, reg->regno))
+	    sparseset_set_bit (insn_regnos_live_after, reg->regno);
+	}
 
+      bool next_point_p = false;
       /* Mark each defined value as live.  We need to do this for
 	 unused values because they still conflict with quantities
 	 that are live at the time of the definition.  */
@@ -861,24 +898,21 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	if (reg->type != OP_IN)
 	  {
 	    update_pseudo_point (reg->regno, curr_point, USE_POINT);
-	    mark_regno_live (reg->regno, reg->biggest_mode);
+	    if (mark_regno_live (reg->regno, reg->biggest_mode))
+	      next_point_p = true;
 	    /* ??? Should be a no-op for unused registers.  */
 	    check_pseudos_live_through_calls (reg->regno, last_call_abi);
 	  }
 
       for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next)
-	if (reg->type != OP_IN)
-	  make_hard_regno_live (reg->regno);
+	if (reg->type != OP_IN && make_hard_regno_live (reg->regno))
+	  next_point_p = true;
 
       if (curr_id->arg_hard_regs != NULL)
 	for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++)
-	  if (!HARD_REGISTER_NUM_P (regno))
-	    /* It is a clobber.  */
-	    make_hard_regno_live (regno - FIRST_PSEUDO_REGISTER);
-
-      sparseset_copy (unused_set, start_living);
-
-      sparseset_clear (start_dying);
+	  if (!HARD_REGISTER_NUM_P (regno) /* It is a clobber.  */
+	      && make_hard_regno_live (regno - FIRST_PSEUDO_REGISTER))
+	    next_point_p = true;
 
       /* See which defined values die here.  */
       for (reg = curr_id->regs; reg != NULL; reg = reg->next)
@@ -887,19 +921,21 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	  {
 	    if (reg->type == OP_OUT)
 	      update_pseudo_point (reg->regno, curr_point, DEF_POINT);
-	    mark_regno_dead (reg->regno, reg->biggest_mode);
+	    if (mark_regno_dead (reg->regno, reg->biggest_mode))
+	      next_point_p = true;
 	  }
 
       for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next)
 	if (reg->type != OP_IN
-	    && ! reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p)
-	  make_hard_regno_dead (reg->regno);
+	    && ! reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p
+	    && make_hard_regno_dead (reg->regno))
+	  next_point_p = true;
 
       if (curr_id->arg_hard_regs != NULL)
 	for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++)
-	  if (!HARD_REGISTER_NUM_P (regno))
-	    /* It is a clobber.  */
-	    make_hard_regno_dead (regno - FIRST_PSEUDO_REGISTER);
+	  if (!HARD_REGISTER_NUM_P (regno) /* It is a clobber.  */
+	      && make_hard_regno_dead (regno - FIRST_PSEUDO_REGISTER))
+	    next_point_p = true;
 
       if (call_p)
 	{
@@ -922,13 +958,11 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	}
 
       /* Increment the current program point if we must.  */
-      if (sparseset_contains_pseudos_p (unused_set)
-	  || sparseset_contains_pseudos_p (start_dying))
+      if (next_point_p)
 	next_program_point (curr_point, freq);
 
       /* If we removed the source reg from a simple register copy from the
-	 live set above, then add it back now so we don't accidentally add
-	 it to the start_living set below.  */
+	 live set above, then add it back now.  */
       if (ignore_reg != NULL_RTX)
 	{
 	  int ignore_regno = REGNO (ignore_reg);
@@ -938,31 +972,27 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	    sparseset_set_bit (pseudos_live, ignore_regno);
 	}
 
-      sparseset_clear (start_living);
-
+      next_point_p = false;
       /* Mark each used value as live.	*/
       for (reg = curr_id->regs; reg != NULL; reg = reg->next)
 	if (reg->type != OP_OUT)
 	  {
 	    if (reg->type == OP_IN)
 	      update_pseudo_point (reg->regno, curr_point, USE_POINT);
-	    mark_regno_live (reg->regno, reg->biggest_mode);
+	    if (mark_regno_live (reg->regno, reg->biggest_mode))
+	      next_point_p = true;
 	    check_pseudos_live_through_calls (reg->regno, last_call_abi);
 	  }
 
       for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next)
-	if (reg->type != OP_OUT)
-	  make_hard_regno_live (reg->regno);
+	if (reg->type != OP_OUT && make_hard_regno_live (reg->regno))
+	  next_point_p = true;
 
       if (curr_id->arg_hard_regs != NULL)
 	/* Make argument hard registers live.  */
 	for (i = 0; (regno = curr_id->arg_hard_regs[i]) >= 0; i++)
-	  if (HARD_REGISTER_NUM_P (regno))
-	    make_hard_regno_live (regno);
-
-      sparseset_and_compl (dead_set, start_living, start_dying);
-
-      sparseset_clear (start_dying);
+	  if (HARD_REGISTER_NUM_P (regno) && make_hard_regno_live (regno))
+	    next_point_p = true;
 
       /* Mark early clobber outputs dead.  */
       for (reg = curr_id->regs; reg != NULL; reg = reg->next)
@@ -971,12 +1001,14 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	  {
 	    if (reg->type == OP_OUT)
 	      update_pseudo_point (reg->regno, curr_point, DEF_POINT);
-	    mark_regno_dead (reg->regno, reg->biggest_mode);
+	    if (mark_regno_dead (reg->regno, reg->biggest_mode))
+	      next_point_p = true;
 
 	    /* We're done processing inputs, so make sure early clobber
 	       operands that are both inputs and outputs are still live.  */
-	    if (reg->type == OP_INOUT)
-	      mark_regno_live (reg->regno, reg->biggest_mode);
+	    if (reg->type == OP_INOUT
+		&& mark_regno_live (reg->regno, reg->biggest_mode))
+	      next_point_p = true;
 	  }
 
       for (reg = curr_static_id->hard_regs; reg != NULL; reg = reg->next)
@@ -988,29 +1020,59 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	    /* We can have early clobbered non-operand hard reg and
 	       the same hard reg as an insn input.  Don't make hard
 	       reg dead before the insns.  */
-	    for (reg2 = curr_static_id->hard_regs; reg2 != NULL; reg2 = reg2->next)
+	    for (reg2 = curr_static_id->hard_regs;
+		 reg2 != NULL;
+		 reg2 = reg2->next)
 	      if (reg2->type != OP_OUT && reg2->regno == reg->regno)
 		break;
-	    if (reg2 != NULL)
-	      continue;
-
-	    HARD_REG_SET clobbered_regset;
-	    CLEAR_HARD_REG_SET (clobbered_regset);
-	    SET_HARD_REG_BIT (clobbered_regset, reg->regno);
-
-	    for (reg2 = curr_id->regs; reg2 != NULL; reg2 = reg2->next)
-	      if (reg2->type != OP_OUT && reg2->regno < FIRST_PSEUDO_REGISTER
-		  && ira_hard_reg_set_intersection_p (reg2->regno,
-						      reg2->biggest_mode,
-						      clobbered_regset))
-		break;
 	    if (reg2 == NULL)
-	      make_hard_regno_dead (reg->regno);
+	      {
+		HARD_REG_SET clobbered_regset;
+		CLEAR_HARD_REG_SET (clobbered_regset);
+		SET_HARD_REG_BIT (clobbered_regset, reg->regno);
+
+		for (reg2 = curr_id->regs; reg2 != NULL; reg2 = reg2->next)
+		  if (reg2->type != OP_OUT && HARD_REGISTER_NUM_P (reg2->regno)
+		      && ira_hard_reg_set_intersection_p (reg2->regno,
+							  reg2->biggest_mode,
+							  clobbered_regset))
+		    break;
+		if (reg2 == NULL)
+		  {
+		    if (make_hard_regno_dead (reg->regno))
+		      next_point_p = true;
+		  }
+		else
+		  {
+		    EXECUTE_IF_SET_IN_SPARSESET (pseudos_live, j)
+		      SET_HARD_REG_BIT (lra_reg_info[j].conflict_hard_regs,
+					reg->regno);
+		  }
+	      }
 	  }
 
+      sparseset_clear (dead_set);
+      sparseset_clear (unused_set);
+      EXECUTE_IF_SET_IN_SPARSESET (insn_regnos, j)
+	{
+	  if (sparseset_bit_p (insn_regnos_live_after, j))
+	    continue;
+	  if (!HARD_REGISTER_NUM_P (j))
+	    {
+	      if (sparseset_bit_p (pseudos_live, j))
+		sparseset_set_bit (dead_set, j);
+	    }
+	  else
+	    {
+	      if (TEST_HARD_REG_BIT (hard_regs_live, j))
+		sparseset_set_bit (dead_set, j);
+	    }
+	  if (sparseset_bit_p (out_insn_regnos, j))
+	    sparseset_set_bit (unused_set, j);
+	}
+
       /* Increment the current program point if we must.  */
-      if (sparseset_contains_pseudos_p (dead_set)
-	  || sparseset_contains_pseudos_p (start_dying))
+      if (next_point_p)
 	next_program_point (curr_point, freq);
 
       /* Update notes.	*/
@@ -1043,10 +1105,16 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 	    }
 	  link_loc = &XEXP (link, 1);
 	}
-      EXECUTE_IF_SET_IN_SPARSESET (dead_set, j)
-	add_reg_note (curr_insn, REG_DEAD, regno_reg_rtx[j]);
       EXECUTE_IF_SET_IN_SPARSESET (unused_set, j)
-	add_reg_note (curr_insn, REG_UNUSED, regno_reg_rtx[j]);
+	{
+	  gcc_assert (find_regno_note (curr_insn, REG_UNUSED, j) == NULL_RTX);
+	  add_reg_note (curr_insn, REG_UNUSED, regno_reg_rtx[j]);
+	}
+      EXECUTE_IF_SET_IN_SPARSESET (dead_set, j)
+	{
+	  gcc_assert (find_regno_note (curr_insn, REG_DEAD, j) == NULL_RTX);
+	  add_reg_note (curr_insn, REG_DEAD, regno_reg_rtx[j]);
+	}
     }
 
   if (bb_has_eh_pred (bb))
@@ -1408,8 +1476,9 @@  lra_create_live_ranges_1 (bool all_p, bool dead_insn_p)
   pseudos_live = sparseset_alloc (max_regno);
   pseudos_live_through_calls = sparseset_alloc (max_regno);
   pseudos_live_through_setjumps = sparseset_alloc (max_regno);
-  start_living = sparseset_alloc (max_regno);
-  start_dying = sparseset_alloc (max_regno);
+  insn_regnos = sparseset_alloc (max_regno);
+  out_insn_regnos = sparseset_alloc (max_regno);
+  insn_regnos_live_after = sparseset_alloc (max_regno);
   dead_set = sparseset_alloc (max_regno);
   unused_set = sparseset_alloc (max_regno);
   curr_point = 0;
@@ -1480,8 +1549,9 @@  lra_create_live_ranges_1 (bool all_p, bool dead_insn_p)
   /* Clean up.	*/
   sparseset_free (unused_set);
   sparseset_free (dead_set);
-  sparseset_free (start_dying);
-  sparseset_free (start_living);
+  sparseset_free (insn_regnos);
+  sparseset_free (out_insn_regnos);
+  sparseset_free (insn_regnos_live_after);
   sparseset_free (pseudos_live_through_calls);
   sparseset_free (pseudos_live_through_setjumps);
   sparseset_free (pseudos_live);