diff mbox series

[v2,1/11] rtl-ssa: Support for inserting new insns

Message ID ZV+Baql3BLsZy1l7@arm.com
State New
Headers show
Series [v2,1/11] rtl-ssa: Support for inserting new insns | expand

Commit Message

Alex Coplan Nov. 23, 2023, 4:44 p.m. UTC
Hi,

This is a v2, original patch is here:
https://gcc.gnu.org/pipermail/gcc-patches/2023-November/637606.html

This addresses review feedback and:
 - Fixes a bug in the previous version in
   function_info::finalize_new_accesses; we should now correctly handle
   the case where properties.refs () has two writes to a resource and we're
   adding a new (temporary) set for that resource.
 - Drops some handling for new uses which isn't needed now that RTL-SSA can
   infer uses of mem (since g:505f1202e3a1a1aecd0df10d0f1620df6fea4ab5).

Bootstrapped/regtested as a series on aarch64-linux-gnu, OK for trunk?

Thanks,
Alex

-- >8 --

The upcoming aarch64 load pair pass needs to form store pairs, and can
re-order stores over loads when alias analysis determines this is safe.
In the case that both mem defs have uses in the RTL-SSA IR, and both
stores require re-ordering over their uses, we represent that as
(tentative) deletion of the original store insns and creation of a new
insn, to prevent requiring repeated re-parenting of uses during the
pass.  We then update all mem uses that require re-parenting in one go
at the end of the pass.

To support this, RTL-SSA needs to handle inserting new insns (rather
than just changing existing ones), so this patch adds support for that.

New insns (and new accesses) are temporaries, allocated above a temporary
obstack_watermark, such that the user can easily back out of a change without
awkward bookkeeping.

gcc/ChangeLog:

        * rtl-ssa/accesses.cc (function_info::create_set): New.
        * rtl-ssa/accesses.h (access_info::is_temporary): New.
        * rtl-ssa/changes.cc (move_insn): Handle new (temporary) insns.
        (function_info::finalize_new_accesses): Handle new/temporary
        user-created accesses.
        (function_info::apply_changes_to_insn): Ensure m_is_temp flag
        on new insns gets cleared.
        (function_info::change_insns): Handle new/temporary insns.
        (function_info::create_insn): New.
        * rtl-ssa/changes.h (class insn_change): Make function_info a
        friend class.
        * rtl-ssa/functions.h (function_info): Declare new entry points:
        create_set, create_insn.  Declare new change_alloc helper.
        * rtl-ssa/insns.cc (insn_info::print_full): Identify temporary insns in
        dump.
        * rtl-ssa/insns.h (insn_info): Add new m_is_temp flag and accompanying
        is_temporary accessor.
        * rtl-ssa/internals.inl (insn_info::insn_info): Initialize m_is_temp to
        false.
        * rtl-ssa/member-fns.inl (function_info::change_alloc): New.
        * rtl-ssa/movement.h (restrict_movement_for_defs_ignoring): Add
        handling for temporary defs.

Comments

Richard Sandiford Nov. 23, 2023, 5:07 p.m. UTC | #1
Alex Coplan <alex.coplan@arm.com> writes:
> Hi,
>
> This is a v2, original patch is here:
> https://gcc.gnu.org/pipermail/gcc-patches/2023-November/637606.html
>
> This addresses review feedback and:
>  - Fixes a bug in the previous version in
>    function_info::finalize_new_accesses; we should now correctly handle
>    the case where properties.refs () has two writes to a resource and we're
>    adding a new (temporary) set for that resource.
>  - Drops some handling for new uses which isn't needed now that RTL-SSA can
>    infer uses of mem (since g:505f1202e3a1a1aecd0df10d0f1620df6fea4ab5).
>
> Bootstrapped/regtested as a series on aarch64-linux-gnu, OK for trunk?

OK, thanks.

Richard

> Thanks,
> Alex
>
> -- >8 --
>
> The upcoming aarch64 load pair pass needs to form store pairs, and can
> re-order stores over loads when alias analysis determines this is safe.
> In the case that both mem defs have uses in the RTL-SSA IR, and both
> stores require re-ordering over their uses, we represent that as
> (tentative) deletion of the original store insns and creation of a new
> insn, to prevent requiring repeated re-parenting of uses during the
> pass.  We then update all mem uses that require re-parenting in one go
> at the end of the pass.
>
> To support this, RTL-SSA needs to handle inserting new insns (rather
> than just changing existing ones), so this patch adds support for that.
>
> New insns (and new accesses) are temporaries, allocated above a temporary
> obstack_watermark, such that the user can easily back out of a change without
> awkward bookkeeping.
>
> gcc/ChangeLog:
>
>         * rtl-ssa/accesses.cc (function_info::create_set): New.
>         * rtl-ssa/accesses.h (access_info::is_temporary): New.
>         * rtl-ssa/changes.cc (move_insn): Handle new (temporary) insns.
>         (function_info::finalize_new_accesses): Handle new/temporary
>         user-created accesses.
>         (function_info::apply_changes_to_insn): Ensure m_is_temp flag
>         on new insns gets cleared.
>         (function_info::change_insns): Handle new/temporary insns.
>         (function_info::create_insn): New.
>         * rtl-ssa/changes.h (class insn_change): Make function_info a
>         friend class.
>         * rtl-ssa/functions.h (function_info): Declare new entry points:
>         create_set, create_insn.  Declare new change_alloc helper.
>         * rtl-ssa/insns.cc (insn_info::print_full): Identify temporary insns in
>         dump.
>         * rtl-ssa/insns.h (insn_info): Add new m_is_temp flag and accompanying
>         is_temporary accessor.
>         * rtl-ssa/internals.inl (insn_info::insn_info): Initialize m_is_temp to
>         false.
>         * rtl-ssa/member-fns.inl (function_info::change_alloc): New.
>         * rtl-ssa/movement.h (restrict_movement_for_defs_ignoring): Add
>         handling for temporary defs.
>
> diff --git a/gcc/rtl-ssa/accesses.cc b/gcc/rtl-ssa/accesses.cc
> index 510545a8bad..76d70fd8bd3 100644
> --- a/gcc/rtl-ssa/accesses.cc
> +++ b/gcc/rtl-ssa/accesses.cc
> @@ -1456,6 +1456,16 @@ function_info::make_uses_available (obstack_watermark &watermark,
>    return use_array (new_uses, num_uses);
>  }
>  
> +set_info *
> +function_info::create_set (obstack_watermark &watermark,
> +			   insn_info *insn,
> +			   resource_info resource)
> +{
> +  auto set = change_alloc<set_info> (watermark, insn, resource);
> +  set->m_is_temp = true;
> +  return set;
> +}
> +
>  // Return true if ACCESS1 can represent ACCESS2 and if ACCESS2 can
>  // represent ACCESS1.
>  static bool
> diff --git a/gcc/rtl-ssa/accesses.h b/gcc/rtl-ssa/accesses.h
> index fce31d46717..7e7a90ece97 100644
> --- a/gcc/rtl-ssa/accesses.h
> +++ b/gcc/rtl-ssa/accesses.h
> @@ -204,6 +204,10 @@ public:
>    // in the main instruction pattern.
>    bool only_occurs_in_notes () const { return m_only_occurs_in_notes; }
>  
> +  // Return true if this is a temporary access, e.g. one created for
> +  // an insn that is about to be inserted.
> +  bool is_temporary () const { return m_is_temp; }
> +
>  protected:
>    access_info (resource_info, access_kind);
>  
> diff --git a/gcc/rtl-ssa/changes.cc b/gcc/rtl-ssa/changes.cc
> index aab532b9f26..2f2d12d5f30 100644
> --- a/gcc/rtl-ssa/changes.cc
> +++ b/gcc/rtl-ssa/changes.cc
> @@ -394,14 +394,20 @@ move_insn (insn_change &change, insn_info *after)
>    // At the moment we don't support moving instructions between EBBs,
>    // but this would be worth adding if it's useful.
>    insn_info *insn = change.insn ();
> -  gcc_assert (after->ebb () == insn->ebb ());
> +
>    bb_info *bb = after->bb ();
>    basic_block cfg_bb = bb->cfg_bb ();
>  
> -  if (insn->bb () != bb)
> -    // Force DF to mark the old block as dirty.
> -    df_insn_delete (rtl);
> -  ::remove_insn (rtl);
> +  if (!insn->is_temporary ())
> +    {
> +      gcc_assert (after->ebb () == insn->ebb ());
> +
> +      if (insn->bb () != bb)
> +	// Force DF to mark the old block as dirty.
> +	df_insn_delete (rtl);
> +      ::remove_insn (rtl);
> +    }
> +
>    ::add_insn_after (rtl, after_rtl, cfg_bb);
>  }
>  
> @@ -437,12 +443,33 @@ function_info::finalize_new_accesses (insn_change &change, insn_info *pos)
>        {
>  	def_info *def = find_access (change.new_defs, ref.regno);
>  	gcc_assert (def);
> +
> +	if (def->m_is_temp && is_a<set_info *> (def) && def->last_def ())
> +	  {
> +	    // For temporary sets being added with this change, we keep track of
> +	    // the corresponding permanent def using the last_def link.
> +	    //
> +	    // So if we have one of these, follow it to get the permanent def.
> +	    def = def->last_def ();
> +	    gcc_assert (!def->m_is_temp && !def->m_has_been_superceded);
> +	  }
> +
>  	if (def->m_is_temp)
>  	  {
> -	    // At present, the only temporary instruction definitions we
> -	    // create are clobbers, such as those added during recog.
> -	    gcc_assert (is_a<clobber_info *> (def));
> -	    def = allocate<clobber_info> (change.insn (), ref.regno);
> +	    if (is_a<clobber_info *> (def))
> +	      def = allocate<clobber_info> (change.insn (), ref.regno);
> +	    else if (is_a<set_info *> (def))
> +	      {
> +		// Install the permanent set in the last_def link of the
> +		// temporary def.  This allows us to find the permanent def
> +		// later in case we see a second write to the same resource.
> +		def_info *perm_def = allocate<set_info> (change.insn (),
> +							 def->resource ());
> +		def->set_last_def (perm_def);
> +		def = perm_def;
> +	      }
> +	    else
> +	      gcc_unreachable ();
>  	  }
>  	else if (!def->m_has_been_superceded)
>  	  {
> @@ -645,6 +672,8 @@ function_info::apply_changes_to_insn (insn_change &change)
>  
>        insn->set_accesses (builder.finish ().begin (), num_defs, num_uses);
>      }
> +
> +  insn->m_is_temp = false;
>  }
>  
>  // Add a temporary placeholder instruction after AFTER.
> @@ -677,7 +706,8 @@ function_info::change_insns (array_slice<insn_change *> changes)
>        if (!change->is_deletion ())
>  	{
>  	  // Remove any notes that are no longer relevant.
> -	  update_notes (change->rtl ());
> +	  if (!change->insn ()->m_is_temp)
> +	    update_notes (change->rtl ());
>  
>  	  // Make sure that the placement of this instruction would still
>  	  // leave room for previous instructions.
> @@ -686,6 +716,17 @@ function_info::change_insns (array_slice<insn_change *> changes)
>  	    // verify_insn_changes is supposed to make sure that this holds.
>  	    gcc_unreachable ();
>  	  min_insn = later_insn (min_insn, change->move_range.first);
> +
> +	  if (change->insn ()->m_is_temp)
> +	    {
> +	      change->m_insn = allocate<insn_info> (change->insn ()->bb (),
> +						    change->rtl (),
> +						    change->insn_uid ());
> +
> +	      // Set the flag again so subsequent logic is aware.
> +	      // It will be cleared later on.
> +	      change->m_insn->m_is_temp = true;
> +	    }
>  	}
>      }
>  
> @@ -784,7 +825,8 @@ function_info::change_insns (array_slice<insn_change *> changes)
>  	      // Remove the placeholder first so that we have a wider range of
>  	      // program points when inserting INSN.
>  	      insn_info *after = placeholder->prev_any_insn ();
> -	      remove_insn (insn);
> +	      if (!insn->is_temporary ())
> +		remove_insn (insn);
>  	      remove_insn (placeholder);
>  	      insn->set_bb (after->bb ());
>  	      add_insn_after (insn, after);
> @@ -1105,6 +1147,28 @@ function_info::perform_pending_updates ()
>    return changed_cfg;
>  }
>  
> +insn_info *
> +function_info::create_insn (obstack_watermark &watermark,
> +			    rtx_code insn_code,
> +			    rtx pat)
> +{
> +  rtx_insn *rti = nullptr;
> +
> +  // TODO: extend, move in to emit-rtl.cc.
> +  switch (insn_code)
> +    {
> +    case INSN:
> +      rti = make_insn_raw (pat);
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
> +
> +  auto insn = change_alloc<insn_info> (watermark, nullptr, rti, INSN_UID (rti));
> +  insn->m_is_temp = true;
> +  return insn;
> +}
> +
>  // Print a description of CHANGE to PP.
>  void
>  rtl_ssa::pp_insn_change (pretty_printer *pp, const insn_change &change)
> diff --git a/gcc/rtl-ssa/changes.h b/gcc/rtl-ssa/changes.h
> index d56e3a646e2..d91cf432afe 100644
> --- a/gcc/rtl-ssa/changes.h
> +++ b/gcc/rtl-ssa/changes.h
> @@ -32,6 +32,8 @@ namespace rtl_ssa {
>  // something that we might do.
>  class insn_change
>  {
> +  friend class function_info;
> +
>  public:
>    enum delete_action { DELETE };
>  
> diff --git a/gcc/rtl-ssa/functions.h b/gcc/rtl-ssa/functions.h
> index ecb40fdaf57..4ffd3fa44e2 100644
> --- a/gcc/rtl-ssa/functions.h
> +++ b/gcc/rtl-ssa/functions.h
> @@ -68,6 +68,16 @@ public:
>    // Return the SSA information for CFG_BB.
>    bb_info *bb (basic_block cfg_bb) const { return m_bbs[cfg_bb->index]; }
>  
> +  // Create a temporary def.
> +  set_info *create_set (obstack_watermark &watermark,
> +			insn_info *insn,
> +			resource_info resource);
> +
> +  // Create a temporary insn with code INSN_CODE and pattern PAT.
> +  insn_info *create_insn (obstack_watermark &watermark,
> +			  rtx_code insn_code,
> +			  rtx pat);
> +
>    // Return a list of all the instructions in the function, in reverse
>    // postorder.  The list includes both real and artificial instructions.
>    //
> @@ -195,6 +205,10 @@ public:
>    // Print the contents of the function to PP.
>    void print (pretty_printer *pp) const;
>  
> +  // Allocate an object of type T above the obstack watermark WM.
> +  template<typename T, typename... Ts>
> +  T *change_alloc (obstack_watermark &wm, Ts... args);
> +
>  private:
>    class bb_phi_info;
>    class build_info;
> diff --git a/gcc/rtl-ssa/insns.cc b/gcc/rtl-ssa/insns.cc
> index 5fde3f2bb4b..2fa48e0dacd 100644
> --- a/gcc/rtl-ssa/insns.cc
> +++ b/gcc/rtl-ssa/insns.cc
> @@ -192,6 +192,11 @@ insn_info::print_full (pretty_printer *pp) const
>  	      pp_newline_and_indent (pp, 0);
>  	      pp_string (pp, "has volatile refs");
>  	    }
> +	  if (m_is_temp)
> +	    {
> +	      pp_newline_and_indent (pp, 0);
> +	      pp_string (pp, "temporary");
> +	    }
>  	}
>        pp_indentation (pp) -= 2;
>      }
> diff --git a/gcc/rtl-ssa/insns.h b/gcc/rtl-ssa/insns.h
> index a604fe295cd..6d0506706ad 100644
> --- a/gcc/rtl-ssa/insns.h
> +++ b/gcc/rtl-ssa/insns.h
> @@ -306,6 +306,8 @@ public:
>    // Print a full description of the instruction.
>    void print_full (pretty_printer *) const;
>  
> +  bool is_temporary () const { return m_is_temp; }
> +
>  private:
>    // The first-order way of representing the order between instructions
>    // is to assign "program points", with higher point numbers coming
> @@ -414,8 +416,11 @@ private:
>    unsigned int m_has_pre_post_modify : 1;
>    unsigned int m_has_volatile_refs : 1;
>  
> +  // Indicates the insn is a temporary / new user-allocated insn.
> +  unsigned int m_is_temp : 1;
> +
>    // For future expansion.
> -  unsigned int m_spare : 27;
> +  unsigned int m_spare : 26;
>  
>    // The program point at which the instruction occurs.
>    //
> diff --git a/gcc/rtl-ssa/internals.inl b/gcc/rtl-ssa/internals.inl
> index e49297c12b3..907c4504352 100644
> --- a/gcc/rtl-ssa/internals.inl
> +++ b/gcc/rtl-ssa/internals.inl
> @@ -415,6 +415,7 @@ inline insn_info::insn_info (bb_info *bb, rtx_insn *rtl, int cost_or_uid)
>      m_is_asm (false),
>      m_has_pre_post_modify (false),
>      m_has_volatile_refs (false),
> +    m_is_temp (false),
>      m_spare (0),
>      m_point (0),
>      m_cost_or_uid (cost_or_uid),
> diff --git a/gcc/rtl-ssa/member-fns.inl b/gcc/rtl-ssa/member-fns.inl
> index ce2db045b78..b8940ca5566 100644
> --- a/gcc/rtl-ssa/member-fns.inl
> +++ b/gcc/rtl-ssa/member-fns.inl
> @@ -962,4 +962,16 @@ function_info::add_regno_clobber (obstack_watermark &watermark,
>    return true;
>  }
>  
> +template<typename T, typename... Ts>
> +inline T *
> +function_info::change_alloc (obstack_watermark &wm, Ts... args)
> +{
> +  static_assert (std::is_trivially_destructible<T>::value,
> +		 "destructor won't be called");
> +  static_assert (alignof (T) <= obstack_alignment,
> +		 "too much alignment required");
> +  void *addr = XOBNEW (wm, T);
> +  return new (addr) T (std::forward<Ts> (args)...);
> +}
> +
>  }
> diff --git a/gcc/rtl-ssa/movement.h b/gcc/rtl-ssa/movement.h
> index ec076db406f..41226dd3666 100644
> --- a/gcc/rtl-ssa/movement.h
> +++ b/gcc/rtl-ssa/movement.h
> @@ -182,6 +182,11 @@ restrict_movement_for_defs_ignoring (insn_range_info &move_range,
>  {
>    for (def_info *def : defs)
>      {
> +      // Skip fresh defs that are being inserted, as these shouldn't
> +      // constrain movement.
> +      if (def->is_temporary ())
> +	continue;
> +
>        // If the definition is a clobber, we can move it with respect
>        // to other clobbers.
>        //
> @@ -247,7 +252,8 @@ restrict_movement_for_defs_ignoring (insn_range_info &move_range,
>  
>    // Make sure that we don't move stores between basic blocks, since we
>    // don't have enough information to tell whether it's safe.
> -  if (def_info *def = memory_access (defs))
> +  def_info *def = memory_access (defs);
> +  if (def && !def->is_temporary ())
>      {
>        move_range = move_later_than (move_range, def->bb ()->head_insn ());
>        move_range = move_earlier_than (move_range, def->bb ()->end_insn ());
diff mbox series

Patch

diff --git a/gcc/rtl-ssa/accesses.cc b/gcc/rtl-ssa/accesses.cc
index 510545a8bad..76d70fd8bd3 100644
--- a/gcc/rtl-ssa/accesses.cc
+++ b/gcc/rtl-ssa/accesses.cc
@@ -1456,6 +1456,16 @@  function_info::make_uses_available (obstack_watermark &watermark,
   return use_array (new_uses, num_uses);
 }
 
+set_info *
+function_info::create_set (obstack_watermark &watermark,
+			   insn_info *insn,
+			   resource_info resource)
+{
+  auto set = change_alloc<set_info> (watermark, insn, resource);
+  set->m_is_temp = true;
+  return set;
+}
+
 // Return true if ACCESS1 can represent ACCESS2 and if ACCESS2 can
 // represent ACCESS1.
 static bool
diff --git a/gcc/rtl-ssa/accesses.h b/gcc/rtl-ssa/accesses.h
index fce31d46717..7e7a90ece97 100644
--- a/gcc/rtl-ssa/accesses.h
+++ b/gcc/rtl-ssa/accesses.h
@@ -204,6 +204,10 @@  public:
   // in the main instruction pattern.
   bool only_occurs_in_notes () const { return m_only_occurs_in_notes; }
 
+  // Return true if this is a temporary access, e.g. one created for
+  // an insn that is about to be inserted.
+  bool is_temporary () const { return m_is_temp; }
+
 protected:
   access_info (resource_info, access_kind);
 
diff --git a/gcc/rtl-ssa/changes.cc b/gcc/rtl-ssa/changes.cc
index aab532b9f26..2f2d12d5f30 100644
--- a/gcc/rtl-ssa/changes.cc
+++ b/gcc/rtl-ssa/changes.cc
@@ -394,14 +394,20 @@  move_insn (insn_change &change, insn_info *after)
   // At the moment we don't support moving instructions between EBBs,
   // but this would be worth adding if it's useful.
   insn_info *insn = change.insn ();
-  gcc_assert (after->ebb () == insn->ebb ());
+
   bb_info *bb = after->bb ();
   basic_block cfg_bb = bb->cfg_bb ();
 
-  if (insn->bb () != bb)
-    // Force DF to mark the old block as dirty.
-    df_insn_delete (rtl);
-  ::remove_insn (rtl);
+  if (!insn->is_temporary ())
+    {
+      gcc_assert (after->ebb () == insn->ebb ());
+
+      if (insn->bb () != bb)
+	// Force DF to mark the old block as dirty.
+	df_insn_delete (rtl);
+      ::remove_insn (rtl);
+    }
+
   ::add_insn_after (rtl, after_rtl, cfg_bb);
 }
 
@@ -437,12 +443,33 @@  function_info::finalize_new_accesses (insn_change &change, insn_info *pos)
       {
 	def_info *def = find_access (change.new_defs, ref.regno);
 	gcc_assert (def);
+
+	if (def->m_is_temp && is_a<set_info *> (def) && def->last_def ())
+	  {
+	    // For temporary sets being added with this change, we keep track of
+	    // the corresponding permanent def using the last_def link.
+	    //
+	    // So if we have one of these, follow it to get the permanent def.
+	    def = def->last_def ();
+	    gcc_assert (!def->m_is_temp && !def->m_has_been_superceded);
+	  }
+
 	if (def->m_is_temp)
 	  {
-	    // At present, the only temporary instruction definitions we
-	    // create are clobbers, such as those added during recog.
-	    gcc_assert (is_a<clobber_info *> (def));
-	    def = allocate<clobber_info> (change.insn (), ref.regno);
+	    if (is_a<clobber_info *> (def))
+	      def = allocate<clobber_info> (change.insn (), ref.regno);
+	    else if (is_a<set_info *> (def))
+	      {
+		// Install the permanent set in the last_def link of the
+		// temporary def.  This allows us to find the permanent def
+		// later in case we see a second write to the same resource.
+		def_info *perm_def = allocate<set_info> (change.insn (),
+							 def->resource ());
+		def->set_last_def (perm_def);
+		def = perm_def;
+	      }
+	    else
+	      gcc_unreachable ();
 	  }
 	else if (!def->m_has_been_superceded)
 	  {
@@ -645,6 +672,8 @@  function_info::apply_changes_to_insn (insn_change &change)
 
       insn->set_accesses (builder.finish ().begin (), num_defs, num_uses);
     }
+
+  insn->m_is_temp = false;
 }
 
 // Add a temporary placeholder instruction after AFTER.
@@ -677,7 +706,8 @@  function_info::change_insns (array_slice<insn_change *> changes)
       if (!change->is_deletion ())
 	{
 	  // Remove any notes that are no longer relevant.
-	  update_notes (change->rtl ());
+	  if (!change->insn ()->m_is_temp)
+	    update_notes (change->rtl ());
 
 	  // Make sure that the placement of this instruction would still
 	  // leave room for previous instructions.
@@ -686,6 +716,17 @@  function_info::change_insns (array_slice<insn_change *> changes)
 	    // verify_insn_changes is supposed to make sure that this holds.
 	    gcc_unreachable ();
 	  min_insn = later_insn (min_insn, change->move_range.first);
+
+	  if (change->insn ()->m_is_temp)
+	    {
+	      change->m_insn = allocate<insn_info> (change->insn ()->bb (),
+						    change->rtl (),
+						    change->insn_uid ());
+
+	      // Set the flag again so subsequent logic is aware.
+	      // It will be cleared later on.
+	      change->m_insn->m_is_temp = true;
+	    }
 	}
     }
 
@@ -784,7 +825,8 @@  function_info::change_insns (array_slice<insn_change *> changes)
 	      // Remove the placeholder first so that we have a wider range of
 	      // program points when inserting INSN.
 	      insn_info *after = placeholder->prev_any_insn ();
-	      remove_insn (insn);
+	      if (!insn->is_temporary ())
+		remove_insn (insn);
 	      remove_insn (placeholder);
 	      insn->set_bb (after->bb ());
 	      add_insn_after (insn, after);
@@ -1105,6 +1147,28 @@  function_info::perform_pending_updates ()
   return changed_cfg;
 }
 
+insn_info *
+function_info::create_insn (obstack_watermark &watermark,
+			    rtx_code insn_code,
+			    rtx pat)
+{
+  rtx_insn *rti = nullptr;
+
+  // TODO: extend, move in to emit-rtl.cc.
+  switch (insn_code)
+    {
+    case INSN:
+      rti = make_insn_raw (pat);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  auto insn = change_alloc<insn_info> (watermark, nullptr, rti, INSN_UID (rti));
+  insn->m_is_temp = true;
+  return insn;
+}
+
 // Print a description of CHANGE to PP.
 void
 rtl_ssa::pp_insn_change (pretty_printer *pp, const insn_change &change)
diff --git a/gcc/rtl-ssa/changes.h b/gcc/rtl-ssa/changes.h
index d56e3a646e2..d91cf432afe 100644
--- a/gcc/rtl-ssa/changes.h
+++ b/gcc/rtl-ssa/changes.h
@@ -32,6 +32,8 @@  namespace rtl_ssa {
 // something that we might do.
 class insn_change
 {
+  friend class function_info;
+
 public:
   enum delete_action { DELETE };
 
diff --git a/gcc/rtl-ssa/functions.h b/gcc/rtl-ssa/functions.h
index ecb40fdaf57..4ffd3fa44e2 100644
--- a/gcc/rtl-ssa/functions.h
+++ b/gcc/rtl-ssa/functions.h
@@ -68,6 +68,16 @@  public:
   // Return the SSA information for CFG_BB.
   bb_info *bb (basic_block cfg_bb) const { return m_bbs[cfg_bb->index]; }
 
+  // Create a temporary def.
+  set_info *create_set (obstack_watermark &watermark,
+			insn_info *insn,
+			resource_info resource);
+
+  // Create a temporary insn with code INSN_CODE and pattern PAT.
+  insn_info *create_insn (obstack_watermark &watermark,
+			  rtx_code insn_code,
+			  rtx pat);
+
   // Return a list of all the instructions in the function, in reverse
   // postorder.  The list includes both real and artificial instructions.
   //
@@ -195,6 +205,10 @@  public:
   // Print the contents of the function to PP.
   void print (pretty_printer *pp) const;
 
+  // Allocate an object of type T above the obstack watermark WM.
+  template<typename T, typename... Ts>
+  T *change_alloc (obstack_watermark &wm, Ts... args);
+
 private:
   class bb_phi_info;
   class build_info;
diff --git a/gcc/rtl-ssa/insns.cc b/gcc/rtl-ssa/insns.cc
index 5fde3f2bb4b..2fa48e0dacd 100644
--- a/gcc/rtl-ssa/insns.cc
+++ b/gcc/rtl-ssa/insns.cc
@@ -192,6 +192,11 @@  insn_info::print_full (pretty_printer *pp) const
 	      pp_newline_and_indent (pp, 0);
 	      pp_string (pp, "has volatile refs");
 	    }
+	  if (m_is_temp)
+	    {
+	      pp_newline_and_indent (pp, 0);
+	      pp_string (pp, "temporary");
+	    }
 	}
       pp_indentation (pp) -= 2;
     }
diff --git a/gcc/rtl-ssa/insns.h b/gcc/rtl-ssa/insns.h
index a604fe295cd..6d0506706ad 100644
--- a/gcc/rtl-ssa/insns.h
+++ b/gcc/rtl-ssa/insns.h
@@ -306,6 +306,8 @@  public:
   // Print a full description of the instruction.
   void print_full (pretty_printer *) const;
 
+  bool is_temporary () const { return m_is_temp; }
+
 private:
   // The first-order way of representing the order between instructions
   // is to assign "program points", with higher point numbers coming
@@ -414,8 +416,11 @@  private:
   unsigned int m_has_pre_post_modify : 1;
   unsigned int m_has_volatile_refs : 1;
 
+  // Indicates the insn is a temporary / new user-allocated insn.
+  unsigned int m_is_temp : 1;
+
   // For future expansion.
-  unsigned int m_spare : 27;
+  unsigned int m_spare : 26;
 
   // The program point at which the instruction occurs.
   //
diff --git a/gcc/rtl-ssa/internals.inl b/gcc/rtl-ssa/internals.inl
index e49297c12b3..907c4504352 100644
--- a/gcc/rtl-ssa/internals.inl
+++ b/gcc/rtl-ssa/internals.inl
@@ -415,6 +415,7 @@  inline insn_info::insn_info (bb_info *bb, rtx_insn *rtl, int cost_or_uid)
     m_is_asm (false),
     m_has_pre_post_modify (false),
     m_has_volatile_refs (false),
+    m_is_temp (false),
     m_spare (0),
     m_point (0),
     m_cost_or_uid (cost_or_uid),
diff --git a/gcc/rtl-ssa/member-fns.inl b/gcc/rtl-ssa/member-fns.inl
index ce2db045b78..b8940ca5566 100644
--- a/gcc/rtl-ssa/member-fns.inl
+++ b/gcc/rtl-ssa/member-fns.inl
@@ -962,4 +962,16 @@  function_info::add_regno_clobber (obstack_watermark &watermark,
   return true;
 }
 
+template<typename T, typename... Ts>
+inline T *
+function_info::change_alloc (obstack_watermark &wm, Ts... args)
+{
+  static_assert (std::is_trivially_destructible<T>::value,
+		 "destructor won't be called");
+  static_assert (alignof (T) <= obstack_alignment,
+		 "too much alignment required");
+  void *addr = XOBNEW (wm, T);
+  return new (addr) T (std::forward<Ts> (args)...);
+}
+
 }
diff --git a/gcc/rtl-ssa/movement.h b/gcc/rtl-ssa/movement.h
index ec076db406f..41226dd3666 100644
--- a/gcc/rtl-ssa/movement.h
+++ b/gcc/rtl-ssa/movement.h
@@ -182,6 +182,11 @@  restrict_movement_for_defs_ignoring (insn_range_info &move_range,
 {
   for (def_info *def : defs)
     {
+      // Skip fresh defs that are being inserted, as these shouldn't
+      // constrain movement.
+      if (def->is_temporary ())
+	continue;
+
       // If the definition is a clobber, we can move it with respect
       // to other clobbers.
       //
@@ -247,7 +252,8 @@  restrict_movement_for_defs_ignoring (insn_range_info &move_range,
 
   // Make sure that we don't move stores between basic blocks, since we
   // don't have enough information to tell whether it's safe.
-  if (def_info *def = memory_access (defs))
+  def_info *def = memory_access (defs);
+  if (def && !def->is_temporary ())
     {
       move_range = move_later_than (move_range, def->bb ()->head_insn ());
       move_range = move_earlier_than (move_range, def->bb ()->end_insn ());