diff --git a/gcc/config/aarch64/aarch64-cc-fusion.cc b/gcc/config/aarch64/aarch64-cc-fusion.cc
index a4f43295680..e97c26682d0 100644
--- a/gcc/config/aarch64/aarch64-cc-fusion.cc
+++ b/gcc/config/aarch64/aarch64-cc-fusion.cc
@@ -183,7 +183,7 @@ cc_fusion::parallelize_insns (def_info *cc_def, rtx cc_set,
   auto other_change = insn_change::delete_insn (other_insn);
   insn_change *changes[] = { &other_change, &cc_change };
   cc_change.move_range = cc_insn->ebb ()->insn_range ();
-  if (!restrict_movement_ignoring (cc_change, insn_is_changing (changes)))
+  if (!restrict_movement (cc_change, ignore_changing_insns (changes)))
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
 	fprintf (dump_file, "-- cannot satisfy all definitions and uses\n");
@@ -205,7 +205,7 @@ cc_fusion::parallelize_insns (def_info *cc_def, rtx cc_set,
   validate_change (cc_rtl, &PATTERN (cc_rtl), m_parallel, 1);
 
   // These routines report failures themselves.
-  if (!recog_ignoring (attempt, cc_change, insn_is_changing (changes))
+  if (!recog (attempt, cc_change, ignore_changing_insns (changes))
       || !changes_are_worthwhile (changes)
       || !crtl->ssa->verify_insn_changes (changes))
     return false;
diff --git a/gcc/doc/rtl.texi b/gcc/doc/rtl.texi
index aa10b5235b5..846a043bdc7 100644
--- a/gcc/doc/rtl.texi
+++ b/gcc/doc/rtl.texi
@@ -5073,7 +5073,7 @@ in the correct order with respect to each other.
 The way to do this is:
 
 @smallexample
-if (!rtl_ssa::restrict_movement_ignoring (change, insn_is_changing (changes)))
+if (!rtl_ssa::restrict_movement (change, ignore_changing_insns (changes)))
   return false;
 @end smallexample
 
@@ -5085,7 +5085,7 @@ changing instructions (which might, for example, no longer need
 to clobber the flags register).  The way to do this is:
 
 @smallexample
-if (!rtl_ssa::recog_ignoring (attempt, change, insn_is_changing (changes)))
+if (!rtl_ssa::recog (attempt, change, ignore_changing_insns (changes)))
   return false;
 @end smallexample
 
@@ -5137,16 +5137,16 @@ change2.move_range = @dots{};
 
 rtl_ssa::insn_change *changes[] = @{ &change1, &change2 @};
 
-auto is_changing = insn_is_changing (changes);
-if (!rtl_ssa::restrict_movement_ignoring (change1, is_changing)
-    || !rtl_ssa::restrict_movement_ignoring (change2, is_changing))
+auto ignore = ignore_changing_insns (changes);
+if (!rtl_ssa::restrict_movement (change1, ignore)
+    || !rtl_ssa::restrict_movement (change2, ignore))
   return false;
 
 insn_change_watermark watermark;
 // Use validate_change etc. to change INSN1's and INSN2's patterns.
 @dots{}
-if (!rtl_ssa::recog_ignoring (attempt, change1, is_changing)
-    || !rtl_ssa::recog_ignoring (attempt, change2, is_changing)
+if (!rtl_ssa::recog (attempt, change1, ignore)
+    || !rtl_ssa::recog (attempt, change2, ignore)
     || !rtl_ssa::changes_are_worthwhile (changes)
     || !crtl->ssa->verify_insn_changes (changes))
   return false;
diff --git a/gcc/pair-fusion.cc b/gcc/pair-fusion.cc
index 26b2284ed37..31d2c21c88f 100644
--- a/gcc/pair-fusion.cc
+++ b/gcc/pair-fusion.cc
@@ -563,9 +563,6 @@ pair_fusion_bb_info::track_access (insn_info *insn, bool load_p, rtx mem)
     }
 }
 
-// Dummy predicate that never ignores any insns.
-static bool no_ignore (insn_info *) { return false; }
-
 // Return the latest dataflow hazard before INSN.
 //
 // If IGNORE is non-NULL, this points to a sub-rtx which we should ignore for
@@ -643,9 +640,8 @@ latest_hazard_before (insn_info *insn, rtx *ignore,
 	  if (!call_group->clobbers (def->resource ()))
 	    continue;
 
-	  auto clobber_insn = prev_call_clobbers_ignoring (*call_group,
-							   def->insn (),
-							   no_ignore);
+	  auto clobber_insn = prev_call_clobbers (*call_group, def->insn (),
+						  ignore_nothing ());
 	  if (clobber_insn)
 	    hazard (clobber_insn);
 	}
@@ -704,9 +700,8 @@ first_hazard_after (insn_info *insn, rtx *ignore)
 	  if (!call_group->clobbers (def->resource ()))
 	    continue;
 
-	  auto clobber_insn = next_call_clobbers_ignoring (*call_group,
-							   def->insn (),
-							   no_ignore);
+	  auto clobber_insn = next_call_clobbers (*call_group, def->insn (),
+						  ignore_nothing ());
 	  if (clobber_insn)
 	    hazard (clobber_insn);
 	}
@@ -733,16 +728,15 @@ first_hazard_after (insn_info *insn, rtx *ignore)
 
       // Also need to handle call clobbers of our uses (again WaR).
       //
-      // See restrict_movement_for_uses_ignoring for why we don't
-      // need to check backwards for call clobbers.
+      // See restrict_movement_for_uses for why we don't need to check
+      // backwards for call clobbers.
       for (auto call_group : use->ebb ()->call_clobbers ())
 	{
 	  if (!call_group->clobbers (use->resource ()))
 	    continue;
 
-	  auto clobber_insn = next_call_clobbers_ignoring (*call_group,
-							   use->insn (),
-							   no_ignore);
+	  auto clobber_insn = next_call_clobbers (*call_group, use->insn (),
+						  ignore_nothing ());
 	  if (clobber_insn)
 	    hazard (clobber_insn);
 	}
@@ -1965,12 +1959,12 @@ pair_fusion_bb_info::fuse_pair (bool load_p,
 	}
     }
 
-  auto is_changing = insn_is_changing (changes);
+  auto ignore = ignore_changing_insns (changes);
   for (unsigned i = 0; i < changes.length (); i++)
-    gcc_assert (rtl_ssa::restrict_movement_ignoring (*changes[i], is_changing));
+    gcc_assert (rtl_ssa::restrict_movement (*changes[i], ignore));
 
   // Check the pair pattern is recog'd.
-  if (!rtl_ssa::recog_ignoring (attempt, *pair_change, is_changing))
+  if (!rtl_ssa::recog (attempt, *pair_change, ignore))
     {
       if (dump_file)
 	fprintf (dump_file, "  failed to form pair, recog failed\n");
@@ -2953,11 +2947,11 @@ pair_fusion::try_promote_writeback (insn_info *insn, bool load_p)
 					pair_change.new_defs);
   gcc_assert (pair_change.new_defs.is_valid ());
 
-  auto is_changing = insn_is_changing (changes);
+  auto ignore = ignore_changing_insns (changes);
   for (unsigned i = 0; i < ARRAY_SIZE (changes); i++)
-    gcc_assert (rtl_ssa::restrict_movement_ignoring (*changes[i], is_changing));
+    gcc_assert (rtl_ssa::restrict_movement (*changes[i], ignore));
 
-  if (!rtl_ssa::recog_ignoring (attempt, pair_change, is_changing))
+  if (!rtl_ssa::recog (attempt, pair_change, ignore))
     {
       if (dump_file)
 	fprintf (dump_file, "i%d: recog failed on wb pair, bailing out\n",
diff --git a/gcc/rtl-ssa.h b/gcc/rtl-ssa.h
index 17337639ae8..2718d97b6d9 100644
--- a/gcc/rtl-ssa.h
+++ b/gcc/rtl-ssa.h
@@ -63,6 +63,7 @@
 #include "rtl-ssa/blocks.h"
 #include "rtl-ssa/changes.h"
 #include "rtl-ssa/functions.h"
+#include "rtl-ssa/predicates.h"
 #include "rtl-ssa/is-a.inl"
 #include "rtl-ssa/access-utils.h"
 #include "rtl-ssa/insn-utils.h"
diff --git a/gcc/rtl-ssa/access-utils.h b/gcc/rtl-ssa/access-utils.h
index f889300666d..8805eec1d7f 100644
--- a/gcc/rtl-ssa/access-utils.h
+++ b/gcc/rtl-ssa/access-utils.h
@@ -321,19 +321,22 @@ int lookup_def (def_splay_tree &, insn_info *);
 int lookup_clobber (clobber_tree &, insn_info *);
 int lookup_call_clobbers (insn_call_clobbers_tree &, insn_info *);
 
-// Search backwards from immediately before INSN for the first instruction
-// recorded in TREE, ignoring any instruction I for which IGNORE (I) is true.
-// Return null if no such instruction exists.
-template<typename IgnorePredicate>
+// Search backwards from immediately before INSN for the first "relevant"
+// instruction recorded in TREE.  IGNORE is an object that provides the same
+// interface as ignore_nothing; it defines which insns are "relevant"
+// and which should be ignored.
+//
+// Return null if no such relevant instruction exists.
+template<typename IgnorePredicates>
 insn_info *
-prev_call_clobbers_ignoring (insn_call_clobbers_tree &tree, insn_info *insn,
-			     IgnorePredicate ignore)
+prev_call_clobbers (insn_call_clobbers_tree &tree, insn_info *insn,
+		    IgnorePredicates ignore)
 {
   if (!tree)
     return nullptr;
 
   int comparison = lookup_call_clobbers (tree, insn);
-  while (comparison <= 0 || ignore (tree->insn ()))
+  while (comparison <= 0 || ignore.should_ignore_insn (tree->insn ()))
     {
       if (!tree.splay_prev_node ())
 	return nullptr;
@@ -343,19 +346,22 @@ prev_call_clobbers_ignoring (insn_call_clobbers_tree &tree, insn_info *insn,
   return tree->insn ();
 }
 
-// Search forwards from immediately after INSN for the first instruction
-// recorded in TREE, ignoring any instruction I for which IGNORE (I) is true.
-// Return null if no such instruction exists.
-template<typename IgnorePredicate>
+// Search forwards from immediately after INSN for the first "relevant"
+// instruction recorded in TREE.  IGNORE is an object that provides the
+// same interface as ignore_nothing; it defines which insns are "relevant"
+// and which should be ignored.
+//
+// Return null if no such relevant instruction exists.
+template<typename IgnorePredicates>
 insn_info *
-next_call_clobbers_ignoring (insn_call_clobbers_tree &tree, insn_info *insn,
-			     IgnorePredicate ignore)
+next_call_clobbers (insn_call_clobbers_tree &tree, insn_info *insn,
+		    IgnorePredicates ignore)
 {
   if (!tree)
     return nullptr;
 
   int comparison = lookup_call_clobbers (tree, insn);
-  while (comparison >= 0 || ignore (tree->insn ()))
+  while (comparison >= 0 || ignore.should_ignore_insn (tree->insn ()))
     {
       if (!tree.splay_next_node ())
 	return nullptr;
@@ -370,17 +376,18 @@ next_call_clobbers_ignoring (insn_call_clobbers_tree &tree, insn_info *insn,
 inline insn_info *
 next_call_clobbers (insn_call_clobbers_tree &tree, insn_info *insn)
 {
-  auto ignore = [](const insn_info *) { return false; };
-  return next_call_clobbers_ignoring (tree, insn, ignore);
+  return next_call_clobbers (tree, insn, ignore_nothing ());
 }
 
-// If ACCESS is a set, return the first use of ACCESS by a nondebug insn I
-// for which IGNORE (I) is false.  Return null if ACCESS is not a set or if
-// no such use exists.
-template<typename IgnorePredicate>
+// If ACCESS is a set, return the first "relevant" use of ACCESS by a
+// nondebug insn.  IGNORE is an object that provides the same interface
+// as ignore_nothing; it defines which accesses and insns are "relevant"
+// and which should be ignored.
+//
+// Return null if ACCESS is not a set or if no such relevant use exists.
+template<typename IgnorePredicates>
 inline use_info *
-first_nondebug_insn_use_ignoring (const access_info *access,
-				  IgnorePredicate ignore)
+first_nondebug_insn_use (const access_info *access, IgnorePredicates ignore)
 {
   if (const set_info *set = set_with_nondebug_insn_uses (access))
     {
@@ -389,7 +396,7 @@ first_nondebug_insn_use_ignoring (const access_info *access,
       use_info *use = set->first_use ();
       do
 	{
-	  if (!ignore (use->insn ()))
+	  if (!ignore.should_ignore_insn (use->insn ()))
 	    return use;
 	  use = use->next_nondebug_insn_use ();
 	}
@@ -398,13 +405,15 @@ first_nondebug_insn_use_ignoring (const access_info *access,
   return nullptr;
 }
 
-// If ACCESS is a set, return the last use of ACCESS by a nondebug insn I for
-// which IGNORE (I) is false.  Return null if ACCESS is not a set or if no
-// such use exists.
-template<typename IgnorePredicate>
+// If ACCESS is a set, return the last "relevant" use of ACCESS by a
+// nondebug insn.  IGNORE is an object that provides the same interface
+// as ignore_nothing; it defines which accesses and insns are "relevant"
+// and which should be ignored.
+//
+// Return null if ACCESS is not a set or if no such relevant use exists.
+template<typename IgnorePredicates>
 inline use_info *
-last_nondebug_insn_use_ignoring (const access_info *access,
-				 IgnorePredicate ignore)
+last_nondebug_insn_use (const access_info *access, IgnorePredicates ignore)
 {
   if (const set_info *set = set_with_nondebug_insn_uses (access))
     {
@@ -413,7 +422,7 @@ last_nondebug_insn_use_ignoring (const access_info *access,
       use_info *use = set->last_nondebug_insn_use ();
       do
 	{
-	  if (!ignore (use->insn ()))
+	  if (!ignore.should_ignore_insn (use->insn ()))
 	    return use;
 	  use = use->prev_use ();
 	}
@@ -427,7 +436,8 @@ last_nondebug_insn_use_ignoring (const access_info *access,
 // Otherwise, search backwards for an access to DEF->resource (), starting at
 // the end of DEF's live range.  Ignore clobbers if IGNORE_CLOBBERS_SETTING
 // is YES, otherwise treat them like any other access.  Also ignore any
-// access A for which IGNORE (access_insn (A)) is true.
+// accesses and insns that IGNORE says should be ignored, where IGNORE
+// is an object that provides the same interface as ignore_nothing.
 //
 // Thus if DEF is a set that is used by nondebug insns, the first access
 // that the function considers is the last such use of the set.  Otherwise,
@@ -438,23 +448,21 @@ last_nondebug_insn_use_ignoring (const access_info *access,
 //
 // Note that this function does not consider separately-recorded call clobbers,
 // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO.
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 access_info *
-last_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting,
-		      IgnorePredicate ignore)
+last_access (def_info *def, ignore_clobbers ignore_clobbers_setting,
+	     IgnorePredicates ignore)
 {
   while (def)
     {
       auto *clobber = dyn_cast<clobber_info *> (def);
       if (clobber && ignore_clobbers_setting == ignore_clobbers::YES)
 	def = first_clobber_in_group (clobber);
-      else
+      else if (!ignore.should_ignore_def (def))
 	{
-	  if (use_info *use = last_nondebug_insn_use_ignoring (def, ignore))
+	  if (use_info *use = last_nondebug_insn_use (def, ignore))
 	    return use;
-
-	  insn_info *insn = def->insn ();
-	  if (!ignore (insn))
+	  if (!ignore.should_ignore_insn (def->insn ()))
 	    return def;
 	}
       def = def->prev_def ();
@@ -465,8 +473,9 @@ last_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting,
 // Search backwards for an access to DEF->resource (), starting
 // immediately before the point at which DEF occurs.  Ignore clobbers
 // if IGNORE_CLOBBERS_SETTING is YES, otherwise treat them like any other
-// access.  Also ignore any access A for which IGNORE (access_insn (A))
-// is true.
+// access.  Also ignore any accesses and insns that IGNORE says should be
+// ignored, where IGNORE is an object that provides the same interface as
+// ignore_nothing.
 //
 // Thus if DEF->insn () uses DEF->resource (), that use is the first access
 // that the function considers, since an instruction's uses occur strictly
@@ -474,40 +483,44 @@ last_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting,
 //
 // Note that this function does not consider separately-recorded call clobbers,
 // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO.
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 inline access_info *
-prev_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting,
-		      IgnorePredicate ignore)
+prev_access (def_info *def, ignore_clobbers ignore_clobbers_setting,
+	     IgnorePredicates ignore)
 {
-  return last_access_ignoring (def->prev_def (), ignore_clobbers_setting,
-			       ignore);
+  return last_access (def->prev_def (), ignore_clobbers_setting, ignore);
 }
 
 // If DEF is null, return null.
 //
-// Otherwise, search forwards for a definition of DEF->resource (),
+// Otherwise, search forwards for an access to DEF->resource (),
 // starting at DEF itself.  Ignore clobbers if IGNORE_CLOBBERS_SETTING
 // is YES, otherwise treat them like any other access.  Also ignore any
-// definition D for which IGNORE (D->insn ()) is true.
+// accesses and insns that IGNORE says should be ignored, where IGNORE
+// is an object that provides the same interface as ignore_nothing.
 //
 // Return the definition found, or null if there is no access that meets
 // the criteria.
 //
 // Note that this function does not consider separately-recorded call clobbers,
 // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO.
-template<typename IgnorePredicate>
-def_info *
-first_def_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting,
-		    IgnorePredicate ignore)
+template<typename IgnorePredicates>
+access_info *
+first_access (def_info *def, ignore_clobbers ignore_clobbers_setting,
+	      IgnorePredicates ignore)
 {
   while (def)
     {
       auto *clobber = dyn_cast<clobber_info *> (def);
       if (clobber && ignore_clobbers_setting == ignore_clobbers::YES)
 	def = last_clobber_in_group (clobber);
-      else if (!ignore (def->insn ()))
-	return def;
-
+      else if (!ignore.should_ignore_def (def))
+	{
+	  if (!ignore.should_ignore_insn (def->insn ()))
+	    return def;
+	  if (use_info *use = first_nondebug_insn_use (def, ignore))
+	    return use;
+	}
       def = def->next_def ();
     }
   return nullptr;
@@ -516,27 +529,29 @@ first_def_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting,
 // Search forwards for the next access to DEF->resource (),
 // starting immediately after DEF's instruction.  Ignore clobbers if
 // IGNORE_CLOBBERS_SETTING is YES, otherwise treat them like any other access.
-// Also ignore any access A for which IGNORE (access_insn (A)) is true;
-// in this context, ignoring a set includes ignoring all uses of the set.
+// Also ignore any accesses and insns that IGNORE says should be ignored,
+// where IGNORE is an object that provides the same interface as
+// ignore_nothing.
 //
 // Thus if DEF is a set with uses by nondebug insns, the first access that the
-// function considers is the first such use of the set.
+// function considers is the first such use of the set.  Otherwise, the first
+// access that the function considers is the definition after DEF.
 //
 // Return the access found, or null if there is no access that meets the
 // criteria.
 //
 // Note that this function does not consider separately-recorded call clobbers,
 // although such clobbers are only relevant if IGNORE_CLOBBERS_SETTING is NO.
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 access_info *
-next_access_ignoring (def_info *def, ignore_clobbers ignore_clobbers_setting,
-		      IgnorePredicate ignore)
+next_access (def_info *def, ignore_clobbers ignore_clobbers_setting,
+	     IgnorePredicates ignore)
 {
-  if (use_info *use = first_nondebug_insn_use_ignoring (def, ignore))
-    return use;
+  if (!ignore.should_ignore_def (def))
+    if (use_info *use = first_nondebug_insn_use (def, ignore))
+      return use;
 
-  return first_def_ignoring (def->next_def (), ignore_clobbers_setting,
-			     ignore);
+  return first_access (def->next_def (), ignore_clobbers_setting, ignore);
 }
 
 // Return true if ACCESS1 should before ACCESS2 in an access_array.
diff --git a/gcc/rtl-ssa/change-utils.h b/gcc/rtl-ssa/change-utils.h
index fce41b0157a..fa27d1ad047 100644
--- a/gcc/rtl-ssa/change-utils.h
+++ b/gcc/rtl-ssa/change-utils.h
@@ -30,25 +30,15 @@ insn_is_changing (array_slice<insn_change *const> changes,
   return false;
 }
 
-// Return a closure of insn_is_changing, for use as a predicate.
-// This could be done using local lambdas instead, but the predicate is
-// used often enough that having a class should be more convenient and allow
-// reuse of template instantiations.
-//
-// We don't use std::bind because it would involve an indirect function call,
-// whereas this function is used in relatively performance-critical code.
-inline insn_is_changing_closure
-insn_is_changing (array_slice<insn_change *const> changes)
-{
-  return insn_is_changing_closure (changes);
-}
-
 // Restrict CHANGE.move_range so that the changed instruction can perform
-// all its definitions and uses.  Assume that if:
+// all its definitions and uses.
+//
+// IGNORE is an object that provides the same interface as ignore_nothing.
+// Assume that if:
 //
 // - CHANGE contains an access A1 of resource R;
 // - an instruction I2 contains another access A2 to R; and
-// - IGNORE (I2) is true
+// - IGNORE says that I2 should be ignored
 //
 // then either:
 //
@@ -56,31 +46,33 @@ insn_is_changing (array_slice<insn_change *const> changes)
 // - something will ensure that A1 and A2 maintain their current order,
 //   without this having to be enforced by CHANGE's move range.
 //
-// IGNORE should return true for CHANGE.insn ().
+// Assume the same thing about a definition D of R, and about all uses of D,
+// if IGNORE says that D should be ignored.
+//
+// IGNORE should ignore CHANGE.insn ().
 //
 // Return true on success, otherwise leave CHANGE.move_range in an invalid
 // state.
 //
 // This function only works correctly for instructions that remain within
 // the same extended basic block.
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 bool
-restrict_movement_ignoring (insn_change &change, IgnorePredicate ignore)
+restrict_movement (insn_change &change, IgnorePredicates ignore)
 {
   // Uses generally lead to failure quicker, so test those first.
-  return (restrict_movement_for_uses_ignoring (change.move_range,
-					       change.new_uses, ignore)
-	  && restrict_movement_for_defs_ignoring (change.move_range,
-						  change.new_defs, ignore)
+  return (restrict_movement_for_uses (change.move_range,
+				      change.new_uses, ignore)
+	  && restrict_movement_for_defs (change.move_range,
+					 change.new_defs, ignore)
 	  && canonicalize_move_range (change.move_range, change.insn ()));
 }
 
-// Like restrict_movement_ignoring, but ignore only the instruction
-// that is being changed.
+// As above, but ignore only the instruction that is being changed.
 inline bool
 restrict_movement (insn_change &change)
 {
-  return restrict_movement_ignoring (change, insn_is (change.insn ()));
+  return restrict_movement (change, ignore_insn (change.insn ()));
 }
 
 using add_regno_clobber_fn = std::function<bool (insn_change &,
@@ -91,18 +83,22 @@ bool recog_internal (insn_change &, add_regno_clobber_fn);
 // tweaking the pattern or adding extra clobbers in order to make it match.
 //
 // When adding an extra clobber for register R, restrict CHANGE.move_range
-// to a range of instructions for which R is not live.  When determining
-// whether R is live, ignore accesses made by an instruction I if
-// IGNORE (I) is true.  The caller then assumes the responsibility
-// of ensuring that CHANGE and I are placed in a valid order.
+// to a range of instructions for which R is not live.  Use IGNORE to guide
+// this process, where IGNORE is an object that provides the same interface
+// as ignore_nothing.  When determining whether R is live, ignore accesses
+// made by an instruction I if IGNORE says that I should be ignored.
+// The caller then assumes the responsibility of ensuring that CHANGE
+// and I are placed in a valid order.  Similarly, ignore live ranges
+// associated with a definition of R if IGNORE says that that definition
+// should be ignored.
 //
-// IGNORE should return true for CHANGE.insn ().
+// IGNORE should ignore CHANGE.insn ().
 //
 // Return true on success.  Leave CHANGE unmodified on failure.
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 inline bool
-recog_ignoring (obstack_watermark &watermark, insn_change &change,
-		IgnorePredicate ignore)
+recog (obstack_watermark &watermark, insn_change &change,
+       IgnorePredicates ignore)
 {
   auto add_regno_clobber = [&](insn_change &change, unsigned int regno)
     {
@@ -111,12 +107,11 @@ recog_ignoring (obstack_watermark &watermark, insn_change &change,
   return recog_internal (change, add_regno_clobber);
 }
 
-// As for recog_ignoring, but ignore only the instruction that is being
-// changed.
+// As above, but ignore only the instruction that is being changed.
 inline bool
 recog (obstack_watermark &watermark, insn_change &change)
 {
-  return recog_ignoring (watermark, change, insn_is (change.insn ()));
+  return recog (watermark, change, ignore_insn (change.insn ()));
 }
 
 // Check whether insn costs indicate that the net effect of the changes
diff --git a/gcc/rtl-ssa/changes.h b/gcc/rtl-ssa/changes.h
index 35ab02243a9..0bcd962fa77 100644
--- a/gcc/rtl-ssa/changes.h
+++ b/gcc/rtl-ssa/changes.h
@@ -98,19 +98,6 @@ private:
   bool m_is_deletion;
 };
 
-// A class that represents a closure of the two-argument form of
-// insn_is_changing.  See the comment above the one-argument form
-// for details.
-class insn_is_changing_closure
-{
-public:
-  insn_is_changing_closure (array_slice<insn_change *const> changes);
-  bool operator() (const insn_info *) const;
-
-private:
-  array_slice<insn_change *const> m_changes;
-};
-
 void pp_insn_change (pretty_printer *, const insn_change &);
 
 }
diff --git a/gcc/rtl-ssa/functions.h b/gcc/rtl-ssa/functions.h
index f5aca643beb..479c6992e97 100644
--- a/gcc/rtl-ssa/functions.h
+++ b/gcc/rtl-ssa/functions.h
@@ -165,16 +165,22 @@ public:
 
   // If CHANGE doesn't already clobber REGNO, try to add such a clobber,
   // limiting the movement range in order to make the clobber valid.
-  // When determining whether REGNO is live, ignore accesses made by an
-  // instruction I if IGNORE (I) is true.  The caller then assumes the
-  // responsibility of ensuring that CHANGE and I are placed in a valid order.
+  // Use IGNORE to guide this process, where IGNORE is an object that
+  // provides the same interface as ignore_nothing.
+  //
+  // That is, when determining whether REGNO is live, ignore accesses made
+  // by an instruction I if IGNORE says that I should be ignored.  The caller
+  // then assumes the responsibility of ensuring that CHANGE and I are placed
+  // in a valid order.  Similarly, ignore live ranges associated/ with a
+  // definition of REGNO if IGNORE says that that definition should be
+  // ignored.
   //
   // Return true on success.  Leave CHANGE unmodified when returning false.
   //
   // WATERMARK is a watermark returned by new_change_attempt ().
-  template<typename IgnorePredicate>
+  template<typename IgnorePredicates>
   bool add_regno_clobber (obstack_watermark &watermark, insn_change &change,
-			  unsigned int regno, IgnorePredicate ignore);
+			  unsigned int regno, IgnorePredicates ignore);
 
   // Return true if change_insns will be able to perform the changes
   // described by CHANGES.
diff --git a/gcc/rtl-ssa/insn-utils.h b/gcc/rtl-ssa/insn-utils.h
index bd3a4cbdcfa..1c54fe662e3 100644
--- a/gcc/rtl-ssa/insn-utils.h
+++ b/gcc/rtl-ssa/insn-utils.h
@@ -35,12 +35,4 @@ later_insn (insn_info *insn1, insn_info *insn2)
   return *insn1 < *insn2 ? insn2 : insn1;
 }
 
-// Return a closure of operator== for INSN.  See insn_is_changing for
-// the rationale for defining the function this way.
-inline insn_is_closure
-insn_is (const insn_info *insn)
-{
-  return insn_is_closure (insn);
-}
-
 }
diff --git a/gcc/rtl-ssa/insns.h b/gcc/rtl-ssa/insns.h
index 334d02888ca..1ba56abc2ca 100644
--- a/gcc/rtl-ssa/insns.h
+++ b/gcc/rtl-ssa/insns.h
@@ -493,18 +493,6 @@ public:
   insn_info *last;
 };
 
-// A class that represents a closure of operator== for instructions.
-// This is used by insn_is; see there for details.
-class insn_is_closure
-{
-public:
-  insn_is_closure (const insn_info *insn) : m_insn (insn) {}
-  bool operator() (const insn_info *other) const { return m_insn == other; }
-
-private:
-  const insn_info *m_insn;
-};
-
 void pp_insn (pretty_printer *, const insn_info *);
 
 }
diff --git a/gcc/rtl-ssa/member-fns.inl b/gcc/rtl-ssa/member-fns.inl
index e4825ad2a18..833907b62c9 100644
--- a/gcc/rtl-ssa/member-fns.inl
+++ b/gcc/rtl-ssa/member-fns.inl
@@ -870,21 +870,6 @@ inline insn_change::insn_change (insn_info *insn, delete_action)
 {
 }
 
-inline insn_is_changing_closure::
-insn_is_changing_closure (array_slice<insn_change *const> changes)
-  : m_changes (changes)
-{
-}
-
-inline bool
-insn_is_changing_closure::operator() (const insn_info *insn) const
-{
-  for (const insn_change *change : m_changes)
-    if (change->insn () == insn)
-      return true;
-  return false;
-}
-
 inline iterator_range<bb_iterator>
 function_info::bbs () const
 {
@@ -963,11 +948,11 @@ function_info::single_dominating_def (unsigned int regno) const
   return nullptr;
 }
 
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 bool
 function_info::add_regno_clobber (obstack_watermark &watermark,
 				  insn_change &change, unsigned int regno,
-				  IgnorePredicate ignore)
+				  IgnorePredicates ignore)
 {
   // Check whether CHANGE already clobbers REGNO.
   if (find_access (change.new_defs, regno))
@@ -1003,4 +988,20 @@ function_info::change_alloc (obstack_watermark &wm, Ts... args)
   return new (addr) T (std::forward<Ts> (args)...);
 }
 
+inline
+ignore_changing_insns::
+ignore_changing_insns (array_slice<insn_change *const> changes)
+  : m_changes (changes)
+{
+}
+
+inline bool
+ignore_changing_insns::should_ignore_insn (const insn_info *insn)
+{
+  for (const insn_change *change : m_changes)
+    if (change->insn () == insn)
+      return true;
+  return false;
+}
+
 }
diff --git a/gcc/rtl-ssa/movement.h b/gcc/rtl-ssa/movement.h
index f55c234e824..17d31e0b5cb 100644
--- a/gcc/rtl-ssa/movement.h
+++ b/gcc/rtl-ssa/movement.h
@@ -83,10 +83,13 @@ canonicalize_move_range (insn_range_info &move_range, insn_info *insn)
 }
 
 // Try to restrict movement range MOVE_RANGE of INSN so that it can set
-// or clobber REGNO.  Assume that if:
+// or clobber REGNO.
+//
+// IGNORE is an object that provides the same interface as ignore_nothing.
+// Assume that if:
 //
 // - an instruction I2 contains another access A to REGNO; and
-// - IGNORE (I2) is true
+// - IGNORE says that I2 should be ignored
 //
 // then either:
 //
@@ -94,15 +97,18 @@ canonicalize_move_range (insn_range_info &move_range, insn_info *insn)
 // - something will ensure that the new definition of REGNO does not
 //   interfere with A, without this having to be enforced by I1's move range.
 //
+// If IGNORE says that a definition D of REGNO should be ignored, assume that
+// the new definition of REGNO will not conflict with D.
+//
 // Return true on success, otherwise leave MOVE_RANGE in an invalid state.
 //
 // This function only works correctly for instructions that remain within
 // the same extended basic block.
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 bool
 restrict_movement_for_dead_range (insn_range_info &move_range,
 				  unsigned int regno, insn_info *insn,
-				  IgnorePredicate ignore)
+				  IgnorePredicates ignore)
 {
   // Find a definition at or neighboring INSN.
   resource_info resource = full_register (regno);
@@ -141,17 +147,18 @@ restrict_movement_for_dead_range (insn_range_info &move_range,
     {
       // Stop the instruction moving beyond the previous relevant access
       // to REGNO.
-      access_info *prev_access
-	= last_access_ignoring (prev, ignore_clobbers::YES, ignore);
+      access_info *prev_access = last_access (prev, ignore_clobbers::YES,
+					      ignore);
       if (prev_access)
 	move_range = move_later_than (move_range, access_insn (prev_access));
     }
 
-  // Stop the instruction moving beyond the next relevant definition of REGNO.
+  // Stop the instruction moving beyond the next relevant use or definition
+  // of REGNO.
   def_info *next = dl.matching_set_or_first_def_of_next_group ();
-  next = first_def_ignoring (next, ignore_clobbers::YES, ignore);
-  if (next)
-    move_range = move_earlier_than (move_range, next->insn ());
+  access_info *next_access = first_access (next, ignore_clobbers::YES, ignore);
+  if (next_access)
+    move_range = move_earlier_than (move_range, access_insn (next_access));
 
   return canonicalize_move_range (move_range, insn);
 }
@@ -159,11 +166,14 @@ restrict_movement_for_dead_range (insn_range_info &move_range,
 // Try to restrict movement range MOVE_RANGE so that it is possible for the
 // instruction being moved ("instruction I1") to perform all the definitions
 // in DEFS while still preserving dependencies between those definitions
-// and surrounding instructions.  Assume that if:
+// and surrounding instructions.
+//
+// IGNORE is an object that provides the same interface as ignore_nothing.
+// Assume that if:
 //
 // - DEFS contains a definition D of resource R;
 // - an instruction I2 contains another access A to R; and
-// - IGNORE (I2) is true
+// - IGNORE says that I2 should be ignored
 //
 // then either:
 //
@@ -171,14 +181,17 @@ restrict_movement_for_dead_range (insn_range_info &move_range,
 // - something will ensure that D and A maintain their current order,
 //   without this having to be enforced by I1's move range.
 //
+// Assume the same thing about a definition D and all uses of D if IGNORE
+// says that D should be ignored.
+//
 // Return true on success, otherwise leave MOVE_RANGE in an invalid state.
 //
 // This function only works correctly for instructions that remain within
 // the same extended basic block.
-template<typename IgnorePredicate>
+template<typename IgnorePredicates>
 bool
-restrict_movement_for_defs_ignoring (insn_range_info &move_range,
-				     def_array defs, IgnorePredicate ignore)
+restrict_movement_for_defs (insn_range_info &move_range, def_array defs,
+			    IgnorePredicates ignore)
 {
   for (def_info *def : defs)
     {
@@ -194,29 +207,16 @@ restrict_movement_for_defs_ignoring (insn_range_info &move_range,
       // are being moved at once.
       bool is_clobber = is_a<clobber_info *> (def);
 
-      // Search back for the first unfiltered use or definition of the
+      // Search back for the first relevant use or definition of the
       // same resource.
       access_info *access;
-      access = prev_access_ignoring (def, ignore_clobbers (is_clobber),
-				     ignore);
+      access = prev_access (def, ignore_clobbers (is_clobber), ignore);
       if (access)
 	move_range = move_later_than (move_range, access_insn (access));
 
-      // Search forward for the first unfiltered use of DEF,
-      // or the first unfiltered definition that follows DEF.
-      //
-      // We don't need to consider uses of following definitions, since
-      // if IGNORE (D->insn ()) is true for some definition D, the caller
-      // is guarantees that either
-      //
-      // - D will be removed, and thus its uses will be removed; or
-      // - D will occur after DEF, and thus D's uses will also occur
-      //   after DEF.
-      //
-      // This is purely a simplification: we could also process D's uses,
-      // but we don't need to.
-      access = next_access_ignoring (def, ignore_clobbers (is_clobber),
-				     ignore);
+      // Search forward for the next relevant use or definition of the
+      // same resource.
+      access = next_access (def, ignore_clobbers (is_clobber), ignore);
       if (access)
 	move_range = move_earlier_than (move_range, access_insn (access));
 
@@ -238,13 +238,11 @@ restrict_movement_for_defs_ignoring (insn_range_info &move_range,
 	    return false;
 
 	  insn_info *insn;
-	  insn = prev_call_clobbers_ignoring (*call_group, def->insn (),
-					      ignore);
+	  insn = prev_call_clobbers (*call_group, def->insn (), ignore);
 	  if (insn)
 	    move_range = move_later_than (move_range, insn);
 
-	  insn = next_call_clobbers_ignoring (*call_group, def->insn (),
-					      ignore);
+	  insn = next_call_clobbers (*call_group, def->insn (), ignore);
 	  if (insn)
 	    move_range = move_earlier_than (move_range, insn);
 	}
@@ -262,11 +260,11 @@ restrict_movement_for_defs_ignoring (insn_range_info &move_range,
   return bool (move_range);
 }
 
-// Like restrict_movement_for_defs_ignoring, but for the uses in USES.
-template<typename IgnorePredicate>
+// Like restrict_movement_for_defs, but for the uses in USES.
+template<typename IgnorePredicates>
 bool
-restrict_movement_for_uses_ignoring (insn_range_info &move_range,
-				     use_array uses, IgnorePredicate ignore)
+restrict_movement_for_uses (insn_range_info &move_range, use_array uses,
+			    IgnorePredicates ignore)
 {
   for (const use_info *use : uses)
     {
@@ -284,31 +282,21 @@ restrict_movement_for_uses_ignoring (insn_range_info &move_range,
       if (use->is_in_debug_insn ())
 	continue;
 
-      // If the used value is defined by an instruction I2 for which
-      // IGNORE (I2) is true, the caller guarantees that I2 will occur
-      // before change.insn ().  Otherwise, make sure that the use occurs
-      // after the definition.
+      // If the used value is defined by an ignored instruction I2,
+      // the caller guarantees that I2 will occur before change.insn ()
+      // and that its value will still be available at change.insn ().
+      // Otherwise, make sure that the use occurs after the definition.
       insn_info *insn = set->insn ();
-      if (!ignore (insn))
+      if (!ignore.should_ignore_def (set)
+	  && !ignore.should_ignore_insn (insn))
 	move_range = move_later_than (move_range, insn);
 
-      // Search forward for the first unfiltered definition that follows SET.
-      //
-      // We don't need to consider the uses of these definitions, since
-      // if IGNORE (D->insn ()) is true for some definition D, the caller
-      // is guarantees that either
-      //
-      // - D will be removed, and thus its uses will be removed; or
-      // - D will occur after USE, and thus D's uses will also occur
-      //   after USE.
-      //
-      // This is purely a simplification: we could also process D's uses,
-      // but we don't need to.
-      def_info *def;
-      def = first_def_ignoring (set->next_def (), ignore_clobbers::NO,
-				ignore);
-      if (def)
-	move_range = move_earlier_than (move_range, def->insn ());
+      // Search forward after SET's live range for the first relevant
+      // use or definition of the same resource.
+      access_info *access;
+      access = first_access (set->next_def (), ignore_clobbers::NO, ignore);
+      if (access)
+	move_range = move_earlier_than (move_range, access_insn (access));
 
       // If USE uses a hard register, take any call clobbers into account too.
       // SET will necessarily occur after any previous call clobber, so we
@@ -326,8 +314,8 @@ restrict_movement_for_uses_ignoring (insn_range_info &move_range,
 	  if (!move_range)
 	    return false;
 
-	  insn_info *insn = next_call_clobbers_ignoring (*call_group,
-							 use->insn (), ignore);
+	  insn_info *insn = next_call_clobbers (*call_group, use->insn (),
+						ignore);
 	  if (insn)
 	    move_range = move_earlier_than (move_range, insn);
 	}
diff --git a/gcc/rtl-ssa/predicates.h b/gcc/rtl-ssa/predicates.h
new file mode 100644
index 00000000000..225a8c658b4
--- /dev/null
+++ b/gcc/rtl-ssa/predicates.h
@@ -0,0 +1,58 @@
+// RTL SSA predicate classes                                        -*- C++ -*-
+// Copyright (C) 2024 Free Software Foundation, Inc.
+//
+// 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/>.
+
+namespace rtl_ssa {
+
+// Collects predicates that affect a scan over the IR, specifying what
+// (if anything) should be ignored.
+struct ignore_nothing
+{
+  // Return true if the scan should ignore the given definition
+  // and all uses of the definition.
+  bool should_ignore_def (const def_info *) { return false; }
+
+  // Return true if the scan should ignore the given instruction.
+  bool should_ignore_insn (const insn_info *) { return false; }
+};
+
+// Predicates that ignore the instruction passed to the constructor
+// (and nothing else).
+class ignore_insn : public ignore_nothing
+{
+public:
+  ignore_insn (const insn_info *insn) : m_insn (insn) {}
+  bool should_ignore_insn (const insn_info *insn) { return insn == m_insn; }
+
+private:
+  const insn_info *m_insn;
+};
+
+// Predicates that ignore all the instructions being changed by a set
+// of insn_changes.
+class ignore_changing_insns : public ignore_nothing
+{
+public:
+  ignore_changing_insns (array_slice<insn_change *const>);
+  bool should_ignore_insn (const insn_info *);
+
+private:
+  array_slice<insn_change *const> m_changes;
+};
+
+}
