1 | // Copyright (c) 2019 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/eliminate_dead_members_pass.h" |
16 | |
17 | #include "ir_builder.h" |
18 | #include "source/opt/ir_context.h" |
19 | |
20 | namespace { |
21 | const uint32_t kRemovedMember = 0xFFFFFFFF; |
22 | } |
23 | |
24 | namespace spvtools { |
25 | namespace opt { |
26 | |
27 | Pass::Status EliminateDeadMembersPass::Process() { |
28 | if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) |
29 | return Status::SuccessWithoutChange; |
30 | |
31 | FindLiveMembers(); |
32 | if (RemoveDeadMembers()) { |
33 | return Status::SuccessWithChange; |
34 | } |
35 | return Status::SuccessWithoutChange; |
36 | } |
37 | |
38 | void EliminateDeadMembersPass::FindLiveMembers() { |
39 | // Until we have implemented the rewritting of OpSpecConsantOp instructions, |
40 | // we have to mark them as fully used just to be safe. |
41 | for (auto& inst : get_module()->types_values()) { |
42 | if (inst.opcode() == SpvOpSpecConstantOp) { |
43 | MarkTypeAsFullyUsed(inst.type_id()); |
44 | } else if (inst.opcode() == SpvOpVariable) { |
45 | switch (inst.GetSingleWordInOperand(0)) { |
46 | case SpvStorageClassInput: |
47 | case SpvStorageClassOutput: |
48 | MarkPointeeTypeAsFullUsed(inst.type_id()); |
49 | break; |
50 | default: |
51 | break; |
52 | } |
53 | } |
54 | } |
55 | |
56 | for (const Function& func : *get_module()) { |
57 | FindLiveMembers(func); |
58 | } |
59 | } |
60 | |
61 | void EliminateDeadMembersPass::FindLiveMembers(const Function& function) { |
62 | function.ForEachInst( |
63 | [this](const Instruction* inst) { FindLiveMembers(inst); }); |
64 | } |
65 | |
66 | void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) { |
67 | switch (inst->opcode()) { |
68 | case SpvOpStore: |
69 | MarkMembersAsLiveForStore(inst); |
70 | break; |
71 | case SpvOpCopyMemory: |
72 | case SpvOpCopyMemorySized: |
73 | MarkMembersAsLiveForCopyMemory(inst); |
74 | break; |
75 | case SpvOpCompositeExtract: |
76 | MarkMembersAsLiveForExtract(inst); |
77 | break; |
78 | case SpvOpAccessChain: |
79 | case SpvOpInBoundsAccessChain: |
80 | case SpvOpPtrAccessChain: |
81 | case SpvOpInBoundsPtrAccessChain: |
82 | MarkMembersAsLiveForAccessChain(inst); |
83 | break; |
84 | case SpvOpReturnValue: |
85 | // This should be an issue only if we are returning from the entry point. |
86 | // However, for now I will keep it more conservative because functions are |
87 | // often inlined leaving only the entry points. |
88 | MarkOperandTypeAsFullyUsed(inst, 0); |
89 | break; |
90 | case SpvOpArrayLength: |
91 | MarkMembersAsLiveForArrayLength(inst); |
92 | break; |
93 | case SpvOpLoad: |
94 | case SpvOpCompositeInsert: |
95 | case SpvOpCompositeConstruct: |
96 | break; |
97 | default: |
98 | // This path is here for safety. All instructions that can reference |
99 | // structs in a function body should be handled above. However, this will |
100 | // keep the pass valid, but not optimal, as new instructions get added |
101 | // or if something was missed. |
102 | MarkStructOperandsAsFullyUsed(inst); |
103 | break; |
104 | } |
105 | } |
106 | |
107 | void EliminateDeadMembersPass::MarkMembersAsLiveForStore( |
108 | const Instruction* inst) { |
109 | // We should only have to mark the members as live if the store is to |
110 | // memory that is read outside of the shader. Other passes can remove all |
111 | // store to memory that is not visible outside of the shader, so we do not |
112 | // complicate the code for now. |
113 | assert(inst->opcode() == SpvOpStore); |
114 | uint32_t object_id = inst->GetSingleWordInOperand(1); |
115 | Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id); |
116 | uint32_t object_type_id = object_inst->type_id(); |
117 | MarkTypeAsFullyUsed(object_type_id); |
118 | } |
119 | |
120 | void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) { |
121 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
122 | assert(type_inst != nullptr); |
123 | if (type_inst->opcode() != SpvOpTypeStruct) { |
124 | return; |
125 | } |
126 | |
127 | // Mark every member of the current struct as used. |
128 | for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { |
129 | used_members_[type_id].insert(i); |
130 | } |
131 | |
132 | // Mark any sub struct as fully used. |
133 | for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { |
134 | MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); |
135 | } |
136 | } |
137 | |
138 | void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) { |
139 | Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); |
140 | assert(ptr_type_inst->opcode() == SpvOpTypePointer); |
141 | MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1)); |
142 | } |
143 | |
144 | void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory( |
145 | const Instruction* inst) { |
146 | uint32_t target_id = inst->GetSingleWordInOperand(0); |
147 | Instruction* target_inst = get_def_use_mgr()->GetDef(target_id); |
148 | uint32_t pointer_type_id = target_inst->type_id(); |
149 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
150 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
151 | MarkTypeAsFullyUsed(type_id); |
152 | } |
153 | |
154 | void EliminateDeadMembersPass::( |
155 | const Instruction* inst) { |
156 | assert(inst->opcode() == SpvOpCompositeExtract); |
157 | |
158 | uint32_t composite_id = inst->GetSingleWordInOperand(0); |
159 | Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); |
160 | uint32_t type_id = composite_inst->type_id(); |
161 | |
162 | for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { |
163 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
164 | uint32_t member_idx = inst->GetSingleWordInOperand(i); |
165 | switch (type_inst->opcode()) { |
166 | case SpvOpTypeStruct: |
167 | used_members_[type_id].insert(member_idx); |
168 | type_id = type_inst->GetSingleWordInOperand(member_idx); |
169 | break; |
170 | case SpvOpTypeArray: |
171 | case SpvOpTypeRuntimeArray: |
172 | case SpvOpTypeVector: |
173 | case SpvOpTypeMatrix: |
174 | type_id = type_inst->GetSingleWordInOperand(0); |
175 | break; |
176 | default: |
177 | assert(false); |
178 | } |
179 | } |
180 | } |
181 | |
182 | void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain( |
183 | const Instruction* inst) { |
184 | assert(inst->opcode() == SpvOpAccessChain || |
185 | inst->opcode() == SpvOpInBoundsAccessChain || |
186 | inst->opcode() == SpvOpPtrAccessChain || |
187 | inst->opcode() == SpvOpInBoundsPtrAccessChain); |
188 | |
189 | uint32_t pointer_id = inst->GetSingleWordInOperand(0); |
190 | Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); |
191 | uint32_t pointer_type_id = pointer_inst->type_id(); |
192 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
193 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
194 | |
195 | analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); |
196 | |
197 | // For a pointer access chain, we need to skip the |element| index. It is not |
198 | // a reference to the member of a struct, and it does not change the type. |
199 | uint32_t i = (inst->opcode() == SpvOpAccessChain || |
200 | inst->opcode() == SpvOpInBoundsAccessChain |
201 | ? 1 |
202 | : 2); |
203 | for (; i < inst->NumInOperands(); ++i) { |
204 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
205 | switch (type_inst->opcode()) { |
206 | case SpvOpTypeStruct: { |
207 | const analysis::IntConstant* member_idx = |
208 | const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) |
209 | ->AsIntConstant(); |
210 | assert(member_idx); |
211 | if (member_idx->type()->AsInteger()->width() == 32) { |
212 | used_members_[type_id].insert(member_idx->GetU32()); |
213 | type_id = type_inst->GetSingleWordInOperand(member_idx->GetU32()); |
214 | } else { |
215 | used_members_[type_id].insert( |
216 | static_cast<uint32_t>(member_idx->GetU64())); |
217 | type_id = type_inst->GetSingleWordInOperand( |
218 | static_cast<uint32_t>(member_idx->GetU64())); |
219 | } |
220 | } break; |
221 | case SpvOpTypeArray: |
222 | case SpvOpTypeRuntimeArray: |
223 | case SpvOpTypeVector: |
224 | case SpvOpTypeMatrix: |
225 | type_id = type_inst->GetSingleWordInOperand(0); |
226 | break; |
227 | default: |
228 | assert(false); |
229 | } |
230 | } |
231 | } |
232 | |
233 | void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed( |
234 | const Instruction* inst, uint32_t in_idx) { |
235 | uint32_t op_id = inst->GetSingleWordInOperand(in_idx); |
236 | Instruction* op_inst = get_def_use_mgr()->GetDef(op_id); |
237 | MarkTypeAsFullyUsed(op_inst->type_id()); |
238 | } |
239 | |
240 | void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength( |
241 | const Instruction* inst) { |
242 | assert(inst->opcode() == SpvOpArrayLength); |
243 | uint32_t object_id = inst->GetSingleWordInOperand(0); |
244 | Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); |
245 | uint32_t pointer_type_id = object_inst->type_id(); |
246 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
247 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
248 | used_members_[type_id].insert(inst->GetSingleWordInOperand(1)); |
249 | } |
250 | |
251 | bool EliminateDeadMembersPass::RemoveDeadMembers() { |
252 | bool modified = false; |
253 | |
254 | // First update all of the OpTypeStruct instructions. |
255 | get_module()->ForEachInst([&modified, this](Instruction* inst) { |
256 | switch (inst->opcode()) { |
257 | case SpvOpTypeStruct: |
258 | modified |= UpdateOpTypeStruct(inst); |
259 | break; |
260 | default: |
261 | break; |
262 | } |
263 | }); |
264 | |
265 | // Now update all of the instructions that reference the OpTypeStructs. |
266 | get_module()->ForEachInst([&modified, this](Instruction* inst) { |
267 | switch (inst->opcode()) { |
268 | case SpvOpMemberName: |
269 | modified |= UpdateOpMemberNameOrDecorate(inst); |
270 | break; |
271 | case SpvOpMemberDecorate: |
272 | modified |= UpdateOpMemberNameOrDecorate(inst); |
273 | break; |
274 | case SpvOpGroupMemberDecorate: |
275 | modified |= UpdateOpGroupMemberDecorate(inst); |
276 | break; |
277 | case SpvOpSpecConstantComposite: |
278 | case SpvOpConstantComposite: |
279 | case SpvOpCompositeConstruct: |
280 | modified |= UpdateConstantComposite(inst); |
281 | break; |
282 | case SpvOpAccessChain: |
283 | case SpvOpInBoundsAccessChain: |
284 | case SpvOpPtrAccessChain: |
285 | case SpvOpInBoundsPtrAccessChain: |
286 | modified |= UpdateAccessChain(inst); |
287 | break; |
288 | case SpvOpCompositeExtract: |
289 | modified |= UpdateCompsiteExtract(inst); |
290 | break; |
291 | case SpvOpCompositeInsert: |
292 | modified |= UpdateCompositeInsert(inst); |
293 | break; |
294 | case SpvOpArrayLength: |
295 | modified |= UpdateOpArrayLength(inst); |
296 | break; |
297 | case SpvOpSpecConstantOp: |
298 | assert(false && "Not yet implemented." ); |
299 | // with OpCompositeExtract, OpCompositeInsert |
300 | // For kernels: OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, |
301 | // OpInBoundsPtrAccessChain |
302 | break; |
303 | default: |
304 | break; |
305 | } |
306 | }); |
307 | return modified; |
308 | } |
309 | |
310 | bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) { |
311 | assert(inst->opcode() == SpvOpTypeStruct); |
312 | |
313 | const auto& live_members = used_members_[inst->result_id()]; |
314 | if (live_members.size() == inst->NumInOperands()) { |
315 | return false; |
316 | } |
317 | |
318 | Instruction::OperandList new_operands; |
319 | for (uint32_t idx : live_members) { |
320 | new_operands.emplace_back(inst->GetInOperand(idx)); |
321 | } |
322 | |
323 | inst->SetInOperands(std::move(new_operands)); |
324 | context()->UpdateDefUse(inst); |
325 | return true; |
326 | } |
327 | |
328 | bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) { |
329 | assert(inst->opcode() == SpvOpMemberName || |
330 | inst->opcode() == SpvOpMemberDecorate); |
331 | |
332 | uint32_t type_id = inst->GetSingleWordInOperand(0); |
333 | auto live_members = used_members_.find(type_id); |
334 | if (live_members == used_members_.end()) { |
335 | return false; |
336 | } |
337 | |
338 | uint32_t orig_member_idx = inst->GetSingleWordInOperand(1); |
339 | uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); |
340 | |
341 | if (new_member_idx == kRemovedMember) { |
342 | context()->KillInst(inst); |
343 | return true; |
344 | } |
345 | |
346 | if (new_member_idx == orig_member_idx) { |
347 | return false; |
348 | } |
349 | |
350 | inst->SetInOperand(1, {new_member_idx}); |
351 | return true; |
352 | } |
353 | |
354 | bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) { |
355 | assert(inst->opcode() == SpvOpGroupMemberDecorate); |
356 | |
357 | bool modified = false; |
358 | |
359 | Instruction::OperandList new_operands; |
360 | new_operands.emplace_back(inst->GetInOperand(0)); |
361 | for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) { |
362 | uint32_t type_id = inst->GetSingleWordInOperand(i); |
363 | uint32_t member_idx = inst->GetSingleWordInOperand(i + 1); |
364 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
365 | |
366 | if (new_member_idx == kRemovedMember) { |
367 | modified = true; |
368 | continue; |
369 | } |
370 | |
371 | new_operands.emplace_back(inst->GetOperand(i)); |
372 | if (new_member_idx != member_idx) { |
373 | new_operands.emplace_back( |
374 | Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); |
375 | modified = true; |
376 | } else { |
377 | new_operands.emplace_back(inst->GetOperand(i + 1)); |
378 | } |
379 | } |
380 | |
381 | if (!modified) { |
382 | return false; |
383 | } |
384 | |
385 | if (new_operands.size() == 1) { |
386 | context()->KillInst(inst); |
387 | return true; |
388 | } |
389 | |
390 | inst->SetInOperands(std::move(new_operands)); |
391 | context()->UpdateDefUse(inst); |
392 | return true; |
393 | } |
394 | |
395 | bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) { |
396 | assert(inst->opcode() == SpvOpConstantComposite || |
397 | inst->opcode() == SpvOpCompositeConstruct); |
398 | uint32_t type_id = inst->type_id(); |
399 | |
400 | bool modified = false; |
401 | Instruction::OperandList new_operands; |
402 | for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { |
403 | uint32_t new_idx = GetNewMemberIndex(type_id, i); |
404 | if (new_idx == kRemovedMember) { |
405 | modified = true; |
406 | } else { |
407 | new_operands.emplace_back(inst->GetInOperand(i)); |
408 | } |
409 | } |
410 | inst->SetInOperands(std::move(new_operands)); |
411 | context()->UpdateDefUse(inst); |
412 | return modified; |
413 | } |
414 | |
415 | bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) { |
416 | assert(inst->opcode() == SpvOpAccessChain || |
417 | inst->opcode() == SpvOpInBoundsAccessChain || |
418 | inst->opcode() == SpvOpPtrAccessChain || |
419 | inst->opcode() == SpvOpInBoundsPtrAccessChain); |
420 | |
421 | uint32_t pointer_id = inst->GetSingleWordInOperand(0); |
422 | Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); |
423 | uint32_t pointer_type_id = pointer_inst->type_id(); |
424 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
425 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
426 | |
427 | analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); |
428 | Instruction::OperandList new_operands; |
429 | bool modified = false; |
430 | new_operands.emplace_back(inst->GetInOperand(0)); |
431 | |
432 | // For pointer access chains we want to copy the element operand. |
433 | if (inst->opcode() == SpvOpPtrAccessChain || |
434 | inst->opcode() == SpvOpInBoundsPtrAccessChain) { |
435 | new_operands.emplace_back(inst->GetInOperand(1)); |
436 | } |
437 | |
438 | for (uint32_t i = static_cast<uint32_t>(new_operands.size()); |
439 | i < inst->NumInOperands(); ++i) { |
440 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
441 | switch (type_inst->opcode()) { |
442 | case SpvOpTypeStruct: { |
443 | const analysis::IntConstant* member_idx = |
444 | const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) |
445 | ->AsIntConstant(); |
446 | assert(member_idx); |
447 | uint32_t orig_member_idx; |
448 | if (member_idx->type()->AsInteger()->width() == 32) { |
449 | orig_member_idx = member_idx->GetU32(); |
450 | } else { |
451 | orig_member_idx = static_cast<uint32_t>(member_idx->GetU64()); |
452 | } |
453 | uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); |
454 | assert(new_member_idx != kRemovedMember); |
455 | if (orig_member_idx != new_member_idx) { |
456 | InstructionBuilder ir_builder( |
457 | context(), inst, |
458 | IRContext::kAnalysisDefUse | |
459 | IRContext::kAnalysisInstrToBlockMapping); |
460 | uint32_t const_id = |
461 | ir_builder.GetUintConstant(new_member_idx)->result_id(); |
462 | new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}})); |
463 | modified = true; |
464 | } else { |
465 | new_operands.emplace_back(inst->GetInOperand(i)); |
466 | } |
467 | // The type will have already been rewritten, so use the new member |
468 | // index. |
469 | type_id = type_inst->GetSingleWordInOperand(new_member_idx); |
470 | } break; |
471 | case SpvOpTypeArray: |
472 | case SpvOpTypeRuntimeArray: |
473 | case SpvOpTypeVector: |
474 | case SpvOpTypeMatrix: |
475 | new_operands.emplace_back(inst->GetInOperand(i)); |
476 | type_id = type_inst->GetSingleWordInOperand(0); |
477 | break; |
478 | default: |
479 | assert(false); |
480 | break; |
481 | } |
482 | } |
483 | |
484 | if (!modified) { |
485 | return false; |
486 | } |
487 | inst->SetInOperands(std::move(new_operands)); |
488 | context()->UpdateDefUse(inst); |
489 | return true; |
490 | } |
491 | |
492 | uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id, |
493 | uint32_t member_idx) { |
494 | auto live_members = used_members_.find(type_id); |
495 | if (live_members == used_members_.end()) { |
496 | return member_idx; |
497 | } |
498 | |
499 | auto current_member = live_members->second.find(member_idx); |
500 | if (current_member == live_members->second.end()) { |
501 | return kRemovedMember; |
502 | } |
503 | |
504 | return static_cast<uint32_t>( |
505 | std::distance(live_members->second.begin(), current_member)); |
506 | } |
507 | |
508 | bool EliminateDeadMembersPass::(Instruction* inst) { |
509 | uint32_t object_id = inst->GetSingleWordInOperand(0); |
510 | Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); |
511 | uint32_t type_id = object_inst->type_id(); |
512 | |
513 | Instruction::OperandList new_operands; |
514 | bool modified = false; |
515 | new_operands.emplace_back(inst->GetInOperand(0)); |
516 | for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { |
517 | uint32_t member_idx = inst->GetSingleWordInOperand(i); |
518 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
519 | assert(new_member_idx != kRemovedMember); |
520 | if (member_idx != new_member_idx) { |
521 | modified = true; |
522 | } |
523 | new_operands.emplace_back( |
524 | Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); |
525 | |
526 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
527 | switch (type_inst->opcode()) { |
528 | case SpvOpTypeStruct: |
529 | assert(i != 1 || (inst->opcode() != SpvOpPtrAccessChain && |
530 | inst->opcode() != SpvOpInBoundsPtrAccessChain)); |
531 | // The type will have already been rewriten, so use the new member |
532 | // index. |
533 | type_id = type_inst->GetSingleWordInOperand(new_member_idx); |
534 | break; |
535 | case SpvOpTypeArray: |
536 | case SpvOpTypeRuntimeArray: |
537 | case SpvOpTypeVector: |
538 | case SpvOpTypeMatrix: |
539 | type_id = type_inst->GetSingleWordInOperand(0); |
540 | break; |
541 | default: |
542 | assert(false); |
543 | } |
544 | } |
545 | |
546 | if (!modified) { |
547 | return false; |
548 | } |
549 | inst->SetInOperands(std::move(new_operands)); |
550 | context()->UpdateDefUse(inst); |
551 | return true; |
552 | } |
553 | |
554 | bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) { |
555 | uint32_t composite_id = inst->GetSingleWordInOperand(1); |
556 | Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); |
557 | uint32_t type_id = composite_inst->type_id(); |
558 | |
559 | Instruction::OperandList new_operands; |
560 | bool modified = false; |
561 | new_operands.emplace_back(inst->GetInOperand(0)); |
562 | new_operands.emplace_back(inst->GetInOperand(1)); |
563 | for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { |
564 | uint32_t member_idx = inst->GetSingleWordInOperand(i); |
565 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
566 | if (new_member_idx == kRemovedMember) { |
567 | context()->KillInst(inst); |
568 | return true; |
569 | } |
570 | |
571 | if (member_idx != new_member_idx) { |
572 | modified = true; |
573 | } |
574 | new_operands.emplace_back( |
575 | Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); |
576 | |
577 | Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); |
578 | switch (type_inst->opcode()) { |
579 | case SpvOpTypeStruct: |
580 | // The type will have already been rewritten, so use the new member |
581 | // index. |
582 | type_id = type_inst->GetSingleWordInOperand(new_member_idx); |
583 | break; |
584 | case SpvOpTypeArray: |
585 | case SpvOpTypeRuntimeArray: |
586 | case SpvOpTypeVector: |
587 | case SpvOpTypeMatrix: |
588 | type_id = type_inst->GetSingleWordInOperand(0); |
589 | break; |
590 | default: |
591 | assert(false); |
592 | } |
593 | } |
594 | |
595 | if (!modified) { |
596 | return false; |
597 | } |
598 | inst->SetInOperands(std::move(new_operands)); |
599 | context()->UpdateDefUse(inst); |
600 | return true; |
601 | } |
602 | |
603 | bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) { |
604 | uint32_t struct_id = inst->GetSingleWordInOperand(0); |
605 | Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id); |
606 | uint32_t pointer_type_id = struct_inst->type_id(); |
607 | Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); |
608 | uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); |
609 | |
610 | uint32_t member_idx = inst->GetSingleWordInOperand(1); |
611 | uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); |
612 | assert(new_member_idx != kRemovedMember); |
613 | |
614 | if (member_idx == new_member_idx) { |
615 | return false; |
616 | } |
617 | |
618 | inst->SetInOperand(1, {new_member_idx}); |
619 | context()->UpdateDefUse(inst); |
620 | return true; |
621 | } |
622 | |
623 | void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed( |
624 | const Instruction* inst) { |
625 | if (inst->type_id() != 0) { |
626 | MarkTypeAsFullyUsed(inst->type_id()); |
627 | } |
628 | |
629 | inst->ForEachInId([this](const uint32_t* id) { |
630 | Instruction* instruction = get_def_use_mgr()->GetDef(*id); |
631 | if (instruction->type_id() != 0) { |
632 | MarkTypeAsFullyUsed(instruction->type_id()); |
633 | } |
634 | }); |
635 | } |
636 | } // namespace opt |
637 | } // namespace spvtools |
638 | |