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 }
24
25 namespace spvtools {
26 namespace opt {
27
Process()28 Pass::Status EliminateDeadMembersPass::Process() {
29 if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader))
30 return Status::SuccessWithoutChange;
31
32 FindLiveMembers();
33 if (RemoveDeadMembers()) {
34 return Status::SuccessWithChange;
35 }
36 return Status::SuccessWithoutChange;
37 }
38
FindLiveMembers()39 void EliminateDeadMembersPass::FindLiveMembers() {
40 // Until we have implemented the rewritting of OpSpecConsantOp instructions,
41 // we have to mark them as fully used just to be safe.
42 for (auto& inst : get_module()->types_values()) {
43 if (inst.opcode() == SpvOpSpecConstantOp) {
44 switch (inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
45 case SpvOpCompositeExtract:
46 MarkMembersAsLiveForExtract(&inst);
47 break;
48 case SpvOpCompositeInsert:
49 // Nothing specific to do.
50 break;
51 case SpvOpAccessChain:
52 case SpvOpInBoundsAccessChain:
53 case SpvOpPtrAccessChain:
54 case SpvOpInBoundsPtrAccessChain:
55 assert(false && "Not implemented yet.");
56 break;
57 default:
58 break;
59 }
60 } else if (inst.opcode() == SpvOpVariable) {
61 switch (inst.GetSingleWordInOperand(0)) {
62 case SpvStorageClassInput:
63 case SpvStorageClassOutput:
64 MarkPointeeTypeAsFullUsed(inst.type_id());
65 break;
66 default:
67 break;
68 }
69 }
70 }
71
72 for (const Function& func : *get_module()) {
73 FindLiveMembers(func);
74 }
75 }
76
FindLiveMembers(const Function & function)77 void EliminateDeadMembersPass::FindLiveMembers(const Function& function) {
78 function.ForEachInst(
79 [this](const Instruction* inst) { FindLiveMembers(inst); });
80 }
81
FindLiveMembers(const Instruction * inst)82 void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) {
83 switch (inst->opcode()) {
84 case SpvOpStore:
85 MarkMembersAsLiveForStore(inst);
86 break;
87 case SpvOpCopyMemory:
88 case SpvOpCopyMemorySized:
89 MarkMembersAsLiveForCopyMemory(inst);
90 break;
91 case SpvOpCompositeExtract:
92 MarkMembersAsLiveForExtract(inst);
93 break;
94 case SpvOpAccessChain:
95 case SpvOpInBoundsAccessChain:
96 case SpvOpPtrAccessChain:
97 case SpvOpInBoundsPtrAccessChain:
98 MarkMembersAsLiveForAccessChain(inst);
99 break;
100 case SpvOpReturnValue:
101 // This should be an issue only if we are returning from the entry point.
102 // However, for now I will keep it more conservative because functions are
103 // often inlined leaving only the entry points.
104 MarkOperandTypeAsFullyUsed(inst, 0);
105 break;
106 case SpvOpArrayLength:
107 MarkMembersAsLiveForArrayLength(inst);
108 break;
109 case SpvOpLoad:
110 case SpvOpCompositeInsert:
111 case SpvOpCompositeConstruct:
112 break;
113 default:
114 // This path is here for safety. All instructions that can reference
115 // structs in a function body should be handled above. However, this will
116 // keep the pass valid, but not optimal, as new instructions get added
117 // or if something was missed.
118 MarkStructOperandsAsFullyUsed(inst);
119 break;
120 }
121 }
122
MarkMembersAsLiveForStore(const Instruction * inst)123 void EliminateDeadMembersPass::MarkMembersAsLiveForStore(
124 const Instruction* inst) {
125 // We should only have to mark the members as live if the store is to
126 // memory that is read outside of the shader. Other passes can remove all
127 // store to memory that is not visible outside of the shader, so we do not
128 // complicate the code for now.
129 assert(inst->opcode() == SpvOpStore);
130 uint32_t object_id = inst->GetSingleWordInOperand(1);
131 Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id);
132 uint32_t object_type_id = object_inst->type_id();
133 MarkTypeAsFullyUsed(object_type_id);
134 }
135
MarkTypeAsFullyUsed(uint32_t type_id)136 void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) {
137 Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
138 assert(type_inst != nullptr);
139 if (type_inst->opcode() != SpvOpTypeStruct) {
140 return;
141 }
142
143 // Mark every member of the current struct as used.
144 for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
145 used_members_[type_id].insert(i);
146 }
147
148 // Mark any sub struct as fully used.
149 for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
150 MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
151 }
152 }
153
MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id)154 void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) {
155 Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
156 assert(ptr_type_inst->opcode() == SpvOpTypePointer);
157 MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1));
158 }
159
MarkMembersAsLiveForCopyMemory(const Instruction * inst)160 void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory(
161 const Instruction* inst) {
162 uint32_t target_id = inst->GetSingleWordInOperand(0);
163 Instruction* target_inst = get_def_use_mgr()->GetDef(target_id);
164 uint32_t pointer_type_id = target_inst->type_id();
165 Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
166 uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
167 MarkTypeAsFullyUsed(type_id);
168 }
169
MarkMembersAsLiveForExtract(const Instruction * inst)170 void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
171 const Instruction* inst) {
172 assert(inst->opcode() == SpvOpCompositeExtract ||
173 (inst->opcode() == SpvOpSpecConstantOp &&
174 inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
175 SpvOpCompositeExtract));
176
177 uint32_t first_operand = (inst->opcode() == SpvOpSpecConstantOp ? 1 : 0);
178 uint32_t composite_id = inst->GetSingleWordInOperand(first_operand);
179 Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
180 uint32_t type_id = composite_inst->type_id();
181
182 for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
183 Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
184 uint32_t member_idx = inst->GetSingleWordInOperand(i);
185 switch (type_inst->opcode()) {
186 case SpvOpTypeStruct:
187 used_members_[type_id].insert(member_idx);
188 type_id = type_inst->GetSingleWordInOperand(member_idx);
189 break;
190 case SpvOpTypeArray:
191 case SpvOpTypeRuntimeArray:
192 case SpvOpTypeVector:
193 case SpvOpTypeMatrix:
194 type_id = type_inst->GetSingleWordInOperand(0);
195 break;
196 default:
197 assert(false);
198 }
199 }
200 }
201
MarkMembersAsLiveForAccessChain(const Instruction * inst)202 void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain(
203 const Instruction* inst) {
204 assert(inst->opcode() == SpvOpAccessChain ||
205 inst->opcode() == SpvOpInBoundsAccessChain ||
206 inst->opcode() == SpvOpPtrAccessChain ||
207 inst->opcode() == SpvOpInBoundsPtrAccessChain);
208
209 uint32_t pointer_id = inst->GetSingleWordInOperand(0);
210 Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
211 uint32_t pointer_type_id = pointer_inst->type_id();
212 Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
213 uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
214
215 analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
216
217 // For a pointer access chain, we need to skip the |element| index. It is not
218 // a reference to the member of a struct, and it does not change the type.
219 uint32_t i = (inst->opcode() == SpvOpAccessChain ||
220 inst->opcode() == SpvOpInBoundsAccessChain
221 ? 1
222 : 2);
223 for (; i < inst->NumInOperands(); ++i) {
224 Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
225 switch (type_inst->opcode()) {
226 case SpvOpTypeStruct: {
227 const analysis::IntConstant* member_idx =
228 const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
229 ->AsIntConstant();
230 assert(member_idx);
231 if (member_idx->type()->AsInteger()->width() == 32) {
232 used_members_[type_id].insert(member_idx->GetU32());
233 type_id = type_inst->GetSingleWordInOperand(member_idx->GetU32());
234 } else {
235 used_members_[type_id].insert(
236 static_cast<uint32_t>(member_idx->GetU64()));
237 type_id = type_inst->GetSingleWordInOperand(
238 static_cast<uint32_t>(member_idx->GetU64()));
239 }
240 } break;
241 case SpvOpTypeArray:
242 case SpvOpTypeRuntimeArray:
243 case SpvOpTypeVector:
244 case SpvOpTypeMatrix:
245 type_id = type_inst->GetSingleWordInOperand(0);
246 break;
247 default:
248 assert(false);
249 }
250 }
251 }
252
MarkOperandTypeAsFullyUsed(const Instruction * inst,uint32_t in_idx)253 void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed(
254 const Instruction* inst, uint32_t in_idx) {
255 uint32_t op_id = inst->GetSingleWordInOperand(in_idx);
256 Instruction* op_inst = get_def_use_mgr()->GetDef(op_id);
257 MarkTypeAsFullyUsed(op_inst->type_id());
258 }
259
MarkMembersAsLiveForArrayLength(const Instruction * inst)260 void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength(
261 const Instruction* inst) {
262 assert(inst->opcode() == SpvOpArrayLength);
263 uint32_t object_id = inst->GetSingleWordInOperand(0);
264 Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
265 uint32_t pointer_type_id = object_inst->type_id();
266 Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
267 uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
268 used_members_[type_id].insert(inst->GetSingleWordInOperand(1));
269 }
270
RemoveDeadMembers()271 bool EliminateDeadMembersPass::RemoveDeadMembers() {
272 bool modified = false;
273
274 // First update all of the OpTypeStruct instructions.
275 get_module()->ForEachInst([&modified, this](Instruction* inst) {
276 switch (inst->opcode()) {
277 case SpvOpTypeStruct:
278 modified |= UpdateOpTypeStruct(inst);
279 break;
280 default:
281 break;
282 }
283 });
284
285 // Now update all of the instructions that reference the OpTypeStructs.
286 get_module()->ForEachInst([&modified, this](Instruction* inst) {
287 switch (inst->opcode()) {
288 case SpvOpMemberName:
289 modified |= UpdateOpMemberNameOrDecorate(inst);
290 break;
291 case SpvOpMemberDecorate:
292 modified |= UpdateOpMemberNameOrDecorate(inst);
293 break;
294 case SpvOpGroupMemberDecorate:
295 modified |= UpdateOpGroupMemberDecorate(inst);
296 break;
297 case SpvOpSpecConstantComposite:
298 case SpvOpConstantComposite:
299 case SpvOpCompositeConstruct:
300 modified |= UpdateConstantComposite(inst);
301 break;
302 case SpvOpAccessChain:
303 case SpvOpInBoundsAccessChain:
304 case SpvOpPtrAccessChain:
305 case SpvOpInBoundsPtrAccessChain:
306 modified |= UpdateAccessChain(inst);
307 break;
308 case SpvOpCompositeExtract:
309 modified |= UpdateCompsiteExtract(inst);
310 break;
311 case SpvOpCompositeInsert:
312 modified |= UpdateCompositeInsert(inst);
313 break;
314 case SpvOpArrayLength:
315 modified |= UpdateOpArrayLength(inst);
316 break;
317 case SpvOpSpecConstantOp:
318 switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
319 case SpvOpCompositeExtract:
320 modified |= UpdateCompsiteExtract(inst);
321 break;
322 case SpvOpCompositeInsert:
323 modified |= UpdateCompositeInsert(inst);
324 break;
325 case SpvOpAccessChain:
326 case SpvOpInBoundsAccessChain:
327 case SpvOpPtrAccessChain:
328 case SpvOpInBoundsPtrAccessChain:
329 assert(false && "Not implemented yet.");
330 break;
331 default:
332 break;
333 }
334 break;
335 default:
336 break;
337 }
338 });
339 return modified;
340 }
341
UpdateOpTypeStruct(Instruction * inst)342 bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) {
343 assert(inst->opcode() == SpvOpTypeStruct);
344
345 const auto& live_members = used_members_[inst->result_id()];
346 if (live_members.size() == inst->NumInOperands()) {
347 return false;
348 }
349
350 Instruction::OperandList new_operands;
351 for (uint32_t idx : live_members) {
352 new_operands.emplace_back(inst->GetInOperand(idx));
353 }
354
355 inst->SetInOperands(std::move(new_operands));
356 context()->UpdateDefUse(inst);
357 return true;
358 }
359
UpdateOpMemberNameOrDecorate(Instruction * inst)360 bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) {
361 assert(inst->opcode() == SpvOpMemberName ||
362 inst->opcode() == SpvOpMemberDecorate);
363
364 uint32_t type_id = inst->GetSingleWordInOperand(0);
365 auto live_members = used_members_.find(type_id);
366 if (live_members == used_members_.end()) {
367 return false;
368 }
369
370 uint32_t orig_member_idx = inst->GetSingleWordInOperand(1);
371 uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
372
373 if (new_member_idx == kRemovedMember) {
374 context()->KillInst(inst);
375 return true;
376 }
377
378 if (new_member_idx == orig_member_idx) {
379 return false;
380 }
381
382 inst->SetInOperand(1, {new_member_idx});
383 return true;
384 }
385
UpdateOpGroupMemberDecorate(Instruction * inst)386 bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) {
387 assert(inst->opcode() == SpvOpGroupMemberDecorate);
388
389 bool modified = false;
390
391 Instruction::OperandList new_operands;
392 new_operands.emplace_back(inst->GetInOperand(0));
393 for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) {
394 uint32_t type_id = inst->GetSingleWordInOperand(i);
395 uint32_t member_idx = inst->GetSingleWordInOperand(i + 1);
396 uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
397
398 if (new_member_idx == kRemovedMember) {
399 modified = true;
400 continue;
401 }
402
403 new_operands.emplace_back(inst->GetOperand(i));
404 if (new_member_idx != member_idx) {
405 new_operands.emplace_back(
406 Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
407 modified = true;
408 } else {
409 new_operands.emplace_back(inst->GetOperand(i + 1));
410 }
411 }
412
413 if (!modified) {
414 return false;
415 }
416
417 if (new_operands.size() == 1) {
418 context()->KillInst(inst);
419 return true;
420 }
421
422 inst->SetInOperands(std::move(new_operands));
423 context()->UpdateDefUse(inst);
424 return true;
425 }
426
UpdateConstantComposite(Instruction * inst)427 bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
428 assert(inst->opcode() == SpvOpSpecConstantComposite ||
429 inst->opcode() == SpvOpConstantComposite ||
430 inst->opcode() == SpvOpCompositeConstruct);
431 uint32_t type_id = inst->type_id();
432
433 bool modified = false;
434 Instruction::OperandList new_operands;
435 for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
436 uint32_t new_idx = GetNewMemberIndex(type_id, i);
437 if (new_idx == kRemovedMember) {
438 modified = true;
439 } else {
440 new_operands.emplace_back(inst->GetInOperand(i));
441 }
442 }
443 inst->SetInOperands(std::move(new_operands));
444 context()->UpdateDefUse(inst);
445 return modified;
446 }
447
UpdateAccessChain(Instruction * inst)448 bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) {
449 assert(inst->opcode() == SpvOpAccessChain ||
450 inst->opcode() == SpvOpInBoundsAccessChain ||
451 inst->opcode() == SpvOpPtrAccessChain ||
452 inst->opcode() == SpvOpInBoundsPtrAccessChain);
453
454 uint32_t pointer_id = inst->GetSingleWordInOperand(0);
455 Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
456 uint32_t pointer_type_id = pointer_inst->type_id();
457 Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
458 uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
459
460 analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
461 Instruction::OperandList new_operands;
462 bool modified = false;
463 new_operands.emplace_back(inst->GetInOperand(0));
464
465 // For pointer access chains we want to copy the element operand.
466 if (inst->opcode() == SpvOpPtrAccessChain ||
467 inst->opcode() == SpvOpInBoundsPtrAccessChain) {
468 new_operands.emplace_back(inst->GetInOperand(1));
469 }
470
471 for (uint32_t i = static_cast<uint32_t>(new_operands.size());
472 i < inst->NumInOperands(); ++i) {
473 Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
474 switch (type_inst->opcode()) {
475 case SpvOpTypeStruct: {
476 const analysis::IntConstant* member_idx =
477 const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
478 ->AsIntConstant();
479 assert(member_idx);
480 uint32_t orig_member_idx;
481 if (member_idx->type()->AsInteger()->width() == 32) {
482 orig_member_idx = member_idx->GetU32();
483 } else {
484 orig_member_idx = static_cast<uint32_t>(member_idx->GetU64());
485 }
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 rewriten, 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