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