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