aggressive_dead_code_elim_pass.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  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/iterator.h"
  23. #include "source/opt/reflect.h"
  24. namespace spvtools {
  25. namespace opt {
  26. namespace {
  27. const uint32_t kTypePointerStorageClassInIdx = 0;
  28. const uint32_t kEntryPointFunctionIdInIdx = 1;
  29. const uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
  30. const uint32_t kLoopMergeMergeBlockIdInIdx = 0;
  31. const uint32_t kLoopMergeContinueBlockIdInIdx = 1;
  32. const uint32_t kCopyMemoryTargetAddrInIdx = 0;
  33. const uint32_t kCopyMemorySourceAddrInIdx = 1;
  34. // Sorting functor to present annotation instructions in an easy-to-process
  35. // order. The functor orders by opcode first and falls back on unique id
  36. // ordering if both instructions have the same opcode.
  37. //
  38. // Desired priority:
  39. // SpvOpGroupDecorate
  40. // SpvOpGroupMemberDecorate
  41. // SpvOpDecorate
  42. // SpvOpMemberDecorate
  43. // SpvOpDecorateId
  44. // SpvOpDecorateStringGOOGLE
  45. // SpvOpDecorationGroup
  46. struct DecorationLess {
  47. bool operator()(const Instruction* lhs, const Instruction* rhs) const {
  48. assert(lhs && rhs);
  49. SpvOp lhsOp = lhs->opcode();
  50. SpvOp rhsOp = rhs->opcode();
  51. if (lhsOp != rhsOp) {
  52. #define PRIORITY_CASE(opcode) \
  53. if (lhsOp == opcode && rhsOp != opcode) return true; \
  54. if (rhsOp == opcode && lhsOp != opcode) return false;
  55. // OpGroupDecorate and OpGroupMember decorate are highest priority to
  56. // eliminate dead targets early and simplify subsequent checks.
  57. PRIORITY_CASE(SpvOpGroupDecorate)
  58. PRIORITY_CASE(SpvOpGroupMemberDecorate)
  59. PRIORITY_CASE(SpvOpDecorate)
  60. PRIORITY_CASE(SpvOpMemberDecorate)
  61. PRIORITY_CASE(SpvOpDecorateId)
  62. PRIORITY_CASE(SpvOpDecorateStringGOOGLE)
  63. // OpDecorationGroup is lowest priority to ensure use/def chains remain
  64. // usable for instructions that target this group.
  65. PRIORITY_CASE(SpvOpDecorationGroup)
  66. #undef PRIORITY_CASE
  67. }
  68. // Fall back to maintain total ordering (compare unique ids).
  69. return *lhs < *rhs;
  70. }
  71. };
  72. } // namespace
  73. bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) {
  74. if (varId == 0) return false;
  75. const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
  76. const SpvOp op = varInst->opcode();
  77. if (op != SpvOpVariable) return false;
  78. const uint32_t varTypeId = varInst->type_id();
  79. const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
  80. if (varTypeInst->opcode() != SpvOpTypePointer) return false;
  81. return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) ==
  82. storageClass;
  83. }
  84. bool AggressiveDCEPass::IsLocalVar(uint32_t varId) {
  85. if (IsVarOfStorage(varId, SpvStorageClassFunction)) {
  86. return true;
  87. }
  88. if (!private_like_local_) {
  89. return false;
  90. }
  91. return IsVarOfStorage(varId, SpvStorageClassPrivate) ||
  92. IsVarOfStorage(varId, SpvStorageClassWorkgroup);
  93. }
  94. void AggressiveDCEPass::AddStores(uint32_t ptrId) {
  95. get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId](Instruction* user) {
  96. switch (user->opcode()) {
  97. case SpvOpAccessChain:
  98. case SpvOpInBoundsAccessChain:
  99. case SpvOpCopyObject:
  100. this->AddStores(user->result_id());
  101. break;
  102. case SpvOpLoad:
  103. break;
  104. case SpvOpCopyMemory:
  105. case SpvOpCopyMemorySized:
  106. if (user->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx) == ptrId) {
  107. AddToWorklist(user);
  108. }
  109. break;
  110. // If default, assume it stores e.g. frexp, modf, function call
  111. case SpvOpStore:
  112. default:
  113. AddToWorklist(user);
  114. break;
  115. }
  116. });
  117. }
  118. bool AggressiveDCEPass::AllExtensionsSupported() const {
  119. // If any extension not in whitelist, return false
  120. for (auto& ei : get_module()->extensions()) {
  121. const char* extName =
  122. reinterpret_cast<const char*>(&ei.GetInOperand(0).words[0]);
  123. if (extensions_whitelist_.find(extName) == extensions_whitelist_.end())
  124. return false;
  125. }
  126. return true;
  127. }
  128. bool AggressiveDCEPass::IsDead(Instruction* inst) {
  129. if (IsLive(inst)) return false;
  130. if (inst->IsBranch() && !IsStructuredHeader(context()->get_instr_block(inst),
  131. nullptr, nullptr, nullptr))
  132. return false;
  133. return true;
  134. }
  135. bool AggressiveDCEPass::IsTargetDead(Instruction* inst) {
  136. const uint32_t tId = inst->GetSingleWordInOperand(0);
  137. Instruction* tInst = get_def_use_mgr()->GetDef(tId);
  138. if (IsAnnotationInst(tInst->opcode())) {
  139. // This must be a decoration group. We go through annotations in a specific
  140. // order. So if this is not used by any group or group member decorates, it
  141. // is dead.
  142. assert(tInst->opcode() == SpvOpDecorationGroup);
  143. bool dead = true;
  144. get_def_use_mgr()->ForEachUser(tInst, [&dead](Instruction* user) {
  145. if (user->opcode() == SpvOpGroupDecorate ||
  146. user->opcode() == SpvOpGroupMemberDecorate)
  147. dead = false;
  148. });
  149. return dead;
  150. }
  151. return IsDead(tInst);
  152. }
  153. void AggressiveDCEPass::ProcessLoad(uint32_t varId) {
  154. // Only process locals
  155. if (!IsLocalVar(varId)) return;
  156. // Return if already processed
  157. if (live_local_vars_.find(varId) != live_local_vars_.end()) return;
  158. // Mark all stores to varId as live
  159. AddStores(varId);
  160. // Cache varId as processed
  161. live_local_vars_.insert(varId);
  162. }
  163. bool AggressiveDCEPass::IsStructuredHeader(BasicBlock* bp,
  164. Instruction** mergeInst,
  165. Instruction** branchInst,
  166. uint32_t* mergeBlockId) {
  167. if (!bp) return false;
  168. Instruction* mi = bp->GetMergeInst();
  169. if (mi == nullptr) return false;
  170. Instruction* bri = &*bp->tail();
  171. if (branchInst != nullptr) *branchInst = bri;
  172. if (mergeInst != nullptr) *mergeInst = mi;
  173. if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0);
  174. return true;
  175. }
  176. void AggressiveDCEPass::ComputeBlock2HeaderMaps(
  177. std::list<BasicBlock*>& structuredOrder) {
  178. block2headerBranch_.clear();
  179. branch2merge_.clear();
  180. structured_order_index_.clear();
  181. std::stack<Instruction*> currentHeaderBranch;
  182. currentHeaderBranch.push(nullptr);
  183. uint32_t currentMergeBlockId = 0;
  184. uint32_t index = 0;
  185. for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();
  186. ++bi, ++index) {
  187. structured_order_index_[*bi] = index;
  188. // If this block is the merge block of the current control construct,
  189. // we are leaving the current construct so we must update state
  190. if ((*bi)->id() == currentMergeBlockId) {
  191. currentHeaderBranch.pop();
  192. Instruction* chb = currentHeaderBranch.top();
  193. if (chb != nullptr)
  194. currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0);
  195. }
  196. Instruction* mergeInst;
  197. Instruction* branchInst;
  198. uint32_t mergeBlockId;
  199. bool is_header =
  200. IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId);
  201. // If this is a loop header, update state first so the block will map to
  202. // the loop.
  203. if (is_header && mergeInst->opcode() == SpvOpLoopMerge) {
  204. currentHeaderBranch.push(branchInst);
  205. branch2merge_[branchInst] = mergeInst;
  206. currentMergeBlockId = mergeBlockId;
  207. }
  208. // Map the block to the current construct.
  209. block2headerBranch_[*bi] = currentHeaderBranch.top();
  210. // If this is an if header, update state so following blocks map to the if.
  211. if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) {
  212. currentHeaderBranch.push(branchInst);
  213. branch2merge_[branchInst] = mergeInst;
  214. currentMergeBlockId = mergeBlockId;
  215. }
  216. }
  217. }
  218. void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) {
  219. std::unique_ptr<Instruction> newBranch(
  220. new Instruction(context(), SpvOpBranch, 0, 0,
  221. {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}}));
  222. context()->AnalyzeDefUse(&*newBranch);
  223. context()->set_instr_block(&*newBranch, bp);
  224. bp->AddInstruction(std::move(newBranch));
  225. }
  226. void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
  227. Instruction* mergeInst) {
  228. assert(mergeInst->opcode() == SpvOpSelectionMerge ||
  229. mergeInst->opcode() == SpvOpLoopMerge);
  230. BasicBlock* header = context()->get_instr_block(mergeInst);
  231. uint32_t headerIndex = structured_order_index_[header];
  232. const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0);
  233. BasicBlock* merge = context()->get_instr_block(mergeId);
  234. uint32_t mergeIndex = structured_order_index_[merge];
  235. get_def_use_mgr()->ForEachUser(
  236. mergeId, [headerIndex, mergeIndex, this](Instruction* user) {
  237. if (!user->IsBranch()) return;
  238. BasicBlock* block = context()->get_instr_block(user);
  239. uint32_t index = structured_order_index_[block];
  240. if (headerIndex < index && index < mergeIndex) {
  241. // This is a break from the loop.
  242. AddToWorklist(user);
  243. // Add branch's merge if there is one.
  244. Instruction* userMerge = branch2merge_[user];
  245. if (userMerge != nullptr) AddToWorklist(userMerge);
  246. }
  247. });
  248. if (mergeInst->opcode() != SpvOpLoopMerge) {
  249. return;
  250. }
  251. // For loops we need to find the continues as well.
  252. const uint32_t contId =
  253. mergeInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx);
  254. get_def_use_mgr()->ForEachUser(contId, [&contId, this](Instruction* user) {
  255. SpvOp op = user->opcode();
  256. if (op == SpvOpBranchConditional || op == SpvOpSwitch) {
  257. // A conditional branch or switch can only be a continue if it does not
  258. // have a merge instruction or its merge block is not the continue block.
  259. Instruction* hdrMerge = branch2merge_[user];
  260. if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) {
  261. uint32_t hdrMergeId =
  262. hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
  263. if (hdrMergeId == contId) return;
  264. // Need to mark merge instruction too
  265. AddToWorklist(hdrMerge);
  266. }
  267. } else if (op == SpvOpBranch) {
  268. // An unconditional branch can only be a continue if it is not
  269. // branching to its own merge block.
  270. BasicBlock* blk = context()->get_instr_block(user);
  271. Instruction* hdrBranch = block2headerBranch_[blk];
  272. if (hdrBranch == nullptr) return;
  273. Instruction* hdrMerge = branch2merge_[hdrBranch];
  274. if (hdrMerge->opcode() == SpvOpLoopMerge) return;
  275. uint32_t hdrMergeId =
  276. hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx);
  277. if (contId == hdrMergeId) return;
  278. } else {
  279. return;
  280. }
  281. AddToWorklist(user);
  282. });
  283. }
  284. bool AggressiveDCEPass::AggressiveDCE(Function* func) {
  285. // Mark function parameters as live.
  286. AddToWorklist(&func->DefInst());
  287. func->ForEachParam(
  288. [this](const Instruction* param) {
  289. AddToWorklist(const_cast<Instruction*>(param));
  290. },
  291. false);
  292. // Compute map from block to controlling conditional branch
  293. std::list<BasicBlock*> structuredOrder;
  294. cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder);
  295. ComputeBlock2HeaderMaps(structuredOrder);
  296. bool modified = false;
  297. // Add instructions with external side effects to worklist. Also add branches
  298. // EXCEPT those immediately contained in an "if" selection construct or a loop
  299. // or continue construct.
  300. // TODO(greg-lunarg): Handle Frexp, Modf more optimally
  301. call_in_func_ = false;
  302. func_is_entry_point_ = false;
  303. private_stores_.clear();
  304. // Stacks to keep track of when we are inside an if- or loop-construct.
  305. // When immediately inside an if- or loop-construct, we do not initially
  306. // mark branches live. All other branches must be marked live.
  307. std::stack<bool> assume_branches_live;
  308. std::stack<uint32_t> currentMergeBlockId;
  309. // Push sentinel values on stack for when outside of any control flow.
  310. assume_branches_live.push(true);
  311. currentMergeBlockId.push(0);
  312. for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) {
  313. // If exiting if or loop, update stacks
  314. if ((*bi)->id() == currentMergeBlockId.top()) {
  315. assume_branches_live.pop();
  316. currentMergeBlockId.pop();
  317. }
  318. for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) {
  319. SpvOp op = ii->opcode();
  320. switch (op) {
  321. case SpvOpStore: {
  322. uint32_t varId;
  323. (void)GetPtr(&*ii, &varId);
  324. // Mark stores as live if their variable is not function scope
  325. // and is not private scope. Remember private stores for possible
  326. // later inclusion. We cannot call IsLocalVar at this point because
  327. // private_like_local_ has not been set yet.
  328. if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
  329. IsVarOfStorage(varId, SpvStorageClassWorkgroup))
  330. private_stores_.push_back(&*ii);
  331. else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
  332. AddToWorklist(&*ii);
  333. } break;
  334. case SpvOpCopyMemory:
  335. case SpvOpCopyMemorySized: {
  336. uint32_t varId;
  337. (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx),
  338. &varId);
  339. if (IsVarOfStorage(varId, SpvStorageClassPrivate) ||
  340. IsVarOfStorage(varId, SpvStorageClassWorkgroup))
  341. private_stores_.push_back(&*ii);
  342. else if (!IsVarOfStorage(varId, SpvStorageClassFunction))
  343. AddToWorklist(&*ii);
  344. } break;
  345. case SpvOpLoopMerge: {
  346. assume_branches_live.push(false);
  347. currentMergeBlockId.push(
  348. ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx));
  349. } break;
  350. case SpvOpSelectionMerge: {
  351. assume_branches_live.push(false);
  352. currentMergeBlockId.push(
  353. ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx));
  354. } break;
  355. case SpvOpSwitch:
  356. case SpvOpBranch:
  357. case SpvOpBranchConditional: {
  358. if (assume_branches_live.top()) {
  359. AddToWorklist(&*ii);
  360. }
  361. } break;
  362. default: {
  363. // Function calls, atomics, function params, function returns, etc.
  364. // TODO(greg-lunarg): function calls live only if write to non-local
  365. if (!ii->IsOpcodeSafeToDelete()) {
  366. AddToWorklist(&*ii);
  367. }
  368. // Remember function calls
  369. if (op == SpvOpFunctionCall) call_in_func_ = true;
  370. } break;
  371. }
  372. }
  373. }
  374. // See if current function is an entry point
  375. for (auto& ei : get_module()->entry_points()) {
  376. if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) ==
  377. func->result_id()) {
  378. func_is_entry_point_ = true;
  379. break;
  380. }
  381. }
  382. // If the current function is an entry point and has no function calls,
  383. // we can optimize private variables as locals
  384. private_like_local_ = func_is_entry_point_ && !call_in_func_;
  385. // If privates are not like local, add their stores to worklist
  386. if (!private_like_local_)
  387. for (auto& ps : private_stores_) AddToWorklist(ps);
  388. // Perform closure on live instruction set.
  389. while (!worklist_.empty()) {
  390. Instruction* liveInst = worklist_.front();
  391. // Add all operand instructions if not already live
  392. liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) {
  393. Instruction* inInst = get_def_use_mgr()->GetDef(*iid);
  394. // Do not add label if an operand of a branch. This is not needed
  395. // as part of live code discovery and can create false live code,
  396. // for example, the branch to a header of a loop.
  397. if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return;
  398. AddToWorklist(inInst);
  399. });
  400. if (liveInst->type_id() != 0) {
  401. AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id()));
  402. }
  403. // If in a structured if or loop construct, add the controlling
  404. // conditional branch and its merge. Any containing control construct
  405. // is marked live when the merge and branch are processed out of the
  406. // worklist.
  407. BasicBlock* blk = context()->get_instr_block(liveInst);
  408. Instruction* branchInst = block2headerBranch_[blk];
  409. if (branchInst != nullptr) {
  410. AddToWorklist(branchInst);
  411. Instruction* mergeInst = branch2merge_[branchInst];
  412. AddToWorklist(mergeInst);
  413. AddBreaksAndContinuesToWorklist(mergeInst);
  414. }
  415. // If local load, add all variable's stores if variable not already live
  416. if (liveInst->opcode() == SpvOpLoad) {
  417. uint32_t varId;
  418. (void)GetPtr(liveInst, &varId);
  419. if (varId != 0) {
  420. ProcessLoad(varId);
  421. }
  422. } else if (liveInst->opcode() == SpvOpCopyMemory ||
  423. liveInst->opcode() == SpvOpCopyMemorySized) {
  424. uint32_t varId;
  425. (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx),
  426. &varId);
  427. if (varId != 0) {
  428. ProcessLoad(varId);
  429. }
  430. // If function call, treat as if it loads from all pointer arguments
  431. } else if (liveInst->opcode() == SpvOpFunctionCall) {
  432. liveInst->ForEachInId([this](const uint32_t* iid) {
  433. // Skip non-ptr args
  434. if (!IsPtr(*iid)) return;
  435. uint32_t varId;
  436. (void)GetPtr(*iid, &varId);
  437. ProcessLoad(varId);
  438. });
  439. // If function parameter, treat as if it's result id is loaded from
  440. } else if (liveInst->opcode() == SpvOpFunctionParameter) {
  441. ProcessLoad(liveInst->result_id());
  442. // We treat an OpImageTexelPointer as a load of the pointer, and
  443. // that value is manipulated to get the result.
  444. } else if (liveInst->opcode() == SpvOpImageTexelPointer) {
  445. uint32_t varId;
  446. (void)GetPtr(liveInst, &varId);
  447. if (varId != 0) {
  448. ProcessLoad(varId);
  449. }
  450. }
  451. worklist_.pop();
  452. }
  453. // Kill dead instructions and remember dead blocks
  454. for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) {
  455. uint32_t mergeBlockId = 0;
  456. (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) {
  457. if (!IsDead(inst)) return;
  458. if (inst->opcode() == SpvOpLabel) return;
  459. // If dead instruction is selection merge, remember merge block
  460. // for new branch at end of block
  461. if (inst->opcode() == SpvOpSelectionMerge ||
  462. inst->opcode() == SpvOpLoopMerge)
  463. mergeBlockId = inst->GetSingleWordInOperand(0);
  464. to_kill_.push_back(inst);
  465. modified = true;
  466. });
  467. // If a structured if or loop was deleted, add a branch to its merge
  468. // block, and traverse to the merge block and continue processing there.
  469. // We know the block still exists because the label is not deleted.
  470. if (mergeBlockId != 0) {
  471. AddBranch(mergeBlockId, *bi);
  472. for (++bi; (*bi)->id() != mergeBlockId; ++bi) {
  473. }
  474. } else {
  475. ++bi;
  476. }
  477. }
  478. return modified;
  479. }
  480. void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
  481. // Keep all execution modes.
  482. for (auto& exec : get_module()->execution_modes()) {
  483. AddToWorklist(&exec);
  484. }
  485. // Keep all entry points.
  486. for (auto& entry : get_module()->entry_points()) {
  487. AddToWorklist(&entry);
  488. }
  489. // Keep workgroup size.
  490. for (auto& anno : get_module()->annotations()) {
  491. if (anno.opcode() == SpvOpDecorate) {
  492. if (anno.GetSingleWordInOperand(1u) == SpvDecorationBuiltIn &&
  493. anno.GetSingleWordInOperand(2u) == SpvBuiltInWorkgroupSize) {
  494. AddToWorklist(&anno);
  495. }
  496. }
  497. }
  498. }
  499. Pass::Status AggressiveDCEPass::ProcessImpl() {
  500. // Current functionality assumes shader capability
  501. // TODO(greg-lunarg): Handle additional capabilities
  502. if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
  503. return Status::SuccessWithoutChange;
  504. // Current functionality assumes relaxed logical addressing (see
  505. // instruction.h)
  506. // TODO(greg-lunarg): Handle non-logical addressing
  507. if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses))
  508. return Status::SuccessWithoutChange;
  509. // If any extensions in the module are not explicitly supported,
  510. // return unmodified.
  511. if (!AllExtensionsSupported()) return Status::SuccessWithoutChange;
  512. // Eliminate Dead functions.
  513. bool modified = EliminateDeadFunctions();
  514. InitializeModuleScopeLiveInstructions();
  515. // Process all entry point functions.
  516. ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); };
  517. modified |= ProcessEntryPointCallTree(pfn, get_module());
  518. // Process module-level instructions. Now that all live instructions have
  519. // been marked, it is safe to remove dead global values.
  520. modified |= ProcessGlobalValues();
  521. // Kill all dead instructions.
  522. for (auto inst : to_kill_) {
  523. context()->KillInst(inst);
  524. }
  525. // Cleanup all CFG including all unreachable blocks.
  526. ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); };
  527. modified |= ProcessEntryPointCallTree(cleanup, get_module());
  528. return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
  529. }
  530. bool AggressiveDCEPass::EliminateDeadFunctions() {
  531. // Identify live functions first. Those that are not live
  532. // are dead. ADCE is disabled for non-shaders so we do not check for exported
  533. // functions here.
  534. std::unordered_set<const Function*> live_function_set;
  535. ProcessFunction mark_live = [&live_function_set](Function* fp) {
  536. live_function_set.insert(fp);
  537. return false;
  538. };
  539. ProcessEntryPointCallTree(mark_live, get_module());
  540. bool modified = false;
  541. for (auto funcIter = get_module()->begin();
  542. funcIter != get_module()->end();) {
  543. if (live_function_set.count(&*funcIter) == 0) {
  544. modified = true;
  545. EliminateFunction(&*funcIter);
  546. funcIter = funcIter.Erase();
  547. } else {
  548. ++funcIter;
  549. }
  550. }
  551. return modified;
  552. }
  553. void AggressiveDCEPass::EliminateFunction(Function* func) {
  554. // Remove all of the instruction in the function body
  555. func->ForEachInst([this](Instruction* inst) { context()->KillInst(inst); },
  556. true);
  557. }
  558. bool AggressiveDCEPass::ProcessGlobalValues() {
  559. // Remove debug and annotation statements referencing dead instructions.
  560. // This must be done before killing the instructions, otherwise there are
  561. // dead objects in the def/use database.
  562. bool modified = false;
  563. Instruction* instruction = &*get_module()->debug2_begin();
  564. while (instruction) {
  565. if (instruction->opcode() != SpvOpName) {
  566. instruction = instruction->NextNode();
  567. continue;
  568. }
  569. if (IsTargetDead(instruction)) {
  570. instruction = context()->KillInst(instruction);
  571. modified = true;
  572. } else {
  573. instruction = instruction->NextNode();
  574. }
  575. }
  576. // This code removes all unnecessary decorations safely (see #1174). It also
  577. // does so in a more efficient manner than deleting them only as the targets
  578. // are deleted.
  579. std::vector<Instruction*> annotations;
  580. for (auto& inst : get_module()->annotations()) annotations.push_back(&inst);
  581. std::sort(annotations.begin(), annotations.end(), DecorationLess());
  582. for (auto annotation : annotations) {
  583. switch (annotation->opcode()) {
  584. case SpvOpDecorate:
  585. case SpvOpMemberDecorate:
  586. case SpvOpDecorateId:
  587. case SpvOpDecorateStringGOOGLE:
  588. if (IsTargetDead(annotation)) {
  589. context()->KillInst(annotation);
  590. modified = true;
  591. }
  592. break;
  593. case SpvOpGroupDecorate: {
  594. // Go through the targets of this group decorate. Remove each dead
  595. // target. If all targets are dead, remove this decoration.
  596. bool dead = true;
  597. for (uint32_t i = 1; i < annotation->NumOperands();) {
  598. Instruction* opInst =
  599. get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
  600. if (IsDead(opInst)) {
  601. // Don't increment |i|.
  602. annotation->RemoveOperand(i);
  603. modified = true;
  604. } else {
  605. i++;
  606. dead = false;
  607. }
  608. }
  609. if (dead) {
  610. context()->KillInst(annotation);
  611. modified = true;
  612. }
  613. break;
  614. }
  615. case SpvOpGroupMemberDecorate: {
  616. // Go through the targets of this group member decorate. Remove each
  617. // dead target (and member index). If all targets are dead, remove this
  618. // decoration.
  619. bool dead = true;
  620. for (uint32_t i = 1; i < annotation->NumOperands();) {
  621. Instruction* opInst =
  622. get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i));
  623. if (IsDead(opInst)) {
  624. // Don't increment |i|.
  625. annotation->RemoveOperand(i + 1);
  626. annotation->RemoveOperand(i);
  627. modified = true;
  628. } else {
  629. i += 2;
  630. dead = false;
  631. }
  632. }
  633. if (dead) {
  634. context()->KillInst(annotation);
  635. modified = true;
  636. }
  637. break;
  638. }
  639. case SpvOpDecorationGroup:
  640. // By the time we hit decoration groups we've checked everything that
  641. // can target them. So if they have no uses they must be dead.
  642. if (get_def_use_mgr()->NumUsers(annotation) == 0) {
  643. context()->KillInst(annotation);
  644. modified = true;
  645. }
  646. break;
  647. default:
  648. assert(false);
  649. break;
  650. }
  651. }
  652. // Since ADCE is disabled for non-shaders, we don't check for export linkage
  653. // attributes here.
  654. for (auto& val : get_module()->types_values()) {
  655. if (IsDead(&val)) {
  656. to_kill_.push_back(&val);
  657. }
  658. }
  659. return modified;
  660. }
  661. AggressiveDCEPass::AggressiveDCEPass() = default;
  662. Pass::Status AggressiveDCEPass::Process() {
  663. // Initialize extensions whitelist
  664. InitExtensions();
  665. return ProcessImpl();
  666. }
  667. void AggressiveDCEPass::InitExtensions() {
  668. extensions_whitelist_.clear();
  669. extensions_whitelist_.insert({
  670. "SPV_AMD_shader_explicit_vertex_parameter",
  671. "SPV_AMD_shader_trinary_minmax",
  672. "SPV_AMD_gcn_shader",
  673. "SPV_KHR_shader_ballot",
  674. "SPV_AMD_shader_ballot",
  675. "SPV_AMD_gpu_shader_half_float",
  676. "SPV_KHR_shader_draw_parameters",
  677. "SPV_KHR_subgroup_vote",
  678. "SPV_KHR_16bit_storage",
  679. "SPV_KHR_device_group",
  680. "SPV_KHR_multiview",
  681. "SPV_NVX_multiview_per_view_attributes",
  682. "SPV_NV_viewport_array2",
  683. "SPV_NV_stereo_view_rendering",
  684. "SPV_NV_sample_mask_override_coverage",
  685. "SPV_NV_geometry_shader_passthrough",
  686. "SPV_AMD_texture_gather_bias_lod",
  687. "SPV_KHR_storage_buffer_storage_class",
  688. // SPV_KHR_variable_pointers
  689. // Currently do not support extended pointer expressions
  690. "SPV_AMD_gpu_shader_int16",
  691. "SPV_KHR_post_depth_coverage",
  692. "SPV_KHR_shader_atomic_counter_ops",
  693. "SPV_EXT_shader_stencil_export",
  694. "SPV_EXT_shader_viewport_index_layer",
  695. "SPV_AMD_shader_image_load_store_lod",
  696. "SPV_AMD_shader_fragment_mask",
  697. "SPV_EXT_fragment_fully_covered",
  698. "SPV_AMD_gpu_shader_half_float_fetch",
  699. "SPV_GOOGLE_decorate_string",
  700. "SPV_GOOGLE_hlsl_functionality1",
  701. "SPV_NV_shader_subgroup_partitioned",
  702. "SPV_EXT_descriptor_indexing",
  703. });
  704. }
  705. } // namespace opt
  706. } // namespace spvtools