• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2018 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/opt/vector_dce.h"
16 
17 #include <utility>
18 
19 namespace spvtools {
20 namespace opt {
21 namespace {
22 constexpr uint32_t kExtractCompositeIdInIdx = 0;
23 constexpr uint32_t kInsertObjectIdInIdx = 0;
24 constexpr uint32_t kInsertCompositeIdInIdx = 1;
25 }  // namespace
26 
Process()27 Pass::Status VectorDCE::Process() {
28   bool modified = false;
29   for (Function& function : *get_module()) {
30     modified |= VectorDCEFunction(&function);
31   }
32   return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
33 }
34 
VectorDCEFunction(Function * function)35 bool VectorDCE::VectorDCEFunction(Function* function) {
36   LiveComponentMap live_components;
37   FindLiveComponents(function, &live_components);
38   return RewriteInstructions(function, live_components);
39 }
40 
FindLiveComponents(Function * function,LiveComponentMap * live_components)41 void VectorDCE::FindLiveComponents(Function* function,
42                                    LiveComponentMap* live_components) {
43   std::vector<WorkListItem> work_list;
44 
45   // Prime the work list.  We will assume that any instruction that does
46   // not result in a vector is live.
47   //
48   // Extending to structures and matrices is not as straight forward because of
49   // the nesting.  We cannot simply us a bit vector to keep track of which
50   // components are live because of arbitrary nesting of structs.
51   function->ForEachInst(
52       [&work_list, this, live_components](Instruction* current_inst) {
53         if (current_inst->IsCommonDebugInstr()) {
54           return;
55         }
56         if (!HasVectorOrScalarResult(current_inst) ||
57             !context()->IsCombinatorInstruction(current_inst)) {
58           MarkUsesAsLive(current_inst, all_components_live_, live_components,
59                          &work_list);
60         }
61       });
62 
63   // Process the work list propagating liveness.
64   for (uint32_t i = 0; i < work_list.size(); i++) {
65     WorkListItem current_item = work_list[i];
66     Instruction* current_inst = current_item.instruction;
67 
68     switch (current_inst->opcode()) {
69       case spv::Op::OpCompositeExtract:
70         MarkExtractUseAsLive(current_inst, current_item.components,
71                              live_components, &work_list);
72         break;
73       case spv::Op::OpCompositeInsert:
74         MarkInsertUsesAsLive(current_item, live_components, &work_list);
75         break;
76       case spv::Op::OpVectorShuffle:
77         MarkVectorShuffleUsesAsLive(current_item, live_components, &work_list);
78         break;
79       case spv::Op::OpCompositeConstruct:
80         MarkCompositeContructUsesAsLive(current_item, live_components,
81                                         &work_list);
82         break;
83       default:
84         if (current_inst->IsScalarizable()) {
85           MarkUsesAsLive(current_inst, current_item.components, live_components,
86                          &work_list);
87         } else {
88           MarkUsesAsLive(current_inst, all_components_live_, live_components,
89                          &work_list);
90         }
91         break;
92     }
93   }
94 }
95 
MarkExtractUseAsLive(const Instruction * current_inst,const utils::BitVector & live_elements,LiveComponentMap * live_components,std::vector<WorkListItem> * work_list)96 void VectorDCE::MarkExtractUseAsLive(const Instruction* current_inst,
97                                      const utils::BitVector& live_elements,
98                                      LiveComponentMap* live_components,
99                                      std::vector<WorkListItem>* work_list) {
100   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
101   uint32_t operand_id =
102       current_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx);
103   Instruction* operand_inst = def_use_mgr->GetDef(operand_id);
104 
105   if (HasVectorOrScalarResult(operand_inst)) {
106     WorkListItem new_item;
107     new_item.instruction = operand_inst;
108     if (current_inst->NumInOperands() < 2) {
109       new_item.components = live_elements;
110     } else {
111       uint32_t element_index = current_inst->GetSingleWordInOperand(1);
112       uint32_t item_size = GetVectorComponentCount(operand_inst->type_id());
113       if (element_index < item_size) {
114         new_item.components.Set(element_index);
115       }
116     }
117     AddItemToWorkListIfNeeded(new_item, live_components, work_list);
118   }
119 }
120 
MarkInsertUsesAsLive(const VectorDCE::WorkListItem & current_item,LiveComponentMap * live_components,std::vector<VectorDCE::WorkListItem> * work_list)121 void VectorDCE::MarkInsertUsesAsLive(
122     const VectorDCE::WorkListItem& current_item,
123     LiveComponentMap* live_components,
124     std::vector<VectorDCE::WorkListItem>* work_list) {
125   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
126 
127   if (current_item.instruction->NumInOperands() > 2) {
128     uint32_t insert_position =
129         current_item.instruction->GetSingleWordInOperand(2);
130 
131     // Add the elements of the composite object that are used.
132     uint32_t operand_id = current_item.instruction->GetSingleWordInOperand(
133         kInsertCompositeIdInIdx);
134     Instruction* operand_inst = def_use_mgr->GetDef(operand_id);
135 
136     WorkListItem new_item;
137     new_item.instruction = operand_inst;
138     new_item.components = current_item.components;
139     new_item.components.Clear(insert_position);
140 
141     AddItemToWorkListIfNeeded(new_item, live_components, work_list);
142 
143     // Add the element being inserted if it is used.
144     if (current_item.components.Get(insert_position)) {
145       uint32_t obj_operand_id =
146           current_item.instruction->GetSingleWordInOperand(
147               kInsertObjectIdInIdx);
148       Instruction* obj_operand_inst = def_use_mgr->GetDef(obj_operand_id);
149       WorkListItem new_item_for_obj;
150       new_item_for_obj.instruction = obj_operand_inst;
151       new_item_for_obj.components.Set(0);
152       AddItemToWorkListIfNeeded(new_item_for_obj, live_components, work_list);
153     }
154   } else {
155     // If there are no indices, then this is a copy of the object being
156     // inserted.
157     uint32_t object_id =
158         current_item.instruction->GetSingleWordInOperand(kInsertObjectIdInIdx);
159     Instruction* object_inst = def_use_mgr->GetDef(object_id);
160 
161     WorkListItem new_item;
162     new_item.instruction = object_inst;
163     new_item.components = current_item.components;
164     AddItemToWorkListIfNeeded(new_item, live_components, work_list);
165   }
166 }
167 
MarkVectorShuffleUsesAsLive(const WorkListItem & current_item,VectorDCE::LiveComponentMap * live_components,std::vector<WorkListItem> * work_list)168 void VectorDCE::MarkVectorShuffleUsesAsLive(
169     const WorkListItem& current_item,
170     VectorDCE::LiveComponentMap* live_components,
171     std::vector<WorkListItem>* work_list) {
172   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
173 
174   WorkListItem first_operand;
175   first_operand.instruction =
176       def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(0));
177   WorkListItem second_operand;
178   second_operand.instruction =
179       def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(1));
180 
181   uint32_t size_of_first_operand =
182       GetVectorComponentCount(first_operand.instruction->type_id());
183   uint32_t size_of_second_operand =
184       GetVectorComponentCount(second_operand.instruction->type_id());
185 
186   for (uint32_t in_op = 2; in_op < current_item.instruction->NumInOperands();
187        ++in_op) {
188     uint32_t index = current_item.instruction->GetSingleWordInOperand(in_op);
189     if (current_item.components.Get(in_op - 2)) {
190       if (index < size_of_first_operand) {
191         first_operand.components.Set(index);
192       } else if (index - size_of_first_operand < size_of_second_operand) {
193         second_operand.components.Set(index - size_of_first_operand);
194       }
195     }
196   }
197 
198   AddItemToWorkListIfNeeded(first_operand, live_components, work_list);
199   AddItemToWorkListIfNeeded(second_operand, live_components, work_list);
200 }
201 
MarkCompositeContructUsesAsLive(VectorDCE::WorkListItem work_item,VectorDCE::LiveComponentMap * live_components,std::vector<VectorDCE::WorkListItem> * work_list)202 void VectorDCE::MarkCompositeContructUsesAsLive(
203     VectorDCE::WorkListItem work_item,
204     VectorDCE::LiveComponentMap* live_components,
205     std::vector<VectorDCE::WorkListItem>* work_list) {
206   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
207 
208   uint32_t current_component = 0;
209   Instruction* current_inst = work_item.instruction;
210   uint32_t num_in_operands = current_inst->NumInOperands();
211   for (uint32_t i = 0; i < num_in_operands; ++i) {
212     uint32_t id = current_inst->GetSingleWordInOperand(i);
213     Instruction* op_inst = def_use_mgr->GetDef(id);
214 
215     if (HasScalarResult(op_inst)) {
216       WorkListItem new_work_item;
217       new_work_item.instruction = op_inst;
218       if (work_item.components.Get(current_component)) {
219         new_work_item.components.Set(0);
220       }
221       AddItemToWorkListIfNeeded(new_work_item, live_components, work_list);
222       current_component++;
223     } else {
224       assert(HasVectorResult(op_inst));
225       WorkListItem new_work_item;
226       new_work_item.instruction = op_inst;
227       uint32_t op_vector_size = GetVectorComponentCount(op_inst->type_id());
228 
229       for (uint32_t op_vector_idx = 0; op_vector_idx < op_vector_size;
230            op_vector_idx++, current_component++) {
231         if (work_item.components.Get(current_component)) {
232           new_work_item.components.Set(op_vector_idx);
233         }
234       }
235       AddItemToWorkListIfNeeded(new_work_item, live_components, work_list);
236     }
237   }
238 }
239 
MarkUsesAsLive(Instruction * current_inst,const utils::BitVector & live_elements,LiveComponentMap * live_components,std::vector<VectorDCE::WorkListItem> * work_list)240 void VectorDCE::MarkUsesAsLive(
241     Instruction* current_inst, const utils::BitVector& live_elements,
242     LiveComponentMap* live_components,
243     std::vector<VectorDCE::WorkListItem>* work_list) {
244   analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
245 
246   current_inst->ForEachInId([&work_list, &live_elements, this, live_components,
247                              def_use_mgr](uint32_t* operand_id) {
248     Instruction* operand_inst = def_use_mgr->GetDef(*operand_id);
249 
250     if (HasVectorResult(operand_inst)) {
251       WorkListItem new_item;
252       new_item.instruction = operand_inst;
253       new_item.components = live_elements;
254       AddItemToWorkListIfNeeded(new_item, live_components, work_list);
255     } else if (HasScalarResult(operand_inst)) {
256       WorkListItem new_item;
257       new_item.instruction = operand_inst;
258       new_item.components.Set(0);
259       AddItemToWorkListIfNeeded(new_item, live_components, work_list);
260     }
261   });
262 }
263 
HasVectorOrScalarResult(const Instruction * inst) const264 bool VectorDCE::HasVectorOrScalarResult(const Instruction* inst) const {
265   return HasScalarResult(inst) || HasVectorResult(inst);
266 }
267 
HasVectorResult(const Instruction * inst) const268 bool VectorDCE::HasVectorResult(const Instruction* inst) const {
269   analysis::TypeManager* type_mgr = context()->get_type_mgr();
270   if (inst->type_id() == 0) {
271     return false;
272   }
273 
274   const analysis::Type* current_type = type_mgr->GetType(inst->type_id());
275   switch (current_type->kind()) {
276     case analysis::Type::kVector:
277       return true;
278     default:
279       return false;
280   }
281 }
282 
HasScalarResult(const Instruction * inst) const283 bool VectorDCE::HasScalarResult(const Instruction* inst) const {
284   analysis::TypeManager* type_mgr = context()->get_type_mgr();
285   if (inst->type_id() == 0) {
286     return false;
287   }
288 
289   const analysis::Type* current_type = type_mgr->GetType(inst->type_id());
290   switch (current_type->kind()) {
291     case analysis::Type::kBool:
292     case analysis::Type::kInteger:
293     case analysis::Type::kFloat:
294       return true;
295     default:
296       return false;
297   }
298 }
299 
GetVectorComponentCount(uint32_t type_id)300 uint32_t VectorDCE::GetVectorComponentCount(uint32_t type_id) {
301   assert(type_id != 0 &&
302          "Trying to get the vector element count, but the type id is 0");
303   analysis::TypeManager* type_mgr = context()->get_type_mgr();
304   const analysis::Type* type = type_mgr->GetType(type_id);
305   const analysis::Vector* vector_type = type->AsVector();
306   assert(
307       vector_type &&
308       "Trying to get the vector element count, but the type is not a vector");
309   return vector_type->element_count();
310 }
311 
RewriteInstructions(Function * function,const VectorDCE::LiveComponentMap & live_components)312 bool VectorDCE::RewriteInstructions(
313     Function* function, const VectorDCE::LiveComponentMap& live_components) {
314   bool modified = false;
315 
316   // Kill DebugValue in the middle of the instruction iteration will result
317   // in accessing a dangling pointer. We keep dead DebugValue instructions
318   // in |dead_dbg_value| to kill them once after the iteration.
319   std::vector<Instruction*> dead_dbg_value;
320 
321   function->ForEachInst([&modified, this, live_components,
322                          &dead_dbg_value](Instruction* current_inst) {
323     if (!context()->IsCombinatorInstruction(current_inst)) {
324       return;
325     }
326 
327     auto live_component = live_components.find(current_inst->result_id());
328     if (live_component == live_components.end()) {
329       // If this instruction is not in live_components then it does not
330       // produce a vector, or it is never referenced and ADCE will remove
331       // it.  No point in trying to differentiate.
332       return;
333     }
334 
335     // If no element in the current instruction is used replace it with an
336     // OpUndef.
337     if (live_component->second.Empty()) {
338       modified = true;
339       MarkDebugValueUsesAsDead(current_inst, &dead_dbg_value);
340       uint32_t undef_id = this->Type2Undef(current_inst->type_id());
341       context()->KillNamesAndDecorates(current_inst);
342       context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id);
343       context()->KillInst(current_inst);
344       return;
345     }
346 
347     switch (current_inst->opcode()) {
348       case spv::Op::OpCompositeInsert:
349         modified |= RewriteInsertInstruction(
350             current_inst, live_component->second, &dead_dbg_value);
351         break;
352       case spv::Op::OpCompositeConstruct:
353         // TODO: The members that are not live can be replaced by an undef
354         // or constant. This will remove uses of those values, and possibly
355         // create opportunities for ADCE.
356         break;
357       default:
358         // Do nothing.
359         break;
360     }
361   });
362   for (auto* i : dead_dbg_value) context()->KillInst(i);
363   return modified;
364 }
365 
RewriteInsertInstruction(Instruction * current_inst,const utils::BitVector & live_components,std::vector<Instruction * > * dead_dbg_value)366 bool VectorDCE::RewriteInsertInstruction(
367     Instruction* current_inst, const utils::BitVector& live_components,
368     std::vector<Instruction*>* dead_dbg_value) {
369   // If the value being inserted is not live, then we can skip the insert.
370 
371   if (current_inst->NumInOperands() == 2) {
372     // If there are no indices, then this is the same as a copy.
373     context()->KillNamesAndDecorates(current_inst->result_id());
374     uint32_t object_id =
375         current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx);
376     context()->ReplaceAllUsesWith(current_inst->result_id(), object_id);
377     return true;
378   }
379 
380   uint32_t insert_index = current_inst->GetSingleWordInOperand(2);
381   if (!live_components.Get(insert_index)) {
382     MarkDebugValueUsesAsDead(current_inst, dead_dbg_value);
383     context()->KillNamesAndDecorates(current_inst->result_id());
384     uint32_t composite_id =
385         current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx);
386     context()->ReplaceAllUsesWith(current_inst->result_id(), composite_id);
387     return true;
388   }
389 
390   // If the values already in the composite are not used, then replace it with
391   // an undef.
392   utils::BitVector temp = live_components;
393   temp.Clear(insert_index);
394   if (temp.Empty()) {
395     context()->ForgetUses(current_inst);
396     uint32_t undef_id = Type2Undef(current_inst->type_id());
397     current_inst->SetInOperand(kInsertCompositeIdInIdx, {undef_id});
398     context()->AnalyzeUses(current_inst);
399     return true;
400   }
401 
402   return false;
403 }
404 
MarkDebugValueUsesAsDead(Instruction * composite,std::vector<Instruction * > * dead_dbg_value)405 void VectorDCE::MarkDebugValueUsesAsDead(
406     Instruction* composite, std::vector<Instruction*>* dead_dbg_value) {
407   context()->get_def_use_mgr()->ForEachUser(
408       composite, [&dead_dbg_value](Instruction* use) {
409         if (use->GetCommonDebugOpcode() == CommonDebugInfoDebugValue)
410           dead_dbg_value->push_back(use);
411       });
412 }
413 
AddItemToWorkListIfNeeded(WorkListItem work_item,VectorDCE::LiveComponentMap * live_components,std::vector<WorkListItem> * work_list)414 void VectorDCE::AddItemToWorkListIfNeeded(
415     WorkListItem work_item, VectorDCE::LiveComponentMap* live_components,
416     std::vector<WorkListItem>* work_list) {
417   Instruction* current_inst = work_item.instruction;
418   auto it = live_components->find(current_inst->result_id());
419   if (it == live_components->end()) {
420     live_components->emplace(
421         std::make_pair(current_inst->result_id(), work_item.components));
422     work_list->emplace_back(work_item);
423   } else {
424     if (it->second.Or(work_item.components)) {
425       work_list->emplace_back(work_item);
426     }
427   }
428 }
429 
430 }  // namespace opt
431 }  // namespace spvtools
432