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

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

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Testing failed

Commit Message

Lehua Ding Nov. 8, 2023, 3:47 a.m. UTC
  This patch implements tracking of the live range of subregs and synchronously
modifies conflict detection.

gcc/ChangeLog:

	* ira-build.cc (print_copy): Adjust print.
	(setup_pseudos_has_subreg_object): New.
	(ira_build): collect subreg object allocno.
	* lra-assigns.cc (set_offset_conflicts): New.
	(setup_live_pseudos_and_spill_after_risky_transforms): Adjust.
	(lra_assign): Adjust.
	* lra-constraints.cc (process_alt_operands): Relax.
	* lra-int.h (GCC_LRA_INT_H): New include.
	(struct lra_live_range): New field subreg.
	(struct lra_insn_reg): New fields.
	(get_range_hard_regs):  Exported.
	(get_nregs): New.
	(has_subreg_object_p): New.
	* lra-lives.cc (INCLUDE_VECTOR): New.
	(lra_live_range_pool): New.
	(create_live_range): Adjust.
	(lra_merge_live_ranges): Adjust.
	(update_pseudo_point): Adjust.
	(class bb_data_pseudos): New.
	(mark_regno_live): Adjust.
	(mark_regno_dead): Adjust.
	(process_bb_lives): Adjust.
	(remove_some_program_points_and_update_live_ranges): Adjust.
	(lra_print_live_range_list): Adjust print.
	(class subreg_live_item): New class.
	(create_subregs_live_ranges): New.
	(lra_create_live_ranges_1): Add subreg live ranges.
	* lra.cc (get_range_blocks): New.
	(get_range_hard_regs): New.
	(new_insn_reg): Adjust.
	(collect_non_operand_hard_regs): Adjust.
	(initialize_lra_reg_info_element): Adjust.
	(reg_same_range_p): New.
	(add_regs_to_insn_regno_info): Adjust.
	* subreg-live-range.h: New constructor.

---
 gcc/ira-build.cc        |  40 ++++-
 gcc/lra-assigns.cc      | 111 ++++++++++--
 gcc/lra-constraints.cc  |  18 +-
 gcc/lra-int.h           |  33 ++++
 gcc/lra-lives.cc        | 361 ++++++++++++++++++++++++++++++++++------
 gcc/lra.cc              | 139 ++++++++++++++--
 gcc/subreg-live-range.h |   1 +
 7 files changed, 614 insertions(+), 89 deletions(-)
  

Patch

diff --git a/gcc/ira-build.cc b/gcc/ira-build.cc
index 379f877ca67..cba38d5fecb 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
@@ -1688,8 +1691,13 @@  print_copy (FILE *f, ira_copy_t cp)
 {
   ira_allocno_t a1 = OBJECT_ALLOCNO (cp->first);
   ira_allocno_t a2 = OBJECT_ALLOCNO (cp->second);
-  fprintf (f, "  cp%d:a%d(r%d)<->a%d(r%d)@%d:%s\n", cp->num, ALLOCNO_NUM (a1),
-	   ALLOCNO_REGNO (a1), ALLOCNO_NUM (a2), ALLOCNO_REGNO (a2), cp->freq,
+  fprintf (f, "  cp%d:a%d(r%d", cp->num, ALLOCNO_NUM (a1), ALLOCNO_REGNO (a1));
+  if (ALLOCNO_NREGS (a1) != OBJECT_NREGS (cp->first))
+    fprintf (f, "_obj%d", OBJECT_INDEX (cp->first));
+  fprintf (f, ")<->a%d(r%d", ALLOCNO_NUM (a2), ALLOCNO_REGNO (a2));
+  if (ALLOCNO_NREGS (a2) != OBJECT_NREGS (cp->second))
+    fprintf (f, "_obj%d", OBJECT_INDEX (cp->second));
+  fprintf (f, ")@%d:%s\n", cp->freq,
 	   cp->insn != NULL   ? "move"
 	   : cp->constraint_p ? "constraint"
 			      : "shuffle");
@@ -3706,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
@@ -3726,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 d0752c2ae50..5a97bd61475 100644
--- a/gcc/lra-int.h
+++ b/gcc/lra-int.h
@@ -21,6 +21,9 @@  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)
 
 /* The parameter used to prevent infinite reloading for an insn.  Each
@@ -46,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;
@@ -108,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
@@ -159,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;
 };
@@ -330,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;
@@ -531,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 477b82786cf..814b3960541 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"
@@ -44,6 +45,7 @@  along with GCC; see the file COPYING3.	If not see
 #include "lra-int.h"
 #include "target.h"
 #include "function-abi.h"
+#include "subreg-live-range.h"
 
 /* Program points are enumerated by numbers from range
    0..LRA_LIVE_MAX_POINT-1.  There are approximately two times more
@@ -97,6 +99,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 +118,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 +179,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 +190,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 +254,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,31 +270,100 @@  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);
+    }
 }
 
-/* The corresponding bitmaps of BB currently being processed.  */
-static bitmap bb_killed_pseudos, bb_gen_pseudos;
+/* 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
+   live-analysis.  */
+class bb_data_pseudos : public basic_block_subreg_live_info
+{
+public:
+  /* Basic block about which the below data are.  */
+  basic_block bb;
+};
+
+/* Array for all BB data.  Indexed by the corresponding BB index.  */
+typedef class bb_data_pseudos *bb_data_t;
+
+/* All basic block data are referred through the following array.  */
+static bb_data_t bb_data;
+
+/* The corresponding basic block info of BB currently being processed.  */
+static bb_data_t curr_bb_info;
+
+/* Flag mean curr function has subreg ref need be tracked.  */
+static bool has_subreg_live_p;
 
 /* Record hard register REGNO as now being live.  It updates
    living hard regs and START_LIVING.  */
@@ -336,12 +426,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)
 {
@@ -352,6 +448,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);
@@ -361,9 +462,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)
 {
@@ -374,6 +492,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);
@@ -384,7 +508,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
@@ -814,7 +953,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);
     }
 
@@ -1007,8 +1151,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);
 	  }
@@ -1029,17 +1176,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)
@@ -1070,7 +1220,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
@@ -1091,9 +1241,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);
 	  }
 
@@ -1113,22 +1266,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;
 
@@ -1144,7 +1300,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.	*/
@@ -1277,13 +1433,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))
@@ -1384,7 +1544,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;
@@ -1402,8 +1563,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");
 }
 
@@ -1476,7 +1647,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;
@@ -1557,6 +1805,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]);
@@ -1639,9 +1889,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..47d378b371e 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))
+    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)
diff --git a/gcc/subreg-live-range.h b/gcc/subreg-live-range.h
index bee97708a52..b703b9642f2 100644
--- a/gcc/subreg-live-range.h
+++ b/gcc/subreg-live-range.h
@@ -76,6 +76,7 @@  public:
   int max;
   std::set<subreg_range> ranges;
 
+  subreg_ranges () : max (1) {}
   subreg_ranges (int max) : max (max) { gcc_assert (maybe_ge (max, 1)); }
 
   /* Modify ranges.  */