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