1  // RTL SSA utility functions for changing instructions              -*- C++ -*-
       2  // Copyright (C) 2020-2023 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  }