• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AddPointerToType(uint32_t type_id,spv::StorageClass storage_class)37 uint32_t InlinePass::AddPointerToType(uint32_t type_id,
38                                       spv::StorageClass storage_class) {
39   uint32_t resultId = context()->TakeNextId();
40   if (resultId == 0) {
41     return resultId;
42   }
43 
44   std::unique_ptr<Instruction> type_inst(
45       new Instruction(context(), spv::Op::OpTypePointer, 0, resultId,
46                       {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
47                         {uint32_t(storage_class)}},
48                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}}));
49   context()->AddType(std::move(type_inst));
50   analysis::Type* pointeeTy;
51   std::unique_ptr<analysis::Pointer> pointerTy;
52   std::tie(pointeeTy, pointerTy) =
53       context()->get_type_mgr()->GetTypeAndPointerType(
54           type_id, spv::StorageClass::Function);
55   context()->get_type_mgr()->RegisterType(resultId, *pointerTy);
56   return resultId;
57 }
58 
AddBranch(uint32_t label_id,std::unique_ptr<BasicBlock> * block_ptr)59 void InlinePass::AddBranch(uint32_t label_id,
60                            std::unique_ptr<BasicBlock>* block_ptr) {
61   std::unique_ptr<Instruction> newBranch(
62       new Instruction(context(), spv::Op::OpBranch, 0, 0,
63                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}}));
64   (*block_ptr)->AddInstruction(std::move(newBranch));
65 }
66 
AddBranchCond(uint32_t cond_id,uint32_t true_id,uint32_t false_id,std::unique_ptr<BasicBlock> * block_ptr)67 void InlinePass::AddBranchCond(uint32_t cond_id, uint32_t true_id,
68                                uint32_t false_id,
69                                std::unique_ptr<BasicBlock>* block_ptr) {
70   std::unique_ptr<Instruction> newBranch(
71       new Instruction(context(), spv::Op::OpBranchConditional, 0, 0,
72                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}},
73                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}},
74                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}}));
75   (*block_ptr)->AddInstruction(std::move(newBranch));
76 }
77 
AddLoopMerge(uint32_t merge_id,uint32_t continue_id,std::unique_ptr<BasicBlock> * block_ptr)78 void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id,
79                               std::unique_ptr<BasicBlock>* block_ptr) {
80   std::unique_ptr<Instruction> newLoopMerge(new Instruction(
81       context(), spv::Op::OpLoopMerge, 0, 0,
82       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}},
83        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}},
84        {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {0}}}));
85   (*block_ptr)->AddInstruction(std::move(newLoopMerge));
86 }
87 
AddStore(uint32_t ptr_id,uint32_t val_id,std::unique_ptr<BasicBlock> * block_ptr,const Instruction * line_inst,const DebugScope & dbg_scope)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                           const DebugScope& dbg_scope) {
92   std::unique_ptr<Instruction> newStore(
93       new Instruction(context(), spv::Op::OpStore, 0, 0,
94                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}},
95                        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}}));
96   if (line_inst != nullptr) {
97     newStore->AddDebugLine(line_inst);
98   }
99   newStore->SetDebugScope(dbg_scope);
100   (*block_ptr)->AddInstruction(std::move(newStore));
101 }
102 
AddLoad(uint32_t type_id,uint32_t resultId,uint32_t ptr_id,std::unique_ptr<BasicBlock> * block_ptr,const Instruction * line_inst,const DebugScope & dbg_scope)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                          const DebugScope& dbg_scope) {
107   std::unique_ptr<Instruction> newLoad(
108       new Instruction(context(), spv::Op::OpLoad, type_id, resultId,
109                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}}));
110   if (line_inst != nullptr) {
111     newLoad->AddDebugLine(line_inst);
112   }
113   newLoad->SetDebugScope(dbg_scope);
114   (*block_ptr)->AddInstruction(std::move(newLoad));
115 }
116 
NewLabel(uint32_t label_id)117 std::unique_ptr<Instruction> InlinePass::NewLabel(uint32_t label_id) {
118   std::unique_ptr<Instruction> newLabel(
119       new Instruction(context(), spv::Op::OpLabel, 0, label_id, {}));
120   return newLabel;
121 }
122 
GetFalseId()123 uint32_t InlinePass::GetFalseId() {
124   if (false_id_ != 0) return false_id_;
125   false_id_ = get_module()->GetGlobalValue(spv::Op::OpConstantFalse);
126   if (false_id_ != 0) return false_id_;
127   uint32_t boolId = get_module()->GetGlobalValue(spv::Op::OpTypeBool);
128   if (boolId == 0) {
129     boolId = context()->TakeNextId();
130     if (boolId == 0) {
131       return 0;
132     }
133     get_module()->AddGlobalValue(spv::Op::OpTypeBool, boolId, 0);
134   }
135   false_id_ = context()->TakeNextId();
136   if (false_id_ == 0) {
137     return 0;
138   }
139   get_module()->AddGlobalValue(spv::Op::OpConstantFalse, false_id_, boolId);
140   return false_id_;
141 }
142 
MapParams(Function * calleeFn,BasicBlock::iterator call_inst_itr,std::unordered_map<uint32_t,uint32_t> * callee2caller)143 void InlinePass::MapParams(
144     Function* calleeFn, BasicBlock::iterator call_inst_itr,
145     std::unordered_map<uint32_t, uint32_t>* callee2caller) {
146   int param_idx = 0;
147   calleeFn->ForEachParam(
148       [&call_inst_itr, &param_idx, &callee2caller](const Instruction* cpi) {
149         const uint32_t pid = cpi->result_id();
150         (*callee2caller)[pid] = call_inst_itr->GetSingleWordOperand(
151             kSpvFunctionCallArgumentId + param_idx);
152         ++param_idx;
153       });
154 }
155 
CloneAndMapLocals(Function * calleeFn,std::vector<std::unique_ptr<Instruction>> * new_vars,std::unordered_map<uint32_t,uint32_t> * callee2caller,analysis::DebugInlinedAtContext * inlined_at_ctx)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     analysis::DebugInlinedAtContext* inlined_at_ctx) {
160   auto callee_block_itr = calleeFn->begin();
161   auto callee_var_itr = callee_block_itr->begin();
162   while (callee_var_itr->opcode() == spv::Op::OpVariable ||
163          callee_var_itr->GetCommonDebugOpcode() ==
164              CommonDebugInfoDebugDeclare) {
165     if (callee_var_itr->opcode() != spv::Op::OpVariable) {
166       ++callee_var_itr;
167       continue;
168     }
169 
170     std::unique_ptr<Instruction> var_inst(callee_var_itr->Clone(context()));
171     uint32_t newId = context()->TakeNextId();
172     if (newId == 0) {
173       return false;
174     }
175     get_decoration_mgr()->CloneDecorations(callee_var_itr->result_id(), newId);
176     var_inst->SetResultId(newId);
177     var_inst->UpdateDebugInlinedAt(
178         context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
179             callee_var_itr->GetDebugInlinedAt(), inlined_at_ctx));
180     (*callee2caller)[callee_var_itr->result_id()] = newId;
181     new_vars->push_back(std::move(var_inst));
182     ++callee_var_itr;
183   }
184   return true;
185 }
186 
CreateReturnVar(Function * calleeFn,std::vector<std::unique_ptr<Instruction>> * new_vars)187 uint32_t InlinePass::CreateReturnVar(
188     Function* calleeFn, std::vector<std::unique_ptr<Instruction>>* new_vars) {
189   uint32_t returnVarId = 0;
190   const uint32_t calleeTypeId = calleeFn->type_id();
191   analysis::TypeManager* type_mgr = context()->get_type_mgr();
192   assert(type_mgr->GetType(calleeTypeId)->AsVoid() == nullptr &&
193          "Cannot create a return variable of type void.");
194   // Find or create ptr to callee return type.
195   uint32_t returnVarTypeId =
196       type_mgr->FindPointerToType(calleeTypeId, spv::StorageClass::Function);
197 
198   if (returnVarTypeId == 0) {
199     returnVarTypeId =
200         AddPointerToType(calleeTypeId, spv::StorageClass::Function);
201     if (returnVarTypeId == 0) {
202       return 0;
203     }
204   }
205 
206   // Add return var to new function scope variables.
207   returnVarId = context()->TakeNextId();
208   if (returnVarId == 0) {
209     return 0;
210   }
211 
212   std::unique_ptr<Instruction> var_inst(new Instruction(
213       context(), spv::Op::OpVariable, returnVarTypeId, returnVarId,
214       {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
215         {(uint32_t)spv::StorageClass::Function}}}));
216   new_vars->push_back(std::move(var_inst));
217   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   auto const pointee_type =
222       type_mgr->GetType(returnVarTypeId)->AsPointer()->pointee_type();
223   if (pointee_type->AsPointer() != nullptr) {
224     if (pointee_type->AsPointer()->storage_class() ==
225         spv::StorageClass::PhysicalStorageBuffer) {
226       get_decoration_mgr()->AddDecoration(
227           returnVarId, uint32_t(spv::Decoration::AliasedPointer));
228     }
229   }
230 
231   return returnVarId;
232 }
233 
IsSameBlockOp(const Instruction * inst) const234 bool InlinePass::IsSameBlockOp(const Instruction* inst) const {
235   return inst->opcode() == spv::Op::OpSampledImage ||
236          inst->opcode() == spv::Op::OpImage;
237 }
238 
CloneSameBlockOps(std::unique_ptr<Instruction> * inst,std::unordered_map<uint32_t,uint32_t> * postCallSB,std::unordered_map<uint32_t,Instruction * > * preCallSB,std::unique_ptr<BasicBlock> * block_ptr)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     std::unique_ptr<BasicBlock>* block_ptr) {
244   return (*inst)->WhileEachInId([&postCallSB, &preCallSB, &block_ptr,
245                                  this](uint32_t* iid) {
246     const auto mapItr = (*postCallSB).find(*iid);
247     if (mapItr == (*postCallSB).end()) {
248       const auto mapItr2 = (*preCallSB).find(*iid);
249       if (mapItr2 != (*preCallSB).end()) {
250         // Clone pre-call same-block ops, map result id.
251         const Instruction* inInst = mapItr2->second;
252         std::unique_ptr<Instruction> sb_inst(inInst->Clone(context()));
253         if (!CloneSameBlockOps(&sb_inst, postCallSB, preCallSB, block_ptr)) {
254           return false;
255         }
256 
257         const uint32_t rid = sb_inst->result_id();
258         const uint32_t nid = context()->TakeNextId();
259         if (nid == 0) {
260           return false;
261         }
262         get_decoration_mgr()->CloneDecorations(rid, nid);
263         sb_inst->SetResultId(nid);
264         (*postCallSB)[rid] = nid;
265         *iid = nid;
266         (*block_ptr)->AddInstruction(std::move(sb_inst));
267       }
268     } else {
269       // Reset same-block op operand.
270       *iid = mapItr->second;
271     }
272     return true;
273   });
274 }
275 
MoveInstsBeforeEntryBlock(std::unordered_map<uint32_t,Instruction * > * preCallSB,BasicBlock * new_blk_ptr,BasicBlock::iterator call_inst_itr,UptrVectorIterator<BasicBlock> call_block_itr)276 void InlinePass::MoveInstsBeforeEntryBlock(
277     std::unordered_map<uint32_t, Instruction*>* preCallSB,
278     BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr,
279     UptrVectorIterator<BasicBlock> call_block_itr) {
280   for (auto cii = call_block_itr->begin(); cii != call_inst_itr;
281        cii = call_block_itr->begin()) {
282     Instruction* inst = &*cii;
283     inst->RemoveFromList();
284     std::unique_ptr<Instruction> cp_inst(inst);
285     // Remember same-block ops for possible regeneration.
286     if (IsSameBlockOp(&*cp_inst)) {
287       auto* sb_inst_ptr = cp_inst.get();
288       (*preCallSB)[cp_inst->result_id()] = sb_inst_ptr;
289     }
290     new_blk_ptr->AddInstruction(std::move(cp_inst));
291   }
292 }
293 
AddGuardBlock(std::vector<std::unique_ptr<BasicBlock>> * new_blocks,std::unordered_map<uint32_t,uint32_t> * callee2caller,std::unique_ptr<BasicBlock> new_blk_ptr,uint32_t entry_blk_label_id)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     std::unique_ptr<BasicBlock> new_blk_ptr, uint32_t entry_blk_label_id) {
298   const auto guard_block_id = context()->TakeNextId();
299   if (guard_block_id == 0) {
300     return nullptr;
301   }
302   AddBranch(guard_block_id, &new_blk_ptr);
303   new_blocks->push_back(std::move(new_blk_ptr));
304   // Start the next block.
305   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   (*callee2caller)[entry_blk_label_id] = guard_block_id;
310   return new_blk_ptr;
311 }
312 
AddStoresForVariableInitializers(const std::unordered_map<uint32_t,uint32_t> & callee2caller,analysis::DebugInlinedAtContext * inlined_at_ctx,std::unique_ptr<BasicBlock> * new_blk_ptr,UptrVectorIterator<BasicBlock> callee_first_block_itr)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     UptrVectorIterator<BasicBlock> callee_first_block_itr) {
318   auto callee_itr = callee_first_block_itr->begin();
319   while (callee_itr->opcode() == spv::Op::OpVariable ||
320          callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
321     if (callee_itr->opcode() == spv::Op::OpVariable &&
322         callee_itr->NumInOperands() == 2) {
323       assert(callee2caller.count(callee_itr->result_id()) &&
324              "Expected the variable to have already been mapped.");
325       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       uint32_t val_id = callee_itr->GetSingleWordInOperand(1);
330       AddStore(new_var_id, val_id, new_blk_ptr, callee_itr->dbg_line_inst(),
331                context()->get_debug_info_mgr()->BuildDebugScope(
332                    callee_itr->GetDebugScope(), inlined_at_ctx));
333     }
334     if (callee_itr->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
335       InlineSingleInstruction(
336           callee2caller, new_blk_ptr->get(), &*callee_itr,
337           context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
338               callee_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx));
339     }
340     ++callee_itr;
341   }
342   return callee_itr;
343 }
344 
InlineSingleInstruction(const std::unordered_map<uint32_t,uint32_t> & callee2caller,BasicBlock * new_blk_ptr,const Instruction * inst,uint32_t dbg_inlined_at)345 bool InlinePass::InlineSingleInstruction(
346     const std::unordered_map<uint32_t, uint32_t>& callee2caller,
347     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   if (inst->opcode() == spv::Op::OpReturnValue ||
351       inst->opcode() == spv::Op::OpReturn)
352     return true;
353 
354   // Copy callee instruction and remap all input Ids.
355   std::unique_ptr<Instruction> cp_inst(inst->Clone(context()));
356   cp_inst->ForEachInId([&callee2caller](uint32_t* iid) {
357     const auto mapItr = callee2caller.find(*iid);
358     if (mapItr != callee2caller.end()) {
359       *iid = mapItr->second;
360     }
361   });
362 
363   // If result id is non-zero, remap it.
364   const uint32_t rid = cp_inst->result_id();
365   if (rid != 0) {
366     const auto mapItr = callee2caller.find(rid);
367     if (mapItr == callee2caller.end()) {
368       return false;
369     }
370     uint32_t nid = mapItr->second;
371     cp_inst->SetResultId(nid);
372     get_decoration_mgr()->CloneDecorations(rid, nid);
373   }
374 
375   cp_inst->UpdateDebugInlinedAt(dbg_inlined_at);
376   new_blk_ptr->AddInstruction(std::move(cp_inst));
377   return true;
378 }
379 
InlineReturn(const std::unordered_map<uint32_t,uint32_t> & callee2caller,std::vector<std::unique_ptr<BasicBlock>> * new_blocks,std::unique_ptr<BasicBlock> new_blk_ptr,analysis::DebugInlinedAtContext * inlined_at_ctx,Function * calleeFn,const Instruction * inst,uint32_t returnVarId)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     const Instruction* inst, uint32_t returnVarId) {
386   // Store return value to return variable.
387   if (inst->opcode() == spv::Op::OpReturnValue) {
388     assert(returnVarId != 0);
389     uint32_t valId = inst->GetInOperand(kSpvReturnValueId).words[0];
390     const auto mapItr = callee2caller.find(valId);
391     if (mapItr != callee2caller.end()) {
392       valId = mapItr->second;
393     }
394     AddStore(returnVarId, valId, &new_blk_ptr, inst->dbg_line_inst(),
395              context()->get_debug_info_mgr()->BuildDebugScope(
396                  inst->GetDebugScope(), inlined_at_ctx));
397   }
398 
399   uint32_t returnLabelId = 0;
400   for (auto callee_block_itr = calleeFn->begin();
401        callee_block_itr != calleeFn->end(); ++callee_block_itr) {
402     if (spvOpcodeIsAbort(callee_block_itr->tail()->opcode())) {
403       returnLabelId = context()->TakeNextId();
404       break;
405     }
406   }
407   if (returnLabelId == 0) return new_blk_ptr;
408 
409   if (inst->opcode() == spv::Op::OpReturn ||
410       inst->opcode() == spv::Op::OpReturnValue)
411     AddBranch(returnLabelId, &new_blk_ptr);
412   new_blocks->push_back(std::move(new_blk_ptr));
413   return MakeUnique<BasicBlock>(NewLabel(returnLabelId));
414 }
415 
InlineEntryBlock(const std::unordered_map<uint32_t,uint32_t> & callee2caller,std::unique_ptr<BasicBlock> * new_blk_ptr,UptrVectorIterator<BasicBlock> callee_first_block,analysis::DebugInlinedAtContext * inlined_at_ctx)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     analysis::DebugInlinedAtContext* inlined_at_ctx) {
421   auto callee_inst_itr = AddStoresForVariableInitializers(
422       callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block);
423 
424   while (callee_inst_itr != callee_first_block->end()) {
425     // Don't inline function definition links, the calling function is not a
426     // definition.
427     if (callee_inst_itr->GetShader100DebugOpcode() ==
428         NonSemanticShaderDebugInfo100DebugFunctionDefinition) {
429       ++callee_inst_itr;
430       continue;
431     }
432 
433     if (!InlineSingleInstruction(
434             callee2caller, new_blk_ptr->get(), &*callee_inst_itr,
435             context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
436                 callee_inst_itr->GetDebugScope().GetInlinedAt(),
437                 inlined_at_ctx))) {
438       return false;
439     }
440     ++callee_inst_itr;
441   }
442   return true;
443 }
444 
InlineBasicBlocks(std::vector<std::unique_ptr<BasicBlock>> * new_blocks,const std::unordered_map<uint32_t,uint32_t> & callee2caller,std::unique_ptr<BasicBlock> new_blk_ptr,analysis::DebugInlinedAtContext * inlined_at_ctx,Function * calleeFn)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     analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn) {
450   auto callee_block_itr = calleeFn->begin();
451   ++callee_block_itr;
452 
453   while (callee_block_itr != calleeFn->end()) {
454     new_blocks->push_back(std::move(new_blk_ptr));
455     const auto mapItr =
456         callee2caller.find(callee_block_itr->GetLabelInst()->result_id());
457     if (mapItr == callee2caller.end()) return nullptr;
458     new_blk_ptr = MakeUnique<BasicBlock>(NewLabel(mapItr->second));
459 
460     auto tail_inst_itr = callee_block_itr->end();
461     for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr;
462          ++inst_itr) {
463       // Don't inline function definition links, the calling function is not a
464       // definition
465       if (inst_itr->GetShader100DebugOpcode() ==
466           NonSemanticShaderDebugInfo100DebugFunctionDefinition)
467         continue;
468       if (!InlineSingleInstruction(
469               callee2caller, new_blk_ptr.get(), &*inst_itr,
470               context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
471                   inst_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx))) {
472         return nullptr;
473       }
474     }
475 
476     ++callee_block_itr;
477   }
478   return new_blk_ptr;
479 }
480 
MoveCallerInstsAfterFunctionCall(std::unordered_map<uint32_t,Instruction * > * preCallSB,std::unordered_map<uint32_t,uint32_t> * postCallSB,std::unique_ptr<BasicBlock> * new_blk_ptr,BasicBlock::iterator call_inst_itr,bool multiBlocks)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     BasicBlock::iterator call_inst_itr, bool multiBlocks) {
486   // Copy remaining instructions from caller block.
487   for (Instruction* inst = call_inst_itr->NextNode(); inst;
488        inst = call_inst_itr->NextNode()) {
489     inst->RemoveFromList();
490     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     if (multiBlocks) {
494       if (!CloneSameBlockOps(&cp_inst, postCallSB, preCallSB, new_blk_ptr)) {
495         return false;
496       }
497 
498       // Remember same-block ops in this block.
499       if (IsSameBlockOp(&*cp_inst)) {
500         const uint32_t rid = cp_inst->result_id();
501         (*postCallSB)[rid] = rid;
502       }
503     }
504     new_blk_ptr->get()->AddInstruction(std::move(cp_inst));
505   }
506 
507   return true;
508 }
509 
MoveLoopMergeInstToFirstBlock(std::vector<std::unique_ptr<BasicBlock>> * new_blocks)510 void InlinePass::MoveLoopMergeInstToFirstBlock(
511     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   auto& first = new_blocks->front();
515   auto& last = new_blocks->back();
516   assert(first != last);
517 
518   // Insert a modified copy of the loop merge into the first block.
519   auto loop_merge_itr = last->tail();
520   --loop_merge_itr;
521   assert(loop_merge_itr->opcode() == spv::Op::OpLoopMerge);
522   std::unique_ptr<Instruction> cp_inst(loop_merge_itr->Clone(context()));
523   first->tail().InsertBefore(std::move(cp_inst));
524 
525   // Remove the loop merge from the last block.
526   loop_merge_itr->RemoveFromList();
527   delete &*loop_merge_itr;
528 }
529 
UpdateSingleBlockLoopContinueTarget(uint32_t new_id,std::vector<std::unique_ptr<BasicBlock>> * new_blocks)530 void InlinePass::UpdateSingleBlockLoopContinueTarget(
531     uint32_t new_id, std::vector<std::unique_ptr<BasicBlock>>* new_blocks) {
532   auto& header = new_blocks->front();
533   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   std::unique_ptr<BasicBlock> new_block =
545       MakeUnique<BasicBlock>(NewLabel(new_id));
546   auto& old_backedge = new_blocks->back();
547   auto old_branch = old_backedge->tail();
548 
549   // Move the old back edge into the new block.
550   std::unique_ptr<Instruction> br(&*old_branch);
551   new_block->AddInstruction(std::move(br));
552 
553   // Add a branch to the new block from the old back-edge block.
554   AddBranch(new_id, &old_backedge);
555   new_blocks->push_back(std::move(new_block));
556 
557   // Update the loop's continue target to the new block.
558   merge_inst->SetInOperand(1u, {new_id});
559 }
560 
GenInlineCode(std::vector<std::unique_ptr<BasicBlock>> * new_blocks,std::vector<std::unique_ptr<Instruction>> * new_vars,BasicBlock::iterator call_inst_itr,UptrVectorIterator<BasicBlock> call_block_itr)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     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   std::unordered_map<uint32_t, uint32_t> callee2caller;
569   // Pre-call same-block insts
570   std::unordered_map<uint32_t, Instruction*> preCallSB;
571   // Post-call same-block op ids
572   std::unordered_map<uint32_t, uint32_t> postCallSB;
573 
574   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   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   bool caller_is_loop_header = call_block_itr->GetLoopMergeInst() != nullptr;
587 
588   // Single-trip loop continue block
589   std::unique_ptr<BasicBlock> single_trip_loop_cont_blk;
590 
591   Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand(
592       kSpvFunctionCallFunctionId)];
593 
594   // Map parameters to actual arguments.
595   MapParams(calleeFn, call_inst_itr, &callee2caller);
596 
597   // Define caller local variables for all callee variables and create map to
598   // them.
599   if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller, &inlined_at_ctx)) {
600     return false;
601   }
602 
603   // First block needs to use label of original block
604   // but map callee label in case of phi reference.
605   uint32_t entry_blk_label_id = calleeFn->begin()->GetLabelInst()->result_id();
606   callee2caller[entry_blk_label_id] = call_block_itr->id();
607   std::unique_ptr<BasicBlock> new_blk_ptr =
608       MakeUnique<BasicBlock>(NewLabel(call_block_itr->id()));
609 
610   // Move instructions of original caller block up to call instruction.
611   MoveInstsBeforeEntryBlock(&preCallSB, new_blk_ptr.get(), call_inst_itr,
612                             call_block_itr);
613 
614   if (caller_is_loop_header &&
615       (*(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     new_blk_ptr = AddGuardBlock(new_blocks, &callee2caller,
624                                 std::move(new_blk_ptr), entry_blk_label_id);
625     if (new_blk_ptr == nullptr) return false;
626   }
627 
628   // Create return var if needed.
629   const uint32_t calleeTypeId = calleeFn->type_id();
630   uint32_t returnVarId = 0;
631   analysis::Type* calleeType = context()->get_type_mgr()->GetType(calleeTypeId);
632   if (calleeType->AsVoid() == nullptr) {
633     returnVarId = CreateReturnVar(calleeFn, new_vars);
634     if (returnVarId == 0) {
635       return false;
636     }
637   }
638 
639   calleeFn->WhileEachInst([&callee2caller, this](const Instruction* cpi) {
640     // Create set of callee result ids. Used to detect forward references
641     const uint32_t rid = cpi->result_id();
642     if (rid != 0 && callee2caller.find(rid) == callee2caller.end()) {
643       const uint32_t nid = context()->TakeNextId();
644       if (nid == 0) return false;
645       callee2caller[rid] = nid;
646     }
647     return true;
648   });
649 
650   // Inline DebugClare instructions in the callee's header.
651   calleeFn->ForEachDebugInstructionsInHeader(
652       [&new_blk_ptr, &callee2caller, &inlined_at_ctx, this](Instruction* inst) {
653         InlineSingleInstruction(
654             callee2caller, new_blk_ptr.get(), inst,
655             context()->get_debug_info_mgr()->BuildDebugInlinedAtChain(
656                 inst->GetDebugScope().GetInlinedAt(), &inlined_at_ctx));
657       });
658 
659   // Inline the entry block of the callee function.
660   if (!InlineEntryBlock(callee2caller, &new_blk_ptr, calleeFn->begin(),
661                         &inlined_at_ctx)) {
662     return false;
663   }
664 
665   // Inline blocks of the callee function other than the entry block.
666   new_blk_ptr =
667       InlineBasicBlocks(new_blocks, callee2caller, std::move(new_blk_ptr),
668                         &inlined_at_ctx, calleeFn);
669   if (new_blk_ptr == nullptr) return false;
670 
671   new_blk_ptr = InlineReturn(callee2caller, new_blocks, std::move(new_blk_ptr),
672                              &inlined_at_ctx, calleeFn,
673                              &*(calleeFn->tail()->tail()), returnVarId);
674 
675   // Load return value into result id of call, if it exists.
676   if (returnVarId != 0) {
677     const uint32_t resId = call_inst_itr->result_id();
678     assert(resId != 0);
679     AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr,
680             call_inst_itr->dbg_line_inst(), call_inst_itr->GetDebugScope());
681   }
682 
683   // Move instructions of original caller block after call instruction.
684   if (!MoveCallerInstsAfterFunctionCall(&preCallSB, &postCallSB, &new_blk_ptr,
685                                         call_inst_itr,
686                                         calleeFn->begin() != calleeFn->end()))
687     return false;
688 
689   // Finalize inline code.
690   new_blocks->push_back(std::move(new_blk_ptr));
691 
692   if (caller_is_loop_header && (new_blocks->size() > 1)) {
693     MoveLoopMergeInstToFirstBlock(new_blocks);
694 
695     // If the loop was a single basic block previously, update it's structure.
696     auto& header = new_blocks->front();
697     auto* merge_inst = header->GetLoopMergeInst();
698     if (merge_inst->GetSingleWordInOperand(1u) == header->id()) {
699       auto new_id = context()->TakeNextId();
700       if (new_id == 0) return false;
701       UpdateSingleBlockLoopContinueTarget(new_id, new_blocks);
702     }
703   }
704 
705   // Update block map given replacement blocks.
706   for (auto& blk : *new_blocks) {
707     id2block_[blk->id()] = &*blk;
708   }
709 
710   // We need to kill the name and decorations for the call, which will be
711   // deleted.
712   context()->KillNamesAndDecorates(&*call_inst_itr);
713 
714   return true;
715 }
716 
IsInlinableFunctionCall(const Instruction * inst)717 bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) {
718   if (inst->opcode() != spv::Op::OpFunctionCall) return false;
719   const uint32_t calleeFnId =
720       inst->GetSingleWordOperand(kSpvFunctionCallFunctionId);
721   const auto ci = inlinable_.find(calleeFnId);
722   if (ci == inlinable_.cend()) return false;
723 
724   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     std::string message =
728         "The function '" + id2function_[calleeFnId]->DefInst().PrettyPrint() +
729         "' could not be inlined because the return instruction "
730         "is not at the end of the function. This could be fixed by "
731         "running merge-return before inlining.";
732     consumer()(SPV_MSG_WARNING, "", {0, 0, 0}, message.c_str());
733     return false;
734   }
735 
736   return true;
737 }
738 
UpdateSucceedingPhis(std::vector<std::unique_ptr<BasicBlock>> & new_blocks)739 void InlinePass::UpdateSucceedingPhis(
740     std::vector<std::unique_ptr<BasicBlock>>& new_blocks) {
741   const auto firstBlk = new_blocks.begin();
742   const auto lastBlk = new_blocks.end() - 1;
743   const uint32_t firstId = (*firstBlk)->id();
744   const uint32_t lastId = (*lastBlk)->id();
745   const BasicBlock& const_last_block = *lastBlk->get();
746   const_last_block.ForEachSuccessorLabel(
747       [&firstId, &lastId, this](const uint32_t succ) {
748         BasicBlock* sbp = this->id2block_[succ];
749         sbp->ForEachPhiInst([&firstId, &lastId](Instruction* phi) {
750           phi->ForEachInId([&firstId, &lastId](uint32_t* id) {
751             if (*id == firstId) *id = lastId;
752           });
753         });
754       });
755 }
756 
HasNoReturnInLoop(Function * func)757 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   if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
761     return false;
762   const auto structured_analysis = context()->GetStructuredCFGAnalysis();
763   // Search for returns in structured construct.
764   bool return_in_loop = false;
765   for (auto& blk : *func) {
766     auto terminal_ii = blk.cend();
767     --terminal_ii;
768     if (spvOpcodeIsReturn(terminal_ii->opcode()) &&
769         structured_analysis->ContainingLoop(blk.id()) != 0) {
770       return_in_loop = true;
771       break;
772     }
773   }
774   return !return_in_loop;
775 }
776 
AnalyzeReturns(Function * func)777 void InlinePass::AnalyzeReturns(Function* func) {
778   // Analyze functions without a return in loop.
779   if (HasNoReturnInLoop(func)) {
780     no_return_in_loop_.insert(func->result_id());
781   }
782   // Analyze functions with a return before its tail basic block.
783   for (auto& blk : *func) {
784     auto terminal_ii = blk.cend();
785     --terminal_ii;
786     if (spvOpcodeIsReturn(terminal_ii->opcode()) && &blk != func->tail()) {
787       early_return_funcs_.insert(func->result_id());
788       break;
789     }
790   }
791 }
792 
IsInlinableFunction(Function * func)793 bool InlinePass::IsInlinableFunction(Function* func) {
794   // We can only inline a function if it has blocks.
795   if (func->cbegin() == func->cend()) return false;
796 
797   // Do not inline functions with DontInline flag.
798   if (func->control_mask() & uint32_t(spv::FunctionControlMask::DontInline)) {
799     return false;
800   }
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   AnalyzeReturns(func);
808   if (no_return_in_loop_.find(func->result_id()) == no_return_in_loop_.cend()) {
809     return false;
810   }
811 
812   if (func->IsRecursive()) {
813     return false;
814   }
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   bool func_is_called_from_continue =
822       funcs_called_from_continue_.count(func->result_id()) != 0;
823 
824   if (func_is_called_from_continue && ContainsAbortOtherThanUnreachable(func)) {
825     return false;
826   }
827 
828   return true;
829 }
830 
ContainsAbortOtherThanUnreachable(Function * func) const831 bool InlinePass::ContainsAbortOtherThanUnreachable(Function* func) const {
832   return !func->WhileEachInst([](Instruction* inst) {
833     return inst->opcode() == spv::Op::OpUnreachable ||
834            !spvOpcodeIsAbort(inst->opcode());
835   });
836 }
837 
InitializeInline()838 void InlinePass::InitializeInline() {
839   false_id_ = 0;
840 
841   // clear collections
842   id2function_.clear();
843   id2block_.clear();
844   inlinable_.clear();
845   no_return_in_loop_.clear();
846   early_return_funcs_.clear();
847   funcs_called_from_continue_ =
848       context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue();
849 
850   for (auto& fn : *get_module()) {
851     // Initialize function and block maps.
852     id2function_[fn.result_id()] = &fn;
853     for (auto& blk : fn) {
854       id2block_[blk.id()] = &blk;
855     }
856     // Compute inlinability
857     if (IsInlinableFunction(&fn)) inlinable_.insert(fn.result_id());
858   }
859 }
860 
InlinePass()861 InlinePass::InlinePass() {}
862 
FixDebugDeclares(Function * func)863 void InlinePass::FixDebugDeclares(Function* func) {
864   std::map<uint32_t, Instruction*> access_chains;
865   std::vector<Instruction*> debug_declare_insts;
866 
867   func->ForEachInst([&access_chains, &debug_declare_insts](Instruction* inst) {
868     if (inst->opcode() == spv::Op::OpAccessChain) {
869       access_chains[inst->result_id()] = inst;
870     }
871     if (inst->GetCommonDebugOpcode() == CommonDebugInfoDebugDeclare) {
872       debug_declare_insts.push_back(inst);
873     }
874   });
875 
876   for (auto& inst : debug_declare_insts) {
877     FixDebugDeclare(inst, access_chains);
878   }
879 }
880 
FixDebugDeclare(Instruction * dbg_declare_inst,const std::map<uint32_t,Instruction * > & access_chains)881 void InlinePass::FixDebugDeclare(
882     Instruction* dbg_declare_inst,
883     const std::map<uint32_t, Instruction*>& access_chains) {
884   do {
885     uint32_t var_id =
886         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     auto it = access_chains.find(var_id);
891     if (it == access_chains.end()) {
892       return;
893     }
894     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     std::vector<Operand> operands;
902     for (int i = 0; i < kSpvDebugDeclareVarInIdx; i++) {
903       operands.push_back(dbg_declare_inst->GetInOperand(i));
904     }
905 
906     uint32_t access_chain_base =
907         access_chain->GetSingleWordInOperand(kSpvAccessChainBaseInIdx);
908     operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {access_chain_base}));
909     operands.push_back(
910         dbg_declare_inst->GetInOperand(kSpvDebugDeclareVarInIdx + 1));
911 
912     for (uint32_t i = kSpvAccessChainBaseInIdx + 1;
913          i < access_chain->NumInOperands(); ++i) {
914       operands.push_back(access_chain->GetInOperand(i));
915     }
916 
917     for (uint32_t i = kSpvDebugDeclareVarInIdx + 2;
918          i < dbg_declare_inst->NumInOperands(); ++i) {
919       operands.push_back(dbg_declare_inst->GetInOperand(i));
920     }
921 
922     dbg_declare_inst->SetInOperands(std::move(operands));
923   } while (true);
924 }
925 
926 }  // namespace opt
927 }  // namespace spvtools
928