1 | /* Analyze RTL for GNU compiler. |
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 | /* Note that for historical reasons, many rtlanal.cc functions are |
21 | declared in rtl.h rather than here. */ |
22 | |
23 | #ifndef GCC_RTLANAL_H |
24 | #define GCC_RTLANAL_H |
25 | |
26 | /* A dummy register value that represents the whole of variable memory. |
27 | Using ~0U means that arrays that track both registers and memory can |
28 | be indexed by regno + 1. */ |
29 | const unsigned int MEM_REGNO = ~0U; |
30 | |
31 | /* Bitmasks of flags describing an rtx_obj_reference. See the accessors |
32 | in the class for details. */ |
33 | namespace rtx_obj_flags |
34 | { |
35 | const uint16_t IS_READ = 1U << 0; |
36 | const uint16_t IS_WRITE = 1U << 1; |
37 | const uint16_t IS_CLOBBER = 1U << 2; |
38 | const uint16_t IS_PRE_POST_MODIFY = 1U << 3; |
39 | const uint16_t IS_MULTIREG = 1U << 4; |
40 | const uint16_t IN_MEM_LOAD = 1U << 5; |
41 | const uint16_t IN_MEM_STORE = 1U << 6; |
42 | const uint16_t IN_SUBREG = 1U << 7; |
43 | const uint16_t IN_NOTE = 1U << 8; |
44 | |
45 | /* Flags that apply to all subrtxes of the rtx they were originally |
46 | added for. */ |
47 | static const uint16_t STICKY_FLAGS = IN_NOTE; |
48 | } |
49 | |
50 | /* Contains information about a reference to a register or variable memory. */ |
51 | class rtx_obj_reference |
52 | { |
53 | public: |
54 | rtx_obj_reference () = default; |
55 | rtx_obj_reference (unsigned int regno, uint16_t flags, |
56 | machine_mode mode, unsigned int multireg_offset = 0); |
57 | |
58 | bool is_reg () const { return regno != MEM_REGNO; } |
59 | bool is_mem () const { return regno == MEM_REGNO; } |
60 | |
61 | /* True if the reference is a read or a write respectively. |
62 | Both flags are set in a read-modify-write context, such as |
63 | for read_modify_subreg_p. */ |
64 | bool is_read () const { return flags & rtx_obj_flags::IS_READ; } |
65 | bool is_write () const { return flags & rtx_obj_flags::IS_WRITE; } |
66 | |
67 | /* True if IS_WRITE and if the write is a clobber rather than a set. */ |
68 | bool is_clobber () const { return flags & rtx_obj_flags::IS_CLOBBER; } |
69 | |
70 | /* True if the reference is updated by an RTX_AUTOINC. Both IS_READ |
71 | and IS_WRITE are also true if so. */ |
72 | bool is_pre_post_modify () const |
73 | { |
74 | return flags & rtx_obj_flags::IS_PRE_POST_MODIFY; |
75 | } |
76 | |
77 | /* True if the register is part of a multi-register hard REG. */ |
78 | bool is_multireg () const { return flags & rtx_obj_flags::IS_MULTIREG; } |
79 | |
80 | /* True if the reference occurs in the address of a load MEM. */ |
81 | bool in_mem_load () const { return flags & rtx_obj_flags::IN_MEM_LOAD; } |
82 | |
83 | /* True if the reference occurs in the address of a store MEM. */ |
84 | bool in_mem_store () const { return flags & rtx_obj_flags::IN_MEM_STORE; } |
85 | |
86 | /* True if the reference occurs in any kind of MEM address. */ |
87 | bool in_address () const { return in_mem_load () || in_mem_store (); } |
88 | |
89 | /* True if the reference occurs in a SUBREG. */ |
90 | bool in_subreg () const { return flags & rtx_obj_flags::IN_SUBREG; } |
91 | |
92 | /* True if the reference occurs in a REG_EQUAL or REG_EQUIV note. */ |
93 | bool in_note () const { return flags & rtx_obj_flags::IN_NOTE; } |
94 | |
95 | /* The referenced register, or MEM_REGNO for variable memory. */ |
96 | unsigned int regno; |
97 | |
98 | /* A bitmask of rtx_obj_flags. */ |
99 | unsigned int flags : 16; |
100 | |
101 | /* The mode of the reference. If IS_MULTIREG, this is the mode of |
102 | REGNO - MULTIREG_OFFSET. */ |
103 | machine_mode mode : MACHINE_MODE_BITSIZE; |
104 | |
105 | /* If IS_MULTIREG, the offset of REGNO from the start of the register. */ |
106 | unsigned int multireg_offset : 8; |
107 | }; |
108 | |
109 | /* Construct a reference with the given fields. */ |
110 | |
111 | inline rtx_obj_reference::rtx_obj_reference (unsigned int regno, uint16_t flags, |
112 | machine_mode mode, |
113 | unsigned int multireg_offset) |
114 | : regno (regno), |
115 | flags (flags), |
116 | mode (mode), |
117 | multireg_offset (multireg_offset) |
118 | { |
119 | } |
120 | |
121 | /* Contains information about an rtx or an instruction, including a |
122 | list of rtx_obj_references. The storage backing the list needs |
123 | to be filled in by assigning to REF_BEGIN and REF_END. */ |
124 | |
125 | class rtx_properties |
126 | { |
127 | public: |
128 | rtx_properties (); |
129 | |
130 | void try_to_add_reg (const_rtx x, unsigned int flags = 0); |
131 | void try_to_add_dest (const_rtx x, unsigned int flags = 0); |
132 | void try_to_add_src (const_rtx x, unsigned int flags = 0); |
133 | void try_to_add_pattern (const_rtx pat); |
134 | void try_to_add_note (const_rtx x); |
135 | void try_to_add_insn (const rtx_insn *insn, bool include_notes); |
136 | |
137 | iterator_range<rtx_obj_reference *> refs () const; |
138 | |
139 | /* Return the number of rtx_obj_references that have been recorded. */ |
140 | size_t num_refs () const { return ref_iter - ref_begin; } |
141 | |
142 | bool has_side_effects () const; |
143 | |
144 | /* [REF_BEGIN, REF_END) is the maximum extent of the memory available |
145 | for recording references. REG_ITER is the first unused entry. */ |
146 | rtx_obj_reference *ref_begin; |
147 | rtx_obj_reference *ref_iter; |
148 | rtx_obj_reference *ref_end; |
149 | |
150 | /* True if the rtx includes an asm. */ |
151 | unsigned int has_asm : 1; |
152 | |
153 | /* True if the rtx includes a call. */ |
154 | unsigned int has_call : 1; |
155 | |
156 | /* True if the rtx includes an RTX_AUTOINC expression. */ |
157 | unsigned int has_pre_post_modify : 1; |
158 | |
159 | /* True if the rtx contains volatile references, in the sense of |
160 | volatile_refs_p. */ |
161 | unsigned int has_volatile_refs : 1; |
162 | |
163 | /* For future expansion. */ |
164 | unsigned int spare : 28; |
165 | }; |
166 | |
167 | inline rtx_properties::rtx_properties () |
168 | : ref_begin (nullptr), |
169 | ref_iter (nullptr), |
170 | ref_end (nullptr), |
171 | has_asm (false), |
172 | has_call (false), |
173 | has_pre_post_modify (false), |
174 | has_volatile_refs (false), |
175 | spare (0) |
176 | { |
177 | } |
178 | |
179 | /* Like add_src, but treat X has being part of a REG_EQUAL or |
180 | REG_EQUIV note. */ |
181 | |
182 | inline void |
183 | rtx_properties::try_to_add_note (const_rtx x) |
184 | { |
185 | try_to_add_src (x, flags: rtx_obj_flags::IN_NOTE); |
186 | } |
187 | |
188 | /* Return true if the rtx has side effects, in the sense of |
189 | side_effects_p (except for side_effects_p's special handling |
190 | of combine.cc clobbers). */ |
191 | |
192 | inline bool |
193 | rtx_properties::has_side_effects () const |
194 | { |
195 | return has_volatile_refs || has_pre_post_modify || has_call; |
196 | } |
197 | |
198 | /* Return an iterator range for all the references, suitable for |
199 | range-based for loops. */ |
200 | |
201 | inline iterator_range<rtx_obj_reference *> |
202 | rtx_properties::refs () const |
203 | { |
204 | return { ref_begin, ref_iter }; |
205 | } |
206 | |
207 | /* BASE is derived from rtx_properties and provides backing storage |
208 | for REF_BEGIN. It has a grow () method that increases the amount |
209 | of memory available if the initial allocation was too small. */ |
210 | |
211 | template<typename Base> |
212 | class growing_rtx_properties : public Base |
213 | { |
214 | public: |
215 | template<typename... Args> |
216 | growing_rtx_properties (Args...); |
217 | |
218 | template<typename AddFn> |
219 | void repeat (AddFn add); |
220 | |
221 | /* Wrappers around the try_to_* functions that always succeed. */ |
222 | void add_dest (const_rtx x, unsigned int flags = 0); |
223 | void add_src (const_rtx x, unsigned int flags = 0); |
224 | void add_pattern (const_rtx pat); |
225 | void add_note (const_rtx x); |
226 | void add_insn (const rtx_insn *insn, bool include_notes); |
227 | }; |
228 | |
229 | template<typename Base> |
230 | template<typename... Args> |
231 | growing_rtx_properties<Base>::growing_rtx_properties (Args... args) |
232 | : Base (std::forward<Args> (args)...) |
233 | { |
234 | } |
235 | |
236 | /* Perform ADD until there is enough room to hold the result. */ |
237 | |
238 | template<typename Base> |
239 | template<typename AddFn> |
240 | inline void |
241 | growing_rtx_properties<Base>::repeat (AddFn add) |
242 | { |
243 | ptrdiff_t count = this->num_refs (); |
244 | for (;;) |
245 | { |
246 | add (); |
247 | /* This retries if the storage happened to be exactly the right size, |
248 | but that's expected to be a rare case and so isn't worth |
249 | optimizing for. */ |
250 | if (LIKELY (this->ref_iter != this->ref_end)) |
251 | break; |
252 | this->grow (count); |
253 | } |
254 | } |
255 | |
256 | template<typename Base> |
257 | inline void |
258 | growing_rtx_properties<Base>::add_dest (const_rtx x, unsigned int flags) |
259 | { |
260 | repeat ([&]() { this->try_to_add_dest (x, flags); }); |
261 | } |
262 | |
263 | template<typename Base> |
264 | inline void |
265 | growing_rtx_properties<Base>::add_src (const_rtx x, unsigned int flags) |
266 | { |
267 | repeat ([&]() { this->try_to_add_src (x, flags); }); |
268 | } |
269 | |
270 | template<typename Base> |
271 | inline void |
272 | growing_rtx_properties<Base>::add_pattern (const_rtx pat) |
273 | { |
274 | repeat ([&]() { this->try_to_add_pattern (pat); }); |
275 | } |
276 | |
277 | template<typename Base> |
278 | inline void |
279 | growing_rtx_properties<Base>::add_note (const_rtx x) |
280 | { |
281 | repeat ([&]() { this->try_to_add_note (x); }); |
282 | } |
283 | |
284 | template<typename Base> |
285 | inline void |
286 | growing_rtx_properties<Base>::add_insn (const rtx_insn *insn, bool include_notes) |
287 | { |
288 | repeat ([&]() { this->try_to_add_insn (insn, include_notes); }); |
289 | } |
290 | |
291 | /* A base class for vec_rtx_properties; see there for details. */ |
292 | |
293 | class vec_rtx_properties_base : public rtx_properties |
294 | { |
295 | static const size_t SIZE = 32; |
296 | |
297 | public: |
298 | vec_rtx_properties_base (); |
299 | ~vec_rtx_properties_base (); |
300 | |
301 | protected: |
302 | void grow (ptrdiff_t); |
303 | |
304 | private: |
305 | rtx_obj_reference m_storage[SIZE]; |
306 | }; |
307 | |
308 | inline vec_rtx_properties_base::vec_rtx_properties_base () |
309 | { |
310 | ref_begin = ref_iter = m_storage; |
311 | ref_end = m_storage + SIZE; |
312 | } |
313 | |
314 | inline vec_rtx_properties_base::~vec_rtx_properties_base () |
315 | { |
316 | if (UNLIKELY (ref_begin != m_storage)) |
317 | free (ptr: ref_begin); |
318 | } |
319 | |
320 | /* A rtx_properties that stores its references in a temporary array. |
321 | Like auto_vec, the array is initially on the stack, but can switch |
322 | to the heap if necessary. |
323 | |
324 | The reason for implementing this as a derived class is that the |
325 | default on-stack size should be enough for the vast majority of |
326 | expressions and instructions. It's therefore not worth paying |
327 | the cost of conditionally calling grow code at every site that |
328 | records a new reference. Instead, the rtx_properties code can use |
329 | trivial iterator updates for the common case, and in the rare case |
330 | that the vector needs to be resized, we can pay the cost of |
331 | collecting the references a second time. */ |
332 | using vec_rtx_properties = growing_rtx_properties<vec_rtx_properties_base>; |
333 | |
334 | bool |
335 | vec_series_highpart_p (machine_mode result_mode, machine_mode op_mode, |
336 | rtx sel); |
337 | |
338 | bool |
339 | vec_series_lowpart_p (machine_mode result_mode, machine_mode op_mode, rtx sel); |
340 | |
341 | bool |
342 | contains_paradoxical_subreg_p (rtx x); |
343 | #endif |
344 | |