mem_pass.cpp 17 KB

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