aggressive_dead_code_elim_pass.cpp 41 KB

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