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