1 // RTL SSA utility functions for changing instructions -*- C++ -*- 2 // Copyright (C) 2020-2022 Free Software Foundation, Inc. 3 // 4 // This file is part of GCC. 5 // 6 // GCC is free software; you can redistribute it and/or modify it under 7 // the terms of the GNU General Public License as published by the Free 8 // Software Foundation; either version 3, or (at your option) any later 9 // version. 10 // 11 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY 12 // WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 // for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with GCC; see the file COPYING3. If not see 18 // <http://www.gnu.org/licenses/>. 19 20 namespace rtl_ssa { 21 22 // Return true if INSN is one of the instructions being changed by CHANGES. 23 inline bool 24 insn_is_changing (array_slice<insn_change *const> changes, 25 const insn_info *insn) 26 { 27 for (const insn_change *change : changes) 28 if (change->insn () == insn) 29 return true; 30 return false; 31 } 32 33 // Return a closure of insn_is_changing, for use as a predicate. 34 // This could be done using local lambdas instead, but the predicate is 35 // used often enough that having a class should be more convenient and allow 36 // reuse of template instantiations. 37 // 38 // We don't use std::bind because it would involve an indirect function call, 39 // whereas this function is used in relatively performance-critical code. 40 inline insn_is_changing_closure 41 insn_is_changing (array_slice<insn_change *const> changes) 42 { 43 return insn_is_changing_closure (changes); 44 } 45 46 // Restrict CHANGE.move_range so that the changed instruction can perform 47 // all its definitions and uses. Assume that if: 48 // 49 // - CHANGE contains an access A1 of resource R; 50 // - an instruction I2 contains another access A2 to R; and 51 // - IGNORE (I2) is true 52 // 53 // then either: 54 // 55 // - A2 will be removed; or 56 // - something will ensure that A1 and A2 maintain their current order, 57 // without this having to be enforced by CHANGE's move range. 58 // 59 // IGNORE should return true for CHANGE.insn (). 60 // 61 // Return true on success, otherwise leave CHANGE.move_range in an invalid 62 // state. 63 // 64 // This function only works correctly for instructions that remain within 65 // the same extended basic block. 66 template<typename IgnorePredicate> 67 bool 68 restrict_movement_ignoring (insn_change &change, IgnorePredicate ignore) 69 { 70 // Uses generally lead to failure quicker, so test those first. 71 return (restrict_movement_for_uses_ignoring (change.move_range, 72 change.new_uses, ignore) 73 && restrict_movement_for_defs_ignoring (change.move_range, 74 change.new_defs, ignore) 75 && canonicalize_move_range (change.move_range, change.insn ())); 76 } 77 78 // Like restrict_movement_ignoring, but ignore only the instruction 79 // that is being changed. 80 inline bool 81 restrict_movement (insn_change &change) 82 { 83 return restrict_movement_ignoring (change, insn_is (change.insn ())); 84 } 85 86 using add_regno_clobber_fn = std::function<bool (insn_change &, 87 unsigned int)>; 88 bool recog_internal (insn_change &, add_regno_clobber_fn); 89 90 // Try to recognize the new instruction pattern for CHANGE, potentially 91 // tweaking the pattern or adding extra clobbers in order to make it match. 92 // 93 // When adding an extra clobber for register R, restrict CHANGE.move_range 94 // to a range of instructions for which R is not live. When determining 95 // whether R is live, ignore accesses made by an instruction I if 96 // IGNORE (I) is true. The caller then assumes the responsibility 97 // of ensuring that CHANGE and I are placed in a valid order. 98 // 99 // IGNORE should return true for CHANGE.insn (). 100 // 101 // Return true on success. Leave CHANGE unmodified on failure. 102 template<typename IgnorePredicate> 103 inline bool 104 recog_ignoring (obstack_watermark &watermark, insn_change &change, 105 IgnorePredicate ignore) 106 { 107 auto add_regno_clobber = [&](insn_change &change, unsigned int regno) 108 { 109 return crtl->ssa->add_regno_clobber (watermark, change, regno, ignore); 110 }; 111 return recog_internal (change, add_regno_clobber); 112 } 113 114 // As for recog_ignoring, but ignore only the instruction that is being 115 // changed. 116 inline bool 117 recog (obstack_watermark &watermark, insn_change &change) 118 { 119 return recog_ignoring (watermark, change, insn_is (change.insn ())); 120 } 121 122 // Check whether insn costs indicate that the net effect of the changes 123 // in CHANGES is worthwhile. Require a strict improvement if STRICT_P, 124 // otherwise allow the new instructions to be the same cost as the old 125 // instructions. 126 bool changes_are_worthwhile (array_slice<insn_change *const> changes, 127 bool strict_p = false); 128 129 // Like changes_are_worthwhile, but for a single change. 130 inline bool 131 change_is_worthwhile (insn_change &change, bool strict_p = false) 132 { 133 insn_change *changes[] = { &change }; 134 return changes_are_worthwhile (changes, strict_p); 135 } 136 137 } 138