2
0

eliminate_dead_output_stores_pass.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright (c) 2022 The Khronos Group Inc.
  2. // Copyright (c) 2022 LunarG Inc.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. #include "source/opt/eliminate_dead_output_stores_pass.h"
  16. #include "source/opt/instruction.h"
  17. #include "source/opt/ir_context.h"
  18. namespace spvtools {
  19. namespace opt {
  20. namespace {
  21. constexpr uint32_t kDecorationLocationInIdx = 2;
  22. constexpr uint32_t kOpDecorateMemberMemberInIdx = 1;
  23. constexpr uint32_t kOpDecorateBuiltInLiteralInIdx = 2;
  24. constexpr uint32_t kOpDecorateMemberBuiltInLiteralInIdx = 3;
  25. constexpr uint32_t kOpAccessChainIdx0InIdx = 1;
  26. constexpr uint32_t kOpConstantValueInIdx = 0;
  27. } // namespace
  28. Pass::Status EliminateDeadOutputStoresPass::Process() {
  29. // Current functionality assumes shader capability
  30. if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
  31. return Status::SuccessWithoutChange;
  32. Pass::Status status = DoDeadOutputStoreElimination();
  33. return status;
  34. }
  35. void EliminateDeadOutputStoresPass::InitializeElimination() {
  36. kill_list_.clear();
  37. }
  38. bool EliminateDeadOutputStoresPass::IsLiveBuiltin(uint32_t bi) {
  39. return live_builtins_->find(bi) != live_builtins_->end();
  40. }
  41. bool EliminateDeadOutputStoresPass::AnyLocsAreLive(uint32_t start,
  42. uint32_t count) {
  43. auto finish = start + count;
  44. for (uint32_t u = start; u < finish; ++u) {
  45. if (live_locs_->find(u) != live_locs_->end()) return true;
  46. }
  47. return false;
  48. }
  49. void EliminateDeadOutputStoresPass::KillAllStoresOfRef(Instruction* ref) {
  50. analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
  51. if (ref->opcode() == spv::Op::OpStore) {
  52. kill_list_.push_back(ref);
  53. return;
  54. }
  55. assert((ref->opcode() == spv::Op::OpAccessChain ||
  56. ref->opcode() == spv::Op::OpInBoundsAccessChain) &&
  57. "unexpected use of output variable");
  58. def_use_mgr->ForEachUser(ref, [this](Instruction* user) {
  59. if (user->opcode() == spv::Op::OpStore) kill_list_.push_back(user);
  60. });
  61. }
  62. void EliminateDeadOutputStoresPass::KillAllDeadStoresOfLocRef(
  63. Instruction* ref, Instruction* var) {
  64. analysis::TypeManager* type_mgr = context()->get_type_mgr();
  65. analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
  66. analysis::LivenessManager* live_mgr = context()->get_liveness_mgr();
  67. // Find variable location if present.
  68. uint32_t start_loc = 0;
  69. auto var_id = var->result_id();
  70. bool no_loc = deco_mgr->WhileEachDecoration(
  71. var_id, uint32_t(spv::Decoration::Location),
  72. [&start_loc](const Instruction& deco) {
  73. assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
  74. start_loc = deco.GetSingleWordInOperand(kDecorationLocationInIdx);
  75. return false;
  76. });
  77. // Find patch decoration if present
  78. bool is_patch = !deco_mgr->WhileEachDecoration(
  79. var_id, uint32_t(spv::Decoration::Patch), [](const Instruction& deco) {
  80. if (deco.opcode() != spv::Op::OpDecorate)
  81. assert(false && "unexpected decoration");
  82. return false;
  83. });
  84. // Compute offset and final type of reference. If no location found
  85. // or any stored locations are live, return without removing stores.
  86. Instruction* ptr_type = get_def_use_mgr()->GetDef(var->type_id());
  87. assert(ptr_type && "unexpected var type");
  88. const uint32_t kPointerTypePointeeIdx = 1;
  89. uint32_t var_type_id =
  90. ptr_type->GetSingleWordInOperand(kPointerTypePointeeIdx);
  91. uint32_t ref_loc = start_loc;
  92. if (ref->opcode() == spv::Op::OpAccessChain ||
  93. ref->opcode() == spv::Op::OpInBoundsAccessChain) {
  94. var_type_id = live_mgr->AnalyzeAccessChainLoc(
  95. ref, var_type_id, &ref_loc, &no_loc, is_patch, /* input */ false);
  96. }
  97. const analysis::Type* curr_type = type_mgr->GetType(var_type_id);
  98. if (no_loc || AnyLocsAreLive(ref_loc, live_mgr->GetLocSize(curr_type)))
  99. return;
  100. // Kill all stores based on this reference
  101. KillAllStoresOfRef(ref);
  102. }
  103. void EliminateDeadOutputStoresPass::KillAllDeadStoresOfBuiltinRef(
  104. Instruction* ref, Instruction* var) {
  105. auto deco_mgr = context()->get_decoration_mgr();
  106. auto def_use_mgr = context()->get_def_use_mgr();
  107. auto type_mgr = context()->get_type_mgr();
  108. auto live_mgr = context()->get_liveness_mgr();
  109. // Search for builtin decoration of base variable
  110. uint32_t builtin = uint32_t(spv::BuiltIn::Max);
  111. auto var_id = var->result_id();
  112. (void)deco_mgr->WhileEachDecoration(
  113. var_id, uint32_t(spv::Decoration::BuiltIn),
  114. [&builtin](const Instruction& deco) {
  115. assert(deco.opcode() == spv::Op::OpDecorate && "unexpected decoration");
  116. builtin = deco.GetSingleWordInOperand(kOpDecorateBuiltInLiteralInIdx);
  117. return false;
  118. });
  119. // If analyzed builtin and not live, kill stores.
  120. if (builtin != uint32_t(spv::BuiltIn::Max)) {
  121. if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin))
  122. KillAllStoresOfRef(ref);
  123. return;
  124. }
  125. // Search for builtin decoration on indexed member
  126. auto ref_op = ref->opcode();
  127. if (ref_op != spv::Op::OpAccessChain &&
  128. ref_op != spv::Op::OpInBoundsAccessChain) {
  129. return;
  130. }
  131. uint32_t in_idx = kOpAccessChainIdx0InIdx;
  132. analysis::Type* var_type = type_mgr->GetType(var->type_id());
  133. analysis::Pointer* ptr_type = var_type->AsPointer();
  134. auto curr_type = ptr_type->pointee_type();
  135. auto arr_type = curr_type->AsArray();
  136. if (arr_type) {
  137. curr_type = arr_type->element_type();
  138. ++in_idx;
  139. }
  140. auto str_type = curr_type->AsStruct();
  141. auto str_type_id = type_mgr->GetId(str_type);
  142. auto member_idx_id = ref->GetSingleWordInOperand(in_idx);
  143. auto member_idx_inst = def_use_mgr->GetDef(member_idx_id);
  144. assert(member_idx_inst->opcode() == spv::Op::OpConstant &&
  145. "unexpected non-constant index");
  146. auto ac_idx = member_idx_inst->GetSingleWordInOperand(kOpConstantValueInIdx);
  147. (void)deco_mgr->WhileEachDecoration(
  148. str_type_id, uint32_t(spv::Decoration::BuiltIn),
  149. [ac_idx, &builtin](const Instruction& deco) {
  150. assert(deco.opcode() == spv::Op::OpMemberDecorate &&
  151. "unexpected decoration");
  152. auto deco_idx =
  153. deco.GetSingleWordInOperand(kOpDecorateMemberMemberInIdx);
  154. if (deco_idx == ac_idx) {
  155. builtin =
  156. deco.GetSingleWordInOperand(kOpDecorateMemberBuiltInLiteralInIdx);
  157. return false;
  158. }
  159. return true;
  160. });
  161. assert(builtin != uint32_t(spv::BuiltIn::Max) && "builtin not found");
  162. // If analyzed builtin and not live, kill stores.
  163. if (live_mgr->IsAnalyzedBuiltin(builtin) && !IsLiveBuiltin(builtin))
  164. KillAllStoresOfRef(ref);
  165. }
  166. Pass::Status EliminateDeadOutputStoresPass::DoDeadOutputStoreElimination() {
  167. // Current implementation only supports vert, tesc, tese, geom shaders
  168. auto stage = context()->GetStage();
  169. if (stage != spv::ExecutionModel::Vertex &&
  170. stage != spv::ExecutionModel::TessellationControl &&
  171. stage != spv::ExecutionModel::TessellationEvaluation &&
  172. stage != spv::ExecutionModel::Geometry)
  173. return Status::Failure;
  174. InitializeElimination();
  175. analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
  176. analysis::TypeManager* type_mgr = context()->get_type_mgr();
  177. analysis::DecorationManager* deco_mgr = context()->get_decoration_mgr();
  178. // Process all output variables
  179. for (auto& var : context()->types_values()) {
  180. if (var.opcode() != spv::Op::OpVariable) {
  181. continue;
  182. }
  183. analysis::Type* var_type = type_mgr->GetType(var.type_id());
  184. analysis::Pointer* ptr_type = var_type->AsPointer();
  185. if (ptr_type->storage_class() != spv::StorageClass::Output) {
  186. continue;
  187. }
  188. // If builtin decoration on variable, process as builtin.
  189. auto var_id = var.result_id();
  190. bool is_builtin = false;
  191. if (deco_mgr->HasDecoration(var_id, uint32_t(spv::Decoration::BuiltIn))) {
  192. is_builtin = true;
  193. } else {
  194. // If interface block with builtin members, process as builtin.
  195. // Strip off outer array type if present.
  196. auto curr_type = ptr_type->pointee_type();
  197. auto arr_type = curr_type->AsArray();
  198. if (arr_type) curr_type = arr_type->element_type();
  199. auto str_type = curr_type->AsStruct();
  200. if (str_type) {
  201. auto str_type_id = type_mgr->GetId(str_type);
  202. if (deco_mgr->HasDecoration(str_type_id,
  203. uint32_t(spv::Decoration::BuiltIn)))
  204. is_builtin = true;
  205. }
  206. }
  207. // For each store or access chain using var, if dead builtin or all its
  208. // locations are dead, kill store or all access chain's stores
  209. def_use_mgr->ForEachUser(
  210. var_id, [this, &var, is_builtin](Instruction* user) {
  211. auto op = user->opcode();
  212. if (op == spv::Op::OpEntryPoint || op == spv::Op::OpName ||
  213. op == spv::Op::OpDecorate || user->IsNonSemanticInstruction())
  214. return;
  215. if (is_builtin)
  216. KillAllDeadStoresOfBuiltinRef(user, &var);
  217. else
  218. KillAllDeadStoresOfLocRef(user, &var);
  219. });
  220. }
  221. for (auto& kinst : kill_list_) context()->KillInst(kinst);
  222. return kill_list_.empty() ? Status::SuccessWithoutChange
  223. : Status::SuccessWithChange;
  224. }
  225. } // namespace opt
  226. } // namespace spvtools