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
15 #include "source/reduce/remove_struct_member_reduction_opportunity.h"
16
17 #include "source/opt/ir_context.h"
18
19 namespace spvtools {
20 namespace reduce {
21
PreconditionHolds()22 bool RemoveStructMemberReductionOpportunity::PreconditionHolds() {
23 return struct_type_->NumInOperands() == original_number_of_members_;
24 }
25
Apply()26 void RemoveStructMemberReductionOpportunity::Apply() {
27 std::set<opt::Instruction*> decorations_to_kill;
28
29 // We need to remove decorations that target the removed struct member, and
30 // adapt decorations that target later struct members by decrementing the
31 // member identifier. We also need to adapt composite construction
32 // instructions so that no id is provided for the member being removed.
33 //
34 // To do this, we consider every use of the struct type.
35 struct_type_->context()->get_def_use_mgr()->ForEachUse(
36 struct_type_, [this, &decorations_to_kill](opt::Instruction* user,
37 uint32_t /*operand_index*/) {
38 switch (user->opcode()) {
39 case SpvOpCompositeConstruct:
40 case SpvOpConstantComposite:
41 // This use is constructing a composite of the struct type, so we
42 // must remove the id that was provided for the member we are
43 // removing.
44 user->RemoveInOperand(member_index_);
45 break;
46 case SpvOpMemberDecorate:
47 // This use is decorating a member of the struct.
48 if (user->GetSingleWordInOperand(1) == member_index_) {
49 // The member we are removing is being decorated, so we record
50 // that we need to get rid of the decoration.
51 decorations_to_kill.insert(user);
52 } else if (user->GetSingleWordInOperand(1) > member_index_) {
53 // A member beyond the one we are removing is being decorated, so
54 // we adjust the index that identifies the member.
55 user->SetInOperand(1, {user->GetSingleWordInOperand(1) - 1});
56 }
57 break;
58 default:
59 break;
60 }
61 });
62
63 // Get rid of all the decorations that were found to target the member being
64 // removed.
65 for (auto decoration_to_kill : decorations_to_kill) {
66 decoration_to_kill->context()->KillInst(decoration_to_kill);
67 }
68
69 // We now look through all instructions that access composites via sequences
70 // of indices. Every time we find an index into the struct whose member is
71 // being removed, and if the member being accessed comes after the member
72 // being removed, we need to adjust the index accordingly.
73 //
74 // We go through every relevant instruction in every block of every function,
75 // and invoke a helper to adjust it.
76 auto context = struct_type_->context();
77 for (auto& function : *context->module()) {
78 for (auto& block : function) {
79 for (auto& inst : block) {
80 switch (inst.opcode()) {
81 case SpvOpAccessChain:
82 case SpvOpInBoundsAccessChain: {
83 // These access chain instructions take sequences of ids for
84 // indexing, starting from input operand 1.
85 auto composite_type_id =
86 context->get_def_use_mgr()
87 ->GetDef(context->get_def_use_mgr()
88 ->GetDef(inst.GetSingleWordInOperand(0))
89 ->type_id())
90 ->GetSingleWordInOperand(1);
91 AdjustAccessedIndices(composite_type_id, 1, false, context, &inst);
92 } break;
93 case SpvOpPtrAccessChain:
94 case SpvOpInBoundsPtrAccessChain: {
95 // These access chain instructions take sequences of ids for
96 // indexing, starting from input operand 2.
97 auto composite_type_id =
98 context->get_def_use_mgr()
99 ->GetDef(context->get_def_use_mgr()
100 ->GetDef(inst.GetSingleWordInOperand(1))
101 ->type_id())
102 ->GetSingleWordInOperand(1);
103 AdjustAccessedIndices(composite_type_id, 2, false, context, &inst);
104 } break;
105 case SpvOpCompositeExtract: {
106 // OpCompositeExtract uses literals for indexing, starting at input
107 // operand 1.
108 auto composite_type_id =
109 context->get_def_use_mgr()
110 ->GetDef(inst.GetSingleWordInOperand(0))
111 ->type_id();
112 AdjustAccessedIndices(composite_type_id, 1, true, context, &inst);
113 } break;
114 case SpvOpCompositeInsert: {
115 // OpCompositeInsert uses literals for indexing, starting at input
116 // operand 2.
117 auto composite_type_id =
118 context->get_def_use_mgr()
119 ->GetDef(inst.GetSingleWordInOperand(1))
120 ->type_id();
121 AdjustAccessedIndices(composite_type_id, 2, true, context, &inst);
122 } break;
123 default:
124 break;
125 }
126 }
127 }
128 }
129
130 // Remove the member from the struct type.
131 struct_type_->RemoveInOperand(member_index_);
132 }
133
AdjustAccessedIndices(uint32_t composite_type_id,uint32_t first_index_input_operand,bool literal_indices,opt::IRContext * context,opt::Instruction * composite_access_instruction) const134 void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices(
135 uint32_t composite_type_id, uint32_t first_index_input_operand,
136 bool literal_indices, opt::IRContext* context,
137 opt::Instruction* composite_access_instruction) const {
138 // Walk the series of types that are encountered by following the
139 // instruction's sequence of indices. For all types except structs, this is
140 // routine: the type of the composite dictates what the next type will be
141 // regardless of the specific index value.
142 uint32_t next_type = composite_type_id;
143 for (uint32_t i = first_index_input_operand;
144 i < composite_access_instruction->NumInOperands(); i++) {
145 auto type_inst = context->get_def_use_mgr()->GetDef(next_type);
146 switch (type_inst->opcode()) {
147 case SpvOpTypeArray:
148 case SpvOpTypeMatrix:
149 case SpvOpTypeRuntimeArray:
150 case SpvOpTypeVector:
151 next_type = type_inst->GetSingleWordInOperand(0);
152 break;
153 case SpvOpTypeStruct: {
154 // Struct types are special becuase (a) we may need to adjust the index
155 // being used, if the struct type is the one from which we are removing
156 // a member, and (b) the type encountered by following the current index
157 // is dependent on the value of the index.
158
159 // Work out the member being accessed. If literal indexing is used this
160 // is simple; otherwise we need to look up the id of the constant
161 // instruction being used as an index and get the value of the constant.
162 uint32_t index_operand =
163 composite_access_instruction->GetSingleWordInOperand(i);
164 uint32_t member = literal_indices ? index_operand
165 : context->get_def_use_mgr()
166 ->GetDef(index_operand)
167 ->GetSingleWordInOperand(0);
168
169 // The next type we will consider is obtained by looking up the struct
170 // type at |member|.
171 next_type = type_inst->GetSingleWordInOperand(member);
172
173 if (type_inst == struct_type_ && member > member_index_) {
174 // The struct type is the struct from which we are removing a member,
175 // and the member being accessed is beyond the member we are removing.
176 // We thus need to decrement the index by 1.
177 uint32_t new_in_operand;
178 if (literal_indices) {
179 // With literal indexing this is straightforward.
180 new_in_operand = member - 1;
181 } else {
182 // With id-based indexing this is more tricky: we need to find or
183 // create a constant instruction whose value is one less than
184 // |member|, and use the id of this constant as the replacement
185 // input operand.
186 auto constant_inst =
187 context->get_def_use_mgr()->GetDef(index_operand);
188 auto int_type = context->get_type_mgr()
189 ->GetType(constant_inst->type_id())
190 ->AsInteger();
191 auto new_index_constant =
192 opt::analysis::IntConstant(int_type, {member - 1});
193 new_in_operand = context->get_constant_mgr()
194 ->GetDefiningInstruction(&new_index_constant)
195 ->result_id();
196 }
197 composite_access_instruction->SetInOperand(i, {new_in_operand});
198 }
199 } break;
200 default:
201 assert(0 && "Unknown composite type.");
202 break;
203 }
204 }
205 }
206
207 } // namespace reduce
208 } // namespace spvtools
209