• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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