[2/7] ira: Add live_subreg problem and apply to ira pass

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

Commit Message

Lehua Ding Nov. 8, 2023, 3:47 a.m. UTC
  This patch adds a live_subreg problem to extend the original live_reg to
track the lifecycle of subreg. At the same time, this old live data is
replaced by the new live data in ira pass.

gcc/ChangeLog:

	* Makefile.in: Add subreg-live-range.o
	* df-problems.cc (struct df_live_subreg_problem_data): New df problem.
	(need_track_subreg): helper function.
	(get_range): helper function.
	(remove_subreg_range): helper function.
	(add_subreg_range): helper function.
	(df_live_subreg_free_bb_info): df function.
	(df_live_subreg_alloc): Ditto.
	(df_live_subreg_reset): Ditto.
	(df_live_subreg_bb_local_compute): Ditto.
	(df_live_subreg_local_compute): Ditto.
	(df_live_subreg_init): Ditto.
	(df_live_subreg_check_result): Ditto.
	(df_live_subreg_confluence_0): Ditto.
	(df_live_subreg_confluence_n): Ditto.
	(df_live_subreg_transfer_function): Ditto.
	(df_live_subreg_finalize): Ditto.
	(df_live_subreg_free): Ditto.
	(df_live_subreg_top_dump): Ditto.
	(df_live_subreg_bottom_dump): Ditto.
	(df_live_subreg_add_problem): Ditto.
	* df.h (enum df_problem_id): New df problem.
	(DF_LIVE_SUBREG_INFO): New macro.
	(DF_LIVE_SUBREG_IN): Ditto.
	(DF_LIVE_SUBREG_OUT): Ditto.
	(DF_LIVE_SUBREG_FULL_IN): Ditto.
	(DF_LIVE_SUBREG_FULL_OUT): Ditto.
	(DF_LIVE_SUBREG_PARTIAL_IN): Ditto.
	(DF_LIVE_SUBREG_PARTIAL_OUT): Ditto.
	(DF_LIVE_SUBREG_RANGE_IN): Ditto.
	(DF_LIVE_SUBREG_RANGE_OUT): Ditto.
	(class subregs_live): New class.
	(class basic_block_subreg_live_info): New class.
	(class df_live_subreg_bb_info): New class.
	(df_live_subreg): New function.
	(df_live_subreg_add_problem): Ditto.
	(df_live_subreg_finalize): Ditto.
	(class subreg_range): New class.
	(need_track_subreg): Exported.
	(remove_subreg_range): Exported.
	(add_subreg_range): Exported.
	(df_live_subreg_get_bb_info): Exported.
	* ira-build.cc (create_bb_allocnos): Use new live data.
	(create_loop_allocnos): Ditto.
	* ira-color.cc (ira_loop_edge_freq): Ditto.
	* ira-emit.cc (generate_edge_moves): Ditto.
	(add_ranges_and_copies): Ditto.
	* ira-lives.cc (process_out_of_region_eh_regs): Ditto.
	(process_bb_node_lives): Ditto.
	* ira.cc (find_moveable_pseudos): Ditto.
	(interesting_dest_for_shprep_1): Ditto.
	(allocate_initial_values): Ditto.
	(ira): Ditto.
	* reginfo.cc (get_nblocks_slow): Helper function.
	* rtl.h (get_nblocks_slow): Helper function.
	(get_nblocks): Helper function.
	* timevar.def (TV_DF_LIVE_SUBREG): New timevar.
	* subreg-live-range.cc: New file.
	* subreg-live-range.h: New file.

---
 gcc/Makefile.in          |   1 +
 gcc/df-problems.cc       | 889 ++++++++++++++++++++++++++++++++++++++-
 gcc/df.h                 |  93 +++-
 gcc/ira-build.cc         |  14 +-
 gcc/ira-color.cc         |   8 +-
 gcc/ira-emit.cc          |  12 +-
 gcc/ira-lives.cc         |   7 +-
 gcc/ira.cc               |  20 +-
 gcc/reginfo.cc           |  14 +
 gcc/rtl.h                |  14 +
 gcc/subreg-live-range.cc | 649 ++++++++++++++++++++++++++++
 gcc/subreg-live-range.h  | 326 ++++++++++++++
 gcc/timevar.def          |   1 +
 13 files changed, 2008 insertions(+), 40 deletions(-)
 create mode 100644 gcc/subreg-live-range.cc
 create mode 100644 gcc/subreg-live-range.h
  

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 29cec21c825..e4403b5a30c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1675,6 +1675,7 @@  OBJS = \
 	store-motion.o \
 	streamer-hooks.o \
 	stringpool.o \
+        subreg-live-range.o \
 	substring-locations.o \
 	target-globals.o \
 	targhooks.o \
diff --git a/gcc/df-problems.cc b/gcc/df-problems.cc
index d2cfaf7f50f..2585c762fd1 100644
--- a/gcc/df-problems.cc
+++ b/gcc/df-problems.cc
@@ -28,6 +28,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "target.h"
 #include "rtl.h"
 #include "df.h"
+#include "subreg-live-range.h"
 #include "memmodel.h"
 #include "tm_p.h"
 #include "insn-config.h"
@@ -1344,8 +1345,894 @@  df_lr_verify_transfer_functions (void)
   bitmap_clear (&all_blocks);
 }
 
+/*----------------------------------------------------------------------------
+   REGISTER AND SUBREG LIVES
+   Like DF_RL, but fine-grained tracking of subreg lifecycle.
+   ----------------------------------------------------------------------------*/
+
+/* Private data used to verify the solution for this problem.  */
+struct df_live_subreg_problem_data
+{
+  /* An obstack for the bitmaps we need for this problem.  */
+  bitmap_obstack live_subreg_bitmaps;
+  bool has_subreg_live_p;
+};
+
+/* Helper functions */
+
+/* Return true if REGNO is a pseudo and MODE is a multil regs size.  */
+bool
+need_track_subreg (int regno, machine_mode reg_mode)
+{
+  poly_int64 total_size = GET_MODE_SIZE (reg_mode);
+  poly_int64 natural_size = REGMODE_NATURAL_SIZE (reg_mode);
+  return maybe_gt (total_size, natural_size)
+	 && multiple_p (total_size, natural_size)
+	 && regno >= FIRST_PSEUDO_REGISTER;
+}
+
+/* Return subreg_range of REF.  */
+static subreg_range
+get_range (df_ref ref)
+{
+  rtx reg = DF_REF_REAL_REG (ref);
+  machine_mode reg_mode = GET_MODE (reg);
+
+  if (!read_modify_subreg_p (DF_REF_REG (ref)))
+    return subreg_range (0, get_nblocks (reg_mode));
+
+  rtx subreg = DF_REF_REG (ref);
+  machine_mode subreg_mode = GET_MODE (subreg);
+  poly_int64 offset = SUBREG_BYTE (subreg);
+  int nblocks = get_nblocks (reg_mode);
+  poly_int64 unit_size = REGMODE_NATURAL_SIZE (reg_mode);
+  poly_int64 subreg_size = GET_MODE_SIZE (subreg_mode);
+  poly_int64 left = offset + subreg_size;
+
+  int subreg_start = -1;
+  int subreg_nblocks = -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_nblocks < 0 && maybe_le (left, right))
+	{
+	  subreg_nblocks = i + 1 - subreg_start;
+	  break;
+	}
+    }
+  gcc_assert (subreg_start >= 0 && subreg_nblocks > 0);
+
+  return subreg_range (subreg_start, subreg_start + subreg_nblocks);
+}
+
+/* Remove REF from BB_INFO use.  */
+void
+remove_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int regno,
+		     machine_mode mode, const subreg_range &range)
+{
+  int max = get_nblocks (mode);
+  bitmap full = &bb_info->full_use;
+  bitmap partial = &bb_info->partial_use;
+  subregs_live *range_live = bb_info->range_use;
+
+  if (!range.full_p (max))
+    {
+      if (bitmap_bit_p (full, regno))
+	{
+	  bitmap_clear_bit (full, regno);
+	  gcc_assert (!bitmap_bit_p (partial, regno));
+	  gcc_assert (range_live->empty_p (regno));
+	  subreg_ranges temp = subreg_ranges (max);
+	  temp.make_full ();
+	  temp.remove_range (max, range);
+	  range_live->add_ranges (regno, temp);
+	  bitmap_set_bit (partial, regno);
+	  return;
+	}
+      else if (bitmap_bit_p (partial, regno))
+	{
+	  range_live->remove_range (regno, max, range);
+	  if (range_live->empty_p (regno))
+	    bitmap_clear_bit (partial, regno);
+	}
+    }
+  else if (bitmap_bit_p (full, regno))
+    {
+      bitmap_clear_bit (full, regno);
+      gcc_assert (!bitmap_bit_p (partial, regno));
+    }
+  else if (bitmap_bit_p (partial, regno))
+    {
+      bitmap_clear_bit (partial, regno);
+      range_live->remove_live (regno);
+    }
+}
+
+/* Return true if ref is a tracked subreg access.  */
+bool
+remove_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref)
+{
+  unsigned int regno = DF_REF_REGNO (ref);
+  machine_mode mode = GET_MODE (DF_REF_REAL_REG (ref));
+  bool subreg_p = read_modify_subreg_p (DF_REF_REG (ref));
+
+  if (need_track_subreg (regno, mode))
+    {
+      remove_subreg_range (bb_info, regno, mode, get_range (ref));
+      return subreg_p;
+    }
+  else
+    {
+      bitmap_clear_bit (&bb_info->full_use, regno);
+      gcc_assert (!bitmap_bit_p (&bb_info->partial_use, regno));
+      gcc_assert (!bitmap_bit_p (&bb_info->partial_def, regno));
+      return false;
+    }
+}
+
+/* add REF to BB_INFO def/use.  */
+void
+add_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int regno,
+		  machine_mode mode, const subreg_range &range, bool is_def)
+{
+  int max = get_nblocks (mode);
+  bitmap full = is_def ? &bb_info->full_def : &bb_info->full_use;
+  bitmap partial = is_def ? &bb_info->partial_def : &bb_info->partial_use;
+  subregs_live *range_live = is_def ? bb_info->range_def : bb_info->range_use;
+
+  if (!range.full_p (max))
+    {
+      if (bitmap_bit_p (full, regno))
+	return;
+      range_live->add_range (regno, max, range);
+      if (range_live->full_p (regno))
+	{
+	  bitmap_set_bit (full, regno);
+	  gcc_assert (bitmap_bit_p (partial, regno));
+	  bitmap_clear_bit (partial, regno);
+	  range_live->remove_live (regno);
+	}
+      else if (!bitmap_bit_p (partial, regno))
+	bitmap_set_bit (partial, regno);
+    }
+  else if (!bitmap_bit_p (full, regno))
+    {
+      bitmap_set_bit (full, regno);
+      if (bitmap_bit_p (partial, regno))
+	{
+	  bitmap_clear_bit (partial, regno);
+	  range_live->remove_live (regno);
+	}
+    }
+}
+
+/* Return true if ref is a tracked subreg access.  */
+bool
+add_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref,
+		  bool is_def)
+{
+  unsigned int regno = DF_REF_REGNO (ref);
+  machine_mode mode = GET_MODE (DF_REF_REAL_REG (ref));
+  bool subreg_p = read_modify_subreg_p (DF_REF_REG (ref));
+
+  if (need_track_subreg (regno, mode))
+    {
+      add_subreg_range (bb_info, regno, mode, get_range (ref), is_def);
+      return subreg_p;
+    }
+  else
+    {
+      bitmap full = is_def ? &bb_info->full_def : &bb_info->full_use;
+      bitmap partial = is_def ? &bb_info->partial_def : &bb_info->partial_use;
+      bitmap_set_bit (full, regno);
+      gcc_assert (!bitmap_bit_p (partial, regno));
+
+      if (is_def && DF_REF_FLAGS (ref) & (DF_REF_PARTIAL | DF_REF_CONDITIONAL))
+	add_subreg_range (bb_info, ref, false);
+      return false;
+    }
+}
+
+/* Free basic block info.  */
+
+static void
+df_live_subreg_free_bb_info (basic_block bb ATTRIBUTE_UNUSED, void *vbb_info)
+{
+  df_live_subreg_bb_info *bb_info = (df_live_subreg_bb_info *) vbb_info;
+  if (bb_info)
+    {
+      delete bb_info->range_def;
+      bb_info->range_def = NULL;
+      delete bb_info->range_use;
+      bb_info->range_use = NULL;
+      delete bb_info->range_in;
+      bb_info->range_in = NULL;
+      delete bb_info->range_out;
+      bb_info->range_out = NULL;
+
+      bitmap_clear (&bb_info->full_use);
+      bitmap_clear (&bb_info->partial_use);
+      bitmap_clear (&bb_info->full_def);
+      bitmap_clear (&bb_info->partial_def);
+      bitmap_clear (&bb_info->all_in);
+      bitmap_clear (&bb_info->full_in);
+      bitmap_clear (&bb_info->partial_in);
+      bitmap_clear (&bb_info->all_out);
+      bitmap_clear (&bb_info->full_out);
+      bitmap_clear (&bb_info->partial_out);
+    }
+}
+
+/* Allocate or reset bitmaps for DF_LIVE_SUBREG blocks. The solution bits are
+   not touched unless the block is new.  */
+
+static void
+df_live_subreg_alloc (bitmap all_blocks ATTRIBUTE_UNUSED)
+{
+  struct df_live_subreg_problem_data *problem_data;
+  df_grow_bb_info (df_live_subreg);
+  if (df_live_subreg->problem_data)
+    problem_data
+      = (struct df_live_subreg_problem_data *) df_live_subreg->problem_data;
+  else
+    {
+      problem_data = XNEW (struct df_live_subreg_problem_data);
+      df_live_subreg->problem_data = problem_data;
+
+      bitmap_obstack_initialize (&problem_data->live_subreg_bitmaps);
+      problem_data->has_subreg_live_p = false;
+    }
+
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, cfun)
+    bitmap_set_bit (df_live_subreg->out_of_date_transfer_functions, bb->index);
+
+  bitmap_set_bit (df_live_subreg->out_of_date_transfer_functions, ENTRY_BLOCK);
+  bitmap_set_bit (df_live_subreg->out_of_date_transfer_functions, EXIT_BLOCK);
+
+  unsigned int bb_index;
+  bitmap_iterator bi;
+  EXECUTE_IF_SET_IN_BITMAP (df_live_subreg->out_of_date_transfer_functions, 0,
+			    bb_index, bi)
+    {
+      df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb_index);
+
+      /* When bitmaps are already initialized, just clear them.  */
+      if (bb_info->full_use.obstack)
+	{
+	  bitmap_clear (&bb_info->full_def);
+	  bitmap_clear (&bb_info->partial_def);
+	  bitmap_clear (&bb_info->full_use);
+	  bitmap_clear (&bb_info->partial_use);
+	  bitmap_clear (&bb_info->all_in);
+	  bitmap_clear (&bb_info->full_in);
+	  bitmap_clear (&bb_info->partial_in);
+	  bitmap_clear (&bb_info->all_out);
+	  bitmap_clear (&bb_info->full_out);
+	  bitmap_clear (&bb_info->partial_out);
+	}
+      else
+	{
+	  bitmap_initialize (&bb_info->full_def,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->partial_def,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->full_use,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->partial_use,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->all_in,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->full_in,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->partial_in,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->all_out,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->full_out,
+			     &problem_data->live_subreg_bitmaps);
+	  bitmap_initialize (&bb_info->partial_out,
+			     &problem_data->live_subreg_bitmaps);
+	}
+
+      if (bb_info->range_def)
+	{
+	  bb_info->range_def->clear ();
+	  bb_info->range_use->clear ();
+	  bb_info->range_in->clear ();
+	  bb_info->range_out->clear ();
+	}
+      else
+	{
+	  bb_info->range_def = new subregs_live ();
+	  bb_info->range_use = new subregs_live ();
+	  bb_info->range_in = new subregs_live ();
+	  bb_info->range_out = new subregs_live ();
+	}
+    }
+  df_live_subreg->optional_p = true;
+}
+
+/* Reset the global solution for recalculation.  */
+
+static void
+df_live_subreg_reset (bitmap all_blocks)
+{
+  unsigned int bb_index;
+  bitmap_iterator bi;
+
+  EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi)
+    {
+      df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb_index);
+      gcc_assert (bb_info);
+      bitmap_clear (&bb_info->all_in);
+      bitmap_clear (&bb_info->full_in);
+      bitmap_clear (&bb_info->partial_in);
+      bitmap_clear (&bb_info->all_out);
+      bitmap_clear (&bb_info->full_out);
+      bitmap_clear (&bb_info->partial_out);
+      bb_info->range_in->clear ();
+      bb_info->range_out->clear ();
+    }
+}
+
+/* Compute local live register info for basic block BB.  */
+
+static void
+df_live_subreg_bb_local_compute (unsigned int bb_index)
+{
+  basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_index);
+  df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb_index);
+  df_live_subreg_problem_data *problem_data
+    = (df_live_subreg_problem_data *) df_live_subreg->problem_data;
+  rtx_insn *insn;
+  df_ref def, use;
+
+  /* Process the registers set in an exception handler.  */
+  FOR_EACH_ARTIFICIAL_DEF (def, bb_index)
+    if ((DF_REF_FLAGS (def) & DF_REF_AT_TOP) == 0)
+      {
+	problem_data->has_subreg_live_p
+	  |= add_subreg_range (bb_info, def, true);
+	problem_data->has_subreg_live_p |= remove_subreg_range (bb_info, def);
+      }
+
+  /* Process the hardware registers that are always live.  */
+  FOR_EACH_ARTIFICIAL_USE (use, bb_index)
+    /* Add use to set of uses in this BB.  */
+    if ((DF_REF_FLAGS (use) & DF_REF_AT_TOP) == 0)
+      problem_data->has_subreg_live_p |= add_subreg_range (bb_info, use);
+
+  FOR_BB_INSNS_REVERSE (bb, insn)
+    {
+      if (!NONDEBUG_INSN_P (insn))
+	continue;
+
+      df_insn_info *insn_info = DF_INSN_INFO_GET (insn);
+      FOR_EACH_INSN_INFO_DEF (def, insn_info)
+	{
+	  problem_data->has_subreg_live_p |= remove_subreg_range (bb_info, def);
+	  problem_data->has_subreg_live_p
+	    |= add_subreg_range (bb_info, def, true);
+	}
+
+      FOR_EACH_INSN_INFO_USE (use, insn_info)
+	{
+	  unsigned int regno = DF_REF_REGNO (use);
+	  machine_mode mode = GET_MODE (DF_REF_REAL_REG (use));
+	  /* Ignore the use of SET_DEST which is (subreg (reg) offset).  */
+	  if (need_track_subreg (regno, mode)
+	      && DF_REF_FLAGS (use) & (DF_REF_READ_WRITE | DF_REF_SUBREG))
+	    continue;
+	  problem_data->has_subreg_live_p |= add_subreg_range (bb_info, use);
+	}
+    }
+
+  /* Process the registers set in an exception handler or the hard
+     frame pointer if this block is the target of a non local
+     goto.  */
+  FOR_EACH_ARTIFICIAL_DEF (def, bb_index)
+    if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+      {
+	problem_data->has_subreg_live_p
+	  |= add_subreg_range (bb_info, def, true);
+	problem_data->has_subreg_live_p |= remove_subreg_range (bb_info, def);
+      }
+
+#ifdef EH_USES
+  /* Process the uses that are live into an exception handler.  */
+  FOR_EACH_ARTIFICIAL_USE (use, bb_index)
+    /* Add use to set of uses in this BB.  */
+    if (DF_REF_FLAGS (use) & DF_REF_AT_TOP)
+      problem_data->has_subreg_live_p |= add_subreg_range (bb_info, use);
+#endif
+}
+
+/* Compute local live register info for each basic block within BLOCKS.  */
+
+static void
+df_live_subreg_local_compute (bitmap all_blocks ATTRIBUTE_UNUSED)
+{
+  unsigned int bb_index, i;
+  bitmap_iterator bi;
+
+  bitmap_clear (&df->hardware_regs_used);
+
+  /* The all-important stack pointer must always be live.  */
+  bitmap_set_bit (&df->hardware_regs_used, STACK_POINTER_REGNUM);
+
+  /* Global regs are always live, too.  */
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (global_regs[i])
+      bitmap_set_bit (&df->hardware_regs_used, i);
+
+  /* Before reload, there are a few registers that must be forced
+     live everywhere -- which might not already be the case for
+     blocks within infinite loops.  */
+  if (!reload_completed)
+    {
+      unsigned int pic_offset_table_regnum = PIC_OFFSET_TABLE_REGNUM;
+      /* Any reference to any pseudo before reload is a potential
+	 reference of the frame pointer.  */
+      bitmap_set_bit (&df->hardware_regs_used, FRAME_POINTER_REGNUM);
+
+      /* Pseudos with argument area equivalences may require
+	 reloading via the argument pointer.  */
+      if (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+	  && fixed_regs[ARG_POINTER_REGNUM])
+	bitmap_set_bit (&df->hardware_regs_used, ARG_POINTER_REGNUM);
+
+      /* Any constant, or pseudo with constant equivalences, may
+	 require reloading from memory using the pic register.  */
+      if (pic_offset_table_regnum != INVALID_REGNUM
+	  && fixed_regs[pic_offset_table_regnum])
+	bitmap_set_bit (&df->hardware_regs_used, pic_offset_table_regnum);
+    }
+
+  EXECUTE_IF_SET_IN_BITMAP (df_live_subreg->out_of_date_transfer_functions, 0,
+			    bb_index, bi)
+    {
+      if (bb_index == EXIT_BLOCK)
+	{
+	  /* The exit block is special for this problem and its bits are
+	     computed from thin air.  */
+	  class df_live_subreg_bb_info *bb_info
+	    = df_live_subreg_get_bb_info (EXIT_BLOCK);
+	  bitmap_copy (&bb_info->full_use, df->exit_block_uses);
+	}
+      else
+	df_live_subreg_bb_local_compute (bb_index);
+    }
+
+  bitmap_clear (df_live_subreg->out_of_date_transfer_functions);
+}
+
+/* Initialize the solution vectors.  */
+
+static void
+df_live_subreg_init (bitmap all_blocks)
+{
+  unsigned int bb_index;
+  bitmap_iterator bi;
+
+  EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi)
+    {
+      df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb_index);
+      bitmap_copy (&bb_info->full_in, &bb_info->full_use);
+      bitmap_copy (&bb_info->partial_in, &bb_info->partial_use);
+      bb_info->range_in->copy_lives (*bb_info->range_use);
+      bitmap_clear (&bb_info->full_out);
+      bitmap_clear (&bb_info->partial_out);
+      bb_info->range_out->clear ();
+    }
+}
+
+/* Check the result is golden.  */
+static void
+df_live_subreg_check_result (bitmap full, bitmap partial,
+			     subregs_live *partial_live)
+{
+  unsigned int regno;
+  bitmap_iterator bi;
+  gcc_assert (!bitmap_intersect_p (full, partial));
+  EXECUTE_IF_SET_IN_BITMAP (full, 0, regno, bi)
+    gcc_assert (partial_live->empty_p (regno));
+  EXECUTE_IF_SET_IN_BITMAP (partial, 0, regno, bi)
+    gcc_assert (!partial_live->empty_p (regno));
+}
+
+/* Confluence function that processes infinite loops.  This might be a
+   noreturn function that throws.  And even if it isn't, getting the
+   unwind info right helps debugging.  */
+static void
+df_live_subreg_confluence_0 (basic_block bb)
+{
+  bitmap full_out = &df_live_subreg_get_bb_info (bb->index)->full_out;
+  if (bb != EXIT_BLOCK_PTR_FOR_FN (cfun))
+    bitmap_copy (full_out, &df->hardware_regs_used);
+}
+
+/* Confluence function that ignores fake edges.  */
+
+static bool
+df_live_subreg_confluence_n (edge e)
+{
+  df_live_subreg_problem_data *problem_data
+    = (df_live_subreg_problem_data *) df_live_subreg->problem_data;
+  class df_live_subreg_bb_info *src_bb_info
+    = df_live_subreg_get_bb_info (e->src->index);
+  class df_live_subreg_bb_info *dest_bb_info
+    = df_live_subreg_get_bb_info (e->dest->index);
+
+  if (!problem_data->has_subreg_live_p)
+    {
+      bool changed = false;
+
+      /* Call-clobbered registers die across exception and call edges.
+	 Conservatively treat partially-clobbered registers as surviving
+	 across the edges; they might or might not, depending on what
+	 mode they have.  */
+      /* ??? Abnormal call edges ignored for the moment, as this gets
+	 confused by sibling call edges, which crashes reg-stack.  */
+      if (e->flags & EDGE_EH)
+	{
+	  bitmap_view<HARD_REG_SET> eh_kills (eh_edge_abi.full_reg_clobbers ());
+	  changed
+	    = bitmap_ior_and_compl_into (&src_bb_info->full_out,
+					 &dest_bb_info->full_in, eh_kills);
+	}
+      else
+	changed
+	  = bitmap_ior_into (&src_bb_info->full_out, &dest_bb_info->full_in);
+
+      changed
+	|= bitmap_ior_into (&src_bb_info->full_out, &df->hardware_regs_used);
+      return changed;
+    }
+
+  /* If there has subreg live need be tracked. Calculation formula:
+       temp_full mean:
+	 1. partial in out/in, full in other in/out
+	 2. partial in out and in, and mrege range is full
+       temp_range mean:
+	 the range of regno which partial live
+       src_bb_info->partial_out = (src_bb_info->partial_out |
+				   dest_bb_info->partial_in) & ~temp_full
+       src_bb_info->range_out = copy(temp_range)
+       src_bb_info->full_out |= dest_bb_info->full_in | temp_full
+       */
+  subregs_live temp_range;
+  temp_range.add_lives (*src_bb_info->range_out);
+  temp_range.add_lives (*dest_bb_info->range_in);
+
+  bitmap_head temp_partial_all;
+  bitmap_initialize (&temp_partial_all, &bitmap_default_obstack);
+  bitmap_ior (&temp_partial_all, &src_bb_info->partial_out,
+	      &dest_bb_info->partial_in);
+
+  bitmap_head temp_full;
+  bitmap_initialize (&temp_full, &bitmap_default_obstack);
+
+  /* Collect regno that become full after merge src_bb_info->partial_out
+     and dest_bb_info->partial_in.  */
+  unsigned int regno;
+  bitmap_iterator bi;
+  EXECUTE_IF_SET_IN_BITMAP (&temp_partial_all, FIRST_PSEUDO_REGISTER, regno, bi)
+    {
+      if (bitmap_bit_p (&src_bb_info->full_out, regno)
+	  || bitmap_bit_p (&dest_bb_info->full_in, regno))
+	{
+	  bitmap_set_bit (&temp_full, regno);
+	  temp_range.remove_live (regno);
+	  continue;
+	}
+      else if (!bitmap_bit_p (&src_bb_info->partial_out, regno)
+	       || !bitmap_bit_p (&dest_bb_info->partial_in, regno))
+	continue;
+
+      subreg_ranges temp = src_bb_info->range_out->lives.at (regno);
+      temp.add_ranges (dest_bb_info->range_in->lives.at (regno));
+      if (temp.full_p ())
+	{
+	  bitmap_set_bit (&temp_full, regno);
+	  temp_range.remove_live (regno);
+	}
+    }
+
+  /* Calculating src_bb_info->partial_out and src_bb_info->range_out.  */
+  bool changed = bitmap_and_compl (&src_bb_info->partial_out, &temp_partial_all,
+				   &temp_full);
+  changed |= src_bb_info->range_out->copy_lives (temp_range);
+
+  /* Calculating src_bb_info->full_out.  */
+  bitmap_ior_into (&temp_full, &dest_bb_info->full_in);
+
+  /* Call-clobbered registers die across exception and call edges.
+     Conservatively treat partially-clobbered registers as surviving
+     across the edges; they might or might not, depending on what
+     mode they have.  */
+  /* ??? Abnormal call edges ignored for the moment, as this gets
+     confused by sibling call edges, which crashes reg-stack.  */
+  if (e->flags & EDGE_EH)
+    {
+      bitmap_view<HARD_REG_SET> eh_kills (eh_edge_abi.full_reg_clobbers ());
+      changed |= bitmap_ior_and_compl_into (&src_bb_info->full_out, &temp_full,
+					    eh_kills);
+    }
+  else
+    changed |= bitmap_ior_into (&src_bb_info->full_out, &temp_full);
+
+  changed |= bitmap_ior_into (&src_bb_info->full_out, &df->hardware_regs_used);
+
+  bitmap_clear (&temp_full);
+  bitmap_clear (&temp_partial_all);
+
+  df_live_subreg_check_result (&src_bb_info->full_out,
+			       &src_bb_info->partial_out,
+			       src_bb_info->range_out);
+  return changed;
+}
+
+/* Transfer function.  */
+
+static bool
+df_live_subreg_transfer_function (int bb_index)
+{
+  class df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb_index);
+  df_live_subreg_problem_data *problem_data
+    = (df_live_subreg_problem_data *) df_live_subreg->problem_data;
+  if (!problem_data->has_subreg_live_p)
+    {
+      bitmap in = &bb_info->full_in;
+      bitmap out = &bb_info->full_out;
+      bitmap use = &bb_info->full_use;
+      bitmap def = &bb_info->full_def;
+
+      return bitmap_ior_and_compl (in, use, out, def);
+    }
+
+  /* If there has subreg live need be tracked, follow the bellow calculation
+     formula:
+       all_def = full_def | partial_def
+       temp_partial_out = ((full_out & partail_def)
+			   | (partail_out & ~all_def)
+			   | (partial_out remove partail_def not empty))
+			  & ~full_use
+       temp_partial_be_full = (temp_partial_out & partial_use) merge be full
+       full_in = full_use | full_out & ~all_def | temp_partial_be_full
+       partail_in = (temp_partial_out | partial_use) & ~temp_partial_be_full  */
+  unsigned int regno;
+  bitmap_iterator bi;
+  bool changed = false;
+  bitmap_head temp_partial_out;
+  bitmap_head temp_partial_be_full;
+  bitmap_head all_def;
+  subregs_live temp_range_out;
+  bitmap_initialize (&temp_partial_out, &bitmap_default_obstack);
+  bitmap_initialize (&temp_partial_be_full, &bitmap_default_obstack);
+  bitmap_initialize (&all_def, &bitmap_default_obstack);
+
+  bitmap_ior (&all_def, &bb_info->full_def, &bb_info->partial_def);
+
+  /* temp_partial_out = (full_out & partail_def) */
+  bitmap_and (&temp_partial_out, &bb_info->full_out, &bb_info->partial_def);
+  EXECUTE_IF_SET_IN_BITMAP (&temp_partial_out, FIRST_PSEUDO_REGISTER, regno, bi)
+    {
+      subreg_ranges temp (bb_info->range_def->lives.at (regno).max);
+      temp.make_full ();
+      temp.remove_ranges (bb_info->range_def->lives.at (regno));
+      temp_range_out.add_ranges (regno, temp);
+    }
+
+  /* temp_partial_out |= (partail_out & ~all_def) */
+  bitmap_ior_and_compl_into (&temp_partial_out, &bb_info->partial_out,
+			     &all_def);
+  EXECUTE_IF_AND_COMPL_IN_BITMAP (&bb_info->partial_out, &all_def,
+				  FIRST_PSEUDO_REGISTER, regno, bi)
+    {
+      temp_range_out.add_ranges (regno, bb_info->range_out->lives.at (regno));
+    }
+
+  /* temp_partial_out |= (partial_out remove partail_def not empty) */
+  EXECUTE_IF_AND_IN_BITMAP (&bb_info->partial_out, &bb_info->partial_def, 0,
+			    regno, bi)
+    {
+      subreg_ranges temp = bb_info->range_out->lives.at (regno);
+      temp.remove_ranges (bb_info->range_def->lives.at (regno));
+      if (!temp.empty_p ())
+	{
+	  bitmap_set_bit (&temp_partial_out, regno);
+	  temp_range_out.add_ranges (regno, temp);
+	}
+    }
+
+  /* temp_partial_out = temp_partial_out & ~full_use */
+  bitmap_and_compl_into (&temp_partial_out, &bb_info->full_use);
+  EXECUTE_IF_SET_IN_BITMAP (&bb_info->full_use, 0, regno, bi)
+    if (!temp_range_out.empty_p (regno))
+      temp_range_out.remove_live (regno);
+
+  /* temp_partial_be_full = (temp_partial_out & partial_use) merge become full
+   */
+  temp_range_out.add_lives (*bb_info->range_use);
+  /* Remove all range which in partial_use and in full_out and not in all_def.
+   */
+  EXECUTE_IF_SET_IN_BITMAP (&bb_info->full_out, 0, regno, bi)
+    if (!bitmap_bit_p (&all_def, regno) && !temp_range_out.empty_p (regno))
+      temp_range_out.remove_live (regno);
+
+  EXECUTE_IF_AND_IN_BITMAP (&temp_partial_out, &bb_info->partial_use, 0, regno,
+			    bi)
+    {
+      subreg_ranges temp = temp_range_out.lives.at (regno);
+      temp.add_ranges (bb_info->range_use->lives.at (regno));
+      if (temp.full_p ())
+	{
+	  bitmap_set_bit (&temp_partial_be_full, regno);
+	  temp_range_out.remove_live (regno);
+	}
+    }
+
+  /* Calculating full_in.  */
+  bitmap_ior_and_compl_into (&temp_partial_be_full, &bb_info->full_out,
+			     &all_def);
+  changed |= bitmap_ior (&bb_info->full_in, &temp_partial_be_full,
+			 &bb_info->full_use);
+
+  /* Calculating partial_in and range_in.  */
+  bitmap_ior_into (&temp_partial_out, &bb_info->partial_use);
+  changed |= bitmap_and_compl (&bb_info->partial_in, &temp_partial_out,
+			       &temp_partial_be_full);
+  changed |= bb_info->range_in->copy_lives (temp_range_out);
+
+  bitmap_clear (&temp_partial_out);
+  bitmap_clear (&temp_partial_be_full);
+  bitmap_clear (&all_def);
+
+  df_live_subreg_check_result (&bb_info->full_in, &bb_info->partial_in,
+			       bb_info->range_in);
+
+  return changed;
+}
+
+/* Run the fast dce as a side effect of building LR.  */
+
+void
+df_live_subreg_finalize (bitmap all_blocks)
+{
+  unsigned int bb_index;
+  bitmap_iterator bi;
+  EXECUTE_IF_SET_IN_BITMAP (all_blocks, 0, bb_index, bi)
+    {
+      class df_live_subreg_bb_info *bb_info
+	= df_live_subreg_get_bb_info (bb_index);
+      gcc_assert (bb_info);
+      bitmap_ior (&bb_info->all_in, &bb_info->full_in, &bb_info->partial_in);
+      bitmap_ior (&bb_info->all_out, &bb_info->full_out, &bb_info->partial_out);
+    }
+}
+
+/* Free all storage associated with the problem.  */
+
+static void
+df_live_subreg_free (void)
+{
+  df_live_subreg_problem_data *problem_data
+    = (df_live_subreg_problem_data *) df_live_subreg->problem_data;
+  if (df_live_subreg->block_info)
+    {
+      df_live_subreg->block_info_size = 0;
+      free (df_live_subreg->block_info);
+      df_live_subreg->block_info = NULL;
+      bitmap_obstack_release (&problem_data->live_subreg_bitmaps);
+      free (df_live_subreg->problem_data);
+      df_live_subreg->problem_data = NULL;
+    }
+
+  BITMAP_FREE (df_live_subreg->out_of_date_transfer_functions);
+  free (df_live_subreg);
+}
+
+/* Debugging info at top of bb.  */
+
+static void
+df_live_subreg_top_dump (basic_block bb, FILE *file)
+{
+  df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb->index);
+  if (!bb_info)
+    return;
+
+  fprintf (file, ";; subreg live all in  \t");
+  df_print_regset (file, &bb_info->all_in);
+  fprintf (file, ";;   subreg live full in  \t");
+  df_print_regset (file, &bb_info->full_in);
+  fprintf (file, ";;   subreg live partial in  \t");
+  df_print_regset (file, &bb_info->partial_in);
+  fprintf (file, ";;   subreg live range in  \t");
+  bb_info->range_in->dump (file, "");
+
+  fprintf (file, "\n;;   subreg live full use  \t");
+  df_print_regset (file, &bb_info->full_use);
+  fprintf (file, ";;   subreg live partial use  \t");
+  df_print_regset (file, &bb_info->partial_use);
+  fprintf (file, ";;   subreg live range use  \t");
+  bb_info->range_use->dump (file, "");
+
+  fprintf (file, "\n;;   subreg live full def  \t");
+  df_print_regset (file, &bb_info->full_def);
+  fprintf (file, ";;   subreg live partial def  \t");
+  df_print_regset (file, &bb_info->partial_def);
+  fprintf (file, ";;   subreg live range def \t");
+  bb_info->range_def->dump (file, "");
+}
+
+/* Debugging info at bottom of bb.  */
+
+static void
+df_live_subreg_bottom_dump (basic_block bb, FILE *file)
+{
+  df_live_subreg_bb_info *bb_info = df_live_subreg_get_bb_info (bb->index);
+  if (!bb_info)
+    return;
+
+  fprintf (file, ";; subreg live all out  \t");
+  df_print_regset (file, &bb_info->all_out);
+  fprintf (file, ";;   subreg live full out  \t");
+  df_print_regset (file, &bb_info->full_out);
+  fprintf (file, ";;   subreg live partial out  \t");
+  df_print_regset (file, &bb_info->partial_out);
+  fprintf (file, ";;   subreg live range out  \t");
+  bb_info->range_out->dump (file, "");
+}
+
+/* All of the information associated with every instance of the problem.  */
+
+static const struct df_problem problem_LIVE_SUBREG = {
+  DF_LIVE_SUBREG,		    /* Problem id.  */
+  DF_BACKWARD,			    /* Direction.  */
+  df_live_subreg_alloc,		    /* Allocate the problem specific data.  */
+  df_live_subreg_reset,		    /* Reset global information.  */
+  df_live_subreg_free_bb_info,	    /* Free basic block info.  */
+  df_live_subreg_local_compute,	    /* Local compute function.  */
+  df_live_subreg_init,		    /* Init the solution specific data.  */
+  df_worklist_dataflow,		    /* Worklist solver.  */
+  df_live_subreg_confluence_0,	    /* Confluence operator 0.  */
+  df_live_subreg_confluence_n,	    /* Confluence operator n.  */
+  df_live_subreg_transfer_function, /* Transfer function.  */
+  df_live_subreg_finalize,	    /* Finalize function.  */
+  df_live_subreg_free,		    /* Free all of the problem information.  */
+  df_live_subreg_free,	      /* Remove this problem from the stack of dataflow
+				 problems.  */
+  NULL,			      /* Debugging.  */
+  df_live_subreg_top_dump,    /* Debugging start block.  */
+  df_live_subreg_bottom_dump, /* Debugging end block.  */
+  NULL,			      /* Debugging start insn.  */
+  NULL,			      /* Debugging end insn.  */
+  NULL,			      /* Incremental solution verify start.  */
+  NULL,			      /* Incremental solution verify end.  */
+  &problem_LR,		      /* Dependent problem.  */
+  sizeof (df_live_subreg_bb_info), /* Size of entry of block_info array. */
+  TV_DF_LIVE_SUBREG,		   /* Timing variable.  */
+  false /* Reset blocks on dropping out of blocks_to_analyze.  */
+};
+
+/* Create a new DATAFLOW instance and add it to an existing instance
+   of DF.  The returned structure is what is used to get at the
+   solution.  */
+
+void
+df_live_subreg_add_problem (void)
+{
+  df_add_problem (&problem_LIVE_SUBREG);
+
+  /* These will be initialized when df_scan_blocks processes each
+     block.  */
+  df_live_subreg->out_of_date_transfer_functions
+    = BITMAP_ALLOC (&df_bitmap_obstack);
+}
 
-
 /*----------------------------------------------------------------------------
    LIVE AND MAY-INITIALIZED REGISTERS.
 
diff --git a/gcc/df.h b/gcc/df.h
index 402657a7076..e5daf91a25e 100644
--- a/gcc/df.h
+++ b/gcc/df.h
@@ -44,19 +44,20 @@  union df_ref_d;
    at any time are always defined (though LIVE is always there at -O2
    or higher); the others are always there.  */
 enum df_problem_id
-  {
-    DF_SCAN,
-    DF_LR,                /* Live Registers backward. */
-    DF_LIVE,              /* Live Registers & Uninitialized Registers */
-    DF_RD,                /* Reaching Defs. */
-    DF_CHAIN,             /* Def-Use and/or Use-Def Chains. */
-    DF_WORD_LR,           /* Subreg tracking lr.  */
-    DF_NOTE,              /* REG_DEAD and REG_UNUSED notes.  */
-    DF_MD,                /* Multiple Definitions. */
-    DF_MIR,               /* Must-initialized Registers.  */
-
-    DF_LAST_PROBLEM_PLUS1
-  };
+{
+  DF_SCAN,
+  DF_LR,	  /* Live Registers backward. */
+  DF_LIVE,	  /* Live Registers & Uninitialized Registers */
+  DF_LIVE_SUBREG, /* Live Ranges and Live Subreg */
+  DF_RD,	  /* Reaching Defs. */
+  DF_CHAIN,	  /* Def-Use and/or Use-Def Chains. */
+  DF_WORD_LR,	  /* Subreg tracking lr.  */
+  DF_NOTE,	  /* REG_DEAD and REG_UNUSED notes.  */
+  DF_MD,	  /* Multiple Definitions. */
+  DF_MIR,	  /* Must-initialized Registers.  */
+
+  DF_LAST_PROBLEM_PLUS1
+};
 
 /* Dataflow direction.  */
 enum df_flow_dir
@@ -619,6 +620,7 @@  public:
 #define DF_SCAN_BB_INFO(BB) (df_scan_get_bb_info ((BB)->index))
 #define DF_RD_BB_INFO(BB) (df_rd_get_bb_info ((BB)->index))
 #define DF_LR_BB_INFO(BB) (df_lr_get_bb_info ((BB)->index))
+#define DF_LIVE_SUBREG_INFO(BB) (df_live_subreg_get_bb_info ((BB)->index))
 #define DF_LIVE_BB_INFO(BB) (df_live_get_bb_info ((BB)->index))
 #define DF_WORD_LR_BB_INFO(BB) (df_word_lr_get_bb_info ((BB)->index))
 #define DF_MD_BB_INFO(BB) (df_md_get_bb_info ((BB)->index))
@@ -632,6 +634,15 @@  public:
 #define DF_MIR_IN(BB) (&DF_MIR_BB_INFO (BB)->in)
 #define DF_MIR_OUT(BB) (&DF_MIR_BB_INFO (BB)->out)
 
+#define DF_LIVE_SUBREG_IN(BB) (&DF_LIVE_SUBREG_INFO (BB)->all_in)
+#define DF_LIVE_SUBREG_OUT(BB) (&DF_LIVE_SUBREG_INFO (BB)->all_out)
+#define DF_LIVE_SUBREG_FULL_IN(BB) (&DF_LIVE_SUBREG_INFO (BB)->full_in)
+#define DF_LIVE_SUBREG_FULL_OUT(BB) (&DF_LIVE_SUBREG_INFO (BB)->full_out)
+#define DF_LIVE_SUBREG_PARTIAL_IN(BB) (&DF_LIVE_SUBREG_INFO (BB)->partial_in)
+#define DF_LIVE_SUBREG_PARTIAL_OUT(BB) (&DF_LIVE_SUBREG_INFO (BB)->partial_out)
+#define DF_LIVE_SUBREG_RANGE_IN(BB) (DF_LIVE_SUBREG_INFO (BB)->range_in)
+#define DF_LIVE_SUBREG_RANGE_OUT(BB) (DF_LIVE_SUBREG_INFO (BB)->range_out)
+
 /* These macros are used by passes that are not tolerant of
    uninitialized variables.  This intolerance should eventually
    be fixed.  */
@@ -878,6 +889,32 @@  public:
   bitmap_head out;   /* At the bottom of the block.  */
 };
 
+class subregs_live;
+
+class basic_block_subreg_live_info
+{
+public:
+  bitmap_head full_def;
+  bitmap_head full_use;
+  /* Only for pseudo registers.  */
+  bitmap_head partial_def;
+  bitmap_head partial_use;
+  subregs_live *range_def = NULL;
+  subregs_live *range_use = NULL;
+};
+
+/* Live registers and live ranges including specifial subreg.  */
+class df_live_subreg_bb_info : public basic_block_subreg_live_info
+{
+public:
+  bitmap_head all_in, full_in;
+  bitmap_head all_out, full_out;
+  /* Only for pseudo registers.  */
+  bitmap_head partial_in;
+  bitmap_head partial_out;
+  subregs_live *range_in = NULL;
+  subregs_live *range_out = NULL;
+};
 
 /* Uninitialized registers.  All bitmaps are referenced by the
    register number.  Anded results of the forwards and backward live
@@ -946,6 +983,7 @@  extern class df_d *df;
 #define df_note    (df->problems_by_index[DF_NOTE])
 #define df_md      (df->problems_by_index[DF_MD])
 #define df_mir     (df->problems_by_index[DF_MIR])
+#define df_live_subreg (df->problems_by_index[DF_LIVE_SUBREG])
 
 /* This symbol turns on checking that each modification of the cfg has
   been identified to the appropriate df routines.  It is not part of
@@ -1031,6 +1069,25 @@  extern void df_lr_add_problem (void);
 extern void df_lr_verify_transfer_functions (void);
 extern void df_live_verify_transfer_functions (void);
 extern void df_live_add_problem (void);
+extern void
+df_live_subreg_add_problem (void);
+extern void
+df_live_subreg_finalize (bitmap all_blocks);
+class subreg_range;
+extern bool
+need_track_subreg (int regno, machine_mode mode);
+extern void
+remove_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int regno,
+		     machine_mode mode, const subreg_range &range);
+extern bool
+remove_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref);
+extern void
+add_subreg_range (basic_block_subreg_live_info *bb_info, unsigned int regno,
+		  machine_mode mode, const subreg_range &range,
+		  bool is_def = false);
+extern bool
+add_subreg_range (basic_block_subreg_live_info *bb_info, df_ref ref,
+		  bool is_def = false);
 extern void df_live_set_all_dirty (void);
 extern void df_chain_add_problem (unsigned int);
 extern void df_word_lr_add_problem (void);
@@ -1124,6 +1181,16 @@  df_lr_get_bb_info (unsigned int index)
     return NULL;
 }
 
+inline class df_live_subreg_bb_info *
+df_live_subreg_get_bb_info (unsigned int index)
+{
+  if (index < df_live_subreg->block_info_size)
+    return &(
+      (class df_live_subreg_bb_info *) df_live_subreg->block_info)[index];
+  else
+    return NULL;
+}
+
 inline class df_md_bb_info *
 df_md_get_bb_info (unsigned int index)
 {
diff --git a/gcc/ira-build.cc b/gcc/ira-build.cc
index 07aba27c1c9..7df98164503 100644
--- a/gcc/ira-build.cc
+++ b/gcc/ira-build.cc
@@ -1991,7 +1991,13 @@  create_bb_allocnos (ira_loop_tree_node_t bb_node)
       create_insn_allocnos (PATTERN (insn), NULL, false);
   /* It might be a allocno living through from one subloop to
      another.  */
-  EXECUTE_IF_SET_IN_REG_SET (df_get_live_in (bb), FIRST_PSEUDO_REGISTER, i, bi)
+  EXECUTE_IF_SET_IN_REG_SET (DF_LIVE_SUBREG_FULL_IN (bb), FIRST_PSEUDO_REGISTER,
+			     i, bi)
+    if (ira_curr_regno_allocno_map[i] == NULL)
+      ira_create_allocno (i, false, ira_curr_loop_tree_node);
+
+  EXECUTE_IF_SET_IN_REG_SET (DF_LIVE_SUBREG_PARTIAL_IN (bb),
+			     FIRST_PSEUDO_REGISTER, i, bi)
     if (ira_curr_regno_allocno_map[i] == NULL)
       ira_create_allocno (i, false, ira_curr_loop_tree_node);
 }
@@ -2007,10 +2013,10 @@  create_loop_allocnos (edge e)
   bitmap_iterator bi;
   ira_loop_tree_node_t parent;
 
-  live_in_regs = df_get_live_in (e->dest);
+  live_in_regs = DF_LIVE_SUBREG_IN (e->dest);
   border_allocnos = ira_curr_loop_tree_node->border_allocnos;
-  EXECUTE_IF_SET_IN_REG_SET (df_get_live_out (e->src),
-			     FIRST_PSEUDO_REGISTER, i, bi)
+  EXECUTE_IF_SET_IN_REG_SET (DF_LIVE_SUBREG_OUT (e->src), FIRST_PSEUDO_REGISTER,
+			     i, bi)
     if (bitmap_bit_p (live_in_regs, i))
       {
 	if (ira_curr_regno_allocno_map[i] == NULL)
diff --git a/gcc/ira-color.cc b/gcc/ira-color.cc
index 6af8318e5f5..f1b96d1aee6 100644
--- a/gcc/ira-color.cc
+++ b/gcc/ira-color.cc
@@ -2862,8 +2862,8 @@  ira_loop_edge_freq (ira_loop_tree_node_t loop_node, int regno, bool exit_p)
       FOR_EACH_EDGE (e, ei, loop_node->loop->header->preds)
 	if (e->src != loop_node->loop->latch
 	    && (regno < 0
-		|| (bitmap_bit_p (df_get_live_out (e->src), regno)
-		    && bitmap_bit_p (df_get_live_in (e->dest), regno))))
+		|| (bitmap_bit_p (DF_LIVE_SUBREG_OUT (e->src), regno)
+		    && bitmap_bit_p (DF_LIVE_SUBREG_IN (e->dest), regno))))
 	  freq += EDGE_FREQUENCY (e);
     }
   else
@@ -2871,8 +2871,8 @@  ira_loop_edge_freq (ira_loop_tree_node_t loop_node, int regno, bool exit_p)
       auto_vec<edge> edges = get_loop_exit_edges (loop_node->loop);
       FOR_EACH_VEC_ELT (edges, i, e)
 	if (regno < 0
-	    || (bitmap_bit_p (df_get_live_out (e->src), regno)
-		&& bitmap_bit_p (df_get_live_in (e->dest), regno)))
+	    || (bitmap_bit_p (DF_LIVE_SUBREG_OUT (e->src), regno)
+		&& bitmap_bit_p (DF_LIVE_SUBREG_IN (e->dest), regno)))
 	  freq += EDGE_FREQUENCY (e);
     }
 
diff --git a/gcc/ira-emit.cc b/gcc/ira-emit.cc
index bcc4f09f7c4..84ed482e568 100644
--- a/gcc/ira-emit.cc
+++ b/gcc/ira-emit.cc
@@ -510,8 +510,8 @@  generate_edge_moves (edge e)
     return;
   src_map = src_loop_node->regno_allocno_map;
   dest_map = dest_loop_node->regno_allocno_map;
-  regs_live_in_dest = df_get_live_in (e->dest);
-  regs_live_out_src = df_get_live_out (e->src);
+  regs_live_in_dest = DF_LIVE_SUBREG_IN (e->dest);
+  regs_live_out_src = DF_LIVE_SUBREG_OUT (e->src);
   EXECUTE_IF_SET_IN_REG_SET (regs_live_in_dest,
 			     FIRST_PSEUDO_REGISTER, regno, bi)
     if (bitmap_bit_p (regs_live_out_src, regno))
@@ -1229,16 +1229,16 @@  add_ranges_and_copies (void)
 	 destination block) to use for searching allocnos by their
 	 regnos because of subsequent IR flattening.  */
       node = IRA_BB_NODE (bb)->parent;
-      bitmap_copy (live_through, df_get_live_in (bb));
+      bitmap_copy (live_through, DF_LIVE_SUBREG_IN (bb));
       add_range_and_copies_from_move_list
 	(at_bb_start[bb->index], node, live_through, REG_FREQ_FROM_BB (bb));
-      bitmap_copy (live_through, df_get_live_out (bb));
+      bitmap_copy (live_through, DF_LIVE_SUBREG_OUT (bb));
       add_range_and_copies_from_move_list
 	(at_bb_end[bb->index], node, live_through, REG_FREQ_FROM_BB (bb));
       FOR_EACH_EDGE (e, ei, bb->succs)
 	{
-	  bitmap_and (live_through,
-		      df_get_live_in (e->dest), df_get_live_out (bb));
+	  bitmap_and (live_through, DF_LIVE_SUBREG_IN (e->dest),
+		      DF_LIVE_SUBREG_OUT (bb));
 	  add_range_and_copies_from_move_list
 	    ((move_t) e->aux, node, live_through,
 	     REG_FREQ_FROM_EDGE_FREQ (EDGE_FREQUENCY (e)));
diff --git a/gcc/ira-lives.cc b/gcc/ira-lives.cc
index bc8493856a4..60e6be0b0ae 100644
--- a/gcc/ira-lives.cc
+++ b/gcc/ira-lives.cc
@@ -1194,13 +1194,14 @@  process_out_of_region_eh_regs (basic_block bb)
   if (! eh_p)
     return;
 
-  EXECUTE_IF_SET_IN_BITMAP (df_get_live_out (bb), FIRST_PSEUDO_REGISTER, i, bi)
+  EXECUTE_IF_SET_IN_BITMAP (DF_LIVE_SUBREG_OUT (bb), FIRST_PSEUDO_REGISTER, i,
+			    bi)
     {
       ira_allocno_t a = ira_curr_regno_allocno_map[i];
       for (int n = ALLOCNO_NUM_OBJECTS (a) - 1; n >= 0; n--)
 	{
 	  ira_object_t obj = ALLOCNO_OBJECT (a, n);
-	  for (int k = 0; ; k++)
+	  for (int k = 0;; k++)
 	    {
 	      unsigned int regno = EH_RETURN_DATA_REGNO (k);
 	      if (regno == INVALID_REGNUM)
@@ -1239,7 +1240,7 @@  process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
 	  high_pressure_start_point[ira_pressure_classes[i]] = -1;
 	}
       curr_bb_node = loop_tree_node;
-      reg_live_out = df_get_live_out (bb);
+      reg_live_out = DF_LIVE_SUBREG_OUT (bb);
       sparseset_clear (objects_live);
       REG_SET_TO_HARD_REG_SET (hard_regs_live, reg_live_out);
       hard_regs_live &= ~(eliminable_regset | ira_no_alloc_regs);
diff --git a/gcc/ira.cc b/gcc/ira.cc
index 2fa6e0e5c94..b9159d089c3 100644
--- a/gcc/ira.cc
+++ b/gcc/ira.cc
@@ -4719,8 +4719,8 @@  find_moveable_pseudos (void)
       bitmap_initialize (local, 0);
       bitmap_initialize (transp, 0);
       bitmap_initialize (moveable, 0);
-      bitmap_copy (live, df_get_live_out (bb));
-      bitmap_and_into (live, df_get_live_in (bb));
+      bitmap_copy (live, DF_LIVE_SUBREG_OUT (bb));
+      bitmap_and_into (live, DF_LIVE_SUBREG_IN (bb));
       bitmap_copy (transp, live);
       bitmap_clear (moveable);
       bitmap_clear (live);
@@ -5018,9 +5018,10 @@  interesting_dest_for_shprep_1 (rtx set, basic_block call_dom)
 {
   rtx src = SET_SRC (set);
   rtx dest = SET_DEST (set);
-  if (!REG_P (src) || !HARD_REGISTER_P (src)
-      || !REG_P (dest) || HARD_REGISTER_P (dest)
-      || (call_dom && !bitmap_bit_p (df_get_live_in (call_dom), REGNO (dest))))
+  if (!REG_P (src) || !HARD_REGISTER_P (src) || !REG_P (dest)
+      || HARD_REGISTER_P (dest)
+      || (call_dom
+	  && !bitmap_bit_p (DF_LIVE_SUBREG_IN (call_dom), REGNO (dest))))
     return NULL;
   return dest;
 }
@@ -5498,10 +5499,10 @@  allocate_initial_values (void)
 		  /* Update global register liveness information.  */
 		  FOR_EACH_BB_FN (bb, cfun)
 		    {
-		      if (REGNO_REG_SET_P (df_get_live_in (bb), regno))
-			SET_REGNO_REG_SET (df_get_live_in (bb), new_regno);
-		      if (REGNO_REG_SET_P (df_get_live_out (bb), regno))
-			SET_REGNO_REG_SET (df_get_live_out (bb), new_regno);
+		      if (REGNO_REG_SET_P (DF_LIVE_SUBREG_IN (bb), regno))
+			SET_REGNO_REG_SET (DF_LIVE_SUBREG_IN (bb), new_regno);
+		      if (REGNO_REG_SET_P (DF_LIVE_SUBREG_OUT (bb), regno))
+			SET_REGNO_REG_SET (DF_LIVE_SUBREG_OUT (bb), new_regno);
 		    }
 		}
 	    }
@@ -5663,6 +5664,7 @@  ira (FILE *f)
   if (optimize > 1)
     df_remove_problem (df_live);
   gcc_checking_assert (df_live == NULL);
+  df_live_subreg_add_problem ();
 
   if (flag_checking)
     df->changeable_flags |= DF_VERIFY_SCHEDULED;
diff --git a/gcc/reginfo.cc b/gcc/reginfo.cc
index d472a35946a..66b269fee41 100644
--- a/gcc/reginfo.cc
+++ b/gcc/reginfo.cc
@@ -485,6 +485,20 @@  init_reg_modes_target (void)
     }
 }
 
+/* For a machine mode, map to the number of blocks the MODE overlap. Use to
+   cache the result by get_nblocks_slow.  */
+int regmode_nblocks[MAX_MACHINE_MODE];
+
+/* Return the number of blocks the MODE overlap. One block equal mode's natural
+   size.  */
+int
+get_nblocks_slow (machine_mode mode)
+{
+  poly_int64 mode_size = GET_MODE_SIZE (mode);
+  poly_int64 unit_size = REGMODE_NATURAL_SIZE (mode);
+  return exact_div (mode_size, unit_size).to_constant ();
+}
+
 /* Finish initializing the register sets and initialize the register modes.
    This function might be invoked more than once, if the target has support
    for changing register usage conventions on a per-function basis.
diff --git a/gcc/rtl.h b/gcc/rtl.h
index e4b6cc0dbb5..a7d37cfb764 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -4627,6 +4627,20 @@  struct rtx_comparison {
   machine_mode mode;
 };
 
+extern int regmode_nblocks[MAX_MACHINE_MODE];
+extern int get_nblocks_slow (machine_mode);
+/* Return the number of blocks the MODE overlap. One block equal mode's natural
+   size. So, satisfy the following equation:
+     (nblocks - 1) * natural_size < GET_MODE_SIZE (mode)
+       <= nblocks * natural_size. */
+inline int
+get_nblocks (machine_mode mode)
+{
+  if (regmode_nblocks[mode] > 0)
+    return regmode_nblocks[mode];
+  return get_nblocks_slow (mode);
+}
+
 /* gtype-desc.cc.  */
 extern void gt_ggc_mx (rtx &);
 extern void gt_pch_nx (rtx &);
diff --git a/gcc/subreg-live-range.cc b/gcc/subreg-live-range.cc
new file mode 100644
index 00000000000..eb14f2ec1ea
--- /dev/null
+++ b/gcc/subreg-live-range.cc
@@ -0,0 +1,649 @@ 
+/* SUBREG live range track classes for DF & IRA & LRA.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by Lehua Ding (lehua.ding@rivai.ai), RiVAI Technologies Ltd.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "subreg-live-range.h"
+#include "selftest.h"
+#include "print-rtl.h"
+
+/* class subreg_range */
+void
+subreg_range::dump (FILE *file) const
+{
+  fprintf (file, "[%d, %d)", start, end);
+}
+
+/* class subreg_ranges */
+bool
+subreg_ranges::add_range (int max, const subreg_range &new_range)
+{
+  subreg_range range = new_range;
+  if (full_p ())
+    return false;
+  else if (maybe_eq (max, 1))
+    {
+      gcc_assert (maybe_eq (range.start, 0) && maybe_eq (range.end, 1));
+      make_full ();
+      return true;
+    }
+
+  if (maybe_eq (this->max, 1))
+    change_max (max);
+
+  gcc_assert (maybe_eq (this->max, max));
+  gcc_assert (maybe_lt (range.start, range.end));
+
+  bool changed = empty_p ();
+  auto it = ranges.begin ();
+  while (it != ranges.end ())
+    {
+      const subreg_range &r = *it;
+      gcc_assert (maybe_lt (r.start, r.end));
+
+      /* The possible positional relationship of R and RANGE.
+	 1~5 means R.start's possible position relative to RANGE
+	 A~G means R.end's possible position relative to RANGE
+	 caseN means when R.start at N positon, the R.end can be in which
+	 positions.
+
+		     RANGE.start     RANGE.end
+			  [               )
+			  |               |
+	R.start   1       2       3       4       5
+	R.end             |               |
+	  case1       A   B       C       D       E
+	  case2           |       C       D       E
+	  case3           |           F   D       E
+	  case4           |               |       E
+	  case5           |               |               G
+
+	*/
+
+      /* R.start at 1 position.   */
+      if (maybe_lt (r.start, range.start))
+	{
+	  /* R.end at A position. That means R and RANGE do not overlap.  */
+	  if (maybe_lt (r.end, range.start))
+	    it++;
+	  /* R.end at B/C position. That means RANGE's left part overlap R's
+	     right part. Expand RANGE.start to R.start and remove R.  */
+	  else if (maybe_lt (r.end, range.end))
+	    {
+	      changed = true;
+	      range.start = r.start;
+	      it = ranges.erase (it);
+	    }
+	  /* R.end at D/E position. That means R already contains RANGE, nothing
+	     todo.  */
+	  else
+	    return false;
+	}
+      /* R.start at 2 position.  */
+      else if (maybe_eq (r.start, range.start))
+	{
+	  /* R.end at C/D position. That means RANGE contains R, remove R and
+	     insert RANGE.  */
+	  if (maybe_lt (r.end, range.end))
+	    {
+	      changed = true;
+	      it = ranges.erase (it);
+	    }
+	  /* R.end at E position. That means R already contains RANGE, nothing
+	     todo.  */
+	  else
+	    return false;
+	}
+      /* R.start at 3 position.  */
+      else if (maybe_gt (r.start, range.start) && maybe_lt (r.start, range.end))
+	{
+	  /* R.end at F/D position. That means RANGE contains R, just remove R
+	     and insert RANGE later.  */
+	  if (maybe_le (r.end, range.end))
+	    {
+	      changed = true;
+	      it = ranges.erase (it);
+	    }
+	  /* R.end at E position.  That means RANGE's right part overlap R's
+	     left part. Expand RANGE.end to R.end and remove R.  */
+	  else if (maybe_gt (r.end, range.end))
+	    {
+	      changed = true;
+	      range.end = r.end;
+	      it = ranges.erase (it);
+	      break;
+	    }
+	}
+      /* R.start at 4 position and R.end at E position. That means RANGE and R
+	 are adjacent and can be merged. */
+      else if (maybe_eq (r.start, range.end))
+	{
+	  changed = true;
+	  range.end = r.end;
+	  it = ranges.erase (it);
+	}
+      /* R.start at 5 position and R.end at G position. That means R and RANGE
+	 do not overlap.  */
+      else
+	break;
+    }
+  ranges.insert (range);
+  return changed;
+}
+
+bool
+subreg_ranges::remove_range (int max, const subreg_range &range)
+{
+  if (empty_p ())
+    return false;
+  else if (maybe_eq (max, 1))
+    {
+      gcc_assert (maybe_eq (range.start, 0) && maybe_eq (range.end, 1));
+      make_empty ();
+      return true;
+    }
+
+  if (maybe_eq (this->max, 1))
+    {
+      gcc_assert (full_p ());
+      change_max (max);
+    }
+  gcc_assert (maybe_eq (this->max, max));
+  gcc_assert (maybe_lt (range.start, range.end));
+
+  bool changed = false;
+  auto it = ranges.begin ();
+  std::set<subreg_range> new_ranges;
+  while (it != ranges.end ())
+    {
+      auto &r = *it;
+      gcc_assert (maybe_lt (r.start, r.end));
+
+      /* The possible positional relationship of R and RANGE.
+	 1~5 means R.start's possible position relative to RANGE
+	 A~G means R.end's possible position relative to RANGE
+	 caseN means when R.start at N positon, the R.end can be in which
+	 positions.
+
+		     RANGE.start     RANGE.end
+			  [               )
+			  |               |
+	R.start   1       2       3       4       5
+	R.end             |               |
+	  case1       A   B       C       D       E
+	  case2           |       C       D       E
+	  case3           |           F   D       E
+	  case4           |               |       E
+	  case5           |               |               G
+
+	*/
+
+      /* R.start at 1 position.  */
+      if (maybe_lt (r.start, range.start))
+	{
+	  /* R.end at A/B position. That means RANGE and R do not overlap,
+	     nothing to remove.  */
+	  if (maybe_le (r.end, range.start))
+	    it++;
+	  /* R.end at C/D position. That means R's rigth part contains RANGE,
+	     need shrink R.end to RANGE.start.  */
+	  else if (maybe_le (r.end, range.end))
+	    {
+	      changed = true;
+	      new_ranges.insert (subreg_range (r.start, range.start));
+	      it = ranges.erase (it);
+	    }
+	  /* R.end at E position. That means R's center part contains RANGE,
+	     need split R to two range, one range is [R.start, RANGE.start),
+	     another range is [RANGE.end, R.end).  */
+	  else
+	    {
+	      changed = true;
+	      new_ranges.insert (subreg_range (r.start, range.start));
+	      new_ranges.insert (subreg_range (range.end, r.end));
+	      it = ranges.erase (it);
+	      break;
+	    }
+	}
+      /* R.start at 2 position.  */
+      else if (maybe_eq (r.start, range.start))
+	{
+	  /* R.end at C/D position. That means RANGE contains R, remove R.  */
+	  if (maybe_le (r.end, range.end))
+	    {
+	      changed = true;
+	      it = ranges.erase (it);
+	    }
+	  /* R.end at E position. That means R's left part contains RANGE,
+	     shrink R.start to RANGE.end.  */
+	  else
+	    {
+	      changed = true;
+	      new_ranges.insert (subreg_range (range.end, r.end));
+	      it = ranges.erase (it);
+	      break;
+	    }
+	}
+      /* R.start at 3 position. */
+      else if (maybe_gt (r.start, range.start) && maybe_lt (r.start, range.end))
+	{
+	  /* R.end at F/D position. That means RANGE contains R, remove R.  */
+	  if (maybe_le (r.end, range.end))
+	    {
+	      changed = true;
+	      it = ranges.erase (it);
+	    }
+	  /* R.end at E position. That means RANGE's right part overlap R's left
+	     part, shrink R.start to RANGE.end.  */
+	  else
+	    {
+	      changed = true;
+	      new_ranges.insert (subreg_range (range.end, r.end));
+	      it = ranges.erase (it);
+	      break;
+	    }
+	}
+      /* R.start at 4/5 position. That means RANGE and R do not overlap.  */
+      else
+	break;
+    }
+  for (auto &r : new_ranges)
+    add_range (this->max, r);
+  return changed;
+}
+
+bool
+subreg_ranges::add_ranges (const subreg_ranges &sr)
+{
+  gcc_assert (maybe_eq (max, sr.max) || maybe_eq (max, 1)
+	      || maybe_eq (sr.max, 1));
+
+  if (full_p () || sr.empty_p ())
+    return false;
+  else if (sr.full_p ())
+    {
+      make_full ();
+      return true;
+    }
+
+  bool changed = false;
+  for (auto &r : sr.ranges)
+    {
+      changed |= add_range (sr.max, r);
+    }
+  return changed;
+}
+
+bool
+subreg_ranges::remove_ranges (const subreg_ranges &sr)
+{
+  if (empty_p () || sr.empty_p ())
+    return false;
+  else if (sr.full_p ())
+    {
+      make_empty ();
+      return true;
+    }
+
+  gcc_assert (maybe_eq (max, sr.max) || maybe_eq (max, 1)
+	      || maybe_eq (sr.max, 1));
+
+  bool changed = false;
+  for (auto &r : sr.ranges)
+    {
+      changed |= remove_range (sr.max, r);
+    }
+  return changed;
+}
+
+bool
+subreg_ranges::same_p (const subreg_ranges &sr) const
+{
+  if (maybe_eq (max, 1) || maybe_eq (sr.max, 1))
+    {
+      return (empty_p () && sr.empty_p ()) || (full_p () && sr.full_p ());
+    }
+  else if (maybe_eq (max, sr.max))
+    {
+      if (ranges.size () != sr.ranges.size ())
+	return false;
+      /* Make sure that the elements in each position are the same.  */
+      auto it1 = ranges.begin ();
+      auto it2 = sr.ranges.begin ();
+      while (it1 != ranges.end ())
+	{
+	  const subreg_range &r1 = *it1;
+	  const subreg_range &r2 = *it2;
+	  if (maybe_ne (r1.start, r2.start) || maybe_ne (r1.end, r2.end))
+	    return false;
+	  it1++;
+	  it2++;
+	}
+      return true;
+    }
+  else
+    gcc_unreachable ();
+}
+
+bool
+subreg_ranges::include_ranges_p (const subreg_ranges &sr) const
+{
+  gcc_assert (maybe_eq (max, sr.max) || maybe_eq (max, 1)
+	      || maybe_eq (sr.max, 1));
+  if (full_p ())
+    return true;
+  if (empty_p () && sr.empty_p ())
+    return true;
+  if (same_p (sr))
+    return true;
+
+  for (const auto &r : sr.ranges)
+    if (!include_range_p (sr.max, r))
+      return false;
+  return true;
+}
+
+bool
+subreg_ranges::include_range_p (int max, const subreg_range &range) const
+{
+  gcc_assert (maybe_eq (this->max, max));
+  for (const auto &r : ranges)
+    {
+      if (maybe_le (r.start, range.start) && maybe_ge (r.end, range.end))
+	{
+	  return true;
+	}
+      else if (maybe_ge (r.start, range.end))
+	return false;
+    }
+  return false;
+}
+
+void
+subreg_ranges::dump (FILE *file) const
+{
+  if (empty_p ())
+    {
+      fprintf (file, "empty");
+      return;
+    }
+  else if (full_p ())
+    {
+      fprintf (file, "full");
+      return;
+    }
+
+  fprintf (file, "patial(max:%d", max);
+  fprintf (file, " {");
+  for (auto &range : ranges)
+    {
+      fprintf (file, " ");
+      range.dump (file);
+    }
+  fprintf (file, " })");
+}
+
+/* class subregs_live */
+bool
+subregs_live::copy_lives (const subregs_live &sl)
+{
+  bool changed = false;
+  subregs_live temp;
+  for (auto &kv : sl.lives)
+    {
+      unsigned int regno = kv.first;
+      const subreg_ranges &sr = kv.second;
+      if (lives.count (regno) == 0 && !sr.empty_p ())
+	{
+	  changed = true;
+	  temp.add_ranges (regno, sr);
+	}
+      else if (lives.count (regno) != 0)
+	{
+	  changed |= !lives.at (regno).same_p (sr);
+	  temp.add_ranges (regno, sr);
+	}
+    }
+
+  for (auto &kv : lives)
+    {
+      unsigned int regno = kv.first;
+      subreg_ranges &sr = kv.second;
+      if (temp.lives.count (regno) == 0 && !sr.empty_p ())
+	changed = true;
+    }
+  lives = temp.lives;
+  return changed;
+}
+
+bool
+subregs_live::add_lives (const subregs_live &sl)
+{
+  bool changed = false;
+  for (auto &kv : sl.lives)
+    {
+      unsigned int regno = kv.first;
+      const subreg_ranges &sr = kv.second;
+      if (sr.empty_p ())
+	continue;
+
+      if (lives.count (regno) == 0)
+	{
+	  changed = true;
+	  lives.insert ({regno, sr});
+	}
+      else
+	{
+	  changed |= lives.at (regno).add_ranges (sr);
+	}
+    }
+  return changed;
+}
+
+bool
+subregs_live::remove_lives (const subregs_live &sl)
+{
+  bool changed = false;
+  for (auto &kv : sl.lives)
+    {
+      unsigned int regno = kv.first;
+      const subreg_ranges &sr = kv.second;
+      if (sr.empty_p ())
+	continue;
+
+      if (lives.count (regno) != 0)
+	{
+	  changed |= lives.at (regno).remove_ranges (sr);
+	  if (lives.at (regno).empty_p ())
+	    lives.erase (regno);
+	}
+    }
+  return changed;
+}
+
+void
+subregs_live::dump (FILE *file, const char *indent) const
+{
+  if (lives.empty ())
+    {
+      fprintf (file, "%sempty\n", indent);
+      return;
+    }
+  fprintf (file, "%s", indent);
+  for (auto &kv : lives)
+    {
+      const subreg_ranges &sr = kv.second;
+      if (sr.empty_p ())
+	continue;
+      fprintf (file, "%d ", kv.first);
+      if (!sr.full_p ())
+	{
+	  sr.dump (file);
+	  fprintf (file, "  ");
+	}
+    }
+  fprintf (file, "\n");
+}
+
+/* class live_point */
+void
+live_point::dump (FILE *file) const
+{
+  if (!use_reg.empty_p ())
+    {
+      fprintf (file, "use ");
+      use_reg.dump (file);
+      if (!def_reg.empty_p ())
+	{
+	  fprintf (file, ", def ");
+	  def_reg.dump (file);
+	}
+    }
+  else if (!def_reg.empty_p ())
+    {
+      fprintf (file, "def ");
+      def_reg.dump (file);
+    }
+  else
+    {
+      gcc_unreachable ();
+    }
+}
+
+/* class live_points */
+void
+live_points::dump (FILE *file) const
+{
+  fprintf (file, "%u :", id);
+  if (points.empty ())
+    {
+      fprintf (file, " empty");
+      return;
+    }
+  for (const auto &kv : points)
+    {
+      fprintf (file, " ");
+      kv.second.dump (file);
+      fprintf (file, " at point %u;", kv.first);
+    }
+}
+
+/* class reg_live_ranges */
+void
+subregs_live_points::dump (FILE *file) const
+{
+  if (subreg_points.empty ())
+    {
+      fprintf (file, ";;     empty\n");
+      return;
+    }
+  for (const auto &kv : subreg_points)
+    {
+      fprintf (file, ";;     ");
+      kv.second.dump (file);
+      fprintf (file, "\n");
+    }
+}
+
+/* Define some usefull debug functions.  */
+
+DEBUG_FUNCTION void
+debug (const subreg_range &r)
+{
+  r.dump (stderr);
+}
+
+DEBUG_FUNCTION void
+debug (const subreg_ranges &sr)
+{
+  sr.dump (stderr);
+}
+
+DEBUG_FUNCTION void
+debug (const subregs_live &l)
+{
+  l.dump (stderr, "");
+}
+
+DEBUG_FUNCTION void
+debug (const subregs_live *l)
+{
+  debug (*l);
+}
+
+DEBUG_FUNCTION void
+debug (const live_point &l)
+{
+  l.dump (stderr);
+}
+
+DEBUG_FUNCTION void
+debug (const live_points &ls)
+{
+  ls.dump (stderr);
+}
+
+DEBUG_FUNCTION void
+debug (const subregs_live_points &sls)
+{
+  sls.dump (stderr);
+}
+
+DEBUG_FUNCTION void
+debug (const subregs_live_points *sls)
+{
+  debug (*sls);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+template <unsigned int N> class poly_int_tests
+{
+public:
+  static void run () {}
+};
+
+template <> class poly_int_tests<1>
+{
+public:
+  static void run ()
+  {
+    /* class subreg_range tests.  */
+    subreg_range r1 = subreg_range (1, 2);
+    subreg_range r2 = subreg_range (2, 3);
+    subreg_range r3 = subreg_range (2, 3);
+    ASSERT_FALSE (r1.same_p (r2));
+    ASSERT_TRUE (r2.same_p (r3));
+    ASSERT_TRUE (r1 < r2);
+    ASSERT_FALSE (r2 < r1);
+
+    /* class subreg_ranges tests.  */
+  }
+};
+
+void
+subreg_live_range_tests ()
+{
+  poly_int_tests<NUM_POLY_INT_COEFFS>::run ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/subreg-live-range.h b/gcc/subreg-live-range.h
new file mode 100644
index 00000000000..56931b53550
--- /dev/null
+++ b/gcc/subreg-live-range.h
@@ -0,0 +1,326 @@ 
+/* SUBREG live range track classes for DF & IRA & LRA.
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   Contributed by Lehua Ding (lehua.ding@rivai.ai), RiVAI Technologies Ltd.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_SUBREG_LIVE_RANGE_H
+#define GCC_SUBREG_LIVE_RANGE_H
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include <set>
+#include <map>
+
+/* class subreg_range represent bytes range [start, end) of a reg.  */
+class subreg_range
+{
+public:
+  int start; /* Range start point.  */
+  int end;   /* Range start point.  */
+
+  subreg_range (int start, int end) : start (start), end (end)
+  {
+    gcc_assert (maybe_lt (start, end));
+  }
+
+  /* For sorting.  */
+  bool operator<(const subreg_range &r) const
+  {
+    if (maybe_le (end, r.start))
+      return true;
+    else if (maybe_ge (start, r.end))
+      return false;
+    else
+      /* Cannot sorting for overlap range.  */
+      gcc_unreachable ();
+  }
+  /* Return true if R same with self.  */
+  bool same_p (const subreg_range &r) const
+  {
+    return maybe_eq (start, r.start) && maybe_eq (end, r.end);
+  }
+
+  /* Return true if range is full for 0-MAX range.  */
+  bool full_p (int max) const
+  {
+    return maybe_eq (start, 0) && maybe_eq (end, max);
+  }
+
+  /* Debug methods.  */
+  void dump (FILE *file) const;
+};
+
+/* class subreg_ranges represent multiple disjoint and discontinuous
+   subreg_range.  */
+class subreg_ranges
+{
+public:
+  /* The maximum boundary value of range. If for a unknown mode hard register,
+     the max set to 1.  */
+  int max;
+  std::set<subreg_range> ranges;
+
+  subreg_ranges (int max) : max (max) { gcc_assert (maybe_ge (max, 1)); }
+
+  /* Modify ranges.  */
+  /* Return true if ranges changed.  */
+  bool add_range (int max, const subreg_range &range);
+  /* Return true if ranges changed.  */
+  bool remove_range (int max, const subreg_range &range);
+  /* Add SR, return true if ranges changed.  */
+  bool add_ranges (const subreg_ranges &sr);
+  /* Clear ranges of SR, return true if ranges changed.  */
+  bool remove_ranges (const subreg_ranges &sr);
+  /* Make range empty.  */
+  void make_empty () { ranges.clear (); }
+  /* Make range full.  */
+  void make_full ()
+  {
+    make_empty ();
+    ranges.insert (subreg_range (0, max));
+  }
+  /* Change max to MAX, corresponding adjust ranges.  */
+  void change_max (int max)
+  {
+    gcc_assert (maybe_eq (this->max, 1));
+    this->max = max;
+    if (full_p ())
+      make_full ();
+  }
+
+  /* Predicators.  */
+  bool full_p () const
+  {
+    if (ranges.size () != 1)
+      return false;
+    const subreg_range &r = *ranges.begin ();
+    return maybe_eq (r.start, 0) && maybe_eq (r.end, max);
+  }
+  bool empty_p () const { return ranges.empty (); }
+  bool same_p (const subreg_ranges &sr) const;
+  bool same_p (int max, const subreg_range &range) const
+  {
+    subreg_ranges sr = subreg_ranges (max);
+    sr.add_range (max, range);
+    return same_p (sr);
+  }
+  bool include_ranges_p (const subreg_ranges &sr) const;
+  bool include_range_p (int max, const subreg_range &range) const;
+
+  /* Debug methods.  */
+  void dump (FILE *file) const;
+};
+
+/* class subregs_live record the live subreg_ranges of registers.  */
+class subregs_live
+{
+public:
+  /* The key is usually the register's regno.  */
+  std::map<unsigned int, subreg_ranges> lives;
+
+  /* Add/clear live range.  */
+  bool add_range (unsigned int regno, int max, const subreg_range &range)
+  {
+    if (lives.count (regno) == 0)
+      {
+	lives.insert ({regno, subreg_ranges (max)});
+      }
+    return lives.at (regno).add_range (max, range);
+  }
+  bool remove_range (unsigned int regno, int max, const subreg_range &range)
+  {
+    if (lives.count (regno) != 0)
+      {
+	bool changed = lives.at (regno).remove_range (max, range);
+	if (lives.at (regno).empty_p ())
+	  remove_live (regno);
+	return changed;
+      }
+    return false;
+  }
+  /* Add a unexist register live range.  */
+  void add_ranges (unsigned int regno, const subreg_ranges &ranges)
+  {
+    if (lives.count (regno) == 0)
+      lives.insert ({regno, ranges});
+    else
+      lives.at (regno).add_ranges (ranges);
+  }
+  bool copy_lives (const subregs_live &sl);
+  bool add_lives (const subregs_live &sl);
+  bool remove_lives (const subregs_live &sl);
+  void remove_live (unsigned int regno) { lives.erase (regno); }
+  /* Remove all register live range.  */
+  void clear () { lives.clear (); }
+  void clear (unsigned min_regno)
+  {
+    if (lives.lower_bound (min_regno) != lives.end ())
+      lives.erase (lives.lower_bound (min_regno), lives.end ());
+  }
+
+  /* Return true if regno's live range is full.  */
+  bool full_p (unsigned int regno) const
+  {
+    return lives.count (regno) != 0 && lives.at (regno).full_p ();
+  }
+  /* Return true if regno's live range is empty.  */
+  bool empty_p (unsigned int regno) const
+  {
+    return lives.count (regno) == 0 || lives.at (regno).empty_p ();
+  }
+  /* Return true if SL same with this.  */
+  bool same_p (const subregs_live &sl)
+  {
+    if (lives.size () != sl.lives.size ())
+      return false;
+    for (auto &kv : lives)
+      {
+	unsigned int regno = kv.first;
+	if (sl.empty_p (regno))
+	  return false;
+	const subreg_ranges &sr = kv.second;
+	if (!sr.same_p (sl.lives.at (regno)))
+	  return false;
+      }
+    return true;
+  }
+
+  /* Debug methods.  */
+  void dump (FILE *file, const char *indent = ";;     ") const;
+};
+
+class live_point
+{
+public:
+  int point;
+  /* subreg range be defined in current point.  */
+  subreg_ranges def_reg;
+  /* subreg range be used in current point.  */
+  subreg_ranges use_reg;
+
+  live_point (int max, const subreg_range &range, bool is_def)
+    : def_reg (max), use_reg (max)
+  {
+    add_range (max, range, is_def);
+  }
+  live_point (const subreg_ranges &sr, bool is_def)
+    : def_reg (sr.max), use_reg (sr.max)
+  {
+    add_ranges (sr, is_def);
+  }
+  live_point (int point, int max) : point (point), def_reg (max), use_reg (max)
+  {}
+
+  void add_range (int max, const subreg_range &r, bool is_def)
+  {
+    if (is_def)
+      def_reg.add_range (max, r);
+    else
+      use_reg.add_range (max, r);
+  }
+
+  void add_ranges (const subreg_ranges &sr, bool is_def)
+  {
+    if (is_def)
+      def_reg.add_ranges (sr);
+    else
+      use_reg.add_ranges (sr);
+  }
+
+  void dump (FILE *file) const;
+};
+
+class live_points
+{
+public:
+  int id;
+  int max;
+  std::map<int, live_point> points;
+
+  live_points (int id, int max) : id (id), max (max) {}
+
+  void add_point (int max, const subreg_range &range, bool is_def, int point)
+  {
+    gcc_assert (maybe_eq (this->max, max) || maybe_eq (this->max, 1)
+		|| maybe_eq (max, 1));
+    if (points.count (point) == 0)
+      {
+	points.insert ({point, {max, range, is_def}});
+      }
+    else
+      {
+	points.at (point).add_range (max, range, is_def);
+      }
+  }
+  void dump (FILE *file) const;
+};
+
+class subregs_live_points
+{
+public:
+  std::map<int, live_points> subreg_points;
+  std::map<int, subreg_ranges> subreg_live_ranges;
+
+  void add_point (int id, int max, const subreg_range &range, bool is_def,
+		  int point)
+  {
+    if (subreg_points.count (id) == 0)
+      subreg_points.insert ({id, live_points (id, max)});
+
+    subreg_points.at (id).add_point (max, range, is_def, point);
+
+    if (subreg_live_ranges.count (id) == 0)
+      subreg_live_ranges.insert ({id, subreg_ranges (max)});
+
+    if (is_def)
+      subreg_live_ranges.at (id).remove_range (max, range);
+    else
+      subreg_live_ranges.at (id).add_range (max, range);
+  }
+
+  void add_range (int id, int max, const subreg_range &range, bool is_def)
+  {
+    if (subreg_live_ranges.count (id) == 0)
+      subreg_live_ranges.insert ({id, subreg_ranges (max)});
+
+    if (is_def)
+      subreg_live_ranges.at (id).remove_range (max, range);
+    else
+      subreg_live_ranges.at (id).add_range (max, range);
+  }
+
+  bool full_live_p (int id)
+  {
+    return subreg_live_ranges.count (id) != 0
+	   && subreg_live_ranges.at (id).full_p ();
+  }
+
+  bool empty_live_p (int id)
+  {
+    return subreg_live_ranges.count (id) == 0
+	   || subreg_live_ranges.at (id).empty_p ();
+  }
+
+  void clear_live_ranges () { subreg_live_ranges.clear (); }
+
+  /* Debug methods.  */
+  void dump (FILE *file) const;
+};
+
+#endif /* GCC_SUBREG_LIVE_RANGE_H */
diff --git a/gcc/timevar.def b/gcc/timevar.def
index d21b08c030d..4b82b6a554b 100644
--- a/gcc/timevar.def
+++ b/gcc/timevar.def
@@ -121,6 +121,7 @@  DEFTIMEVAR (TV_DF_MD		     , "df multiple defs")
 DEFTIMEVAR (TV_DF_RD		     , "df reaching defs")
 DEFTIMEVAR (TV_DF_LR		     , "df live regs")
 DEFTIMEVAR (TV_DF_LIVE		     , "df live&initialized regs")
+DEFTIMEVAR (TV_DF_LIVE_SUBREG	     , "df live subregs")
 DEFTIMEVAR (TV_DF_MIR		     , "df must-initialized regs")
 DEFTIMEVAR (TV_DF_CHAIN		     , "df use-def / def-use chains")
 DEFTIMEVAR (TV_DF_WORD_LR	     , "df live reg subwords")