diff options
Diffstat (limited to 'gcc/rtl-ssa/change-utils.h')
-rw-r--r-- | gcc/rtl-ssa/change-utils.h | 137 |
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); +} + +} |