remove_struct_member_reduction_opportunity.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Copyright (c) 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "source/reduce/remove_struct_member_reduction_opportunity.h"
  15. #include "source/opt/ir_context.h"
  16. namespace spvtools {
  17. namespace reduce {
  18. bool RemoveStructMemberReductionOpportunity::PreconditionHolds() {
  19. return struct_type_->NumInOperands() == original_number_of_members_;
  20. }
  21. void RemoveStructMemberReductionOpportunity::Apply() {
  22. std::set<opt::Instruction*> decorations_to_kill;
  23. // We need to remove decorations that target the removed struct member, and
  24. // adapt decorations that target later struct members by decrementing the
  25. // member identifier. We also need to adapt composite construction
  26. // instructions so that no id is provided for the member being removed.
  27. //
  28. // To do this, we consider every use of the struct type.
  29. struct_type_->context()->get_def_use_mgr()->ForEachUse(
  30. struct_type_, [this, &decorations_to_kill](opt::Instruction* user,
  31. uint32_t /*operand_index*/) {
  32. switch (user->opcode()) {
  33. case spv::Op::OpCompositeConstruct:
  34. case spv::Op::OpConstantComposite:
  35. // This use is constructing a composite of the struct type, so we
  36. // must remove the id that was provided for the member we are
  37. // removing.
  38. user->RemoveInOperand(member_index_);
  39. break;
  40. case spv::Op::OpMemberDecorate:
  41. // This use is decorating a member of the struct.
  42. if (user->GetSingleWordInOperand(1) == member_index_) {
  43. // The member we are removing is being decorated, so we record
  44. // that we need to get rid of the decoration.
  45. decorations_to_kill.insert(user);
  46. } else if (user->GetSingleWordInOperand(1) > member_index_) {
  47. // A member beyond the one we are removing is being decorated, so
  48. // we adjust the index that identifies the member.
  49. user->SetInOperand(1, {user->GetSingleWordInOperand(1) - 1});
  50. }
  51. break;
  52. default:
  53. break;
  54. }
  55. });
  56. // Get rid of all the decorations that were found to target the member being
  57. // removed.
  58. for (auto decoration_to_kill : decorations_to_kill) {
  59. decoration_to_kill->context()->KillInst(decoration_to_kill);
  60. }
  61. // We now look through all instructions that access composites via sequences
  62. // of indices. Every time we find an index into the struct whose member is
  63. // being removed, and if the member being accessed comes after the member
  64. // being removed, we need to adjust the index accordingly.
  65. //
  66. // We go through every relevant instruction in every block of every function,
  67. // and invoke a helper to adjust it.
  68. auto context = struct_type_->context();
  69. for (auto& function : *context->module()) {
  70. for (auto& block : function) {
  71. for (auto& inst : block) {
  72. switch (inst.opcode()) {
  73. case spv::Op::OpAccessChain:
  74. case spv::Op::OpInBoundsAccessChain: {
  75. // These access chain instructions take sequences of ids for
  76. // indexing, starting from input operand 1.
  77. auto composite_type_id =
  78. context->get_def_use_mgr()
  79. ->GetDef(context->get_def_use_mgr()
  80. ->GetDef(inst.GetSingleWordInOperand(0))
  81. ->type_id())
  82. ->GetSingleWordInOperand(1);
  83. AdjustAccessedIndices(composite_type_id, 1, false, context, &inst);
  84. } break;
  85. case spv::Op::OpPtrAccessChain:
  86. case spv::Op::OpInBoundsPtrAccessChain: {
  87. // These access chain instructions take sequences of ids for
  88. // indexing, starting from input operand 2.
  89. auto composite_type_id =
  90. context->get_def_use_mgr()
  91. ->GetDef(context->get_def_use_mgr()
  92. ->GetDef(inst.GetSingleWordInOperand(1))
  93. ->type_id())
  94. ->GetSingleWordInOperand(1);
  95. AdjustAccessedIndices(composite_type_id, 2, false, context, &inst);
  96. } break;
  97. case spv::Op::OpCompositeExtract: {
  98. // OpCompositeExtract uses literals for indexing, starting at input
  99. // operand 1.
  100. auto composite_type_id =
  101. context->get_def_use_mgr()
  102. ->GetDef(inst.GetSingleWordInOperand(0))
  103. ->type_id();
  104. AdjustAccessedIndices(composite_type_id, 1, true, context, &inst);
  105. } break;
  106. case spv::Op::OpCompositeInsert: {
  107. // OpCompositeInsert uses literals for indexing, starting at input
  108. // operand 2.
  109. auto composite_type_id =
  110. context->get_def_use_mgr()
  111. ->GetDef(inst.GetSingleWordInOperand(1))
  112. ->type_id();
  113. AdjustAccessedIndices(composite_type_id, 2, true, context, &inst);
  114. } break;
  115. default:
  116. break;
  117. }
  118. }
  119. }
  120. }
  121. // Remove the member from the struct type.
  122. struct_type_->RemoveInOperand(member_index_);
  123. context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
  124. }
  125. void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices(
  126. uint32_t composite_type_id, uint32_t first_index_input_operand,
  127. bool literal_indices, opt::IRContext* context,
  128. opt::Instruction* composite_access_instruction) const {
  129. // Walk the series of types that are encountered by following the
  130. // instruction's sequence of indices. For all types except structs, this is
  131. // routine: the type of the composite dictates what the next type will be
  132. // regardless of the specific index value.
  133. uint32_t next_type = composite_type_id;
  134. for (uint32_t i = first_index_input_operand;
  135. i < composite_access_instruction->NumInOperands(); i++) {
  136. auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
  137. switch (type_inst->opcode()) {
  138. case spv::Op::OpTypeArray:
  139. case spv::Op::OpTypeMatrix:
  140. case spv::Op::OpTypeRuntimeArray:
  141. case spv::Op::OpTypeVector:
  142. next_type = type_inst->GetSingleWordInOperand(0);
  143. break;
  144. case spv::Op::OpTypeStruct: {
  145. // Struct types are special because (a) we may need to adjust the index
  146. // being used, if the struct type is the one from which we are removing
  147. // a member, and (b) the type encountered by following the current index
  148. // is dependent on the value of the index.
  149. // Work out the member being accessed. If literal indexing is used this
  150. // is simple; otherwise we need to look up the id of the constant
  151. // instruction being used as an index and get the value of the constant.
  152. uint32_t index_operand =
  153. composite_access_instruction->GetSingleWordInOperand(i);
  154. uint32_t member = literal_indices ? index_operand
  155. : context->get_def_use_mgr()
  156. ->GetDef(index_operand)
  157. ->GetSingleWordInOperand(0);
  158. // The next type we will consider is obtained by looking up the struct
  159. // type at |member|.
  160. next_type = type_inst->GetSingleWordInOperand(member);
  161. if (type_inst == struct_type_ && member > member_index_) {
  162. // The struct type is the struct from which we are removing a member,
  163. // and the member being accessed is beyond the member we are removing.
  164. // We thus need to decrement the index by 1.
  165. uint32_t new_in_operand;
  166. if (literal_indices) {
  167. // With literal indexing this is straightforward.
  168. new_in_operand = member - 1;
  169. } else {
  170. // With id-based indexing this is more tricky: we need to find or
  171. // create a constant instruction whose value is one less than
  172. // |member|, and use the id of this constant as the replacement
  173. // input operand.
  174. auto constant_inst =
  175. context->get_def_use_mgr()->GetDef(index_operand);
  176. auto int_type = context->get_type_mgr()
  177. ->GetType(constant_inst->type_id())
  178. ->AsInteger();
  179. auto new_index_constant =
  180. opt::analysis::IntConstant(int_type, {member - 1});
  181. new_in_operand = context->get_constant_mgr()
  182. ->GetDefiningInstruction(&new_index_constant)
  183. ->result_id();
  184. }
  185. composite_access_instruction->SetInOperand(i, {new_in_operand});
  186. }
  187. } break;
  188. default:
  189. assert(0 && "Unknown composite type.");
  190. break;
  191. }
  192. }
  193. }
  194. } // namespace reduce
  195. } // namespace spvtools