Branch data Line data Source code
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 : 28221481 : restrict_movement_ignoring (insn_change &change, IgnorePredicate ignore)
69 : : {
70 : : // Uses generally lead to failure quicker, so test those first.
71 : 28221481 : return (restrict_movement_for_uses_ignoring (change.move_range,
72 : : change.new_uses, ignore)
73 : 26975150 : && restrict_movement_for_defs_ignoring (change.move_range,
74 : : change.new_defs, ignore)
75 : 55196631 : && 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 : 28221481 : restrict_movement (insn_change &change)
82 : : {
83 : 28221481 : 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 : 15064319 : recog_ignoring (obstack_watermark &watermark, insn_change &change,
105 : : IgnorePredicate ignore)
106 : : {
107 : 15064319 : auto add_regno_clobber = [&](insn_change &change, unsigned int regno)
108 : : {
109 : 1600361 : return crtl->ssa->add_regno_clobber (watermark, change, regno, ignore);
110 : : };
111 : 15064319 : 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 : 15064319 : recog (obstack_watermark &watermark, insn_change &change)
118 : : {
119 : 15064319 : 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 : : }
|