• 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 // Copyright (c) 2018-2021 Google LLC
5 //
6 // Licensed under the Apache License, Version 2.0 (the "License");
7 // you may not use this file except in compliance with the License.
8 // You may obtain a copy of the License at
9 //
10 //     http://www.apache.org/licenses/LICENSE-2.0
11 //
12 // Unless required by applicable law or agreed to in writing, software
13 // distributed under the License is distributed on an "AS IS" BASIS,
14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 // See the License for the specific language governing permissions and
16 // limitations under the License.
17 
18 #include "source/opt/aggressive_dead_code_elim_pass.h"
19 
20 #include <memory>
21 #include <stack>
22 
23 #include "source/cfa.h"
24 #include "source/opt/eliminate_dead_functions_util.h"
25 #include "source/opt/ir_builder.h"
26 #include "source/opt/reflect.h"
27 #include "source/spirv_constant.h"
28 #include "source/util/string_utils.h"
29 
30 namespace spvtools {
31 namespace opt {
32 namespace {
33 
34 constexpr uint32_t kTypePointerStorageClassInIdx = 0;
35 constexpr uint32_t kEntryPointFunctionIdInIdx = 1;
36 constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
37 constexpr uint32_t kLoopMergeContinueBlockIdInIdx = 1;
38 constexpr uint32_t kCopyMemoryTargetAddrInIdx = 0;
39 constexpr uint32_t kCopyMemorySourceAddrInIdx = 1;
40 constexpr uint32_t kLoadSourceAddrInIdx = 0;
41 constexpr uint32_t kDebugDeclareOperandVariableIndex = 5;
42 constexpr uint32_t kGlobalVariableVariableIndex = 12;
43 
44 // Sorting functor to present annotation instructions in an easy-to-process
45 // order. The functor orders by opcode first and falls back on unique id
46 // ordering if both instructions have the same opcode.
47 //
48 // Desired priority:
49 // spv::Op::OpGroupDecorate
50 // spv::Op::OpGroupMemberDecorate
51 // spv::Op::OpDecorate
52 // spv::Op::OpMemberDecorate
53 // spv::Op::OpDecorateId
54 // spv::Op::OpDecorateStringGOOGLE
55 // spv::Op::OpDecorationGroup
56 struct DecorationLess {
operator ()spvtools::opt::__anon2f8be7a90111::DecorationLess57   bool operator()(const Instruction* lhs, const Instruction* rhs) const {
58     assert(lhs && rhs);
59     spv::Op lhsOp = lhs->opcode();
60     spv::Op rhsOp = rhs->opcode();
61     if (lhsOp != rhsOp) {
62 #define PRIORITY_CASE(opcode)                          \
63   if (lhsOp == opcode && rhsOp != opcode) return true; \
64   if (rhsOp == opcode && lhsOp != opcode) return false;
65       // OpGroupDecorate and OpGroupMember decorate are highest priority to
66       // eliminate dead targets early and simplify subsequent checks.
67       PRIORITY_CASE(spv::Op::OpGroupDecorate)
68       PRIORITY_CASE(spv::Op::OpGroupMemberDecorate)
69       PRIORITY_CASE(spv::Op::OpDecorate)
70       PRIORITY_CASE(spv::Op::OpMemberDecorate)
71       PRIORITY_CASE(spv::Op::OpDecorateId)
72       PRIORITY_CASE(spv::Op::OpDecorateStringGOOGLE)
73       // OpDecorationGroup is lowest priority to ensure use/def chains remain
74       // usable for instructions that target this group.
75       PRIORITY_CASE(spv::Op::OpDecorationGroup)
76 #undef PRIORITY_CASE
77     }
78 
79     // Fall back to maintain total ordering (compare unique ids).
80     return *lhs < *rhs;
81   }
82 };
83 
84 }  // namespace
85 
IsVarOfStorage(uint32_t varId,spv::StorageClass storageClass)86 bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId,
87                                        spv::StorageClass storageClass) {
88   if (varId == 0) return false;
89   const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
90   const spv::Op op = varInst->opcode();
91   if (op != spv::Op::OpVariable) return false;
92   const uint32_t varTypeId = varInst->type_id();
93   const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
94   if (varTypeInst->opcode() != spv::Op::OpTypePointer) return false;
95   return spv::StorageClass(varTypeInst->GetSingleWordInOperand(
96              kTypePointerStorageClassInIdx)) == storageClass;
97 }
98 
IsLocalVar(uint32_t varId,Function * func)99 bool AggressiveDCEPass::IsLocalVar(uint32_t varId, Function* func) {
100   if (IsVarOfStorage(varId, spv::StorageClass::Function)) {
101     return true;
102   }
103 
104   if (!IsVarOfStorage(varId, spv::StorageClass::Private) &&
105       !IsVarOfStorage(varId, spv::StorageClass::Workgroup)) {
106     return false;
107   }
108 
109   // For a variable in the Private or WorkGroup storage class, the variable will
110   // get a new instance for every call to an entry point.  If the entry point
111   // does not have a call, then no other function can read or write to that
112   // instance of the variable.
113   return IsEntryPointWithNoCalls(func);
114 }
115 
AddStores(Function * func,uint32_t ptrId)116 void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
117   get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId, func](Instruction* user) {
118     // If the user is not a part of |func|, skip it.
119     BasicBlock* blk = context()->get_instr_block(user);
120     if (blk && blk->GetParent() != func) return;
121 
122     switch (user->opcode()) {
123       case spv::Op::OpAccessChain:
124       case spv::Op::OpInBoundsAccessChain:
125       case spv::Op::OpCopyObject:
126         this->AddStores(func, user->result_id());
127         break;
128       case spv::Op::OpLoad:
129         break;
130       case spv::Op::OpCopyMemory:
131       case spv::Op::OpCopyMemorySized:
132         if (user->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx) == ptrId) {
133           AddToWorklist(user);
134         }
135         break;
136       // If default, assume it stores e.g. frexp, modf, function call
137       case spv::Op::OpStore:
138       default:
139         AddToWorklist(user);
140         break;
141     }
142   });
143 }
144 
AllExtensionsSupported() const145 bool AggressiveDCEPass::AllExtensionsSupported() const {
146   // If any extension not in allowlist, return false
147   for (auto& ei : get_module()->extensions()) {
148     const std::string extName = ei.GetInOperand(0).AsString();
149     if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
150       return false;
151   }
152   // Only allow NonSemantic.Shader.DebugInfo.100, we cannot safely optimise
153   // around unknown extended instruction sets even if they are non-semantic
154   for (auto& inst : context()->module()->ext_inst_imports()) {
155     assert(inst.opcode() == spv::Op::OpExtInstImport &&
156            "Expecting an import of an extension's instruction set.");
157     const std::string extension_name = inst.GetInOperand(0).AsString();
158     if (spvtools::utils::starts_with(extension_name, "NonSemantic.") &&
159         (extension_name != "NonSemantic.Shader.DebugInfo.100") &&
160         (extension_name != "NonSemantic.DebugPrintf")) {
161       return false;
162     }
163   }
164   return true;
165 }
166 
IsTargetDead(Instruction * inst)167 bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
168   const uint32_t tId = inst->GetSingleWordInOperand(0);
169   Instruction* tInst = get_def_use_mgr()->GetDef(tId);
170   if (IsAnnotationInst(tInst->opcode())) {
171     // This must be a decoration group. We go through annotations in a specific
172     // order. So if this is not used by any group or group member decorates, it
173     // is dead.
174     assert(tInst->opcode() == spv::Op::OpDecorationGroup);
175     bool dead = true;
176     get_def_use_mgr()->ForEachUser(tInst, [&dead](Instruction* user) {
177       if (user->opcode() == spv::Op::OpGroupDecorate ||
178           user->opcode() == spv::Op::OpGroupMemberDecorate)
179         dead = false;
180     });
181     return dead;
182   }
183   return !IsLive(tInst);
184 }
185 
ProcessLoad(Function * func,uint32_t varId)186 void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
187   // Only process locals
188   if (!IsLocalVar(varId, func)) return;
189   // Return if already processed
190   if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
191   // Mark all stores to varId as live
192   AddStores(func, varId);
193   // Cache varId as processed
194   live_local_vars_.insert(varId);
195 }
196 
AddBranch(uint32_t labelId,BasicBlock * bp)197 void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) {
198   std::unique_ptr<Instruction> newBranch(
199       new Instruction(context(), spv::Op::OpBranch, 0, 0,
200                       {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
201   context()->AnalyzeDefUse(&*newBranch);
202   context()->set_instr_block(&*newBranch, bp);
203   bp->AddInstruction(std::move(newBranch));
204 }
205 
AddBreaksAndContinuesToWorklist(Instruction * mergeInst)206 void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
207     Instruction* mergeInst) {
208   assert(mergeInst->opcode() == spv::Op::OpSelectionMerge ||
209          mergeInst->opcode() == spv::Op::OpLoopMerge);
210 
211   BasicBlock* header = context()->get_instr_block(mergeInst);
212   const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0);
213   get_def_use_mgr()->ForEachUser(mergeId, [header, this](Instruction* user) {
214     if (!user->IsBranch()) return;
215     BasicBlock* block = context()->get_instr_block(user);
216     if (BlockIsInConstruct(header, block)) {
217       // This is a break from the loop.
218       AddToWorklist(user);
219       // Add branch's merge if there is one.
220       Instruction* userMerge = GetMergeInstruction(user);
221       if (userMerge != nullptr) AddToWorklist(userMerge);
222     }
223   });
224 
225   if (mergeInst->opcode() != spv::Op::OpLoopMerge) {
226     return;
227   }
228 
229   // For loops we need to find the continues as well.
230   const uint32_t contId =
231       mergeInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
232   get_def_use_mgr()->ForEachUser(contId, [&contId, this](Instruction* user) {
233     spv::Op op = user->opcode();
234     if (op == spv::Op::OpBranchConditional || op == spv::Op::OpSwitch) {
235       // A conditional branch or switch can only be a continue if it does not
236       // have a merge instruction or its merge block is not the continue block.
237       Instruction* hdrMerge = GetMergeInstruction(user);
238       if (hdrMerge != nullptr &&
239           hdrMerge->opcode() == spv::Op::OpSelectionMerge) {
240         uint32_t hdrMergeId =
241             hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
242         if (hdrMergeId == contId) return;
243         // Need to mark merge instruction too
244         AddToWorklist(hdrMerge);
245       }
246     } else if (op == spv::Op::OpBranch) {
247       // An unconditional branch can only be a continue if it is not
248       // branching to its own merge block.
249       BasicBlock* blk = context()->get_instr_block(user);
250       Instruction* hdrBranch = GetHeaderBranch(blk);
251       if (hdrBranch == nullptr) return;
252       Instruction* hdrMerge = GetMergeInstruction(hdrBranch);
253       if (hdrMerge->opcode() == spv::Op::OpLoopMerge) return;
254       uint32_t hdrMergeId =
255           hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
256       if (contId == hdrMergeId) return;
257     } else {
258       return;
259     }
260     AddToWorklist(user);
261   });
262 }
263 
AggressiveDCE(Function * func)264 bool AggressiveDCEPass::AggressiveDCE(Function* func) {
265   std::list<BasicBlock*> structured_order;
266   cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
267   live_local_vars_.clear();
268   InitializeWorkList(func, structured_order);
269   ProcessWorkList(func);
270   return KillDeadInstructions(func, structured_order);
271 }
272 
KillDeadInstructions(const Function * func,std::list<BasicBlock * > & structured_order)273 bool AggressiveDCEPass::KillDeadInstructions(
274     const Function* func, std::list<BasicBlock*>& structured_order) {
275   bool modified = false;
276   for (auto bi = structured_order.begin(); bi != structured_order.end();) {
277     uint32_t merge_block_id = 0;
278     (*bi)->ForEachInst([this, &modified, &merge_block_id](Instruction* inst) {
279       if (IsLive(inst)) return;
280       if (inst->opcode() == spv::Op::OpLabel) return;
281       // If dead instruction is selection merge, remember merge block
282       // for new branch at end of block
283       if (inst->opcode() == spv::Op::OpSelectionMerge ||
284           inst->opcode() == spv::Op::OpLoopMerge)
285         merge_block_id = inst->GetSingleWordInOperand(0);
286       to_kill_.push_back(inst);
287       modified = true;
288     });
289     // If a structured if or loop was deleted, add a branch to its merge
290     // block, and traverse to the merge block and continue processing there.
291     // We know the block still exists because the label is not deleted.
292     if (merge_block_id != 0) {
293       AddBranch(merge_block_id, *bi);
294       for (++bi; (*bi)->id() != merge_block_id; ++bi) {
295       }
296 
297       auto merge_terminator = (*bi)->terminator();
298       if (merge_terminator->opcode() == spv::Op::OpUnreachable) {
299         // The merge was unreachable. This is undefined behaviour so just
300         // return (or return an undef). Then mark the new return as live.
301         auto func_ret_type_inst = get_def_use_mgr()->GetDef(func->type_id());
302         if (func_ret_type_inst->opcode() == spv::Op::OpTypeVoid) {
303           merge_terminator->SetOpcode(spv::Op::OpReturn);
304         } else {
305           // Find an undef for the return value and make sure it gets kept by
306           // the pass.
307           auto undef_id = Type2Undef(func->type_id());
308           auto undef = get_def_use_mgr()->GetDef(undef_id);
309           live_insts_.Set(undef->unique_id());
310           merge_terminator->SetOpcode(spv::Op::OpReturnValue);
311           merge_terminator->SetInOperands({{SPV_OPERAND_TYPE_ID, {undef_id}}});
312           get_def_use_mgr()->AnalyzeInstUse(merge_terminator);
313         }
314         live_insts_.Set(merge_terminator->unique_id());
315       }
316     } else {
317       Instruction* inst = (*bi)->terminator();
318       if (!IsLive(inst)) {
319         // If the terminator is not live, this block has no live instructions,
320         // and it will be unreachable.
321         AddUnreachable(*bi);
322       }
323       ++bi;
324     }
325   }
326   return modified;
327 }
328 
ProcessWorkList(Function * func)329 void AggressiveDCEPass::ProcessWorkList(Function* func) {
330   while (!worklist_.empty()) {
331     Instruction* live_inst = worklist_.front();
332     worklist_.pop();
333     AddOperandsToWorkList(live_inst);
334     MarkBlockAsLive(live_inst);
335     MarkLoadedVariablesAsLive(func, live_inst);
336     AddDecorationsToWorkList(live_inst);
337     AddDebugInstructionsToWorkList(live_inst);
338   }
339 }
340 
AddDebugScopeToWorkList(const Instruction * inst)341 void AggressiveDCEPass::AddDebugScopeToWorkList(const Instruction* inst) {
342   auto scope = inst->GetDebugScope();
343   auto lex_scope_id = scope.GetLexicalScope();
344   if (lex_scope_id != kNoDebugScope)
345     AddToWorklist(get_def_use_mgr()->GetDef(lex_scope_id));
346   auto inlined_at_id = scope.GetInlinedAt();
347   if (inlined_at_id != kNoInlinedAt)
348     AddToWorklist(get_def_use_mgr()->GetDef(inlined_at_id));
349 }
350 
AddDebugInstructionsToWorkList(const Instruction * inst)351 void AggressiveDCEPass::AddDebugInstructionsToWorkList(
352     const Instruction* inst) {
353   for (auto& line_inst : inst->dbg_line_insts()) {
354     if (line_inst.IsDebugLineInst()) {
355       AddOperandsToWorkList(&line_inst);
356     }
357     AddDebugScopeToWorkList(&line_inst);
358   }
359   AddDebugScopeToWorkList(inst);
360 }
361 
AddDecorationsToWorkList(const Instruction * inst)362 void AggressiveDCEPass::AddDecorationsToWorkList(const Instruction* inst) {
363   // Add OpDecorateId instructions that apply to this instruction to the work
364   // list.  We use the decoration manager to look through the group
365   // decorations to get to the OpDecorate* instructions themselves.
366   auto decorations =
367       get_decoration_mgr()->GetDecorationsFor(inst->result_id(), false);
368   for (Instruction* dec : decorations) {
369     // We only care about OpDecorateId instructions because the are the only
370     // decorations that will reference an id that will have to be kept live
371     // because of that use.
372     if (dec->opcode() != spv::Op::OpDecorateId) {
373       continue;
374     }
375     if (spv::Decoration(dec->GetSingleWordInOperand(1)) ==
376         spv::Decoration::HlslCounterBufferGOOGLE) {
377       // These decorations should not force the use id to be live.  It will be
378       // removed if either the target or the in operand are dead.
379       continue;
380     }
381     AddToWorklist(dec);
382   }
383 }
384 
MarkLoadedVariablesAsLive(Function * func,Instruction * inst)385 void AggressiveDCEPass::MarkLoadedVariablesAsLive(Function* func,
386                                                   Instruction* inst) {
387   std::vector<uint32_t> live_variables = GetLoadedVariables(inst);
388   for (uint32_t var_id : live_variables) {
389     ProcessLoad(func, var_id);
390   }
391 }
392 
GetLoadedVariables(Instruction * inst)393 std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariables(Instruction* inst) {
394   if (inst->opcode() == spv::Op::OpFunctionCall) {
395     return GetLoadedVariablesFromFunctionCall(inst);
396   }
397   uint32_t var_id = GetLoadedVariableFromNonFunctionCalls(inst);
398   if (var_id == 0) {
399     return {};
400   }
401   return {var_id};
402 }
403 
GetLoadedVariableFromNonFunctionCalls(Instruction * inst)404 uint32_t AggressiveDCEPass::GetLoadedVariableFromNonFunctionCalls(
405     Instruction* inst) {
406   std::vector<uint32_t> live_variables;
407   if (inst->IsAtomicWithLoad()) {
408     return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
409   }
410 
411   switch (inst->opcode()) {
412     case spv::Op::OpLoad:
413     case spv::Op::OpImageTexelPointer:
414       return GetVariableId(inst->GetSingleWordInOperand(kLoadSourceAddrInIdx));
415     case spv::Op::OpCopyMemory:
416     case spv::Op::OpCopyMemorySized:
417       return GetVariableId(
418           inst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx));
419     default:
420       break;
421   }
422 
423   switch (inst->GetCommonDebugOpcode()) {
424     case CommonDebugInfoDebugDeclare:
425       return inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
426     case CommonDebugInfoDebugValue: {
427       analysis::DebugInfoManager* debug_info_mgr =
428           context()->get_debug_info_mgr();
429       return debug_info_mgr->GetVariableIdOfDebugValueUsedForDeclare(inst);
430     }
431     default:
432       break;
433   }
434   return 0;
435 }
436 
GetLoadedVariablesFromFunctionCall(const Instruction * inst)437 std::vector<uint32_t> AggressiveDCEPass::GetLoadedVariablesFromFunctionCall(
438     const Instruction* inst) {
439   assert(inst->opcode() == spv::Op::OpFunctionCall);
440   std::vector<uint32_t> live_variables;
441   // NOTE: we should only be checking function call parameters here, not the
442   // function itself, however, `IsPtr` will trivially return false for
443   // OpFunction
444   inst->ForEachInId([this, &live_variables](const uint32_t* operand_id) {
445     if (!IsPtr(*operand_id)) return;
446     uint32_t var_id = GetVariableId(*operand_id);
447     live_variables.push_back(var_id);
448   });
449   return live_variables;
450 }
451 
GetVariableId(uint32_t ptr_id)452 uint32_t AggressiveDCEPass::GetVariableId(uint32_t ptr_id) {
453   assert(IsPtr(ptr_id) &&
454          "Cannot get the variable when input is not a pointer.");
455   uint32_t varId = 0;
456   (void)GetPtr(ptr_id, &varId);
457   return varId;
458 }
459 
MarkBlockAsLive(Instruction * inst)460 void AggressiveDCEPass::MarkBlockAsLive(Instruction* inst) {
461   BasicBlock* basic_block = context()->get_instr_block(inst);
462   if (basic_block == nullptr) {
463     return;
464   }
465 
466   // If we intend to keep this instruction, we need the block label and
467   // block terminator to have a valid block for the instruction.
468   AddToWorklist(basic_block->GetLabelInst());
469 
470   // We need to mark the successors blocks that follow as live.  If this is
471   // header of the merge construct, the construct may be folded, but we will
472   // definitely need the merge label.  If it is not a construct, the terminator
473   // must be live, and the successor blocks will be marked as live when
474   // processing the terminator.
475   uint32_t merge_id = basic_block->MergeBlockIdIfAny();
476   if (merge_id == 0) {
477     AddToWorklist(basic_block->terminator());
478   } else {
479     AddToWorklist(context()->get_def_use_mgr()->GetDef(merge_id));
480   }
481 
482   // Mark the structured control flow constructs that contains this block as
483   // live.  If |inst| is an instruction in the loop header, then it is part of
484   // the loop, so the loop construct must be live.  We exclude the label because
485   // it does not matter how many times it is executed.  This could be extended
486   // to more instructions, but we will need it for now.
487   if (inst->opcode() != spv::Op::OpLabel)
488     MarkLoopConstructAsLiveIfLoopHeader(basic_block);
489 
490   Instruction* next_branch_inst = GetBranchForNextHeader(basic_block);
491   if (next_branch_inst != nullptr) {
492     AddToWorklist(next_branch_inst);
493     Instruction* mergeInst = GetMergeInstruction(next_branch_inst);
494     AddToWorklist(mergeInst);
495   }
496 
497   if (inst->opcode() == spv::Op::OpLoopMerge ||
498       inst->opcode() == spv::Op::OpSelectionMerge) {
499     AddBreaksAndContinuesToWorklist(inst);
500   }
501 }
MarkLoopConstructAsLiveIfLoopHeader(BasicBlock * basic_block)502 void AggressiveDCEPass::MarkLoopConstructAsLiveIfLoopHeader(
503     BasicBlock* basic_block) {
504   // If this is the header for a loop, then loop structure needs to keep as well
505   // because the loop header is also part of the loop.
506   Instruction* merge_inst = basic_block->GetLoopMergeInst();
507   if (merge_inst != nullptr) {
508     AddToWorklist(basic_block->terminator());
509     AddToWorklist(merge_inst);
510   }
511 }
512 
AddOperandsToWorkList(const Instruction * inst)513 void AggressiveDCEPass::AddOperandsToWorkList(const Instruction* inst) {
514   inst->ForEachInId([this](const uint32_t* iid) {
515     Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
516     AddToWorklist(inInst);
517   });
518   if (inst->type_id() != 0) {
519     AddToWorklist(get_def_use_mgr()->GetDef(inst->type_id()));
520   }
521 }
522 
InitializeWorkList(Function * func,std::list<BasicBlock * > & structured_order)523 void AggressiveDCEPass::InitializeWorkList(
524     Function* func, std::list<BasicBlock*>& structured_order) {
525   AddToWorklist(&func->DefInst());
526   MarkFunctionParameterAsLive(func);
527   MarkFirstBlockAsLive(func);
528 
529   // Add instructions with external side effects to the worklist. Also add
530   // branches that are not attached to a structured construct.
531   // TODO(s-perron): The handling of branch seems to be adhoc.  This needs to be
532   // cleaned up.
533   for (auto& bi : structured_order) {
534     for (auto ii = bi->begin(); ii != bi->end(); ++ii) {
535       spv::Op op = ii->opcode();
536       if (ii->IsBranch()) {
537         continue;
538       }
539       switch (op) {
540         case spv::Op::OpStore: {
541           uint32_t var_id = 0;
542           (void)GetPtr(&*ii, &var_id);
543           if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
544         } break;
545         case spv::Op::OpCopyMemory:
546         case spv::Op::OpCopyMemorySized: {
547           uint32_t var_id = 0;
548           uint32_t target_addr_id =
549               ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx);
550           (void)GetPtr(target_addr_id, &var_id);
551           if (!IsLocalVar(var_id, func)) AddToWorklist(&*ii);
552         } break;
553         case spv::Op::OpLoopMerge:
554         case spv::Op::OpSelectionMerge:
555         case spv::Op::OpUnreachable:
556           break;
557         default: {
558           // Function calls, atomics, function params, function returns, etc.
559           if (!ii->IsOpcodeSafeToDelete()) {
560             AddToWorklist(&*ii);
561           }
562         } break;
563       }
564     }
565   }
566 }
567 
InitializeModuleScopeLiveInstructions()568 void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
569   // Keep all execution modes.
570   for (auto& exec : get_module()->execution_modes()) {
571     AddToWorklist(&exec);
572   }
573   // Keep all entry points.
574   for (auto& entry : get_module()->entry_points()) {
575     if (!preserve_interface_) {
576       live_insts_.Set(entry.unique_id());
577       // The actual function is live always.
578       AddToWorklist(
579           get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(1u)));
580       for (uint32_t i = 3; i < entry.NumInOperands(); ++i) {
581         auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
582         auto storage_class = var->GetSingleWordInOperand(0u);
583         // Vulkan support outputs without an associated input, but not inputs
584         // without an associated output. Don't remove outputs unless explicitly
585         // allowed.
586         if (!remove_outputs_ &&
587             spv::StorageClass(storage_class) == spv::StorageClass::Output) {
588           AddToWorklist(var);
589         }
590       }
591     } else {
592       AddToWorklist(&entry);
593     }
594   }
595   for (auto& anno : get_module()->annotations()) {
596     if (anno.opcode() == spv::Op::OpDecorate) {
597       // Keep workgroup size.
598       if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
599               spv::Decoration::BuiltIn &&
600           spv::BuiltIn(anno.GetSingleWordInOperand(2u)) ==
601               spv::BuiltIn::WorkgroupSize) {
602         AddToWorklist(&anno);
603       }
604 
605       if (context()->preserve_bindings()) {
606         // Keep all bindings.
607         if ((spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
608              spv::Decoration::DescriptorSet) ||
609             (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
610              spv::Decoration::Binding)) {
611           AddToWorklist(&anno);
612         }
613       }
614 
615       if (context()->preserve_spec_constants()) {
616         // Keep all specialization constant instructions
617         if (spv::Decoration(anno.GetSingleWordInOperand(1u)) ==
618             spv::Decoration::SpecId) {
619           AddToWorklist(&anno);
620         }
621       }
622     }
623   }
624 
625   // For each DebugInfo GlobalVariable keep all operands except the Variable.
626   // Later, if the variable is killed with KillInst(), we will set the operand
627   // to DebugInfoNone. Create and save DebugInfoNone now for this possible
628   // later use. This is slightly unoptimal, but it avoids generating it during
629   // instruction killing when the module is not consistent.
630   bool debug_global_seen = false;
631   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
632     if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
633       continue;
634     debug_global_seen = true;
635     dbg.ForEachInId([this](const uint32_t* iid) {
636       Instruction* in_inst = get_def_use_mgr()->GetDef(*iid);
637       if (in_inst->opcode() == spv::Op::OpVariable) return;
638       AddToWorklist(in_inst);
639     });
640   }
641   if (debug_global_seen) {
642     auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
643     AddToWorklist(dbg_none);
644   }
645 
646   // Add top level DebugInfo to worklist
647   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
648     auto op = dbg.GetShader100DebugOpcode();
649     if (op == NonSemanticShaderDebugInfo100DebugCompilationUnit ||
650         op == NonSemanticShaderDebugInfo100DebugEntryPoint ||
651         op == NonSemanticShaderDebugInfo100DebugSourceContinued) {
652       AddToWorklist(&dbg);
653     }
654   }
655 }
656 
ProcessImpl()657 Pass::Status AggressiveDCEPass::ProcessImpl() {
658   // Current functionality assumes shader capability
659   // TODO(greg-lunarg): Handle additional capabilities
660   if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
661     return Status::SuccessWithoutChange;
662 
663   // Current functionality assumes relaxed logical addressing (see
664   // instruction.h)
665   // TODO(greg-lunarg): Handle non-logical addressing
666   if (context()->get_feature_mgr()->HasCapability(spv::Capability::Addresses))
667     return Status::SuccessWithoutChange;
668 
669   // The variable pointer extension is no longer needed to use the capability,
670   // so we have to look for the capability.
671   if (context()->get_feature_mgr()->HasCapability(
672           spv::Capability::VariablePointersStorageBuffer))
673     return Status::SuccessWithoutChange;
674 
675   // If any extensions in the module are not explicitly supported,
676   // return unmodified.
677   if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
678 
679   // Eliminate Dead functions.
680   bool modified = EliminateDeadFunctions();
681 
682   InitializeModuleScopeLiveInstructions();
683 
684   // Run |AggressiveDCE| on the remaining functions.  The order does not matter,
685   // since |AggressiveDCE| is intra-procedural.  This can mean that function
686   // will become dead if all function call to them are removed.  These dead
687   // function will still be in the module after this pass.  We expect this to be
688   // rare.
689   for (Function& fp : *context()->module()) {
690     modified |= AggressiveDCE(&fp);
691   }
692 
693   // If the decoration manager is kept live then the context will try to keep it
694   // up to date.  ADCE deals with group decorations by changing the operands in
695   // |OpGroupDecorate| instruction directly without informing the decoration
696   // manager.  This can put it in an invalid state which will cause an error
697   // when the context tries to update it.  To avoid this problem invalidate
698   // the decoration manager upfront.
699   //
700   // We kill it at now because it is used when processing the entry point
701   // functions.
702   context()->InvalidateAnalyses(IRContext::Analysis::kAnalysisDecorations);
703 
704   // Process module-level instructions. Now that all live instructions have
705   // been marked, it is safe to remove dead global values.
706   modified |= ProcessGlobalValues();
707 
708   assert((to_kill_.empty() || modified) &&
709          "A dead instruction was identified, but no change recorded.");
710 
711   // Kill all dead instructions.
712   for (auto inst : to_kill_) {
713     context()->KillInst(inst);
714   }
715 
716   // Cleanup all CFG including all unreachable blocks.
717   for (Function& fp : *context()->module()) {
718     modified |= CFGCleanup(&fp);
719   }
720 
721   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
722 }
723 
EliminateDeadFunctions()724 bool AggressiveDCEPass::EliminateDeadFunctions() {
725   // Identify live functions first. Those that are not live
726   // are dead.
727   std::unordered_set<const Function*> live_function_set;
728   ProcessFunction mark_live = [&live_function_set](Function* fp) {
729     live_function_set.insert(fp);
730     return false;
731   };
732   context()->ProcessReachableCallTree(mark_live);
733 
734   bool modified = false;
735   for (auto funcIter = get_module()->begin();
736        funcIter != get_module()->end();) {
737     if (live_function_set.count(&*funcIter) == 0) {
738       modified = true;
739       funcIter =
740           eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter);
741     } else {
742       ++funcIter;
743     }
744   }
745 
746   return modified;
747 }
748 
ProcessGlobalValues()749 bool AggressiveDCEPass::ProcessGlobalValues() {
750   // Remove debug and annotation statements referencing dead instructions.
751   // This must be done before killing the instructions, otherwise there are
752   // dead objects in the def/use database.
753   bool modified = false;
754   Instruction* instruction = &*get_module()->debug2_begin();
755   while (instruction) {
756     if (instruction->opcode() != spv::Op::OpName) {
757       instruction = instruction->NextNode();
758       continue;
759     }
760 
761     if (IsTargetDead(instruction)) {
762       instruction = context()->KillInst(instruction);
763       modified = true;
764     } else {
765       instruction = instruction->NextNode();
766     }
767   }
768 
769   // This code removes all unnecessary decorations safely (see #1174). It also
770   // does so in a more efficient manner than deleting them only as the targets
771   // are deleted.
772   std::vector<Instruction*> annotations;
773   for (auto& inst : get_module()->annotations()) annotations.push_back(&inst);
774   std::sort(annotations.begin(), annotations.end(), DecorationLess());
775   for (auto annotation : annotations) {
776     switch (annotation->opcode()) {
777       case spv::Op::OpDecorate:
778       case spv::Op::OpMemberDecorate:
779       case spv::Op::OpDecorateStringGOOGLE:
780       case spv::Op::OpMemberDecorateStringGOOGLE:
781         if (IsTargetDead(annotation)) {
782           context()->KillInst(annotation);
783           modified = true;
784         }
785         break;
786       case spv::Op::OpDecorateId:
787         if (IsTargetDead(annotation)) {
788           context()->KillInst(annotation);
789           modified = true;
790         } else {
791           if (spv::Decoration(annotation->GetSingleWordInOperand(1)) ==
792               spv::Decoration::HlslCounterBufferGOOGLE) {
793             // HlslCounterBuffer will reference an id other than the target.
794             // If that id is dead, then the decoration can be removed as well.
795             uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2);
796             Instruction* counter_buffer_inst =
797                 get_def_use_mgr()->GetDef(counter_buffer_id);
798             if (!IsLive(counter_buffer_inst)) {
799               context()->KillInst(annotation);
800               modified = true;
801             }
802           }
803         }
804         break;
805       case spv::Op::OpGroupDecorate: {
806         // Go through the targets of this group decorate. Remove each dead
807         // target. If all targets are dead, remove this decoration.
808         bool dead = true;
809         bool removed_operand = false;
810         for (uint32_t i = 1; i < annotation->NumOperands();) {
811           Instruction* opInst =
812               get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
813           if (!IsLive(opInst)) {
814             // Don't increment |i|.
815             annotation->RemoveOperand(i);
816             modified = true;
817             removed_operand = true;
818           } else {
819             i++;
820             dead = false;
821           }
822         }
823         if (dead) {
824           context()->KillInst(annotation);
825           modified = true;
826         } else if (removed_operand) {
827           context()->UpdateDefUse(annotation);
828         }
829         break;
830       }
831       case spv::Op::OpGroupMemberDecorate: {
832         // Go through the targets of this group member decorate. Remove each
833         // dead target (and member index). If all targets are dead, remove this
834         // decoration.
835         bool dead = true;
836         bool removed_operand = false;
837         for (uint32_t i = 1; i < annotation->NumOperands();) {
838           Instruction* opInst =
839               get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
840           if (!IsLive(opInst)) {
841             // Don't increment |i|.
842             annotation->RemoveOperand(i + 1);
843             annotation->RemoveOperand(i);
844             modified = true;
845             removed_operand = true;
846           } else {
847             i += 2;
848             dead = false;
849           }
850         }
851         if (dead) {
852           context()->KillInst(annotation);
853           modified = true;
854         } else if (removed_operand) {
855           context()->UpdateDefUse(annotation);
856         }
857         break;
858       }
859       case spv::Op::OpDecorationGroup:
860         // By the time we hit decoration groups we've checked everything that
861         // can target them. So if they have no uses they must be dead.
862         if (get_def_use_mgr()->NumUsers(annotation) == 0) {
863           context()->KillInst(annotation);
864           modified = true;
865         }
866         break;
867       default:
868         assert(false);
869         break;
870     }
871   }
872 
873   for (auto& dbg : get_module()->ext_inst_debuginfo()) {
874     if (IsLive(&dbg)) continue;
875     // Save GlobalVariable if its variable is live, otherwise null out variable
876     // index
877     if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
878       auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
879       Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
880       if (IsLive(var_inst)) continue;
881       context()->ForgetUses(&dbg);
882       dbg.SetOperand(
883           kGlobalVariableVariableIndex,
884           {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()});
885       context()->AnalyzeUses(&dbg);
886       continue;
887     }
888     to_kill_.push_back(&dbg);
889     modified = true;
890   }
891 
892   // Since ADCE is disabled for non-shaders, we don't check for export linkage
893   // attributes here.
894   for (auto& val : get_module()->types_values()) {
895     if (!IsLive(&val)) {
896       // Save forwarded pointer if pointer is live since closure does not mark
897       // this live as it does not have a result id. This is a little too
898       // conservative since it is not known if the structure type that needed
899       // it is still live. TODO(greg-lunarg): Only save if needed.
900       if (val.opcode() == spv::Op::OpTypeForwardPointer) {
901         uint32_t ptr_ty_id = val.GetSingleWordInOperand(0);
902         Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
903         if (IsLive(ptr_ty_inst)) continue;
904       }
905       to_kill_.push_back(&val);
906       modified = true;
907     }
908   }
909 
910   if (!preserve_interface_) {
911     // Remove the dead interface variables from the entry point interface list.
912     for (auto& entry : get_module()->entry_points()) {
913       std::vector<Operand> new_operands;
914       for (uint32_t i = 0; i < entry.NumInOperands(); ++i) {
915         if (i < 3) {
916           // Execution model, function id and name are always valid.
917           new_operands.push_back(entry.GetInOperand(i));
918         } else {
919           auto* var =
920               get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
921           if (IsLive(var)) {
922             new_operands.push_back(entry.GetInOperand(i));
923           }
924         }
925       }
926       if (new_operands.size() != entry.NumInOperands()) {
927         entry.SetInOperands(std::move(new_operands));
928         get_def_use_mgr()->UpdateDefUse(&entry);
929       }
930     }
931   }
932 
933   return modified;
934 }
935 
Process()936 Pass::Status AggressiveDCEPass::Process() {
937   // Initialize extensions allowlist
938   InitExtensions();
939   return ProcessImpl();
940 }
941 
InitExtensions()942 void AggressiveDCEPass::InitExtensions() {
943   extensions_allowlist_.clear();
944 
945   // clang-format off
946   extensions_allowlist_.insert({
947       "SPV_AMD_shader_explicit_vertex_parameter",
948       "SPV_AMD_shader_trinary_minmax",
949       "SPV_AMD_gcn_shader",
950       "SPV_KHR_shader_ballot",
951       "SPV_AMD_shader_ballot",
952       "SPV_AMD_gpu_shader_half_float",
953       "SPV_KHR_shader_draw_parameters",
954       "SPV_KHR_subgroup_vote",
955       "SPV_KHR_8bit_storage",
956       "SPV_KHR_16bit_storage",
957       "SPV_KHR_device_group",
958       "SPV_KHR_multiview",
959       "SPV_NVX_multiview_per_view_attributes",
960       "SPV_NV_viewport_array2",
961       "SPV_NV_stereo_view_rendering",
962       "SPV_NV_sample_mask_override_coverage",
963       "SPV_NV_geometry_shader_passthrough",
964       "SPV_AMD_texture_gather_bias_lod",
965       "SPV_KHR_storage_buffer_storage_class",
966       // SPV_KHR_variable_pointers
967       //   Currently do not support extended pointer expressions
968       "SPV_AMD_gpu_shader_int16",
969       "SPV_KHR_post_depth_coverage",
970       "SPV_KHR_shader_atomic_counter_ops",
971       "SPV_EXT_shader_stencil_export",
972       "SPV_EXT_shader_viewport_index_layer",
973       "SPV_AMD_shader_image_load_store_lod",
974       "SPV_AMD_shader_fragment_mask",
975       "SPV_EXT_fragment_fully_covered",
976       "SPV_AMD_gpu_shader_half_float_fetch",
977       "SPV_GOOGLE_decorate_string",
978       "SPV_GOOGLE_hlsl_functionality1",
979       "SPV_GOOGLE_user_type",
980       "SPV_NV_shader_subgroup_partitioned",
981       "SPV_EXT_demote_to_helper_invocation",
982       "SPV_EXT_descriptor_indexing",
983       "SPV_NV_fragment_shader_barycentric",
984       "SPV_NV_compute_shader_derivatives",
985       "SPV_NV_shader_image_footprint",
986       "SPV_NV_shading_rate",
987       "SPV_NV_mesh_shader",
988       "SPV_NV_ray_tracing",
989       "SPV_KHR_ray_tracing",
990       "SPV_KHR_ray_query",
991       "SPV_EXT_fragment_invocation_density",
992       "SPV_EXT_physical_storage_buffer",
993       "SPV_KHR_physical_storage_buffer",
994       "SPV_KHR_terminate_invocation",
995       "SPV_KHR_shader_clock",
996       "SPV_KHR_vulkan_memory_model",
997       "SPV_KHR_subgroup_uniform_control_flow",
998       "SPV_KHR_integer_dot_product",
999       "SPV_EXT_shader_image_int64",
1000       "SPV_KHR_non_semantic_info",
1001       "SPV_KHR_uniform_group_instructions",
1002       "SPV_KHR_fragment_shader_barycentric",
1003       "SPV_NV_bindless_texture",
1004       "SPV_EXT_shader_atomic_float_add",
1005       "SPV_EXT_fragment_shader_interlock",
1006       "SPV_NV_compute_shader_derivatives"
1007   });
1008   // clang-format on
1009 }
1010 
GetHeaderBranch(BasicBlock * blk)1011 Instruction* AggressiveDCEPass::GetHeaderBranch(BasicBlock* blk) {
1012   if (blk == nullptr) {
1013     return nullptr;
1014   }
1015   BasicBlock* header_block = GetHeaderBlock(blk);
1016   if (header_block == nullptr) {
1017     return nullptr;
1018   }
1019   return header_block->terminator();
1020 }
1021 
GetHeaderBlock(BasicBlock * blk) const1022 BasicBlock* AggressiveDCEPass::GetHeaderBlock(BasicBlock* blk) const {
1023   if (blk == nullptr) {
1024     return nullptr;
1025   }
1026 
1027   BasicBlock* header_block = nullptr;
1028   if (blk->IsLoopHeader()) {
1029     header_block = blk;
1030   } else {
1031     uint32_t header =
1032         context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id());
1033     header_block = context()->get_instr_block(header);
1034   }
1035   return header_block;
1036 }
1037 
GetMergeInstruction(Instruction * inst)1038 Instruction* AggressiveDCEPass::GetMergeInstruction(Instruction* inst) {
1039   BasicBlock* bb = context()->get_instr_block(inst);
1040   if (bb == nullptr) {
1041     return nullptr;
1042   }
1043   return bb->GetMergeInst();
1044 }
1045 
GetBranchForNextHeader(BasicBlock * blk)1046 Instruction* AggressiveDCEPass::GetBranchForNextHeader(BasicBlock* blk) {
1047   if (blk == nullptr) {
1048     return nullptr;
1049   }
1050 
1051   if (blk->IsLoopHeader()) {
1052     uint32_t header =
1053         context()->GetStructuredCFGAnalysis()->ContainingConstruct(blk->id());
1054     blk = context()->get_instr_block(header);
1055   }
1056   return GetHeaderBranch(blk);
1057 }
1058 
MarkFunctionParameterAsLive(const Function * func)1059 void AggressiveDCEPass::MarkFunctionParameterAsLive(const Function* func) {
1060   func->ForEachParam(
1061       [this](const Instruction* param) {
1062         AddToWorklist(const_cast<Instruction*>(param));
1063       },
1064       false);
1065 }
1066 
BlockIsInConstruct(BasicBlock * header_block,BasicBlock * bb)1067 bool AggressiveDCEPass::BlockIsInConstruct(BasicBlock* header_block,
1068                                            BasicBlock* bb) {
1069   if (bb == nullptr || header_block == nullptr) {
1070     return false;
1071   }
1072 
1073   uint32_t current_header = bb->id();
1074   while (current_header != 0) {
1075     if (current_header == header_block->id()) return true;
1076     current_header = context()->GetStructuredCFGAnalysis()->ContainingConstruct(
1077         current_header);
1078   }
1079   return false;
1080 }
1081 
IsEntryPointWithNoCalls(Function * func)1082 bool AggressiveDCEPass::IsEntryPointWithNoCalls(Function* func) {
1083   auto cached_result = entry_point_with_no_calls_cache_.find(func->result_id());
1084   if (cached_result != entry_point_with_no_calls_cache_.end()) {
1085     return cached_result->second;
1086   }
1087   bool result = IsEntryPoint(func) && !HasCall(func);
1088   entry_point_with_no_calls_cache_[func->result_id()] = result;
1089   return result;
1090 }
1091 
IsEntryPoint(Function * func)1092 bool AggressiveDCEPass::IsEntryPoint(Function* func) {
1093   for (const Instruction& entry_point : get_module()->entry_points()) {
1094     uint32_t entry_point_id =
1095         entry_point.GetSingleWordInOperand(kEntryPointFunctionIdInIdx);
1096     if (entry_point_id == func->result_id()) {
1097       return true;
1098     }
1099   }
1100   return false;
1101 }
1102 
HasCall(Function * func)1103 bool AggressiveDCEPass::HasCall(Function* func) {
1104   return !func->WhileEachInst([](Instruction* inst) {
1105     return inst->opcode() != spv::Op::OpFunctionCall;
1106   });
1107 }
1108 
MarkFirstBlockAsLive(Function * func)1109 void AggressiveDCEPass::MarkFirstBlockAsLive(Function* func) {
1110   BasicBlock* first_block = &*func->begin();
1111   MarkBlockAsLive(first_block->GetLabelInst());
1112 }
1113 
AddUnreachable(BasicBlock * & block)1114 void AggressiveDCEPass::AddUnreachable(BasicBlock*& block) {
1115   InstructionBuilder builder(
1116       context(), block,
1117       IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse);
1118   builder.AddUnreachable();
1119 }
1120 
1121 }  // namespace opt
1122 }  // namespace spvtools
1123