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