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