• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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