/src/spirv-tools/source/opt/basic_block.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2016 Google Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | |
15 | | // This file defines the language constructs for representing a SPIR-V |
16 | | // module in memory. |
17 | | |
18 | | #ifndef SOURCE_OPT_BASIC_BLOCK_H_ |
19 | | #define SOURCE_OPT_BASIC_BLOCK_H_ |
20 | | |
21 | | #include <functional> |
22 | | #include <iterator> |
23 | | #include <memory> |
24 | | #include <ostream> |
25 | | #include <string> |
26 | | #include <utility> |
27 | | #include <vector> |
28 | | |
29 | | #include "source/opt/instruction.h" |
30 | | #include "source/opt/instruction_list.h" |
31 | | #include "source/opt/iterator.h" |
32 | | |
33 | | namespace spvtools { |
34 | | namespace opt { |
35 | | |
36 | | class Function; |
37 | | class IRContext; |
38 | | |
39 | | // A SPIR-V basic block. |
40 | | class BasicBlock { |
41 | | public: |
42 | | using iterator = InstructionList::iterator; |
43 | | using const_iterator = InstructionList::const_iterator; |
44 | | using reverse_iterator = std::reverse_iterator<InstructionList::iterator>; |
45 | | using const_reverse_iterator = |
46 | | std::reverse_iterator<InstructionList::const_iterator>; |
47 | | |
48 | | // Creates a basic block with the given starting |label|. |
49 | | inline explicit BasicBlock(std::unique_ptr<Instruction> label); |
50 | | |
51 | | explicit BasicBlock(const BasicBlock& bb) = delete; |
52 | | |
53 | | // Creates a clone of the basic block in the given |context| |
54 | | // |
55 | | // The parent function will default to null and needs to be explicitly set by |
56 | | // the user. |
57 | | // |
58 | | // If the inst-to-block map in |context| is valid, then the new instructions |
59 | | // will be inserted into the map. |
60 | | BasicBlock* Clone(IRContext*) const; |
61 | | |
62 | | // Sets the enclosing function for this basic block. |
63 | 2.21M | void SetParent(Function* function) { function_ = function; } |
64 | | |
65 | | // Return the enclosing function |
66 | 4.18M | inline Function* GetParent() const { return function_; } |
67 | | |
68 | | // Appends an instruction to this basic block. |
69 | | inline void AddInstruction(std::unique_ptr<Instruction> i); |
70 | | |
71 | | // Appends all of block's instructions (except label) to this block |
72 | | inline void AddInstructions(BasicBlock* bp); |
73 | | |
74 | | // The pointer to the label starting this basic block. |
75 | 18.1k | std::unique_ptr<Instruction>& GetLabel() { return label_; } |
76 | | |
77 | | // The label starting this basic block. |
78 | 15.7M | Instruction* GetLabelInst() { return label_.get(); } |
79 | 834k | const Instruction* GetLabelInst() const { return label_.get(); } |
80 | | |
81 | | // Returns the merge instruction in this basic block, if it exists. |
82 | | // Otherwise return null. May be used whenever tail() can be used. |
83 | | const Instruction* GetMergeInst() const; |
84 | | Instruction* GetMergeInst(); |
85 | | |
86 | | // Returns the OpLoopMerge instruction in this basic block, if it exists. |
87 | | // Otherwise return null. May be used whenever tail() can be used. |
88 | | const Instruction* GetLoopMergeInst() const; |
89 | | Instruction* GetLoopMergeInst(); |
90 | | |
91 | | // Returns the id of the label at the top of this block |
92 | 205M | inline uint32_t id() const { return label_->result_id(); } |
93 | | |
94 | 56.8M | iterator begin() { return insts_.begin(); } |
95 | 155M | iterator end() { return insts_.end(); } |
96 | 463k | const_iterator begin() const { return insts_.cbegin(); } |
97 | 463k | const_iterator end() const { return insts_.cend(); } |
98 | 49.2M | const_iterator cbegin() const { return insts_.cbegin(); } |
99 | 26.0M | const_iterator cend() const { return insts_.cend(); } |
100 | | |
101 | 0 | reverse_iterator rbegin() { return reverse_iterator(end()); } |
102 | 0 | reverse_iterator rend() { return reverse_iterator(begin()); } |
103 | 0 | const_reverse_iterator rbegin() const { |
104 | 0 | return const_reverse_iterator(cend()); |
105 | 0 | } |
106 | 0 | const_reverse_iterator rend() const { |
107 | 0 | return const_reverse_iterator(cbegin()); |
108 | 0 | } |
109 | 0 | const_reverse_iterator crbegin() const { |
110 | 0 | return const_reverse_iterator(cend()); |
111 | 0 | } |
112 | 0 | const_reverse_iterator crend() const { |
113 | 0 | return const_reverse_iterator(cbegin()); |
114 | 0 | } |
115 | | |
116 | | // Returns an iterator pointing to the last instruction. This may only |
117 | | // be used if this block has an instruction other than the OpLabel |
118 | | // that defines it. |
119 | 51.2M | iterator tail() { |
120 | 51.2M | assert(!insts_.empty()); |
121 | 51.2M | return --end(); |
122 | 51.2M | } |
123 | | |
124 | | // Returns a const iterator, but othewrise similar to tail(). |
125 | 24.2M | const_iterator ctail() const { |
126 | 24.2M | assert(!insts_.empty()); |
127 | 24.2M | return --insts_.cend(); |
128 | 24.2M | } |
129 | | |
130 | | // Returns true if the basic block has at least one successor. |
131 | 0 | inline bool hasSuccessor() const { return ctail()->IsBranch(); } |
132 | | |
133 | | // Runs the given function |f| on each instruction in this basic block, and |
134 | | // optionally on the debug line instructions that might precede them. |
135 | | inline void ForEachInst(const std::function<void(Instruction*)>& f, |
136 | | bool run_on_debug_line_insts = false); |
137 | | inline void ForEachInst(const std::function<void(const Instruction*)>& f, |
138 | | bool run_on_debug_line_insts = false) const; |
139 | | |
140 | | // Runs the given function |f| on each instruction in this basic block, and |
141 | | // optionally on the debug line instructions that might precede them. If |f| |
142 | | // returns false, iteration is terminated and this function returns false. |
143 | | inline bool WhileEachInst(const std::function<bool(Instruction*)>& f, |
144 | | bool run_on_debug_line_insts = false); |
145 | | inline bool WhileEachInst(const std::function<bool(const Instruction*)>& f, |
146 | | bool run_on_debug_line_insts = false) const; |
147 | | |
148 | | // Runs the given function |f| on each Phi instruction in this basic block, |
149 | | // and optionally on the debug line instructions that might precede them. |
150 | | inline void ForEachPhiInst(const std::function<void(Instruction*)>& f, |
151 | | bool run_on_debug_line_insts = false); |
152 | | |
153 | | // Runs the given function |f| on each Phi instruction in this basic block, |
154 | | // and optionally on the debug line instructions that might precede them. If |
155 | | // |f| returns false, iteration is terminated and this function return false. |
156 | | inline bool WhileEachPhiInst(const std::function<bool(Instruction*)>& f, |
157 | | bool run_on_debug_line_insts = false); |
158 | | |
159 | | // Runs the given function |f| on each label id of each successor block |
160 | | void ForEachSuccessorLabel( |
161 | | const std::function<void(const uint32_t)>& f) const; |
162 | | |
163 | | // Runs the given function |f| on each label id of each successor block. If |
164 | | // |f| returns false, iteration is terminated and this function returns false. |
165 | | bool WhileEachSuccessorLabel( |
166 | | const std::function<bool(const uint32_t)>& f) const; |
167 | | |
168 | | // Runs the given function |f| on each label id of each successor block. |
169 | | // Modifying the pointed value will change the branch taken by the basic |
170 | | // block. It is the caller responsibility to update or invalidate the CFG. |
171 | | void ForEachSuccessorLabel(const std::function<void(uint32_t*)>& f); |
172 | | |
173 | | // Returns true if |block| is a direct successor of |this|. |
174 | | bool IsSuccessor(const BasicBlock* block) const; |
175 | | |
176 | | // Runs the given function |f| on the merge and continue label, if any |
177 | | void ForMergeAndContinueLabel(const std::function<void(const uint32_t)>& f); |
178 | | |
179 | | // Returns true if this basic block has any Phi instructions. |
180 | 0 | bool HasPhiInstructions() { |
181 | 0 | return !WhileEachPhiInst([](Instruction*) { return false; }); |
182 | 0 | } |
183 | | |
184 | | // Return true if this block is a loop header block. |
185 | 23.6M | bool IsLoopHeader() const { return GetLoopMergeInst() != nullptr; } |
186 | | |
187 | | // Returns the ID of the merge block declared by a merge instruction in this |
188 | | // block, if any. If none, returns zero. |
189 | | uint32_t MergeBlockIdIfAny() const; |
190 | | |
191 | | // Returns MergeBlockIdIfAny() and asserts that it is non-zero. |
192 | | uint32_t MergeBlockId() const; |
193 | | |
194 | | // Returns the ID of the continue block declared by a merge instruction in |
195 | | // this block, if any. If none, returns zero. |
196 | | uint32_t ContinueBlockIdIfAny() const; |
197 | | |
198 | | // Returns ContinueBlockIdIfAny() and asserts that it is non-zero. |
199 | | uint32_t ContinueBlockId() const; |
200 | | |
201 | | // Returns the terminator instruction. Assumes the terminator exists. |
202 | 20.6M | Instruction* terminator() { return &*tail(); } |
203 | 0 | const Instruction* terminator() const { return &*ctail(); } |
204 | | |
205 | | // Returns true if this basic block exits this function and returns to its |
206 | | // caller. |
207 | 0 | bool IsReturn() const { return ctail()->IsReturn(); } |
208 | | |
209 | | // Returns true if this basic block exits this function or aborts execution. |
210 | 364k | bool IsReturnOrAbort() const { return ctail()->IsReturnOrAbort(); } |
211 | | |
212 | | // Kill all instructions in this block. Whether or not to kill the label is |
213 | | // indicated by |killLabel|. |
214 | | void KillAllInsts(bool killLabel); |
215 | | |
216 | | // Splits this basic block into two. Returns a new basic block with label |
217 | | // |label_id| containing the instructions from |iter| onwards. Instructions |
218 | | // prior to |iter| remain in this basic block. The new block will be added |
219 | | // to the function immediately after the original block. |
220 | | BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id, |
221 | | iterator iter); |
222 | | |
223 | | // Pretty-prints this basic block into a std::string by printing every |
224 | | // instruction in it. |
225 | | // |
226 | | // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
227 | | // is always added to |options|. |
228 | | std::string PrettyPrint(uint32_t options = 0u) const; |
229 | | |
230 | | // Dump this basic block on stderr. Useful when running interactive |
231 | | // debuggers. |
232 | | void Dump() const; |
233 | | |
234 | | private: |
235 | | // The enclosing function. |
236 | | Function* function_; |
237 | | // The label starting this basic block. |
238 | | std::unique_ptr<Instruction> label_; |
239 | | // Instructions inside this basic block, but not the OpLabel. |
240 | | InstructionList insts_; |
241 | | }; |
242 | | |
243 | | // Pretty-prints |block| to |str|. Returns |str|. |
244 | | std::ostream& operator<<(std::ostream& str, const BasicBlock& block); |
245 | | |
246 | | inline BasicBlock::BasicBlock(std::unique_ptr<Instruction> label) |
247 | 1.78M | : function_(nullptr), label_(std::move(label)) {} |
248 | | |
249 | 8.99M | inline void BasicBlock::AddInstruction(std::unique_ptr<Instruction> i) { |
250 | 8.99M | insts_.push_back(std::move(i)); |
251 | 8.99M | } |
252 | | |
253 | 459k | inline void BasicBlock::AddInstructions(BasicBlock* bp) { |
254 | 459k | auto bEnd = end(); |
255 | 459k | (void)bEnd.MoveBefore(&bp->insts_); |
256 | 459k | } |
257 | | |
258 | | inline bool BasicBlock::WhileEachInst( |
259 | 68.0M | const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) { |
260 | 68.0M | if (label_) { |
261 | 68.0M | if (!label_->WhileEachInst(f, run_on_debug_line_insts)) return false; |
262 | 68.0M | } |
263 | 68.0M | if (insts_.empty()) { |
264 | 9.71k | return true; |
265 | 9.71k | } |
266 | | |
267 | 68.0M | Instruction* inst = &insts_.front(); |
268 | 338M | while (inst != nullptr) { |
269 | 270M | Instruction* next_instruction = inst->NextNode(); |
270 | 270M | if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false; |
271 | 270M | inst = next_instruction; |
272 | 270M | } |
273 | 68.0M | return true; |
274 | 68.0M | } |
275 | | |
276 | | inline bool BasicBlock::WhileEachInst( |
277 | | const std::function<bool(const Instruction*)>& f, |
278 | 1.28M | bool run_on_debug_line_insts) const { |
279 | 1.28M | if (label_) { |
280 | 1.28M | if (!static_cast<const Instruction*>(label_.get()) |
281 | 1.28M | ->WhileEachInst(f, run_on_debug_line_insts)) |
282 | 0 | return false; |
283 | 1.28M | } |
284 | 4.31M | for (const auto& inst : insts_) { |
285 | 4.31M | if (!static_cast<const Instruction*>(&inst)->WhileEachInst( |
286 | 4.31M | f, run_on_debug_line_insts)) |
287 | 0 | return false; |
288 | 4.31M | } |
289 | 1.28M | return true; |
290 | 1.28M | } |
291 | | |
292 | | inline void BasicBlock::ForEachInst(const std::function<void(Instruction*)>& f, |
293 | 6.19M | bool run_on_debug_line_insts) { |
294 | 6.19M | WhileEachInst( |
295 | 33.7M | [&f](Instruction* inst) { |
296 | 33.7M | f(inst); |
297 | 33.7M | return true; |
298 | 33.7M | }, |
299 | 6.19M | run_on_debug_line_insts); |
300 | 6.19M | } |
301 | | |
302 | | inline void BasicBlock::ForEachInst( |
303 | | const std::function<void(const Instruction*)>& f, |
304 | 0 | bool run_on_debug_line_insts) const { |
305 | 0 | WhileEachInst( |
306 | 0 | [&f](const Instruction* inst) { |
307 | 0 | f(inst); |
308 | 0 | return true; |
309 | 0 | }, |
310 | 0 | run_on_debug_line_insts); |
311 | 0 | } |
312 | | |
313 | | inline bool BasicBlock::WhileEachPhiInst( |
314 | 3.63M | const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) { |
315 | 3.63M | if (insts_.empty()) { |
316 | 2 | return true; |
317 | 2 | } |
318 | | |
319 | 3.63M | Instruction* inst = &insts_.front(); |
320 | 4.38M | while (inst != nullptr) { |
321 | 4.38M | Instruction* next_instruction = inst->NextNode(); |
322 | 4.38M | if (inst->opcode() != spv::Op::OpPhi) break; |
323 | 751k | if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false; |
324 | 751k | inst = next_instruction; |
325 | 751k | } |
326 | 3.63M | return true; |
327 | 3.63M | } |
328 | | |
329 | | inline void BasicBlock::ForEachPhiInst( |
330 | 3.63M | const std::function<void(Instruction*)>& f, bool run_on_debug_line_insts) { |
331 | 3.63M | WhileEachPhiInst( |
332 | 3.63M | [&f](Instruction* inst) { |
333 | 751k | f(inst); |
334 | 751k | return true; |
335 | 751k | }, |
336 | 3.63M | run_on_debug_line_insts); |
337 | 3.63M | } |
338 | | |
339 | | } // namespace opt |
340 | | } // namespace spvtools |
341 | | |
342 | | #endif // SOURCE_OPT_BASIC_BLOCK_H_ |