// RTL SSA utility functions for changing instructions -*- C++ -*- // Copyright (C) 2020-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 // . namespace rtl_ssa { // Return true if INSN is one of the instructions being changed by CHANGES. inline bool insn_is_changing (array_slice 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 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 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 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 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 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); } }