• 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 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