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 } // namespace
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 uint32_t index =
232 static_cast<uint32_t>(member_idx->GetZeroExtendedValue());
233 used_members_[type_id].insert(index);
234 type_id = type_inst->GetSingleWordInOperand(index);
235 } break;
236 case SpvOpTypeArray:
237 case SpvOpTypeRuntimeArray:
238 case SpvOpTypeVector:
239 case SpvOpTypeMatrix:
240 type_id = type_inst->GetSingleWordInOperand(0);
241 break;
242 default:
243 assert(false);
244 }
245 }
246 }
247
MarkOperandTypeAsFullyUsed(const Instruction * inst,uint32_t in_idx)248 void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed(
249 const Instruction* inst, uint32_t in_idx) {
250 uint32_t op_id = inst->GetSingleWordInOperand(in_idx);
251 Instruction* op_inst = get_def_use_mgr()->GetDef(op_id);
252 MarkTypeAsFullyUsed(op_inst->type_id());
253 }
254
MarkMembersAsLiveForArrayLength(const Instruction * inst)255 void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength(
256 const Instruction* inst) {
257 assert(inst->opcode() == SpvOpArrayLength);
258 uint32_t object_id = inst->GetSingleWordInOperand(0);
259 Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
260 uint32_t pointer_type_id = object_inst->type_id();
261 Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
262 uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
263 used_members_[type_id].insert(inst->GetSingleWordInOperand(1));
264 }
265
RemoveDeadMembers()266 bool EliminateDeadMembersPass::RemoveDeadMembers() {
267 bool modified = false;
268
269 // First update all of the OpTypeStruct instructions.
270 get_module()->ForEachInst([&modified, this](Instruction* inst) {
271 switch (inst->opcode()) {
272 case SpvOpTypeStruct:
273 modified |= UpdateOpTypeStruct(inst);
274 break;
275 default:
276 break;
277 }
278 });
279
280 // Now update all of the instructions that reference the OpTypeStructs.
281 get_module()->ForEachInst([&modified, this](Instruction* inst) {
282 switch (inst->opcode()) {
283 case SpvOpMemberName:
284 modified |= UpdateOpMemberNameOrDecorate(inst);
285 break;
286 case SpvOpMemberDecorate:
287 modified |= UpdateOpMemberNameOrDecorate(inst);
288 break;
289 case SpvOpGroupMemberDecorate:
290 modified |= UpdateOpGroupMemberDecorate(inst);
291 break;
292 case SpvOpSpecConstantComposite:
293 case SpvOpConstantComposite:
294 case SpvOpCompositeConstruct:
295 modified |= UpdateConstantComposite(inst);
296 break;
297 case SpvOpAccessChain:
298 case SpvOpInBoundsAccessChain:
299 case SpvOpPtrAccessChain:
300 case SpvOpInBoundsPtrAccessChain:
301 modified |= UpdateAccessChain(inst);
302 break;
303 case SpvOpCompositeExtract:
304 modified |= UpdateCompsiteExtract(inst);
305 break;
306 case SpvOpCompositeInsert:
307 modified |= UpdateCompositeInsert(inst);
308 break;
309 case SpvOpArrayLength:
310 modified |= UpdateOpArrayLength(inst);
311 break;
312 case SpvOpSpecConstantOp:
313 switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) {
314 case SpvOpCompositeExtract:
315 modified |= UpdateCompsiteExtract(inst);
316 break;
317 case SpvOpCompositeInsert:
318 modified |= UpdateCompositeInsert(inst);
319 break;
320 case SpvOpAccessChain:
321 case SpvOpInBoundsAccessChain:
322 case SpvOpPtrAccessChain:
323 case SpvOpInBoundsPtrAccessChain:
324 assert(false && "Not implemented yet.");
325 break;
326 default:
327 break;
328 }
329 break;
330 default:
331 break;
332 }
333 });
334 return modified;
335 }
336
UpdateOpTypeStruct(Instruction * inst)337 bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) {
338 assert(inst->opcode() == SpvOpTypeStruct);
339
340 const auto& live_members = used_members_[inst->result_id()];
341 if (live_members.size() == inst->NumInOperands()) {
342 return false;
343 }
344
345 Instruction::OperandList new_operands;
346 for (uint32_t idx : live_members) {
347 new_operands.emplace_back(inst->GetInOperand(idx));
348 }
349
350 inst->SetInOperands(std::move(new_operands));
351 context()->UpdateDefUse(inst);
352 return true;
353 }
354
UpdateOpMemberNameOrDecorate(Instruction * inst)355 bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) {
356 assert(inst->opcode() == SpvOpMemberName ||
357 inst->opcode() == SpvOpMemberDecorate);
358
359 uint32_t type_id = inst->GetSingleWordInOperand(0);
360 auto live_members = used_members_.find(type_id);
361 if (live_members == used_members_.end()) {
362 return false;
363 }
364
365 uint32_t orig_member_idx = inst->GetSingleWordInOperand(1);
366 uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
367
368 if (new_member_idx == kRemovedMember) {
369 context()->KillInst(inst);
370 return true;
371 }
372
373 if (new_member_idx == orig_member_idx) {
374 return false;
375 }
376
377 inst->SetInOperand(1, {new_member_idx});
378 return true;
379 }
380
UpdateOpGroupMemberDecorate(Instruction * inst)381 bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) {
382 assert(inst->opcode() == SpvOpGroupMemberDecorate);
383
384 bool modified = false;
385
386 Instruction::OperandList new_operands;
387 new_operands.emplace_back(inst->GetInOperand(0));
388 for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) {
389 uint32_t type_id = inst->GetSingleWordInOperand(i);
390 uint32_t member_idx = inst->GetSingleWordInOperand(i + 1);
391 uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
392
393 if (new_member_idx == kRemovedMember) {
394 modified = true;
395 continue;
396 }
397
398 new_operands.emplace_back(inst->GetOperand(i));
399 if (new_member_idx != member_idx) {
400 new_operands.emplace_back(
401 Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
402 modified = true;
403 } else {
404 new_operands.emplace_back(inst->GetOperand(i + 1));
405 }
406 }
407
408 if (!modified) {
409 return false;
410 }
411
412 if (new_operands.size() == 1) {
413 context()->KillInst(inst);
414 return true;
415 }
416
417 inst->SetInOperands(std::move(new_operands));
418 context()->UpdateDefUse(inst);
419 return true;
420 }
421
UpdateConstantComposite(Instruction * inst)422 bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
423 assert(inst->opcode() == SpvOpSpecConstantComposite ||
424 inst->opcode() == SpvOpConstantComposite ||
425 inst->opcode() == SpvOpCompositeConstruct);
426 uint32_t type_id = inst->type_id();
427
428 bool modified = false;
429 Instruction::OperandList new_operands;
430 for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
431 uint32_t new_idx = GetNewMemberIndex(type_id, i);
432 if (new_idx == kRemovedMember) {
433 modified = true;
434 } else {
435 new_operands.emplace_back(inst->GetInOperand(i));
436 }
437 }
438 inst->SetInOperands(std::move(new_operands));
439 context()->UpdateDefUse(inst);
440 return modified;
441 }
442
UpdateAccessChain(Instruction * inst)443 bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) {
444 assert(inst->opcode() == SpvOpAccessChain ||
445 inst->opcode() == SpvOpInBoundsAccessChain ||
446 inst->opcode() == SpvOpPtrAccessChain ||
447 inst->opcode() == SpvOpInBoundsPtrAccessChain);
448
449 uint32_t pointer_id = inst->GetSingleWordInOperand(0);
450 Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
451 uint32_t pointer_type_id = pointer_inst->type_id();
452 Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
453 uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
454
455 analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
456 Instruction::OperandList new_operands;
457 bool modified = false;
458 new_operands.emplace_back(inst->GetInOperand(0));
459
460 // For pointer access chains we want to copy the element operand.
461 if (inst->opcode() == SpvOpPtrAccessChain ||
462 inst->opcode() == SpvOpInBoundsPtrAccessChain) {
463 new_operands.emplace_back(inst->GetInOperand(1));
464 }
465
466 for (uint32_t i = static_cast<uint32_t>(new_operands.size());
467 i < inst->NumInOperands(); ++i) {
468 Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
469 switch (type_inst->opcode()) {
470 case SpvOpTypeStruct: {
471 const analysis::IntConstant* member_idx =
472 const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
473 ->AsIntConstant();
474 assert(member_idx);
475 uint32_t orig_member_idx =
476 static_cast<uint32_t>(member_idx->GetZeroExtendedValue());
477 uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
478 assert(new_member_idx != kRemovedMember);
479 if (orig_member_idx != new_member_idx) {
480 InstructionBuilder ir_builder(
481 context(), inst,
482 IRContext::kAnalysisDefUse |
483 IRContext::kAnalysisInstrToBlockMapping);
484 uint32_t const_id =
485 ir_builder.GetUintConstant(new_member_idx)->result_id();
486 new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}}));
487 modified = true;
488 } else {
489 new_operands.emplace_back(inst->GetInOperand(i));
490 }
491 // The type will have already been rewritten, so use the new member
492 // index.
493 type_id = type_inst->GetSingleWordInOperand(new_member_idx);
494 } break;
495 case SpvOpTypeArray:
496 case SpvOpTypeRuntimeArray:
497 case SpvOpTypeVector:
498 case SpvOpTypeMatrix:
499 new_operands.emplace_back(inst->GetInOperand(i));
500 type_id = type_inst->GetSingleWordInOperand(0);
501 break;
502 default:
503 assert(false);
504 break;
505 }
506 }
507
508 if (!modified) {
509 return false;
510 }
511 inst->SetInOperands(std::move(new_operands));
512 context()->UpdateDefUse(inst);
513 return true;
514 }
515
GetNewMemberIndex(uint32_t type_id,uint32_t member_idx)516 uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id,
517 uint32_t member_idx) {
518 auto live_members = used_members_.find(type_id);
519 if (live_members == used_members_.end()) {
520 return member_idx;
521 }
522
523 auto current_member = live_members->second.find(member_idx);
524 if (current_member == live_members->second.end()) {
525 return kRemovedMember;
526 }
527
528 return static_cast<uint32_t>(
529 std::distance(live_members->second.begin(), current_member));
530 }
531
UpdateCompsiteExtract(Instruction * inst)532 bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
533 assert(inst->opcode() == SpvOpCompositeExtract ||
534 (inst->opcode() == SpvOpSpecConstantOp &&
535 inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
536 SpvOpCompositeExtract));
537
538 uint32_t first_operand = 0;
539 if (inst->opcode() == SpvOpSpecConstantOp) {
540 first_operand = 1;
541 }
542 uint32_t object_id = inst->GetSingleWordInOperand(first_operand);
543 Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
544 uint32_t type_id = object_inst->type_id();
545
546 Instruction::OperandList new_operands;
547 bool modified = false;
548 for (uint32_t i = 0; i < first_operand + 1; i++) {
549 new_operands.emplace_back(inst->GetInOperand(i));
550 }
551 for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
552 uint32_t member_idx = inst->GetSingleWordInOperand(i);
553 uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
554 assert(new_member_idx != kRemovedMember);
555 if (member_idx != new_member_idx) {
556 modified = true;
557 }
558 new_operands.emplace_back(
559 Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
560
561 Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
562 switch (type_inst->opcode()) {
563 case SpvOpTypeStruct:
564 // The type will have already been rewriten, so use the new member
565 // index.
566 type_id = type_inst->GetSingleWordInOperand(new_member_idx);
567 break;
568 case SpvOpTypeArray:
569 case SpvOpTypeRuntimeArray:
570 case SpvOpTypeVector:
571 case SpvOpTypeMatrix:
572 type_id = type_inst->GetSingleWordInOperand(0);
573 break;
574 default:
575 assert(false);
576 }
577 }
578
579 if (!modified) {
580 return false;
581 }
582 inst->SetInOperands(std::move(new_operands));
583 context()->UpdateDefUse(inst);
584 return true;
585 }
586
UpdateCompositeInsert(Instruction * inst)587 bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) {
588 assert(inst->opcode() == SpvOpCompositeInsert ||
589 (inst->opcode() == SpvOpSpecConstantOp &&
590 inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) ==
591 SpvOpCompositeInsert));
592
593 uint32_t first_operand = 0;
594 if (inst->opcode() == SpvOpSpecConstantOp) {
595 first_operand = 1;
596 }
597
598 uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1);
599 Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
600 uint32_t type_id = composite_inst->type_id();
601
602 Instruction::OperandList new_operands;
603 bool modified = false;
604
605 for (uint32_t i = 0; i < first_operand + 2; ++i) {
606 new_operands.emplace_back(inst->GetInOperand(i));
607 }
608 for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) {
609 uint32_t member_idx = inst->GetSingleWordInOperand(i);
610 uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
611 if (new_member_idx == kRemovedMember) {
612 context()->KillInst(inst);
613 return true;
614 }
615
616 if (member_idx != new_member_idx) {
617 modified = true;
618 }
619 new_operands.emplace_back(
620 Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
621
622 Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
623 switch (type_inst->opcode()) {
624 case SpvOpTypeStruct:
625 // The type will have already been rewritten, so use the new member
626 // index.
627 type_id = type_inst->GetSingleWordInOperand(new_member_idx);
628 break;
629 case SpvOpTypeArray:
630 case SpvOpTypeRuntimeArray:
631 case SpvOpTypeVector:
632 case SpvOpTypeMatrix:
633 type_id = type_inst->GetSingleWordInOperand(0);
634 break;
635 default:
636 assert(false);
637 }
638 }
639
640 if (!modified) {
641 return false;
642 }
643 inst->SetInOperands(std::move(new_operands));
644 context()->UpdateDefUse(inst);
645 return true;
646 }
647
UpdateOpArrayLength(Instruction * inst)648 bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) {
649 uint32_t struct_id = inst->GetSingleWordInOperand(0);
650 Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id);
651 uint32_t pointer_type_id = struct_inst->type_id();
652 Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
653 uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
654
655 uint32_t member_idx = inst->GetSingleWordInOperand(1);
656 uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
657 assert(new_member_idx != kRemovedMember);
658
659 if (member_idx == new_member_idx) {
660 return false;
661 }
662
663 inst->SetInOperand(1, {new_member_idx});
664 context()->UpdateDefUse(inst);
665 return true;
666 }
667
MarkStructOperandsAsFullyUsed(const Instruction * inst)668 void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed(
669 const Instruction* inst) {
670 if (inst->type_id() != 0) {
671 MarkTypeAsFullyUsed(inst->type_id());
672 }
673
674 inst->ForEachInId([this](const uint32_t* id) {
675 Instruction* instruction = get_def_use_mgr()->GetDef(*id);
676 if (instruction->type_id() != 0) {
677 MarkTypeAsFullyUsed(instruction->type_id());
678 }
679 });
680 }
681 } // namespace opt
682 } // namespace spvtools
683