1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | #ifndef _LIR_H_ |
6 | #define _LIR_H_ |
7 | |
8 | class Compiler; |
9 | struct GenTree; |
10 | struct BasicBlock; |
11 | class Rationalizer; |
12 | |
13 | class LIR final |
14 | { |
15 | public: |
16 | class Range; |
17 | |
18 | //------------------------------------------------------------------------ |
19 | // LIR::Flags: Defines the set of flags that may appear in the |
20 | // GenTree::gtLIRFlags field. |
21 | class Flags final |
22 | { |
23 | // Disallow the creation of values of this type. |
24 | Flags() = delete; |
25 | |
26 | public: |
27 | enum : unsigned char |
28 | { |
29 | None = 0x00, |
30 | |
31 | Mark = 0x01, // An aribtrary "mark" bit that can be used in place of |
32 | // a more expensive data structure when processing a set |
33 | // of LIR nodes. See for example `LIR::GetTreeRange`. |
34 | |
35 | UnusedValue = 0x02, // Set on a node if it produces a value that is not |
36 | // subsequently used. Should never be set on nodes |
37 | // that return `false` for `GenTree::IsValue`. Note |
38 | // that this bit should not be assumed to be valid |
39 | // at all points during compilation: it is currently |
40 | // only computed during target-dependent lowering. |
41 | |
42 | RegOptional = 0x04, // Set on a node if it produces a value, but does not |
43 | // require a register (i.e. it can be used from memory). |
44 | }; |
45 | }; |
46 | |
47 | //------------------------------------------------------------------------ |
48 | // LIR::Use: Represents a use <-> def edge between two nodes in a range |
49 | // of LIR. Provides utilities to point the use to a different |
50 | // def. Note that because this type deals in edges between |
51 | // nodes, it represents the single use of the def. |
52 | // |
53 | class Use final |
54 | { |
55 | private: |
56 | Range* m_range; |
57 | GenTree** m_edge; |
58 | GenTree* m_user; |
59 | |
60 | public: |
61 | Use(); |
62 | Use(const Use& other); |
63 | Use(Range& range, GenTree** edge, GenTree* user); |
64 | |
65 | Use& operator=(const Use& other); |
66 | Use& operator=(Use&& other); |
67 | |
68 | static Use GetDummyUse(Range& range, GenTree* node); |
69 | |
70 | GenTree* Def() const; |
71 | GenTree* User() const; |
72 | |
73 | bool IsInitialized() const; |
74 | void AssertIsValid() const; |
75 | bool IsDummyUse() const; |
76 | |
77 | void ReplaceWith(Compiler* compiler, GenTree* replacement); |
78 | unsigned ReplaceWithLclVar(Compiler* compiler, unsigned blockWeight, unsigned lclNum = BAD_VAR_NUM); |
79 | }; |
80 | |
81 | //------------------------------------------------------------------------ |
82 | // LIR::ReadOnlyRange: |
83 | // |
84 | // Represents a contiguous range of LIR nodes that may be a subrange of |
85 | // a containing range. Provides a small set of utilities for iteration. |
86 | // Instances of this type are primarily created by and provided to |
87 | // analysis and utility methods on LIR::Range. |
88 | // |
89 | // Although some pains have been taken to help guard against the existence |
90 | // of invalid subranges, it remains possible to create them. For example, |
91 | // consider the following: |
92 | // |
93 | // // View the block as a range |
94 | // LIR::Range& blockRange = LIR::AsRange(block); |
95 | // |
96 | // // Create a range from the first non-phi node in the block to the |
97 | // // last node in the block |
98 | // LIR::ReadOnlyRange nonPhis = blockRange.NonPhiNodes(); |
99 | // |
100 | // // Remove the last node from the block |
101 | // blockRange.Remove(blockRange.LastNode()); |
102 | // |
103 | // After the removal of the last node in the block, the last node of |
104 | // nonPhis is no longer linked to any of the other nodes in nonPhis. Due |
105 | // to issues such as the above, some care must be taken in order to |
106 | // ensure that ranges are not used once they have been invalidated. |
107 | // |
108 | class ReadOnlyRange |
109 | { |
110 | friend class LIR; |
111 | friend class Range; |
112 | friend struct BasicBlock; |
113 | |
114 | private: |
115 | GenTree* m_firstNode; |
116 | GenTree* m_lastNode; |
117 | |
118 | ReadOnlyRange(const ReadOnlyRange& other) = delete; |
119 | ReadOnlyRange& operator=(const ReadOnlyRange& other) = delete; |
120 | |
121 | public: |
122 | ReadOnlyRange(GenTree* firstNode, GenTree* lastNode); |
123 | |
124 | class Iterator |
125 | { |
126 | friend class ReadOnlyRange; |
127 | |
128 | GenTree* m_node; |
129 | |
130 | Iterator(GenTree* begin) : m_node(begin) |
131 | { |
132 | } |
133 | |
134 | public: |
135 | Iterator() : m_node(nullptr) |
136 | { |
137 | } |
138 | |
139 | inline GenTree* operator*() |
140 | { |
141 | return m_node; |
142 | } |
143 | |
144 | inline GenTree* operator->() |
145 | { |
146 | return m_node; |
147 | } |
148 | |
149 | inline bool operator==(const Iterator& other) const |
150 | { |
151 | return m_node == other.m_node; |
152 | } |
153 | |
154 | inline bool operator!=(const Iterator& other) const |
155 | { |
156 | return m_node != other.m_node; |
157 | } |
158 | |
159 | inline Iterator& operator++() |
160 | { |
161 | m_node = (m_node == nullptr) ? nullptr : m_node->gtNext; |
162 | return *this; |
163 | } |
164 | }; |
165 | |
166 | class ReverseIterator |
167 | { |
168 | friend class ReadOnlyRange; |
169 | |
170 | GenTree* m_node; |
171 | |
172 | ReverseIterator(GenTree* begin) : m_node(begin) |
173 | { |
174 | } |
175 | |
176 | public: |
177 | ReverseIterator() : m_node(nullptr) |
178 | { |
179 | } |
180 | |
181 | inline GenTree* operator*() |
182 | { |
183 | return m_node; |
184 | } |
185 | |
186 | inline GenTree* operator->() |
187 | { |
188 | return m_node; |
189 | } |
190 | |
191 | inline bool operator==(const ReverseIterator& other) const |
192 | { |
193 | return m_node == other.m_node; |
194 | } |
195 | |
196 | inline bool operator!=(const ReverseIterator& other) const |
197 | { |
198 | return m_node != other.m_node; |
199 | } |
200 | |
201 | inline ReverseIterator& operator++() |
202 | { |
203 | m_node = (m_node == nullptr) ? nullptr : m_node->gtPrev; |
204 | return *this; |
205 | } |
206 | }; |
207 | |
208 | ReadOnlyRange(); |
209 | ReadOnlyRange(ReadOnlyRange&& other); |
210 | |
211 | GenTree* FirstNode() const; |
212 | GenTree* LastNode() const; |
213 | |
214 | bool IsEmpty() const; |
215 | |
216 | Iterator begin() const; |
217 | Iterator end() const; |
218 | |
219 | ReverseIterator rbegin() const; |
220 | ReverseIterator rend() const; |
221 | |
222 | #ifdef DEBUG |
223 | bool Contains(GenTree* node) const; |
224 | #endif |
225 | }; |
226 | |
227 | //------------------------------------------------------------------------ |
228 | // LIR::Range: |
229 | // |
230 | // Represents a contiguous range of LIR nodes. Provides a variety of |
231 | // variety of utilites that modify the LIR contained in the range. Unlike |
232 | // `ReadOnlyRange`, values of this type may be edited. |
233 | // |
234 | // Because it is not a final class, it is possible to slice values of this |
235 | // type; this is especially dangerous when the Range value is actually of |
236 | // type `BasicBlock`. As a result, this type is not copyable and it is |
237 | // not possible to view a `BasicBlock` as anything other than a `Range&`. |
238 | // |
239 | class Range : public ReadOnlyRange |
240 | { |
241 | friend class LIR; |
242 | friend struct BasicBlock; |
243 | friend class Rationalizer; |
244 | |
245 | private: |
246 | Range(GenTree* firstNode, GenTree* lastNode); |
247 | |
248 | Range(const Range& other) = delete; |
249 | Range& operator=(const Range& other) = delete; |
250 | |
251 | ReadOnlyRange GetMarkedRange(unsigned markCount, GenTree* start, bool* isClosed, unsigned* sideEffects) const; |
252 | |
253 | void FinishInsertBefore(GenTree* insertionPoint, GenTree* first, GenTree* last); |
254 | void FinishInsertAfter(GenTree* insertionPoint, GenTree* first, GenTree* last); |
255 | |
256 | public: |
257 | Range(); |
258 | Range(Range&& other); |
259 | |
260 | GenTree* LastPhiNode() const; |
261 | GenTree* FirstNonPhiNode() const; |
262 | GenTree* FirstNonPhiOrCatchArgNode() const; |
263 | |
264 | ReadOnlyRange PhiNodes() const; |
265 | ReadOnlyRange NonPhiNodes() const; |
266 | |
267 | void InsertBefore(GenTree* insertionPoint, GenTree* node); |
268 | void InsertAfter(GenTree* insertionPoint, GenTree* node); |
269 | |
270 | void InsertBefore(GenTree* insertionPoint, GenTree* node1, GenTree* node2); |
271 | void InsertBefore(GenTree* insertionPoint, GenTree* node1, GenTree* node2, GenTree* node3); |
272 | void InsertBefore(GenTree* insertionPoint, GenTree* node1, GenTree* node2, GenTree* node3, GenTree* node4); |
273 | |
274 | void InsertAfter(GenTree* insertionPoint, GenTree* node1, GenTree* node2); |
275 | void InsertAfter(GenTree* insertionPoint, GenTree* node1, GenTree* node2, GenTree* node3); |
276 | void InsertAfter(GenTree* insertionPoint, GenTree* node1, GenTree* node2, GenTree* node3, GenTree* node4); |
277 | |
278 | void InsertBefore(GenTree* insertionPoint, Range&& range); |
279 | void InsertAfter(GenTree* insertionPoint, Range&& range); |
280 | |
281 | void InsertAtBeginning(GenTree* node); |
282 | void InsertAtEnd(GenTree* node); |
283 | |
284 | void InsertAtBeginning(Range&& range); |
285 | void InsertAtEnd(Range&& range); |
286 | |
287 | void Remove(GenTree* node, bool markOperandsUnused = false); |
288 | Range Remove(GenTree* firstNode, GenTree* lastNode); |
289 | Range Remove(ReadOnlyRange&& range); |
290 | |
291 | void Delete(Compiler* compiler, BasicBlock* block, GenTree* node); |
292 | void Delete(Compiler* compiler, BasicBlock* block, GenTree* firstNode, GenTree* lastNode); |
293 | void Delete(Compiler* compiler, BasicBlock* block, ReadOnlyRange&& range); |
294 | |
295 | bool TryGetUse(GenTree* node, Use* use); |
296 | |
297 | ReadOnlyRange GetTreeRange(GenTree* root, bool* isClosed) const; |
298 | ReadOnlyRange GetTreeRange(GenTree* root, bool* isClosed, unsigned* sideEffects) const; |
299 | ReadOnlyRange GetRangeOfOperandTrees(GenTree* root, bool* isClosed, unsigned* sideEffects) const; |
300 | |
301 | #ifdef DEBUG |
302 | bool CheckLIR(Compiler* compiler, bool checkUnusedValues = false) const; |
303 | #endif |
304 | }; |
305 | |
306 | public: |
307 | static Range& AsRange(BasicBlock* block); |
308 | |
309 | static Range EmptyRange(); |
310 | static Range SeqTree(Compiler* compiler, GenTree* tree); |
311 | |
312 | static void InsertBeforeTerminator(BasicBlock* block, LIR::Range&& range); |
313 | }; |
314 | |
315 | inline void GenTree::SetUnusedValue() |
316 | { |
317 | gtLIRFlags |= LIR::Flags::UnusedValue; |
318 | ClearContained(); |
319 | } |
320 | |
321 | inline void GenTree::ClearUnusedValue() |
322 | { |
323 | gtLIRFlags &= ~LIR::Flags::UnusedValue; |
324 | } |
325 | |
326 | inline bool GenTree::IsUnusedValue() const |
327 | { |
328 | return (gtLIRFlags & LIR::Flags::UnusedValue) != 0; |
329 | } |
330 | |
331 | inline void GenTree::SetRegOptional() |
332 | { |
333 | gtLIRFlags |= LIR::Flags::RegOptional; |
334 | } |
335 | |
336 | inline void GenTree::ClearRegOptional() |
337 | { |
338 | gtLIRFlags &= ~LIR::Flags::RegOptional; |
339 | } |
340 | |
341 | inline bool GenTree::IsRegOptional() const |
342 | { |
343 | return (gtLIRFlags & LIR::Flags::RegOptional) != 0; |
344 | } |
345 | |
346 | #endif // _LIR_H_ |
347 | |