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 | |
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 (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. |
80 | inline bool |
81 | restrict_movement (insn_change &change) |
82 | { |
83 | return restrict_movement_ignoring (change, ignore: insn_is (insn: 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, 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. |
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 | |