[V3,7/7] lra: Support subreg live range track and conflict detect

Message ID 20231112120817.2635864-8-lehua.ding@rivai.ai
State Unresolved
Headers
Series ira/lra: Support subreg coalesce |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Lehua Ding Nov. 12, 2023, 12:08 p.m. UTC
  This patch supports tracking the liveness of a subreg in a lra pass, with the
goal of getting it to agree with ira's register allocation scheme. There is some
duplication, maybe in the future this part of the code logic can be harmonized.

gcc/ChangeLog:

	* ira-build.cc (setup_pseudos_has_subreg_object):
	Collect new data for lra to use.
	(ira_build): Ditto.
	* lra-assigns.cc (set_offset_conflicts): New function.
	(setup_live_pseudos_and_spill_after_risky_transforms): Adjust.
	(lra_assign): Ditto.
	* lra-constraints.cc (process_alt_operands): Ditto.
	* lra-int.h (GCC_LRA_INT_H): Ditto.
	(struct lra_live_range): Ditto.
	(struct lra_insn_reg): Ditto.
	(get_range_hard_regs): New.
	(get_nregs): New.
	(has_subreg_object_p): New.
	* lra-lives.cc (INCLUDE_VECTOR): Adjust.
	(lra_live_range_pool): Ditto.
	(create_live_range): Ditto.
	(lra_merge_live_ranges): Ditto.
	(update_pseudo_point): Ditto.
	(mark_regno_live): Ditto.
	(mark_regno_dead): Ditto.
	(process_bb_lives): Ditto.
	(remove_some_program_points_and_update_live_ranges): Ditto.
	(lra_print_live_range_list): Ditto.
	(class subreg_live_item): New.
	(create_subregs_live_ranges): New.
	(lra_create_live_ranges_1): Ditto.
	* lra.cc (get_range_blocks): Ditto.
	(get_range_hard_regs): Ditto.
	(new_insn_reg): Ditto.
	(collect_non_operand_hard_regs): Ditto.
	(initialize_lra_reg_info_element): Ditto.
	(reg_same_range_p): New.
	(add_regs_to_insn_regno_info): Adjust.

---
 gcc/ira-build.cc       |  31 ++++
 gcc/lra-assigns.cc     | 111 ++++++++++++--
 gcc/lra-constraints.cc |  18 ++-
 gcc/lra-int.h          |  31 ++++
 gcc/lra-lives.cc       | 340 ++++++++++++++++++++++++++++++++++-------
 gcc/lra.cc             | 139 +++++++++++++++--
 6 files changed, 585 insertions(+), 85 deletions(-)
  

Patch

diff --git a/gcc/ira-build.cc b/gcc/ira-build.cc
index f88aeeeeaef..bb29627d375 100644
--- a/gcc/ira-build.cc
+++ b/gcc/ira-build.cc
@@ -95,6 +95,9 @@  int ira_copies_num;
    basic block.  */
 static int last_basic_block_before_change;
 
+/* Record these pseudos which has subreg object. Used by LRA pass.  */
+bitmap_head pseudos_has_subreg_object;
+
 /* Initialize some members in loop tree node NODE.  Use LOOP_NUM for
    the member loop_num.  */
 static void
@@ -3711,6 +3714,33 @@  update_conflict_hard_reg_costs (void)
     }
 }
 
+/* Setup speudos_has_subreg_object.  */
+static void
+setup_pseudos_has_subreg_object ()
+{
+  bitmap_initialize (&pseudos_has_subreg_object, &reg_obstack);
+  ira_allocno_t a;
+  ira_allocno_iterator ai;
+  FOR_EACH_ALLOCNO (a, ai)
+    if (has_subreg_object_p (a))
+      {
+	bitmap_set_bit (&pseudos_has_subreg_object, ALLOCNO_REGNO (a));
+	if (ira_dump_file != NULL)
+	  {
+	    fprintf (ira_dump_file,
+		     "  a%d(r%d, nregs: %d) has subreg objects:\n",
+		     ALLOCNO_NUM (a), ALLOCNO_REGNO (a), ALLOCNO_NREGS (a));
+	    ira_allocno_object_iterator oi;
+	    ira_object_t obj;
+	    FOR_EACH_ALLOCNO_OBJECT (a, obj, oi)
+	      fprintf (ira_dump_file, "    object %d: start: %d, nregs: %d\n",
+		       OBJECT_INDEX (obj), OBJECT_START (obj),
+		       OBJECT_NREGS (obj));
+	    fprintf (ira_dump_file, "\n");
+	  }
+      }
+}
+
 /* Create a internal representation (IR) for IRA (allocnos, copies,
    loop tree nodes).  The function returns TRUE if we generate loop
    structure (besides nodes representing all function and the basic
@@ -3731,6 +3761,7 @@  ira_build (void)
   create_allocnos ();
   ira_costs ();
   create_allocno_objects ();
+  setup_pseudos_has_subreg_object ();
   ira_create_allocno_live_ranges ();
   remove_unnecessary_regions (false);
   ira_compress_allocno_live_ranges ();
diff --git a/gcc/lra-assigns.cc b/gcc/lra-assigns.cc
index d2ebcfd5056..6588a740162 100644
--- a/gcc/lra-assigns.cc
+++ b/gcc/lra-assigns.cc
@@ -1131,6 +1131,52 @@  assign_hard_regno (int hard_regno, int regno)
 /* Array used for sorting different pseudos.  */
 static int *sorted_pseudos;
 
+/* The detail conflict offsets If two live ranges conflict. Use to record
+   partail conflict.  */
+static bitmap_head live_range_conflicts;
+
+/* Set the conflict offset of the two registers REGNO1 and REGNO2. Use the
+   regno with bigger nregs as the base.  */
+static void
+set_offset_conflicts (int regno1, int regno2)
+{
+  gcc_assert (reg_renumber[regno1] >= 0 && reg_renumber[regno2] >= 0);
+  int nregs1 = get_nregs (regno1);
+  int nregs2 = get_nregs (regno2);
+  if (nregs1 < nregs2)
+    {
+      std::swap (nregs1, nregs2);
+      std::swap (regno1, regno2);
+    }
+
+  lra_live_range_t r1 = lra_reg_info[regno1].live_ranges;
+  lra_live_range_t r2 = lra_reg_info[regno2].live_ranges;
+  int total = nregs1;
+
+  bitmap_clear (&live_range_conflicts);
+  while (r1 != NULL && r2 != NULL)
+    {
+      if (r1->start > r2->finish)
+	r1 = r1->next;
+      else if (r2->start > r1->finish)
+	r2 = r2->next;
+      else
+	{
+	  for (const subreg_range &range1 : r1->subreg.ranges)
+	    for (const subreg_range &range2 : r2->subreg.ranges)
+	      /* Record all overlap offset.  */
+	      for (int i = range1.start - (range2.end - range2.start) + 1;
+		   i < range1.end; i++)
+		if (i >= 0 && i < total)
+		  bitmap_set_bit (&live_range_conflicts, i);
+	  if (r1->finish < r2->finish)
+	    r1 = r1->next;
+	  else
+	    r2 = r2->next;
+	}
+    }
+}
+
 /* The constraints pass is allowed to create equivalences between
    pseudos that make the current allocation "incorrect" (in the sense
    that pseudos are assigned to hard registers from their own conflict
@@ -1226,19 +1272,56 @@  setup_live_pseudos_and_spill_after_risky_transforms (bitmap
 	       the same hard register.	*/
 	    || hard_regno != reg_renumber[conflict_regno])
 	  {
-	    int conflict_hard_regno = reg_renumber[conflict_regno];
-	    
-	    biggest_mode = lra_reg_info[conflict_regno].biggest_mode;
-	    biggest_nregs = hard_regno_nregs (conflict_hard_regno,
-					      biggest_mode);
-	    nregs_diff
-	      = (biggest_nregs
-		 - hard_regno_nregs (conflict_hard_regno,
-				     PSEUDO_REGNO_MODE (conflict_regno)));
-	    add_to_hard_reg_set (&conflict_set,
-				 biggest_mode,
-				 conflict_hard_regno
-				 - (WORDS_BIG_ENDIAN ? nregs_diff : 0));
+	  if (hard_regno >= 0 && reg_renumber[conflict_regno] >= 0
+	      && (has_subreg_object_p (regno)
+		  || has_subreg_object_p (conflict_regno)))
+	    {
+	      int nregs1 = get_nregs (regno);
+	      int nregs2 = get_nregs (conflict_regno);
+	      /* Quick check it is no overlap at all between them.  */
+	      if (hard_regno + nregs1 <= reg_renumber[conflict_regno]
+		  || reg_renumber[conflict_regno] + nregs2 <= hard_regno)
+		continue;
+
+	      /* Check the overlap is ok if them have partial overlap.  */
+	      set_offset_conflicts (regno, conflict_regno);
+	      if (nregs1 >= nregs2)
+		EXECUTE_IF_SET_IN_BITMAP (&live_range_conflicts, 0, k, bi)
+		  {
+		    int start_regno
+		      = WORDS_BIG_ENDIAN
+			  ? reg_renumber[conflict_regno] + nregs2 + k - nregs1
+			  : reg_renumber[conflict_regno] - k;
+		    if (start_regno >= 0 && hard_regno == start_regno)
+		      SET_HARD_REG_BIT (conflict_set, start_regno);
+		  }
+	      else
+		EXECUTE_IF_SET_IN_BITMAP (&live_range_conflicts, 0, k, bi)
+		  {
+		    int start_regno
+		      = WORDS_BIG_ENDIAN
+			  ? reg_renumber[conflict_regno] + nregs2 - k - nregs1
+			  : reg_renumber[conflict_regno] + k;
+		    if (start_regno < FIRST_PSEUDO_REGISTER
+			&& hard_regno == start_regno)
+		      SET_HARD_REG_BIT (conflict_set, start_regno);
+		  }
+	    }
+	  else
+	    {
+	      int conflict_hard_regno = reg_renumber[conflict_regno];
+
+	      biggest_mode = lra_reg_info[conflict_regno].biggest_mode;
+	      biggest_nregs
+		= hard_regno_nregs (conflict_hard_regno, biggest_mode);
+	      nregs_diff
+		= (biggest_nregs
+		   - hard_regno_nregs (conflict_hard_regno,
+				       PSEUDO_REGNO_MODE (conflict_regno)));
+	      add_to_hard_reg_set (&conflict_set, biggest_mode,
+				   conflict_hard_regno
+				     - (WORDS_BIG_ENDIAN ? nregs_diff : 0));
+	    }
 	  }
       if (! overlaps_hard_reg_set_p (conflict_set, mode, hard_regno))
 	{
@@ -1637,7 +1720,9 @@  lra_assign (bool &fails_p)
   init_regno_assign_info ();
   bitmap_initialize (&all_spilled_pseudos, &reg_obstack);
   create_live_range_start_chains ();
+  bitmap_initialize (&live_range_conflicts, &reg_obstack);
   setup_live_pseudos_and_spill_after_risky_transforms (&all_spilled_pseudos);
+  bitmap_clear (&live_range_conflicts);
   if (! lra_hard_reg_split_p && ! lra_asm_error_p && flag_checking)
     /* Check correctness of allocation but only when there are no hard reg
        splits and asm errors as in the case of errors explicit insns involving
diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc
index c3ad846b97b..912d0c3feec 100644
--- a/gcc/lra-constraints.cc
+++ b/gcc/lra-constraints.cc
@@ -2363,13 +2363,19 @@  process_alt_operands (int only_alternative)
 		      {
 			/* We should reject matching of an early
 			   clobber operand if the matching operand is
-			   not dying in the insn.  */
-			if (!TEST_BIT (curr_static_id->operand[m]
-				       .early_clobber_alts, nalt)
+			   not dying in the insn. But for subreg of pseudo which
+			   has subreg live be tracked in ira, the REG_DEAD note
+			   doesn't have. that case we think them the matching is
+			   ok. */
+			if (!TEST_BIT (
+			      curr_static_id->operand[m].early_clobber_alts,
+			      nalt)
 			    || operand_reg[nop] == NULL_RTX
-			    || (find_regno_note (curr_insn, REG_DEAD,
-						 REGNO (op))
-				|| REGNO (op) == REGNO (operand_reg[m])))
+			    || find_regno_note (curr_insn, REG_DEAD, REGNO (op))
+			    || (read_modify_subreg_p (
+				  *curr_id->operand_loc[nop])
+				&& has_subreg_object_p (REGNO (op)))
+			    || REGNO (op) == REGNO (operand_reg[m]))
 			  match_p = true;
 		      }
 		    if (match_p)
diff --git a/gcc/lra-int.h b/gcc/lra-int.h
index 678377d9ec6..5a97bd61475 100644
--- a/gcc/lra-int.h
+++ b/gcc/lra-int.h
@@ -21,6 +21,7 @@  along with GCC; see the file COPYING3.	If not see
 #ifndef GCC_LRA_INT_H
 #define GCC_LRA_INT_H
 
+#include "lra.h"
 #include "subreg-live-range.h"
 
 #define lra_assert(c) gcc_checking_assert (c)
@@ -48,6 +49,8 @@  struct lra_live_range
   lra_live_range_t next;
   /* Pointer to structures with the same start.	 */
   lra_live_range_t start_next;
+  /* Object whose live range is described by given structure.  */
+  subreg_ranges subreg;
 };
 
 typedef struct lra_copy *lra_copy_t;
@@ -110,6 +113,8 @@  public:
   /* The biggest size mode in which each pseudo reg is referred in
      whole function (possibly via subreg).  */
   machine_mode biggest_mode;
+  /* The real reg MODE.  */
+  machine_mode reg_mode;
   /* Live ranges of the pseudo.	 */
   lra_live_range_t live_ranges;
   /* This member is set up in lra-lives.cc for subsequent
@@ -161,6 +166,12 @@  struct lra_insn_reg
   unsigned int subreg_p : 1;
   /* The corresponding regno of the register.  */
   int regno;
+  /* The start and end of current ref of blocks, remember the use/def can be
+     a normal subreg.  */
+  int start, end;
+  /* The start and end of current ref of hard regs, remember the use/def can be
+     a normal subreg.  */
+  int start_reg, end_reg;
   /* Next reg info of the same insn.  */
   struct lra_insn_reg *next;
 };
@@ -332,6 +343,8 @@  extern struct lra_insn_reg *lra_get_insn_regs (int);
 extern void lra_free_copies (void);
 extern void lra_create_copy (int, int, int);
 extern lra_copy_t lra_get_copy (int);
+extern subreg_range
+get_range_hard_regs (int regno, const subreg_range &r);
 
 extern int lra_new_regno_start;
 extern int lra_constraint_new_regno_start;
@@ -533,4 +546,22 @@  lra_assign_reg_val (int from, int to)
   lra_reg_info[to].offset = lra_reg_info[from].offset;
 }
 
+/* Return the number regs of REGNO.  */
+inline int
+get_nregs (int regno)
+{
+  enum reg_class aclass = lra_get_allocno_class (regno);
+  gcc_assert (aclass != NO_REGS);
+  int nregs = ira_reg_class_max_nregs[aclass][lra_reg_info[regno].reg_mode];
+  return nregs;
+}
+
+extern bitmap_head pseudos_has_subreg_object;
+/* Return true if pseudo REGNO has subreg live range.  */
+inline bool
+has_subreg_object_p (int regno)
+{
+  return bitmap_bit_p (&pseudos_has_subreg_object, regno);
+}
+
 #endif /* GCC_LRA_INT_H */
diff --git a/gcc/lra-lives.cc b/gcc/lra-lives.cc
index d93921ad302..8a7c653fb09 100644
--- a/gcc/lra-lives.cc
+++ b/gcc/lra-lives.cc
@@ -26,6 +26,7 @@  along with GCC; see the file COPYING3.	If not see
    stack memory slots to spilled pseudos.  */
 
 #include "config.h"
+#define INCLUDE_VECTOR
 #include "system.h"
 #include "coretypes.h"
 #include "backend.h"
@@ -97,6 +98,9 @@  static bitmap_head temp_bitmap;
 /* Pool for pseudo live ranges.	 */
 static object_allocator<lra_live_range> lra_live_range_pool ("live ranges");
 
+/* Store def/use point of has_subreg_object_p register.  */
+static class subregs_live_points *live_points;
+
 /* Free live range list LR.  */
 static void
 free_live_range_list (lra_live_range_t lr)
@@ -113,16 +117,26 @@  free_live_range_list (lra_live_range_t lr)
 
 /* Create and return pseudo live range with given attributes.  */
 static lra_live_range_t
-create_live_range (int regno, int start, int finish, lra_live_range_t next)
+create_live_range (int regno, const subreg_ranges &sr, int start, int finish,
+		   lra_live_range_t next)
 {
   lra_live_range_t p = lra_live_range_pool.allocate ();
   p->regno = regno;
   p->start = start;
   p->finish = finish;
   p->next = next;
+  p->subreg = sr;
   return p;
 }
 
+static lra_live_range_t
+create_live_range (int regno, int start, int finish, lra_live_range_t next)
+{
+  subreg_ranges sr = subreg_ranges (1);
+  sr.add_range (1, subreg_range (0, 1));
+  return create_live_range (regno, sr, start, finish, next);
+}
+
 /* Copy live range R and return the result.  */
 static lra_live_range_t
 copy_live_range (lra_live_range_t r)
@@ -164,7 +178,8 @@  lra_merge_live_ranges (lra_live_range_t r1, lra_live_range_t r2)
       if (r1->start < r2->start)
 	std::swap (r1, r2);
 
-      if (r1->start == r2->finish + 1)
+      if (r1->start == r2->finish + 1
+	  && (r1->regno != r2->regno || r1->subreg.same_p (r2->subreg)))
 	{
 	  /* Joint ranges: merge r1 and r2 into r1.  */
 	  r1->start = r2->start;
@@ -174,7 +189,8 @@  lra_merge_live_ranges (lra_live_range_t r1, lra_live_range_t r2)
 	}
       else
 	{
-	  gcc_assert (r2->finish + 1 < r1->start);
+	  gcc_assert (r2->finish + 1 < r1->start
+		      || !r1->subreg.same_p (r2->subreg));
 	  /* Add r1 to the result.  */
 	  if (first == NULL)
 	    first = last = r1;
@@ -237,6 +253,10 @@  sparseset_contains_pseudos_p (sparseset a)
   return false;
 }
 
+static void
+update_pseudo_point (int regno, const subreg_range &range, int point,
+		     enum point_type type);
+
 /* 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.  */
@@ -249,27 +269,78 @@  update_pseudo_point (int regno, int point, enum point_type type)
   /* Don't compute points for hard registers.  */
   if (HARD_REGISTER_NUM_P (regno))
     return;
+  if (!complete_info_p && lra_get_regno_hard_regno (regno) >= 0)
+    return;
 
-  if (complete_info_p || lra_get_regno_hard_regno (regno) < 0)
+  if (has_subreg_object_p (regno))
     {
-      if (type == DEF_POINT)
-	{
-	  if (sparseset_bit_p (pseudos_live, regno))
-	    {
-	      p = lra_reg_info[regno].live_ranges;
-	      lra_assert (p != NULL);
-	      p->finish = point;
-	    }
-	}
-      else /* USE_POINT */
+      update_pseudo_point (regno, subreg_range (0, get_nregs (regno)), point,
+			   type);
+      return;
+    }
+
+  if (type == DEF_POINT)
+    {
+      if (sparseset_bit_p (pseudos_live, regno))
 	{
-	  if (!sparseset_bit_p (pseudos_live, regno)
-	      && ((p = lra_reg_info[regno].live_ranges) == NULL
-		  || (p->finish != point && p->finish + 1 != point)))
-	    lra_reg_info[regno].live_ranges
-	      = create_live_range (regno, point, -1, p);
+	  p = lra_reg_info[regno].live_ranges;
+	  lra_assert (p != NULL);
+	  p->finish = point;
 	}
     }
+  else /* USE_POINT */
+    {
+      if (!sparseset_bit_p (pseudos_live, regno)
+	  && ((p = lra_reg_info[regno].live_ranges) == NULL
+	      || (p->finish != point && p->finish + 1 != point)))
+	lra_reg_info[regno].live_ranges
+	  = create_live_range (regno, point, -1, p);
+    }
+}
+
+/* Like the above mark_regno_dead but for has_subreg_object_p REGNO.  */
+static void
+update_pseudo_point (int regno, const subreg_range &range, int point,
+		     enum point_type type)
+{
+  /* Don't compute points for hard registers.  */
+  if (HARD_REGISTER_NUM_P (regno))
+    return;
+
+  if (!complete_info_p && lra_get_regno_hard_regno (regno) >= 0)
+    {
+      if (has_subreg_object_p (regno))
+	live_points->add_range (regno, get_nregs (regno), range,
+				type == DEF_POINT);
+      return;
+    }
+
+  if (!has_subreg_object_p (regno))
+    {
+      update_pseudo_point (regno, point, type);
+      return;
+    }
+
+  if (lra_dump_file != NULL)
+    {
+      fprintf (lra_dump_file, "       %s r%d",
+	       type == DEF_POINT ? "def" : "use", regno);
+      fprintf (lra_dump_file, "[subreg: start %d, nregs: %d]", range.start,
+	       range.end - range.start);
+      fprintf (lra_dump_file, " at point %d\n", point);
+    }
+
+  live_points->add_point (regno, get_nregs (regno), range, type == DEF_POINT,
+			  point);
+}
+
+/* Update each range in SR.  */
+static void
+update_pseudo_point (int regno, const subreg_ranges sr, int point,
+		     enum point_type type)
+{
+  for (const subreg_range &range : sr.ranges)
+    update_pseudo_point (regno, range, point, type);
 }
 
 /* Structure describing local BB data used for pseudo
@@ -354,12 +425,18 @@  mark_pseudo_dead (int regno)
   if (!sparseset_bit_p (pseudos_live, regno))
     return;
 
+  /* Just return if regno have partial subreg live for subreg access.  */
+  if (has_subreg_object_p (regno) && !live_points->empty_live_p (regno))
+    return;
+
   sparseset_clear_bit (pseudos_live, regno);
   sparseset_set_bit (start_dying, regno);
 }
 
+static void
+mark_regno_live (int regno, const subreg_range &range, machine_mode mode);
 /* Mark register REGNO (pseudo or hard register) in MODE as being live
-   and update BB_GEN_PSEUDOS.  */
+   and update CURR_BB_INFO.  */
 static void
 mark_regno_live (int regno, machine_mode mode)
 {
@@ -370,6 +447,11 @@  mark_regno_live (int regno, machine_mode mode)
       for (last = end_hard_regno (mode, regno); regno < last; regno++)
 	make_hard_regno_live (regno);
     }
+  else if (has_subreg_object_p (regno))
+    {
+      machine_mode mode = lra_reg_info[regno].reg_mode;
+      mark_regno_live (regno, subreg_range (0, get_nregs (regno)), mode);
+    }
   else
     {
       mark_pseudo_live (regno);
@@ -379,9 +461,26 @@  mark_regno_live (int regno, machine_mode mode)
     }
 }
 
+/* Like the above mark_regno_dead but for has_subreg_object_p REGNO.  */
+static void
+mark_regno_live (int regno, const subreg_range &range, machine_mode mode)
+{
+  if (HARD_REGISTER_NUM_P (regno) || !has_subreg_object_p (regno))
+    mark_regno_live (regno, mode);
+  else
+    {
+      mark_pseudo_live (regno);
+      machine_mode mode = lra_reg_info[regno].reg_mode;
+      if (!range.full_p (get_nregs (regno)))
+	has_subreg_live_p = true;
+      add_subreg_range (curr_bb_info, regno, mode, range, false);
+    }
+}
 
+static void
+mark_regno_dead (int regno, const subreg_range &range, machine_mode mode);
 /* Mark register REGNO (pseudo or hard register) in MODE as being dead
-   and update BB_GEN_PSEUDOS and BB_KILLED_PSEUDOS.  */
+   and update CURR_BB_INFO.  */
 static void
 mark_regno_dead (int regno, machine_mode mode)
 {
@@ -392,6 +491,12 @@  mark_regno_dead (int regno, machine_mode mode)
       for (last = end_hard_regno (mode, regno); regno < last; regno++)
 	make_hard_regno_dead (regno);
     }
+  else if (has_subreg_object_p (regno))
+    {
+      machine_mode mode = lra_reg_info[regno].reg_mode;
+      subreg_range range = subreg_range (0, get_nregs (regno));
+      mark_regno_dead (regno, range, mode);
+    }
   else
     {
       mark_pseudo_dead (regno);
@@ -402,7 +507,22 @@  mark_regno_dead (int regno, machine_mode mode)
     }
 }
 
-
+/* Like the above mark_regno_dead but for has_subreg_object_p REGNO.  */
+static void
+mark_regno_dead (int regno, const subreg_range &range, machine_mode mode)
+{
+  if (HARD_REGISTER_NUM_P (regno) || !has_subreg_object_p (regno))
+    mark_regno_dead (regno, mode);
+  else
+    {
+      mark_pseudo_dead (regno);
+      machine_mode mode = lra_reg_info[regno].reg_mode;
+      if (!range.full_p (get_nregs (regno)))
+	has_subreg_live_p = true;
+      remove_subreg_range (curr_bb_info, regno, mode, range);
+      add_subreg_range (curr_bb_info, regno, mode, range, true);
+    }
+}
 
 /* This page contains code for making global live analysis of pseudos.
    The code works only when pseudo live info is changed on a BB
@@ -823,6 +943,8 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
   function_abi last_call_abi = default_function_abi;
 
   reg_live_out = DF_LIVE_SUBREG_OUT (bb);
+  bitmap reg_live_partial_out = DF_LIVE_SUBREG_PARTIAL_OUT (bb);
+  subregs_live *range_out = DF_LIVE_SUBREG_RANGE_OUT (bb);
   sparseset_clear (pseudos_live);
   sparseset_clear (pseudos_live_through_calls);
   sparseset_clear (pseudos_live_through_setjumps);
@@ -830,7 +952,12 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
   hard_regs_live &= ~eliminable_regset;
   EXECUTE_IF_SET_IN_BITMAP (reg_live_out, FIRST_PSEUDO_REGISTER, j, bi)
     {
-      update_pseudo_point (j, curr_point, USE_POINT);
+      if (bitmap_bit_p (reg_live_partial_out, j) && has_subreg_object_p (j))
+	for (const subreg_range &r : range_out->lives.at (j).ranges)
+	  update_pseudo_point (j, get_range_hard_regs (j, r), curr_point,
+			       USE_POINT);
+      else
+	update_pseudo_point (j, curr_point, USE_POINT);
       mark_pseudo_live (j);
     }
 
@@ -1023,8 +1150,11 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
       for (reg = curr_id->regs; reg != NULL; reg = reg->next)
 	if (reg->type != OP_IN)
 	  {
-	    update_pseudo_point (reg->regno, curr_point, USE_POINT);
-	    mark_regno_live (reg->regno, reg->biggest_mode);
+	    const subreg_range &range = subreg_range (reg->start, reg->end);
+	    update_pseudo_point (reg->regno,
+				 get_range_hard_regs (reg->regno, range),
+				 curr_point, USE_POINT);
+	    mark_regno_live (reg->regno, range, reg->biggest_mode);
 	    /* ??? Should be a no-op for unused registers.  */
 	    check_pseudos_live_through_calls (reg->regno, last_call_abi);
 	  }
@@ -1045,17 +1175,20 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 
       /* See which defined values die here.  */
       for (reg = curr_id->regs; reg != NULL; reg = reg->next)
-	if (reg->type != OP_IN
-	    && ! reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p)
+	if (reg->type != OP_IN && !reg_early_clobber_p (reg, n_alt)
+	    && (!reg->subreg_p || has_subreg_object_p (reg->regno)))
 	  {
+	    const subreg_range &range = subreg_range (reg->start, reg->end);
 	    if (reg->type == OP_OUT)
-	      update_pseudo_point (reg->regno, curr_point, DEF_POINT);
-	    mark_regno_dead (reg->regno, reg->biggest_mode);
+	      update_pseudo_point (reg->regno,
+				   get_range_hard_regs (reg->regno, range),
+				   curr_point, DEF_POINT);
+	    mark_regno_dead (reg->regno, range, reg->biggest_mode);
 	  }
 
       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)
+	if (reg->type != OP_IN && !reg_early_clobber_p (reg, n_alt)
+	    && !reg->subreg_p)
 	  make_hard_regno_dead (reg->regno);
 
       if (curr_id->arg_hard_regs != NULL)
@@ -1086,7 +1219,7 @@  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))
+	  || sparseset_contains_pseudos_p (start_dying) || has_subreg_live_p)
 	next_program_point (curr_point, freq);
 
       /* If we removed the source reg from a simple register copy from the
@@ -1107,9 +1240,12 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
       for (reg = curr_id->regs; reg != NULL; reg = reg->next)
 	if (reg->type != OP_OUT)
 	  {
+	    const subreg_range &range = subreg_range (reg->start, reg->end);
 	    if (reg->type == OP_IN)
-	      update_pseudo_point (reg->regno, curr_point, USE_POINT);
-	    mark_regno_live (reg->regno, reg->biggest_mode);
+	      update_pseudo_point (reg->regno,
+				   get_range_hard_regs (reg->regno, range),
+				   curr_point, USE_POINT);
+	    mark_regno_live (reg->regno, range, reg->biggest_mode);
 	    check_pseudos_live_through_calls (reg->regno, last_call_abi);
 	  }
 
@@ -1129,22 +1265,25 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 
       /* Mark early clobber outputs dead.  */
       for (reg = curr_id->regs; reg != NULL; reg = reg->next)
-	if (reg->type != OP_IN
-	    && reg_early_clobber_p (reg, n_alt) && ! reg->subreg_p)
+	if (reg->type != OP_IN && reg_early_clobber_p (reg, n_alt)
+	    && (!reg->subreg_p || has_subreg_object_p (reg->regno)))
 	  {
+	    const subreg_range &range = subreg_range (reg->start, reg->end);
 	    if (reg->type == OP_OUT)
-	      update_pseudo_point (reg->regno, curr_point, DEF_POINT);
-	    mark_regno_dead (reg->regno, reg->biggest_mode);
+	      update_pseudo_point (reg->regno,
+				   get_range_hard_regs (reg->regno, range),
+				   curr_point, DEF_POINT);
+	    mark_regno_dead (reg->regno, range, reg->biggest_mode);
 
 	    /* 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);
+	      mark_regno_live (reg->regno, range, reg->biggest_mode);
 	  }
 
       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)
+	if (reg->type != OP_IN && reg_early_clobber_p (reg, n_alt)
+	    && !reg->subreg_p)
 	  {
 	    struct lra_insn_reg *reg2;
 
@@ -1160,7 +1299,7 @@  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 (dead_set)
-	  || sparseset_contains_pseudos_p (start_dying))
+	  || sparseset_contains_pseudos_p (start_dying) || has_subreg_live_p)
 	next_program_point (curr_point, freq);
 
       /* Update notes.	*/
@@ -1293,13 +1432,17 @@  process_bb_lives (basic_block bb, int &curr_point, bool dead_insn_p)
 
   EXECUTE_IF_SET_IN_SPARSESET (pseudos_live, i)
     {
-      update_pseudo_point (i, curr_point, DEF_POINT);
+      if (has_subreg_object_p (i))
+	update_pseudo_point (i, live_points->subreg_live_ranges.at (i),
+			     curr_point, DEF_POINT);
+      else
+	update_pseudo_point (i, curr_point, DEF_POINT);
       mark_pseudo_dead (i);
     }
 
-    EXECUTE_IF_SET_IN_BITMAP (DF_LIVE_SUBREG_IN (bb), FIRST_PSEUDO_REGISTER, j,
-			      bi)
-      {
+  EXECUTE_IF_SET_IN_BITMAP (DF_LIVE_SUBREG_IN (bb), FIRST_PSEUDO_REGISTER, j,
+			    bi)
+    {
       if (sparseset_cardinality (pseudos_live_through_calls) == 0)
 	break;
       if (sparseset_bit_p (pseudos_live_through_calls, j))
@@ -1400,7 +1543,8 @@  remove_some_program_points_and_update_live_ranges (void)
 	      next_r = r->next;
 	      r->start = map[r->start];
 	      r->finish = map[r->finish];
-	      if (prev_r == NULL || prev_r->start > r->finish + 1)
+	      if (prev_r == NULL || prev_r->start > r->finish + 1
+		  || !prev_r->subreg.same_p (r->subreg))
 		{
 		  prev_r = r;
 		  continue;
@@ -1418,8 +1562,18 @@  remove_some_program_points_and_update_live_ranges (void)
 void
 lra_print_live_range_list (FILE *f, lra_live_range_t r)
 {
-  for (; r != NULL; r = r->next)
-    fprintf (f, " [%d..%d]", r->start, r->finish);
+  if (r != NULL && has_subreg_object_p (r->regno))
+    {
+      for (; r != NULL; r = r->next)
+	{
+	  fprintf (f, " [%d..%d]{", r->start, r->finish);
+	  r->subreg.dump (f);
+	  fprintf (f, "}");
+	}
+    }
+  else
+    for (; r != NULL; r = r->next)
+      fprintf (f, " [%d..%d]", r->start, r->finish);
   fprintf (f, "\n");
 }
 
@@ -1492,7 +1646,84 @@  compress_live_ranges (void)
     }
 }
 
-
+/* Use to temp record subregs live range in create_subregs_live_ranges function.
+ */
+class subreg_live_item
+{
+public:
+  subreg_ranges subreg;
+  int start, finish;
+};
+
+/* Create subreg live ranges from objects def/use point info.  */
+static void
+create_subregs_live_ranges ()
+{
+  for (const auto &subreg_point_it : live_points->subreg_points)
+    {
+      unsigned int regno = subreg_point_it.first;
+      const class live_points &points = subreg_point_it.second;
+      class lra_reg *reg_info = &lra_reg_info[regno];
+      std::vector<subreg_live_item> temps;
+      gcc_assert (has_subreg_object_p (regno));
+      for (const auto &point_it : points.points)
+	{
+	  int point = point_it.first;
+	  const live_point &regs = point_it.second;
+	  gcc_assert (temps.empty () || temps.back ().finish <= point);
+	  if (!regs.use_reg.empty_p ())
+	    {
+	      if (temps.empty ())
+		temps.push_back ({regs.use_reg, point, -1});
+	      else if (temps.back ().finish == -1)
+		{
+		  if (!temps.back ().subreg.same_p (regs.use_reg))
+		    {
+		      if (temps.back ().start == point)
+			temps.back ().subreg.add_ranges (regs.use_reg);
+		      else
+			{
+			  temps.back ().finish = point - 1;
+
+			  subreg_ranges temp = regs.use_reg;
+			  temp.add_ranges (temps.back ().subreg);
+			  temps.push_back ({temp, point, -1});
+			}
+		    }
+		}
+	      else if (temps.back ().subreg.same_p (regs.use_reg)
+		       && (temps.back ().finish == point
+			   || temps.back ().finish + 1 == point))
+		temps.back ().finish = -1;
+	      else
+		temps.push_back ({regs.use_reg, point, -1});
+	    }
+	  if (!regs.def_reg.empty_p ())
+	    {
+	      gcc_assert (!temps.empty ());
+	      if (regs.def_reg.include_ranges_p (temps.back ().subreg))
+		temps.back ().finish = point;
+	      else if (temps.back ().subreg.include_ranges_p (regs.def_reg))
+		{
+		  temps.back ().finish = point;
+
+		  subreg_ranges diff = temps.back ().subreg;
+		  diff.remove_ranges (regs.def_reg);
+		  temps.push_back ({diff, point + 1, -1});
+		}
+	      else
+		gcc_unreachable ();
+	    }
+	}
+
+      gcc_assert (reg_info->live_ranges == NULL);
+
+      for (const subreg_live_item &item : temps)
+	reg_info->live_ranges
+	  = create_live_range (regno, item.subreg, item.start, item.finish,
+			       reg_info->live_ranges);
+    }
+}
 
 /* The number of the current live range pass.  */
 int lra_live_range_iter;
@@ -1573,6 +1804,8 @@  lra_create_live_ranges_1 (bool all_p, bool dead_insn_p)
   int n = inverted_rev_post_order_compute (cfun, rpo);
   lra_assert (n == n_basic_blocks_for_fn (cfun));
   bb_live_change_p = false;
+  has_subreg_live_p = false;
+  live_points = new subregs_live_points ();
   for (i = 0; i < n; ++i)
     {
       bb = BASIC_BLOCK_FOR_FN (cfun, rpo[i]);
@@ -1655,9 +1888,14 @@  lra_create_live_ranges_1 (bool all_p, bool dead_insn_p)
 	}
     }
   lra_live_max_point = curr_point;
+  create_subregs_live_ranges ();
   if (lra_dump_file != NULL)
-    print_live_ranges (lra_dump_file);
+    {
+      live_points->dump (lra_dump_file);
+      print_live_ranges (lra_dump_file);
+    }
   /* Clean up.	*/
+  delete live_points;
   sparseset_free (unused_set);
   sparseset_free (dead_set);
   sparseset_free (start_dying);
diff --git a/gcc/lra.cc b/gcc/lra.cc
index bcc00ff7d6b..23fc0daf1ed 100644
--- a/gcc/lra.cc
+++ b/gcc/lra.cc
@@ -566,6 +566,54 @@  lra_asm_insn_error (rtx_insn *insn)
 /* Pools for insn reg info.  */
 object_allocator<lra_insn_reg> lra_insn_reg_pool ("insn regs");
 
+/* Return the subreg range of rtx SUBREG in blocks.  */
+static subreg_range
+get_range_blocks (int regno, bool subreg_p, machine_mode reg_mode,
+		  poly_int64 offset, poly_int64 size)
+{
+  gcc_assert (has_subreg_object_p (regno));
+  int nblocks = get_nblocks (reg_mode);
+  if (!subreg_p)
+    return subreg_range (0, nblocks);
+
+  poly_int64 unit_size = REGMODE_NATURAL_SIZE (reg_mode);
+  poly_int64 left = offset + size;
+
+  int subreg_start = -1;
+  int subreg_nregs = -1;
+  for (int i = 0; i < nblocks; i += 1)
+    {
+      poly_int64 right = unit_size * (i + 1);
+      if (subreg_start < 0 && maybe_lt (offset, right))
+	subreg_start = i;
+      if (subreg_nregs < 0 && maybe_le (left, right))
+	{
+	  subreg_nregs = i + 1 - subreg_start;
+	  break;
+	}
+    }
+  gcc_assert (subreg_start >= 0 && subreg_nregs > 0);
+  return subreg_range (subreg_start, subreg_start + subreg_nregs);
+}
+
+/* Return the subreg range of rtx SUBREG in hard regs.  */
+subreg_range
+get_range_hard_regs (int regno, const subreg_range &r)
+{
+  if (!has_subreg_object_p (regno) || lra_reg_info[regno].reg_mode == VOIDmode)
+    return subreg_range (0, 1);
+  enum reg_class aclass = lra_get_allocno_class (regno);
+  gcc_assert (aclass != NO_REGS);
+  int nregs = ira_reg_class_max_nregs[aclass][lra_reg_info[regno].reg_mode];
+  int nblocks = get_nblocks (lra_reg_info[regno].reg_mode);
+  int times = nblocks / nregs;
+  gcc_assert (nblocks >= nregs && times * nregs == nblocks);
+  int start = r.start / times;
+  int end = CEIL (r.end, times);
+
+  return subreg_range (start, end);
+}
+
 /* Create LRA insn related info about a reference to REGNO in INSN
    with TYPE (in/out/inout), biggest reference mode MODE, flag that it
    is reference through subreg (SUBREG_P), and reference to the next
@@ -573,21 +621,49 @@  object_allocator<lra_insn_reg> lra_insn_reg_pool ("insn regs");
    alternatives in which it can be early clobbered are given by
    EARLY_CLOBBER_ALTS.  */
 static struct lra_insn_reg *
-new_insn_reg (rtx_insn *insn, int regno, enum op_type type,
-	      machine_mode mode, bool subreg_p,
-	      alternative_mask early_clobber_alts,
+new_insn_reg (rtx_insn *insn, int regno, enum op_type type, poly_int64 size,
+	      poly_int64 offset, machine_mode mode, machine_mode reg_mode,
+	      bool subreg_p, alternative_mask early_clobber_alts,
 	      struct lra_insn_reg *next)
 {
   lra_insn_reg *ir = lra_insn_reg_pool.allocate ();
   ir->type = type;
   ir->biggest_mode = mode;
-  if (NONDEBUG_INSN_P (insn)
-      && partial_subreg_p (lra_reg_info[regno].biggest_mode, mode))
-    lra_reg_info[regno].biggest_mode = mode;
+  if (NONDEBUG_INSN_P (insn))
+    {
+      if (partial_subreg_p (lra_reg_info[regno].biggest_mode, mode))
+	{
+	  lra_reg_info[regno].biggest_mode = mode;
+	}
+
+      if (regno >= FIRST_PSEUDO_REGISTER)
+	{
+	  if (lra_reg_info[regno].reg_mode == VOIDmode)
+	    lra_reg_info[regno].reg_mode = reg_mode;
+	  else
+	    gcc_assert (maybe_eq (GET_MODE_SIZE (lra_reg_info[regno].reg_mode),
+				  GET_MODE_SIZE (reg_mode)));
+	}
+    }
   ir->subreg_p = subreg_p;
   ir->early_clobber_alts = early_clobber_alts;
   ir->regno = regno;
   ir->next = next;
+  if (has_subreg_object_p (regno))
+    {
+      const subreg_range &r
+	= get_range_blocks (regno, subreg_p, reg_mode, offset, size);
+      ir->start = r.start;
+      ir->end = r.end;
+      const subreg_range &r_hard = get_range_hard_regs (regno, r);
+      ir->start_reg = r_hard.start;
+      ir->end_reg = r_hard.end;
+    }
+  else
+    {
+      ir->start = 0;
+      ir->end = 1;
+    }
   return ir;
 }
 
@@ -887,11 +963,18 @@  collect_non_operand_hard_regs (rtx_insn *insn, rtx *x,
       return list;
   mode = GET_MODE (op);
   subreg_p = false;
+  poly_int64 size = GET_MODE_SIZE (mode);
+  poly_int64 offset = 0;
   if (code == SUBREG)
     {
       mode = wider_subreg_mode (op);
       if (read_modify_subreg_p (op))
-	subreg_p = true;
+	{
+	  offset = SUBREG_BYTE (op);
+	  subreg_p = true;
+	}
+      else
+	size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (op)));
       op = SUBREG_REG (op);
       code = GET_CODE (op);
     }
@@ -925,7 +1008,8 @@  collect_non_operand_hard_regs (rtx_insn *insn, rtx *x,
 		   && ! (FIRST_STACK_REG <= regno
 			 && regno <= LAST_STACK_REG));
 #endif
-	      list = new_insn_reg (data->insn, regno, type, mode, subreg_p,
+	      list = new_insn_reg (data->insn, regno, type, size, offset, mode,
+				   GET_MODE (op), subreg_p,
 				   early_clobber ? ALL_ALTERNATIVES : 0, list);
 	    }
 	}
@@ -1354,6 +1438,7 @@  initialize_lra_reg_info_element (int i)
   lra_reg_info[i].preferred_hard_regno_profit1 = 0;
   lra_reg_info[i].preferred_hard_regno_profit2 = 0;
   lra_reg_info[i].biggest_mode = VOIDmode;
+  lra_reg_info[i].reg_mode = VOIDmode;
   lra_reg_info[i].live_ranges = NULL;
   lra_reg_info[i].nrefs = lra_reg_info[i].freq = 0;
   lra_reg_info[i].last_reload = 0;
@@ -1459,7 +1544,21 @@  lra_get_copy (int n)
   return copy_vec[n];
 }
 
-
+/* Return true if REG occupied the same blocks as OFFSET + SIZE subreg.  */
+static bool
+reg_same_range_p (lra_insn_reg *reg, poly_int64 offset, poly_int64 size,
+		  bool subreg_p)
+{
+  if (has_subreg_object_p (reg->regno))
+    {
+      const subreg_range &r
+	= get_range_blocks (reg->regno, subreg_p,
+			    lra_reg_info[reg->regno].reg_mode, offset, size);
+      return r.start == reg->start && r.end == reg->end;
+    }
+  else
+    return true;
+}
 
 /* This page contains code dealing with info about registers in
    insns.  */
@@ -1483,11 +1582,18 @@  add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x,
   code = GET_CODE (x);
   mode = GET_MODE (x);
   subreg_p = false;
+  poly_int64 size = GET_MODE_SIZE (mode);
+  poly_int64 offset = 0;
   if (GET_CODE (x) == SUBREG)
     {
       mode = wider_subreg_mode (x);
       if (read_modify_subreg_p (x))
-	subreg_p = true;
+	{
+	  offset = SUBREG_BYTE (x);
+	  subreg_p = true;
+	}
+      else
+	size = GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)));
       x = SUBREG_REG (x);
       code = GET_CODE (x);
     }
@@ -1499,7 +1605,8 @@  add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x,
       expand_reg_info ();
       if (bitmap_set_bit (&lra_reg_info[regno].insn_bitmap, INSN_UID (insn)))
 	{
-	  data->regs = new_insn_reg (data->insn, regno, type, mode, subreg_p,
+	  data->regs = new_insn_reg (data->insn, regno, type, size, offset,
+				     mode, GET_MODE (x), subreg_p,
 				     early_clobber_alts, data->regs);
 	  return;
 	}
@@ -1508,12 +1615,14 @@  add_regs_to_insn_regno_info (lra_insn_recog_data_t data, rtx x,
 	  for (curr = data->regs; curr != NULL; curr = curr->next)
 	    if (curr->regno == regno)
 	      {
-		if (curr->subreg_p != subreg_p || curr->biggest_mode != mode)
+		if (!reg_same_range_p (curr, offset, size, subreg_p)
+		    || curr->biggest_mode != mode)
 		  /* The info cannot be integrated into the found
 		     structure.  */
-		  data->regs = new_insn_reg (data->insn, regno, type, mode,
-					     subreg_p, early_clobber_alts,
-					     data->regs);
+		  data->regs
+		    = new_insn_reg (data->insn, regno, type, size, offset, mode,
+				    GET_MODE (x), subreg_p, early_clobber_alts,
+				    data->regs);
 		else
 		  {
 		    if (curr->type != type)