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