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