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