aggressive_dead_code_elim_pass.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  1. // Copyright (c) 2017 The Khronos Group Inc.
  2. // Copyright (c) 2017 Valve Corporation
  3. // Copyright (c) 2017 LunarG Inc.
  4. // Copyright (c) 2018 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/latest_version_glsl_std_450_header.h"
  22. #include "source/opt/eliminate_dead_functions_util.h"
  23. #include "source/opt/iterator.h"
  24. #include "source/opt/reflect.h"
  25. #include "source/spirv_constant.h"
  26. namespace spvtools {
  27. namespace opt {
  28. namespace {
  29. const uint32_t kTypePointerStorageClassInIdx = 0;
  30. const uint32_t kEntryPointFunctionIdInIdx = 1;
  31. const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
  32. const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
  33. const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
  34. const uint32_t kCopyMemoryTargetAddrInIdx = 0;
  35. const uint32_t kCopyMemorySourceAddrInIdx = 1;
  36. const uint32_t kDebugDeclareOperandVariableIndex = 5;
  37. const uint32_t kGlobalVariableVariableIndex = 12;
  38. // Sorting functor to present annotation instructions in an easy-to-process
  39. // order. The functor orders by opcode first and falls back on unique id
  40. // ordering if both instructions have the same opcode.
  41. //
  42. // Desired priority:
  43. // SpvOpGroupDecorate
  44. // SpvOpGroupMemberDecorate
  45. // SpvOpDecorate
  46. // SpvOpMemberDecorate
  47. // SpvOpDecorateId
  48. // SpvOpDecorateStringGOOGLE
  49. // SpvOpDecorationGroup
  50. struct DecorationLess {
  51. bool operator()(const Instruction* lhs, const Instruction* rhs) const {
  52. assert(lhs && rhs);
  53. SpvOp lhsOp = lhs->opcode();
  54. SpvOp rhsOp = rhs->opcode();
  55. if (lhsOp != rhsOp) {
  56. #define PRIORITY_CASE(opcode) \
  57. if (lhsOp == opcode && rhsOp != opcode) return true; \
  58. if (rhsOp == opcode && lhsOp != opcode) return false;
  59. // OpGroupDecorate and OpGroupMember decorate are highest priority to
  60. // eliminate dead targets early and simplify subsequent checks.
  61. PRIORITY_CASE(SpvOpGroupDecorate)
  62. PRIORITY_CASE(SpvOpGroupMemberDecorate)
  63. PRIORITY_CASE(SpvOpDecorate)
  64. PRIORITY_CASE(SpvOpMemberDecorate)
  65. PRIORITY_CASE(SpvOpDecorateId)
  66. PRIORITY_CASE(SpvOpDecorateStringGOOGLE)
  67. // OpDecorationGroup is lowest priority to ensure use/def chains remain
  68. // usable for instructions that target this group.
  69. PRIORITY_CASE(SpvOpDecorationGroup)
  70. #undef PRIORITY_CASE
  71. }
  72. // Fall back to maintain total ordering (compare unique ids).
  73. return *lhs < *rhs;
  74. }
  75. };
  76. } // namespace
  77. bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
  78. if (varId == 0) return false;
  79. const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
  80. const SpvOp op = varInst->opcode();
  81. if (op != SpvOpVariable) return false;
  82. const uint32_t varTypeId = varInst->type_id();
  83. const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
  84. if (varTypeInst->opcode() != SpvOpTypePointer) return false;
  85. return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
  86. storageClass;
  87. }
  88. bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
  89. if (IsVarOfStorage(varId, SpvStorageClassFunction)) {
  90. return true;
  91. }
  92. if (!private_like_local_) {
  93. return false;
  94. }
  95. return IsVarOfStorage(varId, SpvStorageClassPrivate) ||
  96. IsVarOfStorage(varId, SpvStorageClassWorkgroup);
  97. }
  98. void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) {
  99. get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId, func](Instruction* user) {
  100. // If the user is not a part of |func|, skip it.
  101. BasicBlock* blk = context()->get_instr_block(user);
  102. if (blk && blk->GetParent() != func) return;
  103. switch (user->opcode()) {
  104. case SpvOpAccessChain:
  105. case SpvOpInBoundsAccessChain:
  106. case SpvOpCopyObject:
  107. this->AddStores(func, user->result_id());
  108. break;
  109. case SpvOpLoad:
  110. break;
  111. case SpvOpCopyMemory:
  112. case SpvOpCopyMemorySized:
  113. if (user->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx) == ptrId) {
  114. AddToWorklist(user);
  115. }
  116. break;
  117. // If default, assume it stores e.g. frexp, modf, function call
  118. case SpvOpStore:
  119. default:
  120. AddToWorklist(user);
  121. break;
  122. }
  123. });
  124. }
  125. bool AggressiveDCEPass::AllExtensionsSupported() const {
  126. // If any extension not in allowlist, return false
  127. for (auto& ei : get_module()->extensions()) {
  128. const char* extName =
  129. reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
  130. if (extensions_allowlist_.find(extName) == extensions_allowlist_.end())
  131. return false;
  132. }
  133. return true;
  134. }
  135. bool AggressiveDCEPass::IsDead(Instruction* inst) {
  136. if (IsLive(inst)) return false;
  137. if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) &&
  138. !IsStructuredHeader(context()->get_instr_block(inst), nullptr, nullptr,
  139. nullptr))
  140. return false;
  141. return true;
  142. }
  143. bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
  144. const uint32_t tId = inst->GetSingleWordInOperand(0);
  145. Instruction* tInst = get_def_use_mgr()->GetDef(tId);
  146. if (IsAnnotationInst(tInst->opcode())) {
  147. // This must be a decoration group. We go through annotations in a specific
  148. // order. So if this is not used by any group or group member decorates, it
  149. // is dead.
  150. assert(tInst->opcode() == SpvOpDecorationGroup);
  151. bool dead = true;
  152. get_def_use_mgr()->ForEachUser(tInst, [&dead](Instruction* user) {
  153. if (user->opcode() == SpvOpGroupDecorate ||
  154. user->opcode() == SpvOpGroupMemberDecorate)
  155. dead = false;
  156. });
  157. return dead;
  158. }
  159. return IsDead(tInst);
  160. }
  161. void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) {
  162. // Only process locals
  163. if (!IsLocalVar(varId)) return;
  164. // Return if already processed
  165. if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
  166. // Mark all stores to varId as live
  167. AddStores(func, varId);
  168. // Cache varId as processed
  169. live_local_vars_.insert(varId);
  170. }
  171. bool AggressiveDCEPass::IsStructuredHeader(BasicBlock* bp,
  172. Instruction** mergeInst,
  173. Instruction** branchInst,
  174. uint32_t* mergeBlockId) {
  175. if (!bp) return false;
  176. Instruction* mi = bp->GetMergeInst();
  177. if (mi == nullptr) return false;
  178. Instruction* bri = &*bp->tail();
  179. if (branchInst != nullptr) *branchInst = bri;
  180. if (mergeInst != nullptr) *mergeInst = mi;
  181. if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0);
  182. return true;
  183. }
  184. void AggressiveDCEPass::ComputeBlock2HeaderMaps(
  185. std::list<BasicBlock*>& structuredOrder) {
  186. block2headerBranch_.clear();
  187. header2nextHeaderBranch_.clear();
  188. branch2merge_.clear();
  189. structured_order_index_.clear();
  190. std::stack<Instruction*> currentHeaderBranch;
  191. currentHeaderBranch.push(nullptr);
  192. uint32_t currentMergeBlockId = 0;
  193. uint32_t index = 0;
  194. for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();
  195. ++bi, ++index) {
  196. structured_order_index_[*bi] = index;
  197. // If this block is the merge block of the current control construct,
  198. // we are leaving the current construct so we must update state
  199. if ((*bi)->id() == currentMergeBlockId) {
  200. currentHeaderBranch.pop();
  201. Instruction* chb = currentHeaderBranch.top();
  202. if (chb != nullptr)
  203. currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0);
  204. }
  205. Instruction* mergeInst;
  206. Instruction* branchInst;
  207. uint32_t mergeBlockId;
  208. bool is_header =
  209. IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId);
  210. // Map header block to next enclosing header.
  211. if (is_header) header2nextHeaderBranch_[*bi] = currentHeaderBranch.top();
  212. // If this is a loop header, update state first so the block will map to
  213. // itself.
  214. if (is_header && mergeInst->opcode() == SpvOpLoopMerge) {
  215. currentHeaderBranch.push(branchInst);
  216. branch2merge_[branchInst] = mergeInst;
  217. currentMergeBlockId = mergeBlockId;
  218. }
  219. // Map the block to the current construct.
  220. block2headerBranch_[*bi] = currentHeaderBranch.top();
  221. // If this is an if header, update state so following blocks map to the if.
  222. if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) {
  223. currentHeaderBranch.push(branchInst);
  224. branch2merge_[branchInst] = mergeInst;
  225. currentMergeBlockId = mergeBlockId;
  226. }
  227. }
  228. }
  229. void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) {
  230. std::unique_ptr<Instruction> newBranch(
  231. new Instruction(context(), SpvOpBranch, 0, 0,
  232. {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
  233. context()->AnalyzeDefUse(&*newBranch);
  234. context()->set_instr_block(&*newBranch, bp);
  235. bp->AddInstruction(std::move(newBranch));
  236. }
  237. void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
  238. Instruction* mergeInst) {
  239. assert(mergeInst->opcode() == SpvOpSelectionMerge ||
  240. mergeInst->opcode() == SpvOpLoopMerge);
  241. BasicBlock* header = context()->get_instr_block(mergeInst);
  242. uint32_t headerIndex = structured_order_index_[header];
  243. const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0);
  244. BasicBlock* merge = context()->get_instr_block(mergeId);
  245. uint32_t mergeIndex = structured_order_index_[merge];
  246. get_def_use_mgr()->ForEachUser(
  247. mergeId, [headerIndex, mergeIndex, this](Instruction* user) {
  248. if (!user->IsBranch()) return;
  249. BasicBlock* block = context()->get_instr_block(user);
  250. uint32_t index = structured_order_index_[block];
  251. if (headerIndex < index && index < mergeIndex) {
  252. // This is a break from the loop.
  253. AddToWorklist(user);
  254. // Add branch's merge if there is one.
  255. Instruction* userMerge = branch2merge_[user];
  256. if (userMerge != nullptr) AddToWorklist(userMerge);
  257. }
  258. });
  259. if (mergeInst->opcode() != SpvOpLoopMerge) {
  260. return;
  261. }
  262. // For loops we need to find the continues as well.
  263. const uint32_t contId =
  264. mergeInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
  265. get_def_use_mgr()->ForEachUser(contId, [&contId, this](Instruction* user) {
  266. SpvOp op = user->opcode();
  267. if (op == SpvOpBranchConditional || op == SpvOpSwitch) {
  268. // A conditional branch or switch can only be a continue if it does not
  269. // have a merge instruction or its merge block is not the continue block.
  270. Instruction* hdrMerge = branch2merge_[user];
  271. if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) {
  272. uint32_t hdrMergeId =
  273. hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
  274. if (hdrMergeId == contId) return;
  275. // Need to mark merge instruction too
  276. AddToWorklist(hdrMerge);
  277. }
  278. } else if (op == SpvOpBranch) {
  279. // An unconditional branch can only be a continue if it is not
  280. // branching to its own merge block.
  281. BasicBlock* blk = context()->get_instr_block(user);
  282. Instruction* hdrBranch = block2headerBranch_[blk];
  283. if (hdrBranch == nullptr) return;
  284. Instruction* hdrMerge = branch2merge_[hdrBranch];
  285. if (hdrMerge->opcode() == SpvOpLoopMerge) return;
  286. uint32_t hdrMergeId =
  287. hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
  288. if (contId == hdrMergeId) return;
  289. } else {
  290. return;
  291. }
  292. AddToWorklist(user);
  293. });
  294. }
  295. bool AggressiveDCEPass::AggressiveDCE(Function* func) {
  296. // Mark function parameters as live.
  297. AddToWorklist(&func->DefInst());
  298. func->ForEachParam(
  299. [this](const Instruction* param) {
  300. AddToWorklist(const_cast<Instruction*>(param));
  301. },
  302. false);
  303. // Compute map from block to controlling conditional branch
  304. std::list<BasicBlock*> structuredOrder;
  305. cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
  306. ComputeBlock2HeaderMaps(structuredOrder);
  307. bool modified = false;
  308. // Add instructions with external side effects to worklist. Also add branches
  309. // EXCEPT those immediately contained in an "if" selection construct or a loop
  310. // or continue construct.
  311. // TODO(greg-lunarg): Handle Frexp, Modf more optimally
  312. call_in_func_ = false;
  313. func_is_entry_point_ = false;
  314. private_stores_.clear();
  315. live_local_vars_.clear();
  316. // Stacks to keep track of when we are inside an if- or loop-construct.
  317. // When immediately inside an if- or loop-construct, we do not initially
  318. // mark branches live. All other branches must be marked live.
  319. std::stack<bool> assume_branches_live;
  320. std::stack<uint32_t> currentMergeBlockId;
  321. // Push sentinel values on stack for when outside of any control flow.
  322. assume_branches_live.push(true);
  323. currentMergeBlockId.push(0);
  324. for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
  325. // If exiting if or loop, update stacks
  326. if ((*bi)->id() == currentMergeBlockId.top()) {
  327. assume_branches_live.pop();
  328. currentMergeBlockId.pop();
  329. }
  330. for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
  331. SpvOp op = ii->opcode();
  332. switch (op) {
  333. case SpvOpStore: {
  334. uint32_t varId;
  335. (void)GetPtr(&*ii, &varId);
  336. // Mark stores as live if their variable is not function scope
  337. // and is not private scope. Remember private stores for possible
  338. // later inclusion. We cannot call IsLocalVar at this point because
  339. // private_like_local_ has not been set yet.
  340. if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
  341. IsVarOfStorage(varId, SpvStorageClassWorkgroup))
  342. private_stores_.push_back(&*ii);
  343. else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
  344. AddToWorklist(&*ii);
  345. } break;
  346. case SpvOpCopyMemory:
  347. case SpvOpCopyMemorySized: {
  348. uint32_t varId;
  349. (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx),
  350. &varId);
  351. if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
  352. IsVarOfStorage(varId, SpvStorageClassWorkgroup))
  353. private_stores_.push_back(&*ii);
  354. else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
  355. AddToWorklist(&*ii);
  356. } break;
  357. case SpvOpLoopMerge: {
  358. assume_branches_live.push(false);
  359. currentMergeBlockId.push(
  360. ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx));
  361. } break;
  362. case SpvOpSelectionMerge: {
  363. assume_branches_live.push(false);
  364. currentMergeBlockId.push(
  365. ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx));
  366. } break;
  367. case SpvOpSwitch:
  368. case SpvOpBranch:
  369. case SpvOpBranchConditional:
  370. case SpvOpUnreachable: {
  371. if (assume_branches_live.top()) {
  372. AddToWorklist(&*ii);
  373. }
  374. } break;
  375. default: {
  376. // Function calls, atomics, function params, function returns, etc.
  377. // TODO(greg-lunarg): function calls live only if write to non-local
  378. if (!ii->IsOpcodeSafeToDelete()) {
  379. AddToWorklist(&*ii);
  380. }
  381. // Remember function calls
  382. if (op == SpvOpFunctionCall) call_in_func_ = true;
  383. } break;
  384. }
  385. }
  386. }
  387. // See if current function is an entry point
  388. for (auto& ei : get_module()->entry_points()) {
  389. if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) ==
  390. func->result_id()) {
  391. func_is_entry_point_ = true;
  392. break;
  393. }
  394. }
  395. // If the current function is an entry point and has no function calls,
  396. // we can optimize private variables as locals
  397. private_like_local_ = func_is_entry_point_ && !call_in_func_;
  398. // If privates are not like local, add their stores to worklist
  399. if (!private_like_local_)
  400. for (auto& ps : private_stores_) AddToWorklist(ps);
  401. // Perform closure on live instruction set.
  402. while (!worklist_.empty()) {
  403. Instruction* liveInst = worklist_.front();
  404. // Add all operand instructions if not already live
  405. liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
  406. Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
  407. // Do not add label if an operand of a branch. This is not needed
  408. // as part of live code discovery and can create false live code,
  409. // for example, the branch to a header of a loop.
  410. if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return;
  411. AddToWorklist(inInst);
  412. });
  413. if (liveInst->type_id() != 0) {
  414. AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id()));
  415. }
  416. // If in a structured if or loop construct, add the controlling
  417. // conditional branch and its merge.
  418. BasicBlock* blk = context()->get_instr_block(liveInst);
  419. Instruction* branchInst = block2headerBranch_[blk];
  420. if (branchInst != nullptr) {
  421. AddToWorklist(branchInst);
  422. Instruction* mergeInst = branch2merge_[branchInst];
  423. AddToWorklist(mergeInst);
  424. }
  425. // If the block is a header, add the next outermost controlling
  426. // conditional branch and its merge.
  427. Instruction* nextBranchInst = header2nextHeaderBranch_[blk];
  428. if (nextBranchInst != nullptr) {
  429. AddToWorklist(nextBranchInst);
  430. Instruction* mergeInst = branch2merge_[nextBranchInst];
  431. AddToWorklist(mergeInst);
  432. }
  433. // If local load, add all variable's stores if variable not already live
  434. if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) {
  435. uint32_t varId;
  436. (void)GetPtr(liveInst, &varId);
  437. if (varId != 0) {
  438. ProcessLoad(func, varId);
  439. }
  440. // Process memory copies like loads
  441. } else if (liveInst->opcode() == SpvOpCopyMemory ||
  442. liveInst->opcode() == SpvOpCopyMemorySized) {
  443. uint32_t varId;
  444. (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx),
  445. &varId);
  446. if (varId != 0) {
  447. ProcessLoad(func, varId);
  448. }
  449. // If DebugDeclare, process as load of variable
  450. } else if (liveInst->GetCommonDebugOpcode() ==
  451. CommonDebugInfoDebugDeclare) {
  452. uint32_t varId =
  453. liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex);
  454. ProcessLoad(func, varId);
  455. // If DebugValue with Deref, process as load of variable
  456. } else if (liveInst->GetCommonDebugOpcode() == CommonDebugInfoDebugValue) {
  457. uint32_t varId = context()
  458. ->get_debug_info_mgr()
  459. ->GetVariableIdOfDebugValueUsedForDeclare(liveInst);
  460. if (varId != 0) ProcessLoad(func, varId);
  461. // If merge, add other branches that are part of its control structure
  462. } else if (liveInst->opcode() == SpvOpLoopMerge ||
  463. liveInst->opcode() == SpvOpSelectionMerge) {
  464. AddBreaksAndContinuesToWorklist(liveInst);
  465. // If function call, treat as if it loads from all pointer arguments
  466. } else if (liveInst->opcode() == SpvOpFunctionCall) {
  467. liveInst->ForEachInId([this, func](const uint32_t* iid) {
  468. // Skip non-ptr args
  469. if (!IsPtr(*iid)) return;
  470. uint32_t varId;
  471. (void)GetPtr(*iid, &varId);
  472. ProcessLoad(func, varId);
  473. });
  474. // If function parameter, treat as if it's result id is loaded from
  475. } else if (liveInst->opcode() == SpvOpFunctionParameter) {
  476. ProcessLoad(func, liveInst->result_id());
  477. // We treat an OpImageTexelPointer as a load of the pointer, and
  478. // that value is manipulated to get the result.
  479. } else if (liveInst->opcode() == SpvOpImageTexelPointer) {
  480. uint32_t varId;
  481. (void)GetPtr(liveInst, &varId);
  482. if (varId != 0) {
  483. ProcessLoad(func, varId);
  484. }
  485. }
  486. // Add OpDecorateId instructions that apply to this instruction to the work
  487. // list. We use the decoration manager to look through the group
  488. // decorations to get to the OpDecorate* instructions themselves.
  489. auto decorations =
  490. get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false);
  491. for (Instruction* dec : decorations) {
  492. // We only care about OpDecorateId instructions because the are the only
  493. // decorations that will reference an id that will have to be kept live
  494. // because of that use.
  495. if (dec->opcode() != SpvOpDecorateId) {
  496. continue;
  497. }
  498. if (dec->GetSingleWordInOperand(1) ==
  499. SpvDecorationHlslCounterBufferGOOGLE) {
  500. // These decorations should not force the use id to be live. It will be
  501. // removed if either the target or the in operand are dead.
  502. continue;
  503. }
  504. AddToWorklist(dec);
  505. }
  506. // Add DebugScope and DebugInlinedAt for |liveInst| to the work list.
  507. if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) {
  508. auto* scope = get_def_use_mgr()->GetDef(
  509. liveInst->GetDebugScope().GetLexicalScope());
  510. AddToWorklist(scope);
  511. }
  512. if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) {
  513. auto* inlined_at =
  514. get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt());
  515. AddToWorklist(inlined_at);
  516. }
  517. worklist_.pop();
  518. }
  519. // Kill dead instructions and remember dead blocks
  520. for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) {
  521. uint32_t mergeBlockId = 0;
  522. (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) {
  523. if (!IsDead(inst)) return;
  524. if (inst->opcode() == SpvOpLabel) return;
  525. // If dead instruction is selection merge, remember merge block
  526. // for new branch at end of block
  527. if (inst->opcode() == SpvOpSelectionMerge ||
  528. inst->opcode() == SpvOpLoopMerge)
  529. mergeBlockId = inst->GetSingleWordInOperand(0);
  530. to_kill_.push_back(inst);
  531. modified = true;
  532. });
  533. // If a structured if or loop was deleted, add a branch to its merge
  534. // block, and traverse to the merge block and continue processing there.
  535. // We know the block still exists because the label is not deleted.
  536. if (mergeBlockId != 0) {
  537. AddBranch(mergeBlockId, *bi);
  538. for (++bi; (*bi)->id() != mergeBlockId; ++bi) {
  539. }
  540. auto merge_terminator = (*bi)->terminator();
  541. if (merge_terminator->opcode() == SpvOpUnreachable) {
  542. // The merge was unreachable. This is undefined behaviour so just
  543. // return (or return an undef). Then mark the new return as live.
  544. auto func_ret_type_inst = get_def_use_mgr()->GetDef(func->type_id());
  545. if (func_ret_type_inst->opcode() == SpvOpTypeVoid) {
  546. merge_terminator->SetOpcode(SpvOpReturn);
  547. } else {
  548. // Find an undef for the return value and make sure it gets kept by
  549. // the pass.
  550. auto undef_id = Type2Undef(func->type_id());
  551. auto undef = get_def_use_mgr()->GetDef(undef_id);
  552. live_insts_.Set(undef->unique_id());
  553. merge_terminator->SetOpcode(SpvOpReturnValue);
  554. merge_terminator->SetInOperands({{SPV_OPERAND_TYPE_ID, {undef_id}}});
  555. get_def_use_mgr()->AnalyzeInstUse(merge_terminator);
  556. }
  557. live_insts_.Set(merge_terminator->unique_id());
  558. }
  559. } else {
  560. ++bi;
  561. }
  562. }
  563. return modified;
  564. }
  565. void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
  566. // Keep all execution modes.
  567. for (auto& exec : get_module()->execution_modes()) {
  568. AddToWorklist(&exec);
  569. }
  570. // Keep all entry points.
  571. for (auto& entry : get_module()->entry_points()) {
  572. if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
  573. // In SPIR-V 1.4 and later, entry points must list all global variables
  574. // used. DCE can still remove non-input/output variables and update the
  575. // interface list. Mark the entry point as live and inputs and outputs as
  576. // live, but defer decisions all other interfaces.
  577. live_insts_.Set(entry.unique_id());
  578. // The actual function is live always.
  579. AddToWorklist(
  580. get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(1u)));
  581. for (uint32_t i = 3; i < entry.NumInOperands(); ++i) {
  582. auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
  583. auto storage_class = var->GetSingleWordInOperand(0u);
  584. if (storage_class == SpvStorageClassInput ||
  585. storage_class == SpvStorageClassOutput) {
  586. AddToWorklist(var);
  587. }
  588. }
  589. } else {
  590. AddToWorklist(&entry);
  591. }
  592. }
  593. for (auto& anno : get_module()->annotations()) {
  594. if (anno.opcode() == SpvOpDecorate) {
  595. // Keep workgroup size.
  596. if (anno.GetSingleWordInOperand(1u) == SpvDecorationBuiltIn &&
  597. anno.GetSingleWordInOperand(2u) == SpvBuiltInWorkgroupSize) {
  598. AddToWorklist(&anno);
  599. }
  600. if (context()->preserve_bindings()) {
  601. // Keep all bindings.
  602. if ((anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet) ||
  603. (anno.GetSingleWordInOperand(1u) == SpvDecorationBinding)) {
  604. AddToWorklist(&anno);
  605. }
  606. }
  607. if (context()->preserve_spec_constants()) {
  608. // Keep all specialization constant instructions
  609. if (anno.GetSingleWordInOperand(1u) == SpvDecorationSpecId) {
  610. AddToWorklist(&anno);
  611. }
  612. }
  613. }
  614. }
  615. // For each DebugInfo GlobalVariable keep all operands except the Variable.
  616. // Later, if the variable is dead, we will set the operand to DebugInfoNone.
  617. for (auto& dbg : get_module()->ext_inst_debuginfo()) {
  618. if (dbg.GetCommonDebugOpcode() != CommonDebugInfoDebugGlobalVariable)
  619. continue;
  620. dbg.ForEachInId([this](const uint32_t* iid) {
  621. Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
  622. if (inInst->opcode() == SpvOpVariable) return;
  623. AddToWorklist(inInst);
  624. });
  625. }
  626. }
  627. Pass::Status AggressiveDCEPass::ProcessImpl() {
  628. // Current functionality assumes shader capability
  629. // TODO(greg-lunarg): Handle additional capabilities
  630. if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
  631. return Status::SuccessWithoutChange;
  632. // Current functionality assumes relaxed logical addressing (see
  633. // instruction.h)
  634. // TODO(greg-lunarg): Handle non-logical addressing
  635. if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
  636. return Status::SuccessWithoutChange;
  637. // The variable pointer extension is no longer needed to use the capability,
  638. // so we have to look for the capability.
  639. if (context()->get_feature_mgr()->HasCapability(
  640. SpvCapabilityVariablePointersStorageBuffer))
  641. return Status::SuccessWithoutChange;
  642. // If any extensions in the module are not explicitly supported,
  643. // return unmodified.
  644. if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
  645. // Eliminate Dead functions.
  646. bool modified = EliminateDeadFunctions();
  647. InitializeModuleScopeLiveInstructions();
  648. // Process all entry point functions.
  649. ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); };
  650. modified |= context()->ProcessEntryPointCallTree(pfn);
  651. // If the decoration manager is kept live then the context will try to keep it
  652. // up to date. ADCE deals with group decorations by changing the operands in
  653. // |OpGroupDecorate| instruction directly without informing the decoration
  654. // manager. This can put it in an invalid state which will cause an error
  655. // when the context tries to update it. To avoid this problem invalidate
  656. // the decoration manager upfront.
  657. //
  658. // We kill it at now because it is used when processing the entry point
  659. // functions.
  660. context()->InvalidateAnalyses(IRContext::Analysis::kAnalysisDecorations);
  661. // Process module-level instructions. Now that all live instructions have
  662. // been marked, it is safe to remove dead global values.
  663. modified |= ProcessGlobalValues();
  664. assert((to_kill_.empty() || modified) &&
  665. "A dead instruction was identified, but no change recorded.");
  666. // Kill all dead instructions.
  667. for (auto inst : to_kill_) {
  668. context()->KillInst(inst);
  669. }
  670. // Cleanup all CFG including all unreachable blocks.
  671. ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); };
  672. modified |= context()->ProcessEntryPointCallTree(cleanup);
  673. return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
  674. }
  675. bool AggressiveDCEPass::EliminateDeadFunctions() {
  676. // Identify live functions first. Those that are not live
  677. // are dead. ADCE is disabled for non-shaders so we do not check for exported
  678. // functions here.
  679. std::unordered_set<const Function*> live_function_set;
  680. ProcessFunction mark_live = [&live_function_set](Function* fp) {
  681. live_function_set.insert(fp);
  682. return false;
  683. };
  684. context()->ProcessEntryPointCallTree(mark_live);
  685. bool modified = false;
  686. for (auto funcIter = get_module()->begin();
  687. funcIter != get_module()->end();) {
  688. if (live_function_set.count(&*funcIter) == 0) {
  689. modified = true;
  690. funcIter =
  691. eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter);
  692. } else {
  693. ++funcIter;
  694. }
  695. }
  696. return modified;
  697. }
  698. bool AggressiveDCEPass::ProcessGlobalValues() {
  699. // Remove debug and annotation statements referencing dead instructions.
  700. // This must be done before killing the instructions, otherwise there are
  701. // dead objects in the def/use database.
  702. bool modified = false;
  703. Instruction* instruction = &*get_module()->debug2_begin();
  704. while (instruction) {
  705. if (instruction->opcode() != SpvOpName) {
  706. instruction = instruction->NextNode();
  707. continue;
  708. }
  709. if (IsTargetDead(instruction)) {
  710. instruction = context()->KillInst(instruction);
  711. modified = true;
  712. } else {
  713. instruction = instruction->NextNode();
  714. }
  715. }
  716. // This code removes all unnecessary decorations safely (see #1174). It also
  717. // does so in a more efficient manner than deleting them only as the targets
  718. // are deleted.
  719. std::vector<Instruction*> annotations;
  720. for (auto& inst : get_module()->annotations()) annotations.push_back(&inst);
  721. std::sort(annotations.begin(), annotations.end(), DecorationLess());
  722. for (auto annotation : annotations) {
  723. switch (annotation->opcode()) {
  724. case SpvOpDecorate:
  725. case SpvOpMemberDecorate:
  726. case SpvOpDecorateStringGOOGLE:
  727. case SpvOpMemberDecorateStringGOOGLE:
  728. if (IsTargetDead(annotation)) {
  729. context()->KillInst(annotation);
  730. modified = true;
  731. }
  732. break;
  733. case SpvOpDecorateId:
  734. if (IsTargetDead(annotation)) {
  735. context()->KillInst(annotation);
  736. modified = true;
  737. } else {
  738. if (annotation->GetSingleWordInOperand(1) ==
  739. SpvDecorationHlslCounterBufferGOOGLE) {
  740. // HlslCounterBuffer will reference an id other than the target.
  741. // If that id is dead, then the decoration can be removed as well.
  742. uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2);
  743. Instruction* counter_buffer_inst =
  744. get_def_use_mgr()->GetDef(counter_buffer_id);
  745. if (IsDead(counter_buffer_inst)) {
  746. context()->KillInst(annotation);
  747. modified = true;
  748. }
  749. }
  750. }
  751. break;
  752. case SpvOpGroupDecorate: {
  753. // Go through the targets of this group decorate. Remove each dead
  754. // target. If all targets are dead, remove this decoration.
  755. bool dead = true;
  756. bool removed_operand = false;
  757. for (uint32_t i = 1; i < annotation->NumOperands();) {
  758. Instruction* opInst =
  759. get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
  760. if (IsDead(opInst)) {
  761. // Don't increment |i|.
  762. annotation->RemoveOperand(i);
  763. modified = true;
  764. removed_operand = true;
  765. } else {
  766. i++;
  767. dead = false;
  768. }
  769. }
  770. if (dead) {
  771. context()->KillInst(annotation);
  772. modified = true;
  773. } else if (removed_operand) {
  774. context()->UpdateDefUse(annotation);
  775. }
  776. break;
  777. }
  778. case SpvOpGroupMemberDecorate: {
  779. // Go through the targets of this group member decorate. Remove each
  780. // dead target (and member index). If all targets are dead, remove this
  781. // decoration.
  782. bool dead = true;
  783. bool removed_operand = false;
  784. for (uint32_t i = 1; i < annotation->NumOperands();) {
  785. Instruction* opInst =
  786. get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
  787. if (IsDead(opInst)) {
  788. // Don't increment |i|.
  789. annotation->RemoveOperand(i + 1);
  790. annotation->RemoveOperand(i);
  791. modified = true;
  792. removed_operand = true;
  793. } else {
  794. i += 2;
  795. dead = false;
  796. }
  797. }
  798. if (dead) {
  799. context()->KillInst(annotation);
  800. modified = true;
  801. } else if (removed_operand) {
  802. context()->UpdateDefUse(annotation);
  803. }
  804. break;
  805. }
  806. case SpvOpDecorationGroup:
  807. // By the time we hit decoration groups we've checked everything that
  808. // can target them. So if they have no uses they must be dead.
  809. if (get_def_use_mgr()->NumUsers(annotation) == 0) {
  810. context()->KillInst(annotation);
  811. modified = true;
  812. }
  813. break;
  814. default:
  815. assert(false);
  816. break;
  817. }
  818. }
  819. for (auto& dbg : get_module()->ext_inst_debuginfo()) {
  820. if (!IsDead(&dbg)) continue;
  821. // Save GlobalVariable if its variable is live, otherwise null out variable
  822. // index
  823. if (dbg.GetCommonDebugOpcode() == CommonDebugInfoDebugGlobalVariable) {
  824. auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex);
  825. Instruction* var_inst = get_def_use_mgr()->GetDef(var_id);
  826. if (!IsDead(var_inst)) continue;
  827. context()->ForgetUses(&dbg);
  828. dbg.SetOperand(
  829. kGlobalVariableVariableIndex,
  830. {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()});
  831. context()->AnalyzeUses(&dbg);
  832. continue;
  833. }
  834. to_kill_.push_back(&dbg);
  835. modified = true;
  836. }
  837. // Since ADCE is disabled for non-shaders, we don't check for export linkage
  838. // attributes here.
  839. for (auto& val : get_module()->types_values()) {
  840. if (IsDead(&val)) {
  841. // Save forwarded pointer if pointer is live since closure does not mark
  842. // this live as it does not have a result id. This is a little too
  843. // conservative since it is not known if the structure type that needed
  844. // it is still live. TODO(greg-lunarg): Only save if needed.
  845. if (val.opcode() == SpvOpTypeForwardPointer) {
  846. uint32_t ptr_ty_id = val.GetSingleWordInOperand(0);
  847. Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id);
  848. if (!IsDead(ptr_ty_inst)) continue;
  849. }
  850. to_kill_.push_back(&val);
  851. modified = true;
  852. }
  853. }
  854. if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) {
  855. // Remove the dead interface variables from the entry point interface list.
  856. for (auto& entry : get_module()->entry_points()) {
  857. std::vector<Operand> new_operands;
  858. for (uint32_t i = 0; i < entry.NumInOperands(); ++i) {
  859. if (i < 3) {
  860. // Execution model, function id and name are always valid.
  861. new_operands.push_back(entry.GetInOperand(i));
  862. } else {
  863. auto* var =
  864. get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i));
  865. if (!IsDead(var)) {
  866. new_operands.push_back(entry.GetInOperand(i));
  867. }
  868. }
  869. }
  870. if (new_operands.size() != entry.NumInOperands()) {
  871. entry.SetInOperands(std::move(new_operands));
  872. get_def_use_mgr()->UpdateDefUse(&entry);
  873. }
  874. }
  875. }
  876. return modified;
  877. }
  878. AggressiveDCEPass::AggressiveDCEPass() = default;
  879. Pass::Status AggressiveDCEPass::Process() {
  880. // Initialize extensions allowlist
  881. InitExtensions();
  882. return ProcessImpl();
  883. }
  884. void AggressiveDCEPass::InitExtensions() {
  885. extensions_allowlist_.clear();
  886. extensions_allowlist_.insert({
  887. "SPV_AMD_shader_explicit_vertex_parameter",
  888. "SPV_AMD_shader_trinary_minmax",
  889. "SPV_AMD_gcn_shader",
  890. "SPV_KHR_shader_ballot",
  891. "SPV_AMD_shader_ballot",
  892. "SPV_AMD_gpu_shader_half_float",
  893. "SPV_KHR_shader_draw_parameters",
  894. "SPV_KHR_subgroup_vote",
  895. "SPV_KHR_8bit_storage",
  896. "SPV_KHR_16bit_storage",
  897. "SPV_KHR_device_group",
  898. "SPV_KHR_multiview",
  899. "SPV_NVX_multiview_per_view_attributes",
  900. "SPV_NV_viewport_array2",
  901. "SPV_NV_stereo_view_rendering",
  902. "SPV_NV_sample_mask_override_coverage",
  903. "SPV_NV_geometry_shader_passthrough",
  904. "SPV_AMD_texture_gather_bias_lod",
  905. "SPV_KHR_storage_buffer_storage_class",
  906. // SPV_KHR_variable_pointers
  907. // Currently do not support extended pointer expressions
  908. "SPV_AMD_gpu_shader_int16",
  909. "SPV_KHR_post_depth_coverage",
  910. "SPV_KHR_shader_atomic_counter_ops",
  911. "SPV_EXT_shader_stencil_export",
  912. "SPV_EXT_shader_viewport_index_layer",
  913. "SPV_AMD_shader_image_load_store_lod",
  914. "SPV_AMD_shader_fragment_mask",
  915. "SPV_EXT_fragment_fully_covered",
  916. "SPV_AMD_gpu_shader_half_float_fetch",
  917. "SPV_GOOGLE_decorate_string",
  918. "SPV_GOOGLE_hlsl_functionality1",
  919. "SPV_GOOGLE_user_type",
  920. "SPV_NV_shader_subgroup_partitioned",
  921. "SPV_EXT_demote_to_helper_invocation",
  922. "SPV_EXT_descriptor_indexing",
  923. "SPV_NV_fragment_shader_barycentric",
  924. "SPV_NV_compute_shader_derivatives",
  925. "SPV_NV_shader_image_footprint",
  926. "SPV_NV_shading_rate",
  927. "SPV_NV_mesh_shader",
  928. "SPV_NV_ray_tracing",
  929. "SPV_KHR_ray_tracing",
  930. "SPV_KHR_ray_query",
  931. "SPV_EXT_fragment_invocation_density",
  932. "SPV_EXT_physical_storage_buffer",
  933. "SPV_KHR_terminate_invocation",
  934. "SPV_KHR_shader_clock",
  935. "SPV_KHR_vulkan_memory_model",
  936. "SPV_KHR_subgroup_uniform_control_flow",
  937. "SPV_KHR_integer_dot_product",
  938. "SPV_EXT_shader_image_int64",
  939. });
  940. }
  941. } // namespace opt
  942. } // namespace spvtools