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.
SetParent(Function * function)63 void SetParent(Function* function) { function_ = function; }
64
65 // Return the enclosing function
GetParent()66 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.
GetLabel()75 std::unique_ptr<Instruction>& GetLabel() { return label_; }
76
77 // The label starting this basic block.
GetLabelInst()78 Instruction* GetLabelInst() { return label_.get(); }
GetLabelInst()79 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 instruciton 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
id()92 inline uint32_t id() const { return label_->result_id(); }
93
begin()94 iterator begin() { return insts_.begin(); }
end()95 iterator end() { return insts_.end(); }
begin()96 const_iterator begin() const { return insts_.cbegin(); }
end()97 const_iterator end() const { return insts_.cend(); }
cbegin()98 const_iterator cbegin() const { return insts_.cbegin(); }
cend()99 const_iterator cend() const { return insts_.cend(); }
100
rbegin()101 reverse_iterator rbegin() { return reverse_iterator(end()); }
rend()102 reverse_iterator rend() { return reverse_iterator(begin()); }
rbegin()103 const_reverse_iterator rbegin() const {
104 return const_reverse_iterator(cend());
105 }
rend()106 const_reverse_iterator rend() const {
107 return const_reverse_iterator(cbegin());
108 }
crbegin()109 const_reverse_iterator crbegin() const {
110 return const_reverse_iterator(cend());
111 }
crend()112 const_reverse_iterator crend() const {
113 return const_reverse_iterator(cbegin());
114 }
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.
tail()119 iterator tail() {
120 assert(!insts_.empty());
121 return --end();
122 }
123
124 // Returns a const iterator, but othewrise similar to tail().
ctail()125 const_iterator ctail() const {
126 assert(!insts_.empty());
127 return --insts_.cend();
128 }
129
130 // Returns true if the basic block has at least one successor.
hasSuccessor()131 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.
164 // Modifying the pointed value will change the branch taken by the basic
165 // block. It is the caller responsibility to update or invalidate the CFG.
166 void ForEachSuccessorLabel(const std::function<void(uint32_t*)>& f);
167
168 // Returns true if |block| is a direct successor of |this|.
169 bool IsSuccessor(const BasicBlock* block) const;
170
171 // Runs the given function |f| on the merge and continue label, if any
172 void ForMergeAndContinueLabel(const std::function<void(const uint32_t)>& f);
173
174 // Returns true if this basic block has any Phi instructions.
HasPhiInstructions()175 bool HasPhiInstructions() {
176 return !WhileEachPhiInst([](Instruction*) { return false; });
177 }
178
179 // Return true if this block is a loop header block.
IsLoopHeader()180 bool IsLoopHeader() const { return GetLoopMergeInst() != nullptr; }
181
182 // Returns the ID of the merge block declared by a merge instruction in this
183 // block, if any. If none, returns zero.
184 uint32_t MergeBlockIdIfAny() const;
185
186 // Returns the ID of the continue block declared by a merge instruction in
187 // this block, if any. If none, returns zero.
188 uint32_t ContinueBlockIdIfAny() const;
189
190 // Returns the terminator instruction. Assumes the terminator exists.
terminator()191 Instruction* terminator() { return &*tail(); }
terminator()192 const Instruction* terminator() const { return &*ctail(); }
193
194 // Returns true if this basic block exits this function and returns to its
195 // caller.
IsReturn()196 bool IsReturn() const { return ctail()->IsReturn(); }
197
198 // Returns true if this basic block exits this function or aborts execution.
IsReturnOrAbort()199 bool IsReturnOrAbort() const { return ctail()->IsReturnOrAbort(); }
200
201 // Kill all instructions in this block. Whether or not to kill the label is
202 // indicated by |killLabel|.
203 void KillAllInsts(bool killLabel);
204
205 // Splits this basic block into two. Returns a new basic block with label
206 // |labelId| containing the instructions from |iter| onwards. Instructions
207 // prior to |iter| remain in this basic block. The new block will be added
208 // to the function immediately after the original block.
209 BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id,
210 iterator iter);
211
212 // Pretty-prints this basic block into a std::string by printing every
213 // instruction in it.
214 //
215 // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
216 // is always added to |options|.
217 std::string PrettyPrint(uint32_t options = 0u) const;
218
219 // Dump this basic block on stderr. Useful when running interactive
220 // debuggers.
221 void Dump() const;
222
223 private:
224 // The enclosing function.
225 Function* function_;
226 // The label starting this basic block.
227 std::unique_ptr<Instruction> label_;
228 // Instructions inside this basic block, but not the OpLabel.
229 InstructionList insts_;
230 };
231
232 // Pretty-prints |block| to |str|. Returns |str|.
233 std::ostream& operator<<(std::ostream& str, const BasicBlock& block);
234
BasicBlock(std::unique_ptr<Instruction> label)235 inline BasicBlock::BasicBlock(std::unique_ptr<Instruction> label)
236 : function_(nullptr), label_(std::move(label)) {}
237
AddInstruction(std::unique_ptr<Instruction> i)238 inline void BasicBlock::AddInstruction(std::unique_ptr<Instruction> i) {
239 insts_.push_back(std::move(i));
240 }
241
AddInstructions(BasicBlock * bp)242 inline void BasicBlock::AddInstructions(BasicBlock* bp) {
243 auto bEnd = end();
244 (void)bEnd.MoveBefore(&bp->insts_);
245 }
246
WhileEachInst(const std::function<bool (Instruction *)> & f,bool run_on_debug_line_insts)247 inline bool BasicBlock::WhileEachInst(
248 const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) {
249 if (label_) {
250 if (!label_->WhileEachInst(f, run_on_debug_line_insts)) return false;
251 }
252 if (insts_.empty()) {
253 return true;
254 }
255
256 Instruction* inst = &insts_.front();
257 while (inst != nullptr) {
258 Instruction* next_instruction = inst->NextNode();
259 if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false;
260 inst = next_instruction;
261 }
262 return true;
263 }
264
WhileEachInst(const std::function<bool (const Instruction *)> & f,bool run_on_debug_line_insts)265 inline bool BasicBlock::WhileEachInst(
266 const std::function<bool(const Instruction*)>& f,
267 bool run_on_debug_line_insts) const {
268 if (label_) {
269 if (!static_cast<const Instruction*>(label_.get())
270 ->WhileEachInst(f, run_on_debug_line_insts))
271 return false;
272 }
273 for (const auto& inst : insts_) {
274 if (!static_cast<const Instruction*>(&inst)->WhileEachInst(
275 f, run_on_debug_line_insts))
276 return false;
277 }
278 return true;
279 }
280
ForEachInst(const std::function<void (Instruction *)> & f,bool run_on_debug_line_insts)281 inline void BasicBlock::ForEachInst(const std::function<void(Instruction*)>& f,
282 bool run_on_debug_line_insts) {
283 WhileEachInst(
284 [&f](Instruction* inst) {
285 f(inst);
286 return true;
287 },
288 run_on_debug_line_insts);
289 }
290
ForEachInst(const std::function<void (const Instruction *)> & f,bool run_on_debug_line_insts)291 inline void BasicBlock::ForEachInst(
292 const std::function<void(const Instruction*)>& f,
293 bool run_on_debug_line_insts) const {
294 WhileEachInst(
295 [&f](const Instruction* inst) {
296 f(inst);
297 return true;
298 },
299 run_on_debug_line_insts);
300 }
301
WhileEachPhiInst(const std::function<bool (Instruction *)> & f,bool run_on_debug_line_insts)302 inline bool BasicBlock::WhileEachPhiInst(
303 const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) {
304 if (insts_.empty()) {
305 return true;
306 }
307
308 Instruction* inst = &insts_.front();
309 while (inst != nullptr) {
310 Instruction* next_instruction = inst->NextNode();
311 if (inst->opcode() != SpvOpPhi) break;
312 if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false;
313 inst = next_instruction;
314 }
315 return true;
316 }
317
ForEachPhiInst(const std::function<void (Instruction *)> & f,bool run_on_debug_line_insts)318 inline void BasicBlock::ForEachPhiInst(
319 const std::function<void(Instruction*)>& f, bool run_on_debug_line_insts) {
320 WhileEachPhiInst(
321 [&f](Instruction* inst) {
322 f(inst);
323 return true;
324 },
325 run_on_debug_line_insts);
326 }
327
328 } // namespace opt
329 } // namespace spvtools
330
331 #endif // SOURCE_OPT_BASIC_BLOCK_H_
332