1// RTL SSA utility functions for changing instructions -*- C++ -*-
2// Copyright (C) 2020-2024 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
20namespace rtl_ssa {
21
22// Return true if INSN is one of the instructions being changed by CHANGES.
23inline bool
24insn_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.
40inline insn_is_changing_closure
41insn_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.
66template<typename IgnorePredicate>
67bool
68restrict_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 (move_range&: change.move_range, insn: change.insn ()));
76}
77
78// Like restrict_movement_ignoring, but ignore only the instruction
79// that is being changed.
80inline bool
81restrict_movement (insn_change &change)
82{
83 return restrict_movement_ignoring (change, ignore: insn_is (insn: change.insn ()));
84}
85
86using add_regno_clobber_fn = std::function<bool (insn_change &,
87 unsigned int)>;
88bool 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.
102template<typename IgnorePredicate>
103inline bool
104recog_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.
116inline bool
117recog (obstack_watermark &watermark, insn_change &change)
118{
119 return recog_ignoring (watermark, change, ignore: insn_is (insn: 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.
126bool 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.
130inline bool
131change_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

source code of gcc/rtl-ssa/change-utils.h