mem_pass.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. // Copyright (c) 2017 The Khronos Group Inc.
  2. // Copyright (c) 2017 Valve Corporation
  3. // Copyright (c) 2017 LunarG Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. #include "source/opt/mem_pass.h"
  17. #include <memory>
  18. #include <set>
  19. #include <vector>
  20. #include "source/cfa.h"
  21. #include "source/opt/basic_block.h"
  22. #include "source/opt/dominator_analysis.h"
  23. #include "source/opt/ir_context.h"
  24. #include "source/opt/iterator.h"
  25. namespace spvtools {
  26. namespace opt {
  27. namespace {
  28. const uint32_t kCopyObjectOperandInIdx = 0;
  29. const uint32_t kTypePointerStorageClassInIdx = 0;
  30. const uint32_t kTypePointerTypeIdInIdx = 1;
  31. } // namespace
  32. bool MemPass::IsBaseTargetType(const Instruction* typeInst) const {
  33. switch (typeInst->opcode()) {
  34. case SpvOpTypeInt:
  35. case SpvOpTypeFloat:
  36. case SpvOpTypeBool:
  37. case SpvOpTypeVector:
  38. case SpvOpTypeMatrix:
  39. case SpvOpTypeImage:
  40. case SpvOpTypeSampler:
  41. case SpvOpTypeSampledImage:
  42. case SpvOpTypePointer:
  43. return true;
  44. default:
  45. break;
  46. }
  47. return false;
  48. }
  49. bool MemPass::IsTargetType(const Instruction* typeInst) const {
  50. if (IsBaseTargetType(typeInst)) return true;
  51. if (typeInst->opcode() == SpvOpTypeArray) {
  52. if (!IsTargetType(
  53. get_def_use_mgr()->GetDef(typeInst->GetSingleWordOperand(1)))) {
  54. return false;
  55. }
  56. return true;
  57. }
  58. if (typeInst->opcode() != SpvOpTypeStruct) return false;
  59. // All struct members must be math type
  60. return typeInst->WhileEachInId([this](const uint32_t* tid) {
  61. Instruction* compTypeInst = get_def_use_mgr()->GetDef(*tid);
  62. if (!IsTargetType(compTypeInst)) return false;
  63. return true;
  64. });
  65. }
  66. bool MemPass::IsNonPtrAccessChain(const SpvOp opcode) const {
  67. return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain;
  68. }
  69. bool MemPass::IsPtr(uint32_t ptrId) {
  70. uint32_t varId = ptrId;
  71. Instruction* ptrInst = get_def_use_mgr()->GetDef(varId);
  72. while (ptrInst->opcode() == SpvOpCopyObject) {
  73. varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx);
  74. ptrInst = get_def_use_mgr()->GetDef(varId);
  75. }
  76. const SpvOp op = ptrInst->opcode();
  77. if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true;
  78. const uint32_t varTypeId = ptrInst->type_id();
  79. if (varTypeId == 0) return false;
  80. const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
  81. return varTypeInst->opcode() == SpvOpTypePointer;
  82. }
  83. Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) {
  84. *varId = ptrId;
  85. Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId);
  86. Instruction* varInst;
  87. if (ptrInst->opcode() == SpvOpConstantNull) {
  88. *varId = 0;
  89. return ptrInst;
  90. }
  91. if (ptrInst->opcode() != SpvOpVariable &&
  92. ptrInst->opcode() != SpvOpFunctionParameter) {
  93. varInst = ptrInst->GetBaseAddress();
  94. } else {
  95. varInst = ptrInst;
  96. }
  97. if (varInst->opcode() == SpvOpVariable) {
  98. *varId = varInst->result_id();
  99. } else {
  100. *varId = 0;
  101. }
  102. while (ptrInst->opcode() == SpvOpCopyObject) {
  103. uint32_t temp = ptrInst->GetSingleWordInOperand(0);
  104. ptrInst = get_def_use_mgr()->GetDef(temp);
  105. }
  106. return ptrInst;
  107. }
  108. Instruction* MemPass::GetPtr(Instruction* ip, uint32_t* varId) {
  109. assert(ip->opcode() == SpvOpStore || ip->opcode() == SpvOpLoad ||
  110. ip->opcode() == SpvOpImageTexelPointer || ip->IsAtomicWithLoad());
  111. // All of these opcode place the pointer in position 0.
  112. const uint32_t ptrId = ip->GetSingleWordInOperand(0);
  113. return GetPtr(ptrId, varId);
  114. }
  115. bool MemPass::HasOnlyNamesAndDecorates(uint32_t id) const {
  116. return get_def_use_mgr()->WhileEachUser(id, [this](Instruction* user) {
  117. SpvOp op = user->opcode();
  118. if (op != SpvOpName && !IsNonTypeDecorate(op)) {
  119. return false;
  120. }
  121. return true;
  122. });
  123. }
  124. void MemPass::KillAllInsts(BasicBlock* bp, bool killLabel) {
  125. bp->KillAllInsts(killLabel);
  126. }
  127. bool MemPass::HasLoads(uint32_t varId) const {
  128. return !get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
  129. SpvOp op = user->opcode();
  130. // TODO(): The following is slightly conservative. Could be
  131. // better handling of non-store/name.
  132. if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
  133. if (HasLoads(user->result_id())) {
  134. return false;
  135. }
  136. } else if (op != SpvOpStore && op != SpvOpName && !IsNonTypeDecorate(op)) {
  137. return false;
  138. }
  139. return true;
  140. });
  141. }
  142. bool MemPass::IsLiveVar(uint32_t varId) const {
  143. const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
  144. // assume live if not a variable eg. function parameter
  145. if (varInst->opcode() != SpvOpVariable) return true;
  146. // non-function scope vars are live
  147. const uint32_t varTypeId = varInst->type_id();
  148. const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
  149. if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
  150. SpvStorageClassFunction)
  151. return true;
  152. // test if variable is loaded from
  153. return HasLoads(varId);
  154. }
  155. void MemPass::AddStores(uint32_t ptr_id, std::queue<Instruction*>* insts) {
  156. get_def_use_mgr()->ForEachUser(ptr_id, [this, insts](Instruction* user) {
  157. SpvOp op = user->opcode();
  158. if (IsNonPtrAccessChain(op)) {
  159. AddStores(user->result_id(), insts);
  160. } else if (op == SpvOpStore) {
  161. insts->push(user);
  162. }
  163. });
  164. }
  165. void MemPass::DCEInst(Instruction* inst,
  166. const std::function<void(Instruction*)>& call_back) {
  167. std::queue<Instruction*> deadInsts;
  168. deadInsts.push(inst);
  169. while (!deadInsts.empty()) {
  170. Instruction* di = deadInsts.front();
  171. // Don't delete labels
  172. if (di->opcode() == SpvOpLabel) {
  173. deadInsts.pop();
  174. continue;
  175. }
  176. // Remember operands
  177. std::set<uint32_t> ids;
  178. di->ForEachInId([&ids](uint32_t* iid) { ids.insert(*iid); });
  179. uint32_t varId = 0;
  180. // Remember variable if dead load
  181. if (di->opcode() == SpvOpLoad) (void)GetPtr(di, &varId);
  182. if (call_back) {
  183. call_back(di);
  184. }
  185. context()->KillInst(di);
  186. // For all operands with no remaining uses, add their instruction
  187. // to the dead instruction queue.
  188. for (auto id : ids)
  189. if (HasOnlyNamesAndDecorates(id)) {
  190. Instruction* odi = get_def_use_mgr()->GetDef(id);
  191. if (context()->IsCombinatorInstruction(odi)) deadInsts.push(odi);
  192. }
  193. // if a load was deleted and it was the variable's
  194. // last load, add all its stores to dead queue
  195. if (varId != 0 && !IsLiveVar(varId)) AddStores(varId, &deadInsts);
  196. deadInsts.pop();
  197. }
  198. }
  199. MemPass::MemPass() {}
  200. bool MemPass::HasOnlySupportedRefs(uint32_t varId) {
  201. return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) {
  202. auto dbg_op = user->GetCommonDebugOpcode();
  203. if (dbg_op == CommonDebugInfoDebugDeclare ||
  204. dbg_op == CommonDebugInfoDebugValue) {
  205. return true;
  206. }
  207. SpvOp op = user->opcode();
  208. if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName &&
  209. !IsNonTypeDecorate(op)) {
  210. return false;
  211. }
  212. return true;
  213. });
  214. }
  215. uint32_t MemPass::Type2Undef(uint32_t type_id) {
  216. const auto uitr = type2undefs_.find(type_id);
  217. if (uitr != type2undefs_.end()) return uitr->second;
  218. const uint32_t undefId = TakeNextId();
  219. if (undefId == 0) {
  220. return 0;
  221. }
  222. std::unique_ptr<Instruction> undef_inst(
  223. new Instruction(context(), SpvOpUndef, type_id, undefId, {}));
  224. get_def_use_mgr()->AnalyzeInstDefUse(&*undef_inst);
  225. get_module()->AddGlobalValue(std::move(undef_inst));
  226. type2undefs_[type_id] = undefId;
  227. return undefId;
  228. }
  229. bool MemPass::IsTargetVar(uint32_t varId) {
  230. if (varId == 0) {
  231. return false;
  232. }
  233. if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end())
  234. return false;
  235. if (seen_target_vars_.find(varId) != seen_target_vars_.end()) return true;
  236. const Instruction* varInst = get_def_use_mgr()->GetDef(varId);
  237. if (varInst->opcode() != SpvOpVariable) return false;
  238. const uint32_t varTypeId = varInst->type_id();
  239. const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId);
  240. if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) !=
  241. SpvStorageClassFunction) {
  242. seen_non_target_vars_.insert(varId);
  243. return false;
  244. }
  245. const uint32_t varPteTypeId =
  246. varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx);
  247. Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId);
  248. if (!IsTargetType(varPteTypeInst)) {
  249. seen_non_target_vars_.insert(varId);
  250. return false;
  251. }
  252. seen_target_vars_.insert(varId);
  253. return true;
  254. }
  255. // Remove all |phi| operands coming from unreachable blocks (i.e., blocks not in
  256. // |reachable_blocks|). There are two types of removal that this function can
  257. // perform:
  258. //
  259. // 1- Any operand that comes directly from an unreachable block is completely
  260. // removed. Since the block is unreachable, the edge between the unreachable
  261. // block and the block holding |phi| has been removed.
  262. //
  263. // 2- Any operand that comes via a live block and was defined at an unreachable
  264. // block gets its value replaced with an OpUndef value. Since the argument
  265. // was generated in an unreachable block, it no longer exists, so it cannot
  266. // be referenced. However, since the value does not reach |phi| directly
  267. // from the unreachable block, the operand cannot be removed from |phi|.
  268. // Therefore, we replace the argument value with OpUndef.
  269. //
  270. // For example, in the switch() below, assume that we want to remove the
  271. // argument with value %11 coming from block %41.
  272. //
  273. // [ ... ]
  274. // %41 = OpLabel <--- Unreachable block
  275. // %11 = OpLoad %int %y
  276. // [ ... ]
  277. // OpSelectionMerge %16 None
  278. // OpSwitch %12 %16 10 %13 13 %14 18 %15
  279. // %13 = OpLabel
  280. // OpBranch %16
  281. // %14 = OpLabel
  282. // OpStore %outparm %int_14
  283. // OpBranch %16
  284. // %15 = OpLabel
  285. // OpStore %outparm %int_15
  286. // OpBranch %16
  287. // %16 = OpLabel
  288. // %30 = OpPhi %int %11 %41 %int_42 %13 %11 %14 %11 %15
  289. //
  290. // Since %41 is now an unreachable block, the first operand of |phi| needs to
  291. // be removed completely. But the operands (%11 %14) and (%11 %15) cannot be
  292. // removed because %14 and %15 are reachable blocks. Since %11 no longer exist,
  293. // in those arguments, we replace all references to %11 with an OpUndef value.
  294. // This results in |phi| looking like:
  295. //
  296. // %50 = OpUndef %int
  297. // [ ... ]
  298. // %30 = OpPhi %int %int_42 %13 %50 %14 %50 %15
  299. void MemPass::RemovePhiOperands(
  300. Instruction* phi, const std::unordered_set<BasicBlock*>& reachable_blocks) {
  301. std::vector<Operand> keep_operands;
  302. uint32_t type_id = 0;
  303. // The id of an undefined value we've generated.
  304. uint32_t undef_id = 0;
  305. // Traverse all the operands in |phi|. Build the new operand vector by adding
  306. // all the original operands from |phi| except the unwanted ones.
  307. for (uint32_t i = 0; i < phi->NumOperands();) {
  308. if (i < 2) {
  309. // The first two arguments are always preserved.
  310. keep_operands.push_back(phi->GetOperand(i));
  311. ++i;
  312. continue;
  313. }
  314. // The remaining Phi arguments come in pairs. Index 'i' contains the
  315. // variable id, index 'i + 1' is the originating block id.
  316. assert(i % 2 == 0 && i < phi->NumOperands() - 1 &&
  317. "malformed Phi arguments");
  318. BasicBlock* in_block = cfg()->block(phi->GetSingleWordOperand(i + 1));
  319. if (reachable_blocks.find(in_block) == reachable_blocks.end()) {
  320. // If the incoming block is unreachable, remove both operands as this
  321. // means that the |phi| has lost an incoming edge.
  322. i += 2;
  323. continue;
  324. }
  325. // In all other cases, the operand must be kept but may need to be changed.
  326. uint32_t arg_id = phi->GetSingleWordOperand(i);
  327. Instruction* arg_def_instr = get_def_use_mgr()->GetDef(arg_id);
  328. BasicBlock* def_block = context()->get_instr_block(arg_def_instr);
  329. if (def_block &&
  330. reachable_blocks.find(def_block) == reachable_blocks.end()) {
  331. // If the current |phi| argument was defined in an unreachable block, it
  332. // means that this |phi| argument is no longer defined. Replace it with
  333. // |undef_id|.
  334. if (!undef_id) {
  335. type_id = arg_def_instr->type_id();
  336. undef_id = Type2Undef(type_id);
  337. }
  338. keep_operands.push_back(
  339. Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {undef_id}));
  340. } else {
  341. // Otherwise, the argument comes from a reachable block or from no block
  342. // at all (meaning that it was defined in the global section of the
  343. // program). In both cases, keep the argument intact.
  344. keep_operands.push_back(phi->GetOperand(i));
  345. }
  346. keep_operands.push_back(phi->GetOperand(i + 1));
  347. i += 2;
  348. }
  349. context()->ForgetUses(phi);
  350. phi->ReplaceOperands(keep_operands);
  351. context()->AnalyzeUses(phi);
  352. }
  353. void MemPass::RemoveBlock(Function::iterator* bi) {
  354. auto& rm_block = **bi;
  355. // Remove instructions from the block.
  356. rm_block.ForEachInst([&rm_block, this](Instruction* inst) {
  357. // Note that we do not kill the block label instruction here. The label
  358. // instruction is needed to identify the block, which is needed by the
  359. // removal of phi operands.
  360. if (inst != rm_block.GetLabelInst()) {
  361. context()->KillInst(inst);
  362. }
  363. });
  364. // Remove the label instruction last.
  365. auto label = rm_block.GetLabelInst();
  366. context()->KillInst(label);
  367. *bi = bi->Erase();
  368. }
  369. bool MemPass::RemoveUnreachableBlocks(Function* func) {
  370. bool modified = false;
  371. // Mark reachable all blocks reachable from the function's entry block.
  372. std::unordered_set<BasicBlock*> reachable_blocks;
  373. std::unordered_set<BasicBlock*> visited_blocks;
  374. std::queue<BasicBlock*> worklist;
  375. reachable_blocks.insert(func->entry().get());
  376. // Initially mark the function entry point as reachable.
  377. worklist.push(func->entry().get());
  378. auto mark_reachable = [&reachable_blocks, &visited_blocks, &worklist,
  379. this](uint32_t label_id) {
  380. auto successor = cfg()->block(label_id);
  381. if (visited_blocks.count(successor) == 0) {
  382. reachable_blocks.insert(successor);
  383. worklist.push(successor);
  384. visited_blocks.insert(successor);
  385. }
  386. };
  387. // Transitively mark all blocks reachable from the entry as reachable.
  388. while (!worklist.empty()) {
  389. BasicBlock* block = worklist.front();
  390. worklist.pop();
  391. // All the successors of a live block are also live.
  392. static_cast<const BasicBlock*>(block)->ForEachSuccessorLabel(
  393. mark_reachable);
  394. // All the Merge and ContinueTarget blocks of a live block are also live.
  395. block->ForMergeAndContinueLabel(mark_reachable);
  396. }
  397. // Update operands of Phi nodes that reference unreachable blocks.
  398. for (auto& block : *func) {
  399. // If the block is about to be removed, don't bother updating its
  400. // Phi instructions.
  401. if (reachable_blocks.count(&block) == 0) {
  402. continue;
  403. }
  404. // If the block is reachable and has Phi instructions, remove all
  405. // operands from its Phi instructions that reference unreachable blocks.
  406. // If the block has no Phi instructions, this is a no-op.
  407. block.ForEachPhiInst([&reachable_blocks, this](Instruction* phi) {
  408. RemovePhiOperands(phi, reachable_blocks);
  409. });
  410. }
  411. // Erase unreachable blocks.
  412. for (auto ebi = func->begin(); ebi != func->end();) {
  413. if (reachable_blocks.count(&*ebi) == 0) {
  414. RemoveBlock(&ebi);
  415. modified = true;
  416. } else {
  417. ++ebi;
  418. }
  419. }
  420. return modified;
  421. }
  422. bool MemPass::CFGCleanup(Function* func) {
  423. bool modified = false;
  424. modified |= RemoveUnreachableBlocks(func);
  425. return modified;
  426. }
  427. void MemPass::CollectTargetVars(Function* func) {
  428. seen_target_vars_.clear();
  429. seen_non_target_vars_.clear();
  430. type2undefs_.clear();
  431. // Collect target (and non-) variable sets. Remove variables with
  432. // non-load/store refs from target variable set
  433. for (auto& blk : *func) {
  434. for (auto& inst : blk) {
  435. switch (inst.opcode()) {
  436. case SpvOpStore:
  437. case SpvOpLoad: {
  438. uint32_t varId;
  439. (void)GetPtr(&inst, &varId);
  440. if (!IsTargetVar(varId)) break;
  441. if (HasOnlySupportedRefs(varId)) break;
  442. seen_non_target_vars_.insert(varId);
  443. seen_target_vars_.erase(varId);
  444. } break;
  445. default:
  446. break;
  447. }
  448. }
  449. }
  450. }
  451. } // namespace opt
  452. } // namespace spvtools