1 // Copyright (c) 2015-2016 The Khronos Group 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 #include "source/val/function.h"
16
17 #include <cassert>
18
19 #include <algorithm>
20 #include <sstream>
21 #include <unordered_map>
22 #include <unordered_set>
23 #include <utility>
24
25 #include "source/cfa.h"
26 #include "source/val/basic_block.h"
27 #include "source/val/construct.h"
28 #include "source/val/validate.h"
29
30 namespace spvtools {
31 namespace val {
32
33 // Universal Limit of ResultID + 1
34 static const uint32_t kInvalidId = 0x400000;
35
Function(uint32_t function_id,uint32_t result_type_id,SpvFunctionControlMask function_control,uint32_t function_type_id)36 Function::Function(uint32_t function_id, uint32_t result_type_id,
37 SpvFunctionControlMask function_control,
38 uint32_t function_type_id)
39 : id_(function_id),
40 function_type_id_(function_type_id),
41 result_type_id_(result_type_id),
42 function_control_(function_control),
43 declaration_type_(FunctionDecl::kFunctionDeclUnknown),
44 end_has_been_registered_(false),
45 blocks_(),
46 current_block_(nullptr),
47 pseudo_entry_block_(0),
48 pseudo_exit_block_(kInvalidId),
49 cfg_constructs_(),
50 variable_ids_(),
51 parameter_ids_() {}
52
IsFirstBlock(uint32_t block_id) const53 bool Function::IsFirstBlock(uint32_t block_id) const {
54 return !ordered_blocks_.empty() && *first_block() == block_id;
55 }
56
RegisterFunctionParameter(uint32_t parameter_id,uint32_t type_id)57 spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id,
58 uint32_t type_id) {
59 assert(current_block_ == nullptr &&
60 "RegisterFunctionParameter can only be called when parsing the binary "
61 "ouside of a block");
62 // TODO(umar): Validate function parameter type order and count
63 // TODO(umar): Use these variables to validate parameter type
64 (void)parameter_id;
65 (void)type_id;
66 return SPV_SUCCESS;
67 }
68
RegisterLoopMerge(uint32_t merge_id,uint32_t continue_id)69 spv_result_t Function::RegisterLoopMerge(uint32_t merge_id,
70 uint32_t continue_id) {
71 RegisterBlock(merge_id, false);
72 RegisterBlock(continue_id, false);
73 BasicBlock& merge_block = blocks_.at(merge_id);
74 BasicBlock& continue_target_block = blocks_.at(continue_id);
75 assert(current_block_ &&
76 "RegisterLoopMerge must be called when called within a block");
77
78 current_block_->set_type(kBlockTypeLoop);
79 merge_block.set_type(kBlockTypeMerge);
80 continue_target_block.set_type(kBlockTypeContinue);
81 Construct& loop_construct =
82 AddConstruct({ConstructType::kLoop, current_block_, &merge_block});
83 Construct& continue_construct =
84 AddConstruct({ConstructType::kContinue, &continue_target_block});
85
86 continue_construct.set_corresponding_constructs({&loop_construct});
87 loop_construct.set_corresponding_constructs({&continue_construct});
88 merge_block_header_[&merge_block] = current_block_;
89
90 return SPV_SUCCESS;
91 }
92
RegisterSelectionMerge(uint32_t merge_id)93 spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
94 RegisterBlock(merge_id, false);
95 BasicBlock& merge_block = blocks_.at(merge_id);
96 current_block_->set_type(kBlockTypeHeader);
97 merge_block.set_type(kBlockTypeMerge);
98 merge_block_header_[&merge_block] = current_block_;
99
100 AddConstruct({ConstructType::kSelection, current_block(), &merge_block});
101
102 return SPV_SUCCESS;
103 }
104
RegisterSetFunctionDeclType(FunctionDecl type)105 spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) {
106 assert(declaration_type_ == FunctionDecl::kFunctionDeclUnknown);
107 declaration_type_ = type;
108 return SPV_SUCCESS;
109 }
110
RegisterBlock(uint32_t block_id,bool is_definition)111 spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
112 assert(
113 declaration_type_ == FunctionDecl::kFunctionDeclDefinition &&
114 "RegisterBlocks can only be called after declaration_type_ is defined");
115
116 std::unordered_map<uint32_t, BasicBlock>::iterator inserted_block;
117 bool success = false;
118 tie(inserted_block, success) =
119 blocks_.insert({block_id, BasicBlock(block_id)});
120 if (is_definition) { // new block definition
121 assert(current_block_ == nullptr &&
122 "Register Block can only be called when parsing a binary outside of "
123 "a BasicBlock");
124
125 undefined_blocks_.erase(block_id);
126 current_block_ = &inserted_block->second;
127 ordered_blocks_.push_back(current_block_);
128 if (IsFirstBlock(block_id)) current_block_->set_reachable(true);
129 } else if (success) { // Block doesn't exsist but this is not a definition
130 undefined_blocks_.insert(block_id);
131 }
132
133 return SPV_SUCCESS;
134 }
135
RegisterBlockEnd(std::vector<uint32_t> next_list,SpvOp branch_instruction)136 void Function::RegisterBlockEnd(std::vector<uint32_t> next_list,
137 SpvOp branch_instruction) {
138 assert(
139 current_block_ &&
140 "RegisterBlockEnd can only be called when parsing a binary in a block");
141 std::vector<BasicBlock*> next_blocks;
142 next_blocks.reserve(next_list.size());
143
144 std::unordered_map<uint32_t, BasicBlock>::iterator inserted_block;
145 bool success;
146 for (uint32_t successor_id : next_list) {
147 tie(inserted_block, success) =
148 blocks_.insert({successor_id, BasicBlock(successor_id)});
149 if (success) {
150 undefined_blocks_.insert(successor_id);
151 }
152 next_blocks.push_back(&inserted_block->second);
153 }
154
155 if (current_block_->is_type(kBlockTypeLoop)) {
156 // For each loop header, record the set of its successors, and include
157 // its continue target if the continue target is not the loop header
158 // itself.
159 std::vector<BasicBlock*>& next_blocks_plus_continue_target =
160 loop_header_successors_plus_continue_target_map_[current_block_];
161 next_blocks_plus_continue_target = next_blocks;
162 auto continue_target =
163 FindConstructForEntryBlock(current_block_, ConstructType::kLoop)
164 .corresponding_constructs()
165 .back()
166 ->entry_block();
167 if (continue_target != current_block_) {
168 next_blocks_plus_continue_target.push_back(continue_target);
169 }
170 }
171
172 current_block_->RegisterBranchInstruction(branch_instruction);
173 current_block_->RegisterSuccessors(next_blocks);
174 current_block_ = nullptr;
175 return;
176 }
177
RegisterFunctionEnd()178 void Function::RegisterFunctionEnd() {
179 if (!end_has_been_registered_) {
180 end_has_been_registered_ = true;
181
182 ComputeAugmentedCFG();
183 }
184 }
185
block_count() const186 size_t Function::block_count() const { return blocks_.size(); }
187
undefined_block_count() const188 size_t Function::undefined_block_count() const {
189 return undefined_blocks_.size();
190 }
191
ordered_blocks() const192 const std::vector<BasicBlock*>& Function::ordered_blocks() const {
193 return ordered_blocks_;
194 }
ordered_blocks()195 std::vector<BasicBlock*>& Function::ordered_blocks() { return ordered_blocks_; }
196
current_block() const197 const BasicBlock* Function::current_block() const { return current_block_; }
current_block()198 BasicBlock* Function::current_block() { return current_block_; }
199
constructs() const200 const std::list<Construct>& Function::constructs() const {
201 return cfg_constructs_;
202 }
constructs()203 std::list<Construct>& Function::constructs() { return cfg_constructs_; }
204
first_block() const205 const BasicBlock* Function::first_block() const {
206 if (ordered_blocks_.empty()) return nullptr;
207 return ordered_blocks_[0];
208 }
first_block()209 BasicBlock* Function::first_block() {
210 if (ordered_blocks_.empty()) return nullptr;
211 return ordered_blocks_[0];
212 }
213
IsBlockType(uint32_t merge_block_id,BlockType type) const214 bool Function::IsBlockType(uint32_t merge_block_id, BlockType type) const {
215 bool ret = false;
216 const BasicBlock* block;
217 std::tie(block, std::ignore) = GetBlock(merge_block_id);
218 if (block) {
219 ret = block->is_type(type);
220 }
221 return ret;
222 }
223
GetBlock(uint32_t block_id) const224 std::pair<const BasicBlock*, bool> Function::GetBlock(uint32_t block_id) const {
225 const auto b = blocks_.find(block_id);
226 if (b != end(blocks_)) {
227 const BasicBlock* block = &(b->second);
228 bool defined =
229 undefined_blocks_.find(block->id()) == std::end(undefined_blocks_);
230 return std::make_pair(block, defined);
231 } else {
232 return std::make_pair(nullptr, false);
233 }
234 }
235
GetBlock(uint32_t block_id)236 std::pair<BasicBlock*, bool> Function::GetBlock(uint32_t block_id) {
237 const BasicBlock* out;
238 bool defined;
239 std::tie(out, defined) =
240 const_cast<const Function*>(this)->GetBlock(block_id);
241 return std::make_pair(const_cast<BasicBlock*>(out), defined);
242 }
243
AugmentedCFGSuccessorsFunction() const244 Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const {
245 return [this](const BasicBlock* block) {
246 auto where = augmented_successors_map_.find(block);
247 return where == augmented_successors_map_.end() ? block->successors()
248 : &(*where).second;
249 };
250 }
251
252 Function::GetBlocksFunction
AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const253 Function::AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const {
254 return [this](const BasicBlock* block) {
255 auto where = loop_header_successors_plus_continue_target_map_.find(block);
256 return where == loop_header_successors_plus_continue_target_map_.end()
257 ? AugmentedCFGSuccessorsFunction()(block)
258 : &(*where).second;
259 };
260 }
261
AugmentedCFGPredecessorsFunction() const262 Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const {
263 return [this](const BasicBlock* block) {
264 auto where = augmented_predecessors_map_.find(block);
265 return where == augmented_predecessors_map_.end() ? block->predecessors()
266 : &(*where).second;
267 };
268 }
269
ComputeAugmentedCFG()270 void Function::ComputeAugmentedCFG() {
271 // Compute the successors of the pseudo-entry block, and
272 // the predecessors of the pseudo exit block.
273 auto succ_func = [](const BasicBlock* b) { return b->successors(); };
274 auto pred_func = [](const BasicBlock* b) { return b->predecessors(); };
275 CFA<BasicBlock>::ComputeAugmentedCFG(
276 ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_,
277 &augmented_successors_map_, &augmented_predecessors_map_, succ_func,
278 pred_func);
279 }
280
AddConstruct(const Construct & new_construct)281 Construct& Function::AddConstruct(const Construct& new_construct) {
282 cfg_constructs_.push_back(new_construct);
283 auto& result = cfg_constructs_.back();
284 entry_block_to_construct_[std::make_pair(new_construct.entry_block(),
285 new_construct.type())] = &result;
286 return result;
287 }
288
FindConstructForEntryBlock(const BasicBlock * entry_block,ConstructType type)289 Construct& Function::FindConstructForEntryBlock(const BasicBlock* entry_block,
290 ConstructType type) {
291 auto where =
292 entry_block_to_construct_.find(std::make_pair(entry_block, type));
293 assert(where != entry_block_to_construct_.end());
294 auto construct_ptr = (*where).second;
295 assert(construct_ptr);
296 return *construct_ptr;
297 }
298
GetBlockDepth(BasicBlock * bb)299 int Function::GetBlockDepth(BasicBlock* bb) {
300 // Guard against nullptr.
301 if (!bb) {
302 return 0;
303 }
304 // Only calculate the depth if it's not already calculated.
305 // This function uses memoization to avoid duplicate CFG depth calculations.
306 if (block_depth_.find(bb) != block_depth_.end()) {
307 return block_depth_[bb];
308 }
309
310 BasicBlock* bb_dom = bb->immediate_dominator();
311 if (!bb_dom || bb == bb_dom) {
312 // This block has no dominator, so it's at depth 0.
313 block_depth_[bb] = 0;
314 } else if (bb->is_type(kBlockTypeMerge)) {
315 // If this is a merge block, its depth is equal to the block before
316 // branching.
317 BasicBlock* header = merge_block_header_[bb];
318 assert(header);
319 block_depth_[bb] = GetBlockDepth(header);
320 } else if (bb->is_type(kBlockTypeContinue)) {
321 // The depth of the continue block entry point is 1 + loop header depth.
322 Construct* continue_construct =
323 entry_block_to_construct_[std::make_pair(bb, ConstructType::kContinue)];
324 assert(continue_construct);
325 // Continue construct has only 1 corresponding construct (loop header).
326 Construct* loop_construct =
327 continue_construct->corresponding_constructs()[0];
328 assert(loop_construct);
329 BasicBlock* loop_header = loop_construct->entry_block();
330 // The continue target may be the loop itself (while 1).
331 // In such cases, the depth of the continue block is: 1 + depth of the
332 // loop's dominator block.
333 if (loop_header == bb) {
334 block_depth_[bb] = 1 + GetBlockDepth(bb_dom);
335 } else {
336 block_depth_[bb] = 1 + GetBlockDepth(loop_header);
337 }
338 } else if (bb_dom->is_type(kBlockTypeHeader) ||
339 bb_dom->is_type(kBlockTypeLoop)) {
340 // The dominator of the given block is a header block. So, the nesting
341 // depth of this block is: 1 + nesting depth of the header.
342 block_depth_[bb] = 1 + GetBlockDepth(bb_dom);
343 } else {
344 block_depth_[bb] = GetBlockDepth(bb_dom);
345 }
346 return block_depth_[bb];
347 }
348
RegisterExecutionModelLimitation(SpvExecutionModel model,const std::string & message)349 void Function::RegisterExecutionModelLimitation(SpvExecutionModel model,
350 const std::string& message) {
351 execution_model_limitations_.push_back(
352 [model, message](SpvExecutionModel in_model, std::string* out_message) {
353 if (model != in_model) {
354 if (out_message) {
355 *out_message = message;
356 }
357 return false;
358 }
359 return true;
360 });
361 }
362
IsCompatibleWithExecutionModel(SpvExecutionModel model,std::string * reason) const363 bool Function::IsCompatibleWithExecutionModel(SpvExecutionModel model,
364 std::string* reason) const {
365 bool return_value = true;
366 std::stringstream ss_reason;
367
368 for (const auto& is_compatible : execution_model_limitations_) {
369 std::string message;
370 if (!is_compatible(model, &message)) {
371 if (!reason) return false;
372 return_value = false;
373 if (!message.empty()) {
374 ss_reason << message << "\n";
375 }
376 }
377 }
378
379 if (!return_value && reason) {
380 *reason = ss_reason.str();
381 }
382
383 return return_value;
384 }
385
386 } // namespace val
387 } // namespace spvtools
388