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 #ifndef SOURCE_OPT_FUNCTION_H_
16 #define SOURCE_OPT_FUNCTION_H_
17
18 #include <algorithm>
19 #include <functional>
20 #include <memory>
21 #include <string>
22 #include <unordered_set>
23 #include <utility>
24 #include <vector>
25
26 #include "source/opt/basic_block.h"
27 #include "source/opt/instruction.h"
28 #include "source/opt/iterator.h"
29
30 namespace spvtools {
31 namespace opt {
32
33 class CFG;
34 class IRContext;
35 class Module;
36
37 // A SPIR-V function.
38 class Function {
39 public:
40 using iterator = UptrVectorIterator<BasicBlock>;
41 using const_iterator = UptrVectorIterator<BasicBlock, true>;
42
43 // Creates a function instance declared by the given OpFunction instruction
44 // |def_inst|.
45 inline explicit Function(std::unique_ptr<Instruction> def_inst);
46
47 explicit Function(const Function& f) = delete;
48
49 // Creates a clone of the instruction in the given |context|
50 //
51 // The parent module will default to null and needs to be explicitly set by
52 // the user.
53 Function* Clone(IRContext*) const;
54 // The OpFunction instruction that begins the definition of this function.
DefInst()55 Instruction& DefInst() { return *def_inst_; }
DefInst()56 const Instruction& DefInst() const { return *def_inst_; }
57
58 // Appends a parameter to this function.
59 inline void AddParameter(std::unique_ptr<Instruction> p);
60 // Appends a debug instruction in function header to this function.
61 inline void AddDebugInstructionInHeader(std::unique_ptr<Instruction> p);
62 // Appends a basic block to this function.
63 inline void AddBasicBlock(std::unique_ptr<BasicBlock> b);
64 // Appends a basic block to this function at the position |ip|.
65 inline void AddBasicBlock(std::unique_ptr<BasicBlock> b, iterator ip);
66 template <typename T>
67 inline void AddBasicBlocks(T begin, T end, iterator ip);
68
69 // Move basic block with |id| to the position after |ip|. Both have to be
70 // contained in this function.
71 inline void MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip);
72
73 // Delete all basic blocks that contain no instructions.
74 inline void RemoveEmptyBlocks();
75
76 // Removes a parameter from the function with result id equal to |id|.
77 // Does nothing if the function doesn't have such a parameter.
78 inline void RemoveParameter(uint32_t id);
79
80 // Saves the given function end instruction.
81 inline void SetFunctionEnd(std::unique_ptr<Instruction> end_inst);
82
83 // Add a non-semantic instruction that succeeds this function in the module.
84 // These instructions are maintained in the order they are added.
85 inline void AddNonSemanticInstruction(
86 std::unique_ptr<Instruction> non_semantic);
87
88 // Returns the given function end instruction.
EndInst()89 inline Instruction* EndInst() { return end_inst_.get(); }
EndInst()90 inline const Instruction* EndInst() const { return end_inst_.get(); }
91
92 // Returns function's id
result_id()93 inline uint32_t result_id() const { return def_inst_->result_id(); }
94
95 // Returns function's return type id
type_id()96 inline uint32_t type_id() const { return def_inst_->type_id(); }
97
98 // Returns the function's control mask
control_mask()99 inline uint32_t control_mask() const { return def_inst_->GetSingleWordInOperand(0); }
100
101 // Returns the entry basic block for this function.
entry()102 const std::unique_ptr<BasicBlock>& entry() const { return blocks_.front(); }
103
104 // Returns the last basic block in this function.
tail()105 BasicBlock* tail() { return blocks_.back().get(); }
tail()106 const BasicBlock* tail() const { return blocks_.back().get(); }
107
begin()108 iterator begin() { return iterator(&blocks_, blocks_.begin()); }
end()109 iterator end() { return iterator(&blocks_, blocks_.end()); }
begin()110 const_iterator begin() const { return cbegin(); }
end()111 const_iterator end() const { return cend(); }
cbegin()112 const_iterator cbegin() const {
113 return const_iterator(&blocks_, blocks_.cbegin());
114 }
cend()115 const_iterator cend() const {
116 return const_iterator(&blocks_, blocks_.cend());
117 }
118
119 // Returns an iterator to the basic block |id|.
FindBlock(uint32_t bb_id)120 iterator FindBlock(uint32_t bb_id) {
121 return std::find_if(begin(), end(), [bb_id](const BasicBlock& it_bb) {
122 return bb_id == it_bb.id();
123 });
124 }
125
126 // Runs the given function |f| on instructions in this function, in order,
127 // and optionally on debug line instructions that might precede them and
128 // non-semantic instructions that succceed the function.
129 void ForEachInst(const std::function<void(Instruction*)>& f,
130 bool run_on_debug_line_insts = false,
131 bool run_on_non_semantic_insts = false);
132 void ForEachInst(const std::function<void(const Instruction*)>& f,
133 bool run_on_debug_line_insts = false,
134 bool run_on_non_semantic_insts = false) const;
135 // Runs the given function |f| on instructions in this function, in order,
136 // and optionally on debug line instructions that might precede them and
137 // non-semantic instructions that succeed the function. If |f| returns
138 // false, iteration is terminated and this function returns false.
139 bool WhileEachInst(const std::function<bool(Instruction*)>& f,
140 bool run_on_debug_line_insts = false,
141 bool run_on_non_semantic_insts = false);
142 bool WhileEachInst(const std::function<bool(const Instruction*)>& f,
143 bool run_on_debug_line_insts = false,
144 bool run_on_non_semantic_insts = false) const;
145
146 // Runs the given function |f| on each parameter instruction in this function,
147 // in order, and optionally on debug line instructions that might precede
148 // them.
149 void ForEachParam(const std::function<void(const Instruction*)>& f,
150 bool run_on_debug_line_insts = false) const;
151 void ForEachParam(const std::function<void(Instruction*)>& f,
152 bool run_on_debug_line_insts = false);
153
154 // Runs the given function |f| on each debug instruction in this function's
155 // header in order.
156 void ForEachDebugInstructionsInHeader(
157 const std::function<void(Instruction*)>& f);
158
159 BasicBlock* InsertBasicBlockAfter(std::unique_ptr<BasicBlock>&& new_block,
160 BasicBlock* position);
161
162 BasicBlock* InsertBasicBlockBefore(std::unique_ptr<BasicBlock>&& new_block,
163 BasicBlock* position);
164
165 // Returns true if the function has a return block other than the exit block.
166 bool HasEarlyReturn() const;
167
168 // Returns true if the function calls itself either directly or indirectly.
169 bool IsRecursive() const;
170
171 // Pretty-prints all the basic blocks in this function into a std::string.
172 //
173 // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER
174 // is always added to |options|.
175 std::string PrettyPrint(uint32_t options = 0u) const;
176
177 // Dump this function on stderr. Useful when running interactive
178 // debuggers.
179 void Dump() const;
180
181 // Returns true is a function declaration and not a function definition.
IsDeclaration()182 bool IsDeclaration() { return begin() == end(); }
183
184 // Reorders the basic blocks in the function to match the structured order.
185 void ReorderBasicBlocksInStructuredOrder();
186
187 private:
188 // Reorders the basic blocks in the function to match the order given by the
189 // range |{begin,end}|. The range must contain every basic block in the
190 // function, and no extras.
191 template <class It>
192 void ReorderBasicBlocks(It begin, It end);
193
194 template <class It>
195 bool ContainsAllBlocksInTheFunction(It begin, It end);
196
197 // The OpFunction instruction that begins the definition of this function.
198 std::unique_ptr<Instruction> def_inst_;
199 // All parameters to this function.
200 std::vector<std::unique_ptr<Instruction>> params_;
201 // All debug instructions in this function's header.
202 InstructionList debug_insts_in_header_;
203 // All basic blocks inside this function in specification order
204 std::vector<std::unique_ptr<BasicBlock>> blocks_;
205 // The OpFunctionEnd instruction.
206 std::unique_ptr<Instruction> end_inst_;
207 // Non-semantic instructions succeeded by this function.
208 std::vector<std::unique_ptr<Instruction>> non_semantic_;
209 };
210
211 // Pretty-prints |func| to |str|. Returns |str|.
212 std::ostream& operator<<(std::ostream& str, const Function& func);
213
Function(std::unique_ptr<Instruction> def_inst)214 inline Function::Function(std::unique_ptr<Instruction> def_inst)
215 : def_inst_(std::move(def_inst)), end_inst_() {}
216
AddParameter(std::unique_ptr<Instruction> p)217 inline void Function::AddParameter(std::unique_ptr<Instruction> p) {
218 params_.emplace_back(std::move(p));
219 }
220
AddDebugInstructionInHeader(std::unique_ptr<Instruction> p)221 inline void Function::AddDebugInstructionInHeader(
222 std::unique_ptr<Instruction> p) {
223 debug_insts_in_header_.push_back(std::move(p));
224 }
225
AddBasicBlock(std::unique_ptr<BasicBlock> b)226 inline void Function::AddBasicBlock(std::unique_ptr<BasicBlock> b) {
227 AddBasicBlock(std::move(b), end());
228 }
229
AddBasicBlock(std::unique_ptr<BasicBlock> b,iterator ip)230 inline void Function::AddBasicBlock(std::unique_ptr<BasicBlock> b,
231 iterator ip) {
232 b->SetParent(this);
233 ip.InsertBefore(std::move(b));
234 }
235
236 template <typename T>
AddBasicBlocks(T src_begin,T src_end,iterator ip)237 inline void Function::AddBasicBlocks(T src_begin, T src_end, iterator ip) {
238 blocks_.insert(ip.Get(), std::make_move_iterator(src_begin),
239 std::make_move_iterator(src_end));
240 }
241
MoveBasicBlockToAfter(uint32_t id,BasicBlock * ip)242 inline void Function::MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip) {
243 std::unique_ptr<BasicBlock> block_to_move = std::move(*FindBlock(id).Get());
244 blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr));
245
246 assert(block_to_move->GetParent() == ip->GetParent() &&
247 "Both blocks have to be in the same function.");
248
249 InsertBasicBlockAfter(std::move(block_to_move), ip);
250 }
251
RemoveEmptyBlocks()252 inline void Function::RemoveEmptyBlocks() {
253 auto first_empty =
254 std::remove_if(std::begin(blocks_), std::end(blocks_),
255 [](const std::unique_ptr<BasicBlock>& bb) -> bool {
256 return bb->GetLabelInst()->opcode() == SpvOpNop;
257 });
258 blocks_.erase(first_empty, std::end(blocks_));
259 }
260
RemoveParameter(uint32_t id)261 inline void Function::RemoveParameter(uint32_t id) {
262 params_.erase(std::remove_if(params_.begin(), params_.end(),
263 [id](const std::unique_ptr<Instruction>& param) {
264 return param->result_id() == id;
265 }),
266 params_.end());
267 }
268
SetFunctionEnd(std::unique_ptr<Instruction> end_inst)269 inline void Function::SetFunctionEnd(std::unique_ptr<Instruction> end_inst) {
270 end_inst_ = std::move(end_inst);
271 }
272
AddNonSemanticInstruction(std::unique_ptr<Instruction> non_semantic)273 inline void Function::AddNonSemanticInstruction(
274 std::unique_ptr<Instruction> non_semantic) {
275 non_semantic_.emplace_back(std::move(non_semantic));
276 }
277
278 template <class It>
ReorderBasicBlocks(It begin,It end)279 void Function::ReorderBasicBlocks(It begin, It end) {
280 // Asserts to make sure every node in the function is in new_order.
281 assert(ContainsAllBlocksInTheFunction(begin, end));
282
283 // We have a pointer to all the elements in order, so we can release all
284 // pointers in |block_|, and then create the new unique pointers from |{begin,
285 // end}|.
286 std::for_each(blocks_.begin(), blocks_.end(),
287 [](std::unique_ptr<BasicBlock>& bb) { bb.release(); });
288 std::transform(begin, end, blocks_.begin(), [](BasicBlock* bb) {
289 return std::unique_ptr<BasicBlock>(bb);
290 });
291 }
292
293 template <class It>
ContainsAllBlocksInTheFunction(It begin,It end)294 bool Function::ContainsAllBlocksInTheFunction(It begin, It end) {
295 std::unordered_multiset<BasicBlock*> range(begin, end);
296 if (range.size() != blocks_.size()) {
297 return false;
298 }
299
300 for (auto& bb : blocks_) {
301 if (range.count(bb.get()) == 0) return false;
302 }
303 return true;
304 }
305
306 } // namespace opt
307 } // namespace spvtools
308
309 #endif // SOURCE_OPT_FUNCTION_H_
310