aboutsummaryrefslogtreecommitdiff
path: root/gcc/rtl-ssa/change-utils.h
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/rtl-ssa/change-utils.h')
-rw-r--r--gcc/rtl-ssa/change-utils.h137
1 files changed, 137 insertions, 0 deletions
diff --git a/gcc/rtl-ssa/change-utils.h b/gcc/rtl-ssa/change-utils.h
new file mode 100644
index 0000000..8245330
--- /dev/null
+++ b/gcc/rtl-ssa/change-utils.h
@@ -0,0 +1,137 @@
+// RTL SSA utility functions for changing instructions -*- C++ -*-
+// Copyright (C) 2020 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 {
+
+// Return true if INSN is one of the instructions being changed by CHANGES.
+inline bool
+insn_is_changing (array_slice<insn_change *const> changes,
+ const insn_info *insn)
+{
+ for (const insn_change *change : changes)
+ if (change->insn () == insn)
+ return true;
+ 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:
+//
+// - CHANGE contains an access A1 of resource R;
+// - an instruction I2 contains another access A2 to R; and
+// - IGNORE (I2) is true
+//
+// then either:
+//
+// - A2 will be removed; or
+// - 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 ().
+//
+// 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>
+bool
+restrict_movement_ignoring (insn_change &change, IgnorePredicate 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)
+ && canonicalize_move_range (change.move_range, change.insn ()));
+}
+
+// Like restrict_movement_ignoring, 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 ()));
+}
+
+using add_regno_clobber_fn = std::function<bool (insn_change &,
+ unsigned int)>;
+bool recog_internal (insn_change &, add_regno_clobber_fn);
+
+// Try to recognize the new instruction pattern for CHANGE, potentially
+// 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.
+//
+// IGNORE should return true for CHANGE.insn ().
+//
+// Return true on success. Leave CHANGE unmodified on failure.
+template<typename IgnorePredicate>
+inline bool
+recog_ignoring (obstack_watermark &watermark, insn_change &change,
+ IgnorePredicate ignore)
+{
+ auto add_regno_clobber = [&](insn_change &change, unsigned int regno)
+ {
+ return crtl->ssa->add_regno_clobber (watermark, change, regno, ignore);
+ };
+ return recog_internal (change, add_regno_clobber);
+}
+
+// As for recog_ignoring, 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 ()));
+}
+
+// Check whether insn costs indicate that the net effect of the changes
+// in CHANGES is worthwhile. Require a strict improvement if STRICT_P,
+// otherwise allow the new instructions to be the same cost as the old
+// instructions.
+bool changes_are_worthwhile (array_slice<insn_change *const> changes,
+ bool strict_p = false);
+
+// Like changes_are_worthwhile, but for a single change.
+inline bool
+change_is_worthwhile (insn_change &change, bool strict_p = false)
+{
+ insn_change *changes[] = { &change };
+ return changes_are_worthwhile (changes, strict_p);
+}
+
+}