/src/spirv-tools/source/opt/inline_pass.cpp
Line | Count | Source |
1 | | // Copyright (c) 2017 The Khronos Group Inc. |
2 | | // Copyright (c) 2017 Valve Corporation |
3 | | // Copyright (c) 2017 LunarG Inc. |
4 | | // |
5 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
6 | | // you may not use this file except in compliance with the License. |
7 | | // You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, software |
12 | | // distributed under the License is distributed on an "AS IS" BASIS, |
13 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | // See the License for the specific language governing permissions and |
15 | | // limitations under the License. |
16 | | |
17 | | #include "source/opt/inline_pass.h" |
18 | | |
19 | | #include <unordered_set> |
20 | | #include <utility> |
21 | | |
22 | | #include "source/cfa.h" |
23 | | #include "source/opt/reflect.h" |
24 | | #include "source/util/make_unique.h" |
25 | | |
26 | | namespace spvtools { |
27 | | namespace opt { |
28 | | namespace { |
29 | | // Indices of operands in SPIR-V instructions |
30 | | constexpr int kSpvFunctionCallFunctionId = 2; |
31 | | constexpr int kSpvFunctionCallArgumentId = 3; |
32 | | constexpr int kSpvReturnValueId = 0; |
33 | | constexpr int kSpvDebugDeclareVarInIdx = 3; |
34 | | constexpr int kSpvAccessChainBaseInIdx = 0; |
35 | | } // namespace |
36 | | |
37 | | uint32_t InlinePass::AddPointerToType(uint32_t type_id, |
38 | 0 | spv::StorageClass storage_class) { |
39 | 0 | uint32_t resultId = context()->TakeNextId(); |
40 | 0 | if (resultId == 0) { |
41 | 0 | return resultId; |
42 | 0 | } |
43 | | |
44 | 0 | std::unique_ptr<Instruction> type_inst( |
45 | 0 | new Instruction(context(), spv::Op::OpTypePointer, 0, resultId, |
46 | 0 | {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, |
47 | 0 | {uint32_t(storage_class)}}, |
48 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}})); |
49 | 0 | context()->AddType(std::move(type_inst)); |
50 | 0 | analysis::Type* pointeeTy; |
51 | 0 | std::unique_ptr<analysis::Pointer> pointerTy; |
52 | 0 | std::tie(pointeeTy, pointerTy) = |
53 | 0 | context()->get_type_mgr()->GetTypeAndPointerType( |
54 | 0 | type_id, spv::StorageClass::Function); |
55 | 0 | context()->get_type_mgr()->RegisterType(resultId, *pointerTy); |
56 | 0 | return resultId; |
57 | 0 | } |
58 | | |
59 | | void InlinePass::AddBranch(uint32_t label_id, |
60 | 2.01k | std::unique_ptr<BasicBlock>* block_ptr) { |
61 | 2.01k | std::unique_ptr<Instruction> newBranch( |
62 | 2.01k | new Instruction(context(), spv::Op::OpBranch, 0, 0, |
63 | 2.01k | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}})); |
64 | 2.01k | (*block_ptr)->AddInstruction(std::move(newBranch)); |
65 | 2.01k | } |
66 | | |
67 | | void InlinePass::AddBranchCond(uint32_t cond_id, uint32_t true_id, |
68 | | uint32_t false_id, |
69 | 0 | std::unique_ptr<BasicBlock>* block_ptr) { |
70 | 0 | std::unique_ptr<Instruction> newBranch( |
71 | 0 | new Instruction(context(), spv::Op::OpBranchConditional, 0, 0, |
72 | 0 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}}, |
73 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}}, |
74 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}})); |
75 | 0 | (*block_ptr)->AddInstruction(std::move(newBranch)); |
76 | 0 | } |
77 | | |
78 | | void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id, |
79 | 0 | std::unique_ptr<BasicBlock>* block_ptr) { |
80 | 0 | std::unique_ptr<Instruction> newLoopMerge(new Instruction( |
81 | 0 | context(), spv::Op::OpLoopMerge, 0, 0, |
82 | 0 | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, |
83 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}}, |
84 | 0 | {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {0}}})); |
85 | 0 | (*block_ptr)->AddInstruction(std::move(newLoopMerge)); |
86 | 0 | } |
87 | | |
88 | | void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id, |
89 | | std::unique_ptr<BasicBlock>* block_ptr, |
90 | | const Instruction* line_inst, |
91 | 29.2k | const DebugScope& dbg_scope) { |
92 | 29.2k | std::unique_ptr<Instruction> newStore( |
93 | 29.2k | new Instruction(context(), spv::Op::OpStore, 0, 0, |
94 | 29.2k | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}, |
95 | 29.2k | {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}})); |
96 | 29.2k | if (line_inst != nullptr) { |
97 | 0 | newStore->AddDebugLine(line_inst); |
98 | 0 | } |
99 | 29.2k | newStore->SetDebugScope(dbg_scope); |
100 | 29.2k | (*block_ptr)->AddInstruction(std::move(newStore)); |
101 | 29.2k | } |
102 | | |
103 | | void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id, |
104 | | std::unique_ptr<BasicBlock>* block_ptr, |
105 | | const Instruction* line_inst, |
106 | 16.2k | const DebugScope& dbg_scope) { |
107 | 16.2k | std::unique_ptr<Instruction> newLoad( |
108 | 16.2k | new Instruction(context(), spv::Op::OpLoad, type_id, resultId, |
109 | 16.2k | {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}})); |
110 | 16.2k | if (line_inst != nullptr) { |
111 | 0 | newLoad->AddDebugLine(line_inst); |
112 | 0 | } |
113 | 16.2k | newLoad->SetDebugScope(dbg_scope); |
114 | 16.2k | (*block_ptr)->AddInstruction(std::move(newLoad)); |
115 | 16.2k | } |
116 | | |
117 | 238k | std::unique_ptr<Instruction> InlinePass::NewLabel(uint32_t label_id) { |
118 | 238k | std::unique_ptr<Instruction> newLabel( |
119 | 238k | new Instruction(context(), spv::Op::OpLabel, 0, label_id, {})); |
120 | 238k | return newLabel; |
121 | 238k | } |
122 | | |
123 | 0 | uint32_t InlinePass::GetFalseId() { |
124 | 0 | if (false_id_ != 0) return false_id_; |
125 | 0 | false_id_ = get_module()->GetGlobalValue(spv::Op::OpConstantFalse); |
126 | 0 | if (false_id_ != 0) return false_id_; |
127 | 0 | uint32_t boolId = get_module()->GetGlobalValue(spv::Op::OpTypeBool); |
128 | 0 | if (boolId == 0) { |
129 | 0 | boolId = context()->TakeNextId(); |
130 | 0 | if (boolId == 0) { |
131 | 0 | return 0; |
132 | 0 | } |
133 | 0 | get_module()->AddGlobalValue(spv::Op::OpTypeBool, boolId, 0); |
134 | 0 | } |
135 | 0 | false_id_ = context()->TakeNextId(); |
136 | 0 | if (false_id_ == 0) { |
137 | 0 | return 0; |
138 | 0 | } |
139 | 0 | get_module()->AddGlobalValue(spv::Op::OpConstantFalse, false_id_, boolId); |
140 | 0 | return false_id_; |
141 | 0 | } |
142 | | |
143 | | void InlinePass::MapParams( |
144 | | Function* calleeFn, BasicBlock::iterator call_inst_itr, |
145 | 35.4k | std::unordered_map<uint32_t, uint32_t>* callee2caller) { |
146 | 35.4k | int param_idx = 0; |
147 | 35.4k | calleeFn->ForEachParam( |
148 | 51.4k | [&call_inst_itr, ¶m_idx, &callee2caller](const Instruction* cpi) { |
149 | 51.4k | const uint32_t pid = cpi->result_id(); |
150 | 51.4k | (*callee2caller)[pid] = call_inst_itr->GetSingleWordOperand( |
151 | 51.4k | kSpvFunctionCallArgumentId + param_idx); |
152 | 51.4k | ++param_idx; |
153 | 51.4k | }); |
154 | 35.4k | } |
155 | | |
156 | | bool InlinePass::CloneAndMapLocals( |
157 | | Function* calleeFn, std::vector<std::unique_ptr<Instruction>>* new_vars, |
158 | | std::unordered_map<uint32_t, uint32_t>* callee2caller, |
159 | 35.4k | analysis::DebugInlinedAtContext* inlined_at_ctx) { |
160 | 35.4k | auto callee_block_itr = calleeFn->begin(); |
161 | 35.4k | auto callee_var_itr = callee_block_itr->begin(); |
162 | 137k | while (callee_var_itr->opcode() == spv::Op::OpVariable || |
163 | 35.4k | callee_var_itr->GetCommonDebugOpcode() == |
164 | 102k | CommonDebugInfoDebugDeclare) { |
165 | 102k | if (callee_var_itr->opcode() != spv::Op::OpVariable) { |
166 | 0 | ++callee_var_itr; |
167 | 0 | continue; |
168 | 0 | } |
169 | | |
170 | 102k | std::unique_ptr<Instruction> var_inst(callee_var_itr->Clone(context())); |
171 | 102k | uint32_t newId = context()->TakeNextId(); |
172 | 102k | if (newId == 0) { |
173 | 0 | return false; |
174 | 0 | } |
175 | 102k | get_decoration_mgr()->CloneDecorations(callee_var_itr->result_id(), newId); |
176 | 102k | var_inst->SetResultId(newId); |
177 | 102k | var_inst->UpdateDebugInlinedAt( |
178 | 102k | context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( |
179 | 102k | callee_var_itr->GetDebugInlinedAt(), inlined_at_ctx)); |
180 | 102k | (*callee2caller)[callee_var_itr->result_id()] = newId; |
181 | 102k | new_vars->push_back(std::move(var_inst)); |
182 | 102k | ++callee_var_itr; |
183 | 102k | } |
184 | 35.4k | return true; |
185 | 35.4k | } |
186 | | |
187 | | uint32_t InlinePass::CreateReturnVar( |
188 | 16.2k | Function* calleeFn, std::vector<std::unique_ptr<Instruction>>* new_vars) { |
189 | 16.2k | uint32_t returnVarId = 0; |
190 | 16.2k | const uint32_t calleeTypeId = calleeFn->type_id(); |
191 | 16.2k | analysis::TypeManager* type_mgr = context()->get_type_mgr(); |
192 | 16.2k | assert(type_mgr->GetType(calleeTypeId)->AsVoid() == nullptr && |
193 | 16.2k | "Cannot create a return variable of type void."); |
194 | | // Find or create ptr to callee return type. |
195 | 16.2k | uint32_t returnVarTypeId = |
196 | 16.2k | type_mgr->FindPointerToType(calleeTypeId, spv::StorageClass::Function); |
197 | | |
198 | 16.2k | if (returnVarTypeId == 0) { |
199 | 0 | returnVarTypeId = |
200 | 0 | AddPointerToType(calleeTypeId, spv::StorageClass::Function); |
201 | 0 | if (returnVarTypeId == 0) { |
202 | 0 | return 0; |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | // Add return var to new function scope variables. |
207 | 16.2k | returnVarId = context()->TakeNextId(); |
208 | 16.2k | if (returnVarId == 0) { |
209 | 0 | return 0; |
210 | 0 | } |
211 | | |
212 | 16.2k | std::unique_ptr<Instruction> var_inst(new Instruction( |
213 | 16.2k | context(), spv::Op::OpVariable, returnVarTypeId, returnVarId, |
214 | 16.2k | {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, |
215 | 16.2k | {(uint32_t)spv::StorageClass::Function}}})); |
216 | 16.2k | new_vars->push_back(std::move(var_inst)); |
217 | 16.2k | get_decoration_mgr()->CloneDecorations(calleeFn->result_id(), returnVarId); |
218 | | |
219 | | // Decorate the return var with AliasedPointer if the storage class of the |
220 | | // pointee type is PhysicalStorageBuffer. |
221 | 16.2k | auto const pointee_type = |
222 | 16.2k | type_mgr->GetType(returnVarTypeId)->AsPointer()->pointee_type(); |
223 | 16.2k | if (pointee_type->AsPointer() != nullptr) { |
224 | 0 | if (pointee_type->AsPointer()->storage_class() == |
225 | 0 | spv::StorageClass::PhysicalStorageBuffer) { |
226 | 0 | get_decoration_mgr()->AddDecoration( |
227 | 0 | returnVarId, uint32_t(spv::Decoration::AliasedPointer)); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | 16.2k | return returnVarId; |
232 | 16.2k | } |
233 | | |
234 | 859k | bool InlinePass::IsSameBlockOp(const Instruction* inst) const { |
235 | 859k | return inst->opcode() == spv::Op::OpSampledImage || |
236 | 859k | inst->opcode() == spv::Op::OpImage; |
237 | 859k | } |
238 | | |
239 | | bool InlinePass::CloneSameBlockOps( |
240 | | std::unique_ptr<Instruction>* inst, |
241 | | std::unordered_map<uint32_t, uint32_t>* postCallSB, |
242 | | std::unordered_map<uint32_t, Instruction*>* preCallSB, |
243 | 523k | std::unique_ptr<BasicBlock>* block_ptr) { |
244 | 523k | return (*inst)->WhileEachInId([&postCallSB, &preCallSB, &block_ptr, |
245 | 923k | this](uint32_t* iid) { |
246 | 923k | const auto mapItr = (*postCallSB).find(*iid); |
247 | 923k | if (mapItr == (*postCallSB).end()) { |
248 | 923k | const auto mapItr2 = (*preCallSB).find(*iid); |
249 | 923k | if (mapItr2 != (*preCallSB).end()) { |
250 | | // Clone pre-call same-block ops, map result id. |
251 | 0 | const Instruction* inInst = mapItr2->second; |
252 | 0 | std::unique_ptr<Instruction> sb_inst(inInst->Clone(context())); |
253 | 0 | if (!CloneSameBlockOps(&sb_inst, postCallSB, preCallSB, block_ptr)) { |
254 | 0 | return false; |
255 | 0 | } |
256 | | |
257 | 0 | const uint32_t rid = sb_inst->result_id(); |
258 | 0 | const uint32_t nid = context()->TakeNextId(); |
259 | 0 | if (nid == 0) { |
260 | 0 | return false; |
261 | 0 | } |
262 | 0 | get_decoration_mgr()->CloneDecorations(rid, nid); |
263 | 0 | sb_inst->SetResultId(nid); |
264 | 0 | (*postCallSB)[rid] = nid; |
265 | 0 | *iid = nid; |
266 | 0 | (*block_ptr)->AddInstruction(std::move(sb_inst)); |
267 | 0 | } |
268 | 923k | } else { |
269 | | // Reset same-block op operand. |
270 | 0 | *iid = mapItr->second; |
271 | 0 | } |
272 | 923k | return true; |
273 | 923k | }); |
274 | 523k | } |
275 | | |
276 | | void InlinePass::MoveInstsBeforeEntryBlock( |
277 | | std::unordered_map<uint32_t, Instruction*>* preCallSB, |
278 | | BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr, |
279 | 35.4k | UptrVectorIterator<BasicBlock> call_block_itr) { |
280 | 371k | for (auto cii = call_block_itr->begin(); cii != call_inst_itr; |
281 | 336k | cii = call_block_itr->begin()) { |
282 | 336k | Instruction* inst = &*cii; |
283 | 336k | inst->RemoveFromList(); |
284 | 336k | std::unique_ptr<Instruction> cp_inst(inst); |
285 | | // Remember same-block ops for possible regeneration. |
286 | 336k | if (IsSameBlockOp(&*cp_inst)) { |
287 | 0 | auto* sb_inst_ptr = cp_inst.get(); |
288 | 0 | (*preCallSB)[cp_inst->result_id()] = sb_inst_ptr; |
289 | 0 | } |
290 | 336k | new_blk_ptr->AddInstruction(std::move(cp_inst)); |
291 | 336k | } |
292 | 35.4k | } |
293 | | |
294 | | std::unique_ptr<BasicBlock> InlinePass::AddGuardBlock( |
295 | | std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
296 | | std::unordered_map<uint32_t, uint32_t>* callee2caller, |
297 | 8 | std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id) { |
298 | 8 | const auto guard_block_id = context()->TakeNextId(); |
299 | 8 | if (guard_block_id == 0) { |
300 | 0 | return nullptr; |
301 | 0 | } |
302 | 8 | AddBranch(guard_block_id, &new_blk_ptr); |
303 | 8 | new_blocks->push_back(std::move(new_blk_ptr)); |
304 | | // Start the next block. |
305 | 8 | new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(guard_block_id)); |
306 | | // Reset the mapping of the callee's entry block to point to |
307 | | // the guard block. Do this so we can fix up phis later on to |
308 | | // satisfy dominance. |
309 | 8 | (*callee2caller)[entry_blk_label_id] = guard_block_id; |
310 | 8 | return new_blk_ptr; |
311 | 8 | } |
312 | | |
313 | | InstructionList::iterator InlinePass::AddStoresForVariableInitializers( |
314 | | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
315 | | analysis::DebugInlinedAtContext* inlined_at_ctx, |
316 | | std::unique_ptr<BasicBlock>* new_blk_ptr, |
317 | 35.4k | UptrVectorIterator<BasicBlock> callee_first_block_itr) { |
318 | 35.4k | auto callee_itr = callee_first_block_itr->begin(); |
319 | 137k | while (callee_itr->opcode() == spv::Op::OpVariable || |
320 | 102k | callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { |
321 | 102k | if (callee_itr->opcode() == spv::Op::OpVariable && |
322 | 102k | callee_itr->NumInOperands() == 2) { |
323 | 13.2k | assert(callee2caller.count(callee_itr->result_id()) && |
324 | 13.2k | "Expected the variable to have already been mapped."); |
325 | 13.2k | uint32_t new_var_id = callee2caller.at(callee_itr->result_id()); |
326 | | |
327 | | // The initializer must be a constant or global value. No mapped |
328 | | // should be used. |
329 | 13.2k | uint32_t val_id = callee_itr->GetSingleWordInOperand(1); |
330 | 13.2k | AddStore(new_var_id, val_id, new_blk_ptr, callee_itr->dbg_line_inst(), |
331 | 13.2k | context()->get_debug_info_mgr()->BuildDebugScope( |
332 | 13.2k | callee_itr->GetDebugScope(), inlined_at_ctx)); |
333 | 13.2k | } |
334 | 102k | if (callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { |
335 | 0 | InlineSingleInstruction( |
336 | 0 | callee2caller, new_blk_ptr->get(), &*callee_itr, |
337 | 0 | context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( |
338 | 0 | callee_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx)); |
339 | 0 | } |
340 | 102k | ++callee_itr; |
341 | 102k | } |
342 | 35.4k | return callee_itr; |
343 | 35.4k | } |
344 | | |
345 | | bool InlinePass::InlineSingleInstruction( |
346 | | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
347 | 1.29M | BasicBlock* new_blk_ptr, const Instruction* inst, uint32_t dbg_inlined_at) { |
348 | | // If we have return, it must be at the end of the callee. We will handle |
349 | | // it at the end. |
350 | 1.29M | if (inst->opcode() == spv::Op::OpReturnValue || |
351 | 1.27M | inst->opcode() == spv::Op::OpReturn) |
352 | 34.0k | return true; |
353 | | |
354 | | // Copy callee instruction and remap all input Ids. |
355 | 1.26M | std::unique_ptr<Instruction> cp_inst(inst->Clone(context())); |
356 | 2.10M | cp_inst->ForEachInId([&callee2caller](uint32_t* iid) { |
357 | 2.10M | const auto mapItr = callee2caller.find(*iid); |
358 | 2.10M | if (mapItr != callee2caller.end()) { |
359 | 1.57M | *iid = mapItr->second; |
360 | 1.57M | } |
361 | 2.10M | }); |
362 | | |
363 | | // If result id is non-zero, remap it. |
364 | 1.26M | const uint32_t rid = cp_inst->result_id(); |
365 | 1.26M | if (rid != 0) { |
366 | 762k | const auto mapItr = callee2caller.find(rid); |
367 | 762k | if (mapItr == callee2caller.end()) { |
368 | 0 | return false; |
369 | 0 | } |
370 | 762k | uint32_t nid = mapItr->second; |
371 | 762k | cp_inst->SetResultId(nid); |
372 | 762k | get_decoration_mgr()->CloneDecorations(rid, nid); |
373 | 762k | } |
374 | | |
375 | 1.26M | cp_inst->UpdateDebugInlinedAt(dbg_inlined_at); |
376 | 1.26M | new_blk_ptr->AddInstruction(std::move(cp_inst)); |
377 | 1.26M | return true; |
378 | 1.26M | } |
379 | | |
380 | | std::unique_ptr<BasicBlock> InlinePass::InlineReturn( |
381 | | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
382 | | std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
383 | | std::unique_ptr<BasicBlock> new_blk_ptr, |
384 | | analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn, |
385 | 35.4k | const Instruction* inst, uint32_t returnVarId) { |
386 | | // Store return value to return variable. |
387 | 35.4k | if (inst->opcode() == spv::Op::OpReturnValue) { |
388 | 15.9k | assert(returnVarId != 0); |
389 | 15.9k | uint32_t valId = inst->GetInOperand(kSpvReturnValueId).words[0]; |
390 | 15.9k | const auto mapItr = callee2caller.find(valId); |
391 | 15.9k | if (mapItr != callee2caller.end()) { |
392 | 12.5k | valId = mapItr->second; |
393 | 12.5k | } |
394 | 15.9k | AddStore(returnVarId, valId, &new_blk_ptr, inst->dbg_line_inst(), |
395 | 15.9k | context()->get_debug_info_mgr()->BuildDebugScope( |
396 | 15.9k | inst->GetDebugScope(), inlined_at_ctx)); |
397 | 15.9k | } |
398 | | |
399 | 35.4k | uint32_t returnLabelId = 0; |
400 | 35.4k | for (auto callee_block_itr = calleeFn->begin(); |
401 | 260k | callee_block_itr != calleeFn->end(); ++callee_block_itr) { |
402 | 228k | if (spvOpcodeIsAbort(callee_block_itr->tail()->opcode())) { |
403 | 3.34k | returnLabelId = context()->TakeNextId(); |
404 | 3.34k | break; |
405 | 3.34k | } |
406 | 228k | } |
407 | 35.4k | if (returnLabelId == 0) return new_blk_ptr; |
408 | | |
409 | 3.34k | if (inst->opcode() == spv::Op::OpReturn || |
410 | 1.46k | inst->opcode() == spv::Op::OpReturnValue) |
411 | 1.99k | AddBranch(returnLabelId, &new_blk_ptr); |
412 | 3.34k | new_blocks->push_back(std::move(new_blk_ptr)); |
413 | 3.34k | return MakeUnique<BasicBlock>(NewLabel(returnLabelId)); |
414 | 35.4k | } |
415 | | |
416 | | bool InlinePass::InlineEntryBlock( |
417 | | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
418 | | std::unique_ptr<BasicBlock>* new_blk_ptr, |
419 | | UptrVectorIterator<BasicBlock> callee_first_block, |
420 | 35.4k | analysis::DebugInlinedAtContext* inlined_at_ctx) { |
421 | 35.4k | auto callee_inst_itr = AddStoresForVariableInitializers( |
422 | 35.4k | callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block); |
423 | | |
424 | 382k | while (callee_inst_itr != callee_first_block->end()) { |
425 | | // Don't inline function definition links, the calling function is not a |
426 | | // definition. |
427 | 347k | if (callee_inst_itr->GetShader100DebugOpcode() == |
428 | 347k | NonSemanticShaderDebugInfo100DebugFunctionDefinition) { |
429 | 0 | ++callee_inst_itr; |
430 | 0 | continue; |
431 | 0 | } |
432 | | |
433 | 347k | if (!InlineSingleInstruction( |
434 | 347k | callee2caller, new_blk_ptr->get(), &*callee_inst_itr, |
435 | 347k | context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( |
436 | 347k | callee_inst_itr->GetDebugScope().GetInlinedAt(), |
437 | 347k | inlined_at_ctx))) { |
438 | 0 | return false; |
439 | 0 | } |
440 | 347k | ++callee_inst_itr; |
441 | 347k | } |
442 | 35.4k | return true; |
443 | 35.4k | } |
444 | | |
445 | | std::unique_ptr<BasicBlock> InlinePass::InlineBasicBlocks( |
446 | | std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
447 | | const std::unordered_map<uint32_t, uint32_t>& callee2caller, |
448 | | std::unique_ptr<BasicBlock> new_blk_ptr, |
449 | 35.4k | analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn) { |
450 | 35.4k | auto callee_block_itr = calleeFn->begin(); |
451 | 35.4k | ++callee_block_itr; |
452 | | |
453 | 235k | while (callee_block_itr != calleeFn->end()) { |
454 | 200k | new_blocks->push_back(std::move(new_blk_ptr)); |
455 | 200k | const auto mapItr = |
456 | 200k | callee2caller.find(callee_block_itr->GetLabelInst()->result_id()); |
457 | 200k | if (mapItr == callee2caller.end()) return nullptr; |
458 | 200k | new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(mapItr->second)); |
459 | | |
460 | 200k | auto tail_inst_itr = callee_block_itr->end(); |
461 | 1.14M | for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr; |
462 | 946k | ++inst_itr) { |
463 | | // Don't inline function definition links, the calling function is not a |
464 | | // definition |
465 | 946k | if (inst_itr->GetShader100DebugOpcode() == |
466 | 946k | NonSemanticShaderDebugInfo100DebugFunctionDefinition) |
467 | 0 | continue; |
468 | 946k | if (!InlineSingleInstruction( |
469 | 946k | callee2caller, new_blk_ptr.get(), &*inst_itr, |
470 | 946k | context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( |
471 | 946k | inst_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx))) { |
472 | 0 | return nullptr; |
473 | 0 | } |
474 | 946k | } |
475 | | |
476 | 200k | ++callee_block_itr; |
477 | 200k | } |
478 | 35.4k | return new_blk_ptr; |
479 | 35.4k | } |
480 | | |
481 | | bool InlinePass::MoveCallerInstsAfterFunctionCall( |
482 | | std::unordered_map<uint32_t, Instruction*>* preCallSB, |
483 | | std::unordered_map<uint32_t, uint32_t>* postCallSB, |
484 | | std::unique_ptr<BasicBlock>* new_blk_ptr, |
485 | 35.4k | BasicBlock::iterator call_inst_itr, bool multiBlocks) { |
486 | | // Copy remaining instructions from caller block. |
487 | 559k | for (Instruction* inst = call_inst_itr->NextNode(); inst; |
488 | 523k | inst = call_inst_itr->NextNode()) { |
489 | 523k | inst->RemoveFromList(); |
490 | 523k | std::unique_ptr<Instruction> cp_inst(inst); |
491 | | // If multiple blocks generated, regenerate any same-block |
492 | | // instruction that has not been seen in this last block. |
493 | 523k | if (multiBlocks) { |
494 | 523k | if (!CloneSameBlockOps(&cp_inst, postCallSB, preCallSB, new_blk_ptr)) { |
495 | 0 | return false; |
496 | 0 | } |
497 | | |
498 | | // Remember same-block ops in this block. |
499 | 523k | if (IsSameBlockOp(&*cp_inst)) { |
500 | 0 | const uint32_t rid = cp_inst->result_id(); |
501 | 0 | (*postCallSB)[rid] = rid; |
502 | 0 | } |
503 | 523k | } |
504 | 523k | new_blk_ptr->get()->AddInstruction(std::move(cp_inst)); |
505 | 523k | } |
506 | | |
507 | 35.4k | return true; |
508 | 35.4k | } |
509 | | |
510 | | void InlinePass::MoveLoopMergeInstToFirstBlock( |
511 | 8 | std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { |
512 | | // Move the OpLoopMerge from the last block back to the first, where |
513 | | // it belongs. |
514 | 8 | auto& first = new_blocks->front(); |
515 | 8 | auto& last = new_blocks->back(); |
516 | 8 | assert(first != last); |
517 | | |
518 | | // Insert a modified copy of the loop merge into the first block. |
519 | 8 | auto loop_merge_itr = last->tail(); |
520 | 8 | --loop_merge_itr; |
521 | 8 | assert(loop_merge_itr->opcode() == spv::Op::OpLoopMerge); |
522 | 8 | std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context())); |
523 | 8 | first->tail().InsertBefore(std::move(cp_inst)); |
524 | | |
525 | | // Remove the loop merge from the last block. |
526 | 8 | loop_merge_itr->RemoveFromList(); |
527 | 8 | delete &*loop_merge_itr; |
528 | 8 | } |
529 | | |
530 | | void InlinePass::UpdateSingleBlockLoopContinueTarget( |
531 | 8 | uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) { |
532 | 8 | auto& header = new_blocks->front(); |
533 | 8 | auto* merge_inst = header->GetLoopMergeInst(); |
534 | | |
535 | | // The back-edge block is split at the branch to create a new back-edge |
536 | | // block. The old block is modified to branch to the new block. The loop |
537 | | // merge instruction is updated to declare the new block as the continue |
538 | | // target. This has the effect of changing the loop from being a large |
539 | | // continue construct and an empty loop construct to being a loop with a loop |
540 | | // construct and a trivial continue construct. This change is made to satisfy |
541 | | // structural dominance. |
542 | | |
543 | | // Add the new basic block. |
544 | 8 | std::unique_ptr<BasicBlock> new_block = |
545 | 8 | MakeUnique<BasicBlock>(NewLabel(new_id)); |
546 | 8 | auto& old_backedge = new_blocks->back(); |
547 | 8 | auto old_branch = old_backedge->tail(); |
548 | | |
549 | | // Move the old back edge into the new block. |
550 | 8 | std::unique_ptr<Instruction> br(&*old_branch); |
551 | 8 | new_block->AddInstruction(std::move(br)); |
552 | | |
553 | | // Add a branch to the new block from the old back-edge block. |
554 | 8 | AddBranch(new_id, &old_backedge); |
555 | 8 | new_blocks->push_back(std::move(new_block)); |
556 | | |
557 | | // Update the loop's continue target to the new block. |
558 | 8 | merge_inst->SetInOperand(1u, {new_id}); |
559 | 8 | } |
560 | | |
561 | | bool InlinePass::GenInlineCode( |
562 | | std::vector<std::unique_ptr<BasicBlock>>* new_blocks, |
563 | | std::vector<std::unique_ptr<Instruction>>* new_vars, |
564 | | BasicBlock::iterator call_inst_itr, |
565 | 35.4k | UptrVectorIterator<BasicBlock> call_block_itr) { |
566 | | // Map from all ids in the callee to their equivalent id in the caller |
567 | | // as callee instructions are copied into caller. |
568 | 35.4k | std::unordered_map<uint32_t, uint32_t> callee2caller; |
569 | | // Pre-call same-block insts |
570 | 35.4k | std::unordered_map<uint32_t, Instruction*> preCallSB; |
571 | | // Post-call same-block op ids |
572 | 35.4k | std::unordered_map<uint32_t, uint32_t> postCallSB; |
573 | | |
574 | 35.4k | analysis::DebugInlinedAtContext inlined_at_ctx(&*call_inst_itr); |
575 | | |
576 | | // Invalidate the def-use chains. They are not kept up to date while |
577 | | // inlining. However, certain calls try to keep them up-to-date if they are |
578 | | // valid. These operations can fail. |
579 | 35.4k | context()->InvalidateAnalyses(IRContext::kAnalysisDefUse); |
580 | | |
581 | | // If the caller is a loop header and the callee has multiple blocks, then the |
582 | | // normal inlining logic will place the OpLoopMerge in the last of several |
583 | | // blocks in the loop. Instead, it should be placed at the end of the first |
584 | | // block. We'll wait to move the OpLoopMerge until the end of the regular |
585 | | // inlining logic, and only if necessary. |
586 | 35.4k | bool caller_is_loop_header = call_block_itr->GetLoopMergeInst() != nullptr; |
587 | | |
588 | | // Single-trip loop continue block |
589 | 35.4k | std::unique_ptr<BasicBlock> single_trip_loop_cont_blk; |
590 | | |
591 | 35.4k | Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand( |
592 | 35.4k | kSpvFunctionCallFunctionId)]; |
593 | | |
594 | | // Map parameters to actual arguments. |
595 | 35.4k | MapParams(calleeFn, call_inst_itr, &callee2caller); |
596 | | |
597 | | // Define caller local variables for all callee variables and create map to |
598 | | // them. |
599 | 35.4k | if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller, &inlined_at_ctx)) { |
600 | 0 | return false; |
601 | 0 | } |
602 | | |
603 | | // First block needs to use label of original block |
604 | | // but map callee label in case of phi reference. |
605 | 35.4k | uint32_t entry_blk_label_id = calleeFn->begin()->GetLabelInst()->result_id(); |
606 | 35.4k | callee2caller[entry_blk_label_id] = call_block_itr->id(); |
607 | 35.4k | std::unique_ptr<BasicBlock> new_blk_ptr = |
608 | 35.4k | MakeUnique<BasicBlock>(NewLabel(call_block_itr->id())); |
609 | | |
610 | | // Move instructions of original caller block up to call instruction. |
611 | 35.4k | MoveInstsBeforeEntryBlock(&preCallSB, new_blk_ptr.get(), call_inst_itr, |
612 | 35.4k | call_block_itr); |
613 | | |
614 | 35.4k | if (caller_is_loop_header && |
615 | 9 | (*(calleeFn->begin())).GetMergeInst() != nullptr) { |
616 | | // We can't place both the caller's merge instruction and |
617 | | // another merge instruction in the same block. So split the |
618 | | // calling block. Insert an unconditional branch to a new guard |
619 | | // block. Later, once we know the ID of the last block, we |
620 | | // will move the caller's OpLoopMerge from the last generated |
621 | | // block into the first block. We also wait to avoid |
622 | | // invalidating various iterators. |
623 | 8 | new_blk_ptr = AddGuardBlock(new_blocks, &callee2caller, |
624 | 8 | std::move(new_blk_ptr), entry_blk_label_id); |
625 | 8 | if (new_blk_ptr == nullptr) return false; |
626 | 8 | } |
627 | | |
628 | | // Create return var if needed. |
629 | 35.4k | const uint32_t calleeTypeId = calleeFn->type_id(); |
630 | 35.4k | uint32_t returnVarId = 0; |
631 | 35.4k | analysis::Type* calleeType = context()->get_type_mgr()->GetType(calleeTypeId); |
632 | 35.4k | if (calleeType->AsVoid() == nullptr) { |
633 | 16.2k | returnVarId = CreateReturnVar(calleeFn, new_vars); |
634 | 16.2k | if (returnVarId == 0) { |
635 | 0 | return false; |
636 | 0 | } |
637 | 16.2k | } |
638 | | |
639 | 1.75M | calleeFn->WhileEachInst([&callee2caller, this](const Instruction* cpi) { |
640 | | // Create set of callee result ids. Used to detect forward references |
641 | 1.75M | const uint32_t rid = cpi->result_id(); |
642 | 1.75M | if (rid != 0 && callee2caller.find(rid) == callee2caller.end()) { |
643 | 997k | const uint32_t nid = context()->TakeNextId(); |
644 | 997k | if (nid == 0) return false; |
645 | 997k | callee2caller[rid] = nid; |
646 | 997k | } |
647 | 1.75M | return true; |
648 | 1.75M | }); |
649 | | |
650 | | // Inline DebugClare instructions in the callee's header. |
651 | 35.4k | calleeFn->ForEachDebugInstructionsInHeader( |
652 | 35.4k | [&new_blk_ptr, &callee2caller, &inlined_at_ctx, this](Instruction* inst) { |
653 | 0 | InlineSingleInstruction( |
654 | 0 | callee2caller, new_blk_ptr.get(), inst, |
655 | 0 | context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( |
656 | 0 | inst->GetDebugScope().GetInlinedAt(), &inlined_at_ctx)); |
657 | 0 | }); |
658 | | |
659 | | // Inline the entry block of the callee function. |
660 | 35.4k | if (!InlineEntryBlock(callee2caller, &new_blk_ptr, calleeFn->begin(), |
661 | 35.4k | &inlined_at_ctx)) { |
662 | 0 | return false; |
663 | 0 | } |
664 | | |
665 | | // Inline blocks of the callee function other than the entry block. |
666 | 35.4k | new_blk_ptr = |
667 | 35.4k | InlineBasicBlocks(new_blocks, callee2caller, std::move(new_blk_ptr), |
668 | 35.4k | &inlined_at_ctx, calleeFn); |
669 | 35.4k | if (new_blk_ptr == nullptr) return false; |
670 | | |
671 | 35.4k | new_blk_ptr = InlineReturn(callee2caller, new_blocks, std::move(new_blk_ptr), |
672 | 35.4k | &inlined_at_ctx, calleeFn, |
673 | 35.4k | &*(calleeFn->tail()->tail()), returnVarId); |
674 | | |
675 | | // Load return value into result id of call, if it exists. |
676 | 35.4k | if (returnVarId != 0) { |
677 | 16.2k | const uint32_t resId = call_inst_itr->result_id(); |
678 | 16.2k | assert(resId != 0); |
679 | 16.2k | AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr, |
680 | 16.2k | call_inst_itr->dbg_line_inst(), call_inst_itr->GetDebugScope()); |
681 | 16.2k | } |
682 | | |
683 | | // Move instructions of original caller block after call instruction. |
684 | 35.4k | if (!MoveCallerInstsAfterFunctionCall(&preCallSB, &postCallSB, &new_blk_ptr, |
685 | 35.4k | call_inst_itr, |
686 | 35.4k | calleeFn->begin() != calleeFn->end())) |
687 | 0 | return false; |
688 | | |
689 | | // Finalize inline code. |
690 | 35.4k | new_blocks->push_back(std::move(new_blk_ptr)); |
691 | | |
692 | 35.4k | if (caller_is_loop_header && (new_blocks->size() > 1)) { |
693 | 8 | MoveLoopMergeInstToFirstBlock(new_blocks); |
694 | | |
695 | | // If the loop was a single basic block previously, update it's structure. |
696 | 8 | auto& header = new_blocks->front(); |
697 | 8 | auto* merge_inst = header->GetLoopMergeInst(); |
698 | 8 | if (merge_inst->GetSingleWordInOperand(1u) == header->id()) { |
699 | 8 | auto new_id = context()->TakeNextId(); |
700 | 8 | if (new_id == 0) return false; |
701 | 8 | UpdateSingleBlockLoopContinueTarget(new_id, new_blocks); |
702 | 8 | } |
703 | 8 | } |
704 | | |
705 | | // Update block map given replacement blocks. |
706 | 238k | for (auto& blk : *new_blocks) { |
707 | 238k | id2block_[blk->id()] = &*blk; |
708 | 238k | } |
709 | | |
710 | | // We need to kill the name and decorations for the call, which will be |
711 | | // deleted. |
712 | 35.4k | context()->KillNamesAndDecorates(&*call_inst_itr); |
713 | | |
714 | 35.4k | return true; |
715 | 35.4k | } |
716 | | |
717 | 2.65M | bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) { |
718 | 2.65M | if (inst->opcode() != spv::Op::OpFunctionCall) return false; |
719 | 41.4k | const uint32_t calleeFnId = |
720 | 41.4k | inst->GetSingleWordOperand(kSpvFunctionCallFunctionId); |
721 | 41.4k | const auto ci = inlinable_.find(calleeFnId); |
722 | 41.4k | if (ci == inlinable_.cend()) return false; |
723 | | |
724 | 35.4k | if (early_return_funcs_.find(calleeFnId) != early_return_funcs_.end()) { |
725 | | // We rely on the merge-return pass to handle the early return case |
726 | | // in advance. |
727 | 0 | std::string message = |
728 | 0 | "The function '" + id2function_[calleeFnId]->DefInst().PrettyPrint() + |
729 | 0 | "' could not be inlined because the return instruction " |
730 | 0 | "is not at the end of the function. This could be fixed by " |
731 | 0 | "running merge-return before inlining."; |
732 | 0 | consumer()(SPV_MSG_WARNING, "", {0, 0, 0}, message.c_str()); |
733 | 0 | return false; |
734 | 0 | } |
735 | | |
736 | 35.4k | return true; |
737 | 35.4k | } |
738 | | |
739 | | void InlinePass::UpdateSucceedingPhis( |
740 | 19.2k | std::vector<std::unique_ptr<BasicBlock>>& new_blocks) { |
741 | 19.2k | const auto firstBlk = new_blocks.begin(); |
742 | 19.2k | const auto lastBlk = new_blocks.end() - 1; |
743 | 19.2k | const uint32_t firstId = (*firstBlk)->id(); |
744 | 19.2k | const uint32_t lastId = (*lastBlk)->id(); |
745 | 19.2k | const BasicBlock& const_last_block = *lastBlk->get(); |
746 | 19.2k | const_last_block.ForEachSuccessorLabel( |
747 | 22.8k | [&firstId, &lastId, this](const uint32_t succ) { |
748 | 22.8k | BasicBlock* sbp = this->id2block_[succ]; |
749 | 22.8k | sbp->ForEachPhiInst([&firstId, &lastId](Instruction* phi) { |
750 | 3.93k | phi->ForEachInId([&firstId, &lastId](uint32_t* id) { |
751 | 3.93k | if (*id == firstId) *id = lastId; |
752 | 3.93k | }); |
753 | 983 | }); |
754 | 22.8k | }); |
755 | 19.2k | } |
756 | | |
757 | 19.7k | bool InlinePass::HasNoReturnInLoop(Function* func) { |
758 | | // If control not structured, do not do loop/return analysis |
759 | | // TODO: Analyze returns in non-structured control flow |
760 | 19.7k | if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) |
761 | 19 | return false; |
762 | 19.7k | const auto structured_analysis = context()->GetStructuredCFGAnalysis(); |
763 | | // Search for returns in structured construct. |
764 | 19.7k | bool return_in_loop = false; |
765 | 233k | for (auto& blk : *func) { |
766 | 233k | auto terminal_ii = blk.cend(); |
767 | 233k | --terminal_ii; |
768 | 233k | if (spvOpcodeIsReturn(terminal_ii->opcode()) && |
769 | 18.1k | structured_analysis->ContainingLoop(blk.id()) != 0) { |
770 | 157 | return_in_loop = true; |
771 | 157 | break; |
772 | 157 | } |
773 | 233k | } |
774 | 19.7k | return !return_in_loop; |
775 | 19.7k | } |
776 | | |
777 | 19.7k | void InlinePass::AnalyzeReturns(Function* func) { |
778 | | // Analyze functions without a return in loop. |
779 | 19.7k | if (HasNoReturnInLoop(func)) { |
780 | 19.5k | no_return_in_loop_.insert(func->result_id()); |
781 | 19.5k | } |
782 | | // Analyze functions with a return before its tail basic block. |
783 | 231k | for (auto& blk : *func) { |
784 | 231k | auto terminal_ii = blk.cend(); |
785 | 231k | --terminal_ii; |
786 | 231k | if (spvOpcodeIsReturn(terminal_ii->opcode()) && &blk != func->tail()) { |
787 | 431 | early_return_funcs_.insert(func->result_id()); |
788 | 431 | break; |
789 | 431 | } |
790 | 231k | } |
791 | 19.7k | } |
792 | | |
793 | 23.7k | bool InlinePass::IsInlinableFunction(Function* func) { |
794 | | // We can only inline a function if it has blocks. |
795 | 23.7k | if (func->cbegin() == func->cend()) return false; |
796 | | |
797 | | // Do not inline functions with DontInline flag. |
798 | 23.7k | if (func->control_mask() & uint32_t(spv::FunctionControlMask::DontInline)) { |
799 | 3.99k | return false; |
800 | 3.99k | } |
801 | | |
802 | | // Do not inline functions with returns in loops. Currently early return |
803 | | // functions are inlined by wrapping them in a one trip loop and implementing |
804 | | // the returns as a branch to the loop's merge block. However, this can only |
805 | | // done validly if the return was not in a loop in the original function. |
806 | | // Also remember functions with multiple (early) returns. |
807 | 19.7k | AnalyzeReturns(func); |
808 | 19.7k | if (no_return_in_loop_.find(func->result_id()) == no_return_in_loop_.cend()) { |
809 | 176 | return false; |
810 | 176 | } |
811 | | |
812 | 19.5k | if (func->IsRecursive()) { |
813 | 35 | return false; |
814 | 35 | } |
815 | | |
816 | | // Do not inline functions with an abort instruction if they are called from a |
817 | | // continue construct. If it is inlined into a continue construct the backedge |
818 | | // will no longer post-dominate the continue target, which is invalid. An |
819 | | // `OpUnreachable` is acceptable because it will not change post-dominance if |
820 | | // it is statically unreachable. |
821 | 19.5k | bool func_is_called_from_continue = |
822 | 19.5k | funcs_called_from_continue_.count(func->result_id()) != 0; |
823 | | |
824 | 19.5k | if (func_is_called_from_continue && ContainsAbortOtherThanUnreachable(func)) { |
825 | 10 | return false; |
826 | 10 | } |
827 | | |
828 | 19.5k | return true; |
829 | 19.5k | } |
830 | | |
831 | 80 | bool InlinePass::ContainsAbortOtherThanUnreachable(Function* func) const { |
832 | 2.02k | return !func->WhileEachInst([](Instruction* inst) { |
833 | 2.02k | return inst->opcode() == spv::Op::OpUnreachable || |
834 | 2.00k | !spvOpcodeIsAbort(inst->opcode()); |
835 | 2.02k | }); |
836 | 80 | } |
837 | | |
838 | 16.1k | void InlinePass::InitializeInline() { |
839 | 16.1k | false_id_ = 0; |
840 | | |
841 | | // clear collections |
842 | 16.1k | id2function_.clear(); |
843 | 16.1k | id2block_.clear(); |
844 | 16.1k | inlinable_.clear(); |
845 | 16.1k | no_return_in_loop_.clear(); |
846 | 16.1k | early_return_funcs_.clear(); |
847 | 16.1k | funcs_called_from_continue_ = |
848 | 16.1k | context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue(); |
849 | | |
850 | 23.7k | for (auto& fn : *get_module()) { |
851 | | // Initialize function and block maps. |
852 | 23.7k | id2function_[fn.result_id()] = &fn; |
853 | 251k | for (auto& blk : fn) { |
854 | 251k | id2block_[blk.id()] = &blk; |
855 | 251k | } |
856 | | // Compute inlinability |
857 | 23.7k | if (IsInlinableFunction(&fn)) inlinable_.insert(fn.result_id()); |
858 | 23.7k | } |
859 | 16.1k | } |
860 | | |
861 | 29.9k | InlinePass::InlinePass() {} |
862 | | |
863 | 3.35k | void InlinePass::FixDebugDeclares(Function* func) { |
864 | 3.35k | std::map<uint32_t, Instruction*> access_chains; |
865 | 3.35k | std::vector<Instruction*> debug_declare_insts; |
866 | | |
867 | 1.92M | func->ForEachInst([&access_chains, &debug_declare_insts](Instruction* inst) { |
868 | 1.92M | if (inst->opcode() == spv::Op::OpAccessChain) { |
869 | 179k | access_chains[inst->result_id()] = inst; |
870 | 179k | } |
871 | 1.92M | if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) { |
872 | 0 | debug_declare_insts.push_back(inst); |
873 | 0 | } |
874 | 1.92M | }); |
875 | | |
876 | 3.35k | for (auto& inst : debug_declare_insts) { |
877 | 0 | FixDebugDeclare(inst, access_chains); |
878 | 0 | } |
879 | 3.35k | } |
880 | | |
881 | | void InlinePass::FixDebugDeclare( |
882 | | Instruction* dbg_declare_inst, |
883 | 0 | const std::map<uint32_t, Instruction*>& access_chains) { |
884 | 0 | do { |
885 | 0 | uint32_t var_id = |
886 | 0 | dbg_declare_inst->GetSingleWordInOperand(kSpvDebugDeclareVarInIdx); |
887 | | |
888 | | // The def-use chains are not kept up to date while inlining, so we need to |
889 | | // get the variable by traversing the functions. |
890 | 0 | auto it = access_chains.find(var_id); |
891 | 0 | if (it == access_chains.end()) { |
892 | 0 | return; |
893 | 0 | } |
894 | 0 | Instruction* access_chain = it->second; |
895 | | |
896 | | // If the variable id in the debug declare is an access chain, it is |
897 | | // invalid. it needs to be fixed up. The debug declare will be updated so |
898 | | // that its Var operand becomes the base of the access chain. The indexes of |
899 | | // the access chain are prepended before the indexes of the debug declare. |
900 | |
|
901 | 0 | std::vector<Operand> operands; |
902 | 0 | for (int i = 0; i < kSpvDebugDeclareVarInIdx; i++) { |
903 | 0 | operands.push_back(dbg_declare_inst->GetInOperand(i)); |
904 | 0 | } |
905 | |
|
906 | 0 | uint32_t access_chain_base = |
907 | 0 | access_chain->GetSingleWordInOperand(kSpvAccessChainBaseInIdx); |
908 | 0 | operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {access_chain_base})); |
909 | 0 | operands.push_back( |
910 | 0 | dbg_declare_inst->GetInOperand(kSpvDebugDeclareVarInIdx + 1)); |
911 | |
|
912 | 0 | for (uint32_t i = kSpvAccessChainBaseInIdx + 1; |
913 | 0 | i < access_chain->NumInOperands(); ++i) { |
914 | 0 | operands.push_back(access_chain->GetInOperand(i)); |
915 | 0 | } |
916 | |
|
917 | 0 | for (uint32_t i = kSpvDebugDeclareVarInIdx + 2; |
918 | 0 | i < dbg_declare_inst->NumInOperands(); ++i) { |
919 | 0 | operands.push_back(dbg_declare_inst->GetInOperand(i)); |
920 | 0 | } |
921 | |
|
922 | 0 | dbg_declare_inst->SetInOperands(std::move(operands)); |
923 | 0 | } while (true); |
924 | 0 | } |
925 | | |
926 | | } // namespace opt |
927 | | } // namespace spvtools |