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