• 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 spvtools {
21 namespace opt {
22 namespace {
23 constexpr uint32_t kRemovedMember = 0xFFFFFFFF;
24 constexpr uint32_t kSpecConstOpOpcodeIdx = 0;
25 constexpr uint32_t kArrayElementTypeIdx = 0;
26 }  // namespace
27 
Process()28 Pass::Status EliminateDeadMembersPass::Process() {
29   if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader))
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 rewriting 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() == spv::Op::OpSpecConstantOp) {
44       switch (spv::Op(inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
45         case spv::Op::OpCompositeExtract:
46           MarkMembersAsLiveForExtract(&inst);
47           break;
48         case spv::Op::OpCompositeInsert:
49           // Nothing specific to do.
50           break;
51         case spv::Op::OpAccessChain:
52         case spv::Op::OpInBoundsAccessChain:
53         case spv::Op::OpPtrAccessChain:
54         case spv::Op::OpInBoundsPtrAccessChain:
55           assert(false && "Not implemented yet.");
56           break;
57         default:
58           break;
59       }
60     } else if (inst.opcode() == spv::Op::OpVariable) {
61       switch (spv::StorageClass(inst.GetSingleWordInOperand(0))) {
62         case spv::StorageClass::Input:
63         case spv::StorageClass::Output:
64           MarkPointeeTypeAsFullUsed(inst.type_id());
65           break;
66         default:
67           // Ignore structured buffers as layout(offset) qualifiers cannot be
68           // applied to structure fields
69           if (inst.IsVulkanStorageBufferVariable())
70             MarkPointeeTypeAsFullUsed(inst.type_id());
71           break;
72       }
73     }
74   }
75 
76   for (const Function& func : *get_module()) {
77     FindLiveMembers(func);
78   }
79 }
80 
FindLiveMembers(const Function & function)81 void EliminateDeadMembersPass::FindLiveMembers(const Function& function) {
82   function.ForEachInst(
83       [this](const Instruction* inst) { FindLiveMembers(inst); });
84 }
85 
FindLiveMembers(const Instruction * inst)86 void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) {
87   switch (inst->opcode()) {
88     case spv::Op::OpStore:
89       MarkMembersAsLiveForStore(inst);
90       break;
91     case spv::Op::OpCopyMemory:
92     case spv::Op::OpCopyMemorySized:
93       MarkMembersAsLiveForCopyMemory(inst);
94       break;
95     case spv::Op::OpCompositeExtract:
96       MarkMembersAsLiveForExtract(inst);
97       break;
98     case spv::Op::OpAccessChain:
99     case spv::Op::OpInBoundsAccessChain:
100     case spv::Op::OpPtrAccessChain:
101     case spv::Op::OpInBoundsPtrAccessChain:
102       MarkMembersAsLiveForAccessChain(inst);
103       break;
104     case spv::Op::OpReturnValue:
105       // This should be an issue only if we are returning from the entry point.
106       // However, for now I will keep it more conservative because functions are
107       // often inlined leaving only the entry points.
108       MarkOperandTypeAsFullyUsed(inst, 0);
109       break;
110     case spv::Op::OpArrayLength:
111       MarkMembersAsLiveForArrayLength(inst);
112       break;
113     case spv::Op::OpLoad:
114     case spv::Op::OpCompositeInsert:
115     case spv::Op::OpCompositeConstruct:
116       break;
117     default:
118       // This path is here for safety.  All instructions that can reference
119       // structs in a function body should be handled above.  However, this will
120       // keep the pass valid, but not optimal, as new instructions get added
121       // or if something was missed.
122       MarkStructOperandsAsFullyUsed(inst);
123       break;
124   }
125 }
126 
MarkMembersAsLiveForStore(const Instruction * inst)127 void EliminateDeadMembersPass::MarkMembersAsLiveForStore(
128     const Instruction* inst) {
129   // We should only have to mark the members as live if the store is to
130   // memory that is read outside of the shader.  Other passes can remove all
131   // store to memory that is not visible outside of the shader, so we do not
132   // complicate the code for now.
133   assert(inst->opcode() == spv::Op::OpStore);
134   uint32_t object_id = inst->GetSingleWordInOperand(1);
135   Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id);
136   uint32_t object_type_id = object_inst->type_id();
137   MarkTypeAsFullyUsed(object_type_id);
138 }
139 
MarkTypeAsFullyUsed(uint32_t type_id)140 void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) {
141   Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
142   assert(type_inst != nullptr);
143 
144   switch (type_inst->opcode()) {
145     case spv::Op::OpTypeStruct:
146       // Mark every member and its type as fully used.
147       for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
148         used_members_[type_id].insert(i);
149         MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
150       }
151       break;
152     case spv::Op::OpTypeArray:
153     case spv::Op::OpTypeRuntimeArray:
154       MarkTypeAsFullyUsed(
155           type_inst->GetSingleWordInOperand(kArrayElementTypeIdx));
156       break;
157     default:
158       break;
159   }
160 }
161 
MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id)162 void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) {
163   Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
164   assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer);
165   MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1));
166 }
167 
MarkMembersAsLiveForCopyMemory(const Instruction * inst)168 void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory(
169     const Instruction* inst) {
170   uint32_t target_id = inst->GetSingleWordInOperand(0);
171   Instruction* target_inst = get_def_use_mgr()->GetDef(target_id);
172   uint32_t pointer_type_id = target_inst->type_id();
173   Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
174   uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
175   MarkTypeAsFullyUsed(type_id);
176 }
177 
MarkMembersAsLiveForExtract(const Instruction * inst)178 void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
179     const Instruction* inst) {
180   assert(inst->opcode() == spv::Op::OpCompositeExtract ||
181          (inst->opcode() == spv::Op::OpSpecConstantOp &&
182           spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
183               spv::Op::OpCompositeExtract));
184 
185   uint32_t first_operand =
186       (inst->opcode() == spv::Op::OpSpecConstantOp ? 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 spv::Op::OpTypeStruct:
196         used_members_[type_id].insert(member_idx);
197         type_id = type_inst->GetSingleWordInOperand(member_idx);
198         break;
199       case spv::Op::OpTypeArray:
200       case spv::Op::OpTypeRuntimeArray:
201       case spv::Op::OpTypeVector:
202       case spv::Op::OpTypeMatrix:
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() == spv::Op::OpAccessChain ||
214          inst->opcode() == spv::Op::OpInBoundsAccessChain ||
215          inst->opcode() == spv::Op::OpPtrAccessChain ||
216          inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
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() == spv::Op::OpAccessChain ||
229                         inst->opcode() == spv::Op::OpInBoundsAccessChain
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 spv::Op::OpTypeStruct: {
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 spv::Op::OpTypeArray:
246       case spv::Op::OpTypeRuntimeArray:
247       case spv::Op::OpTypeVector:
248       case spv::Op::OpTypeMatrix:
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() == spv::Op::OpArrayLength);
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 spv::Op::OpTypeStruct:
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 spv::Op::OpMemberName:
293         modified |= UpdateOpMemberNameOrDecorate(inst);
294         break;
295       case spv::Op::OpMemberDecorate:
296         modified |= UpdateOpMemberNameOrDecorate(inst);
297         break;
298       case spv::Op::OpGroupMemberDecorate:
299         modified |= UpdateOpGroupMemberDecorate(inst);
300         break;
301       case spv::Op::OpSpecConstantComposite:
302       case spv::Op::OpConstantComposite:
303       case spv::Op::OpCompositeConstruct:
304         modified |= UpdateConstantComposite(inst);
305         break;
306       case spv::Op::OpAccessChain:
307       case spv::Op::OpInBoundsAccessChain:
308       case spv::Op::OpPtrAccessChain:
309       case spv::Op::OpInBoundsPtrAccessChain:
310         modified |= UpdateAccessChain(inst);
311         break;
312       case spv::Op::OpCompositeExtract:
313         modified |= UpdateCompsiteExtract(inst);
314         break;
315       case spv::Op::OpCompositeInsert:
316         modified |= UpdateCompositeInsert(inst);
317         break;
318       case spv::Op::OpArrayLength:
319         modified |= UpdateOpArrayLength(inst);
320         break;
321       case spv::Op::OpSpecConstantOp:
322         switch (spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
323           case spv::Op::OpCompositeExtract:
324             modified |= UpdateCompsiteExtract(inst);
325             break;
326           case spv::Op::OpCompositeInsert:
327             modified |= UpdateCompositeInsert(inst);
328             break;
329           case spv::Op::OpAccessChain:
330           case spv::Op::OpInBoundsAccessChain:
331           case spv::Op::OpPtrAccessChain:
332           case spv::Op::OpInBoundsPtrAccessChain:
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() == spv::Op::OpTypeStruct);
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() == spv::Op::OpMemberName ||
366          inst->opcode() == spv::Op::OpMemberDecorate);
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() == spv::Op::OpGroupMemberDecorate);
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() == spv::Op::OpSpecConstantComposite ||
433          inst->opcode() == spv::Op::OpConstantComposite ||
434          inst->opcode() == spv::Op::OpCompositeConstruct);
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() == spv::Op::OpAccessChain ||
454          inst->opcode() == spv::Op::OpInBoundsAccessChain ||
455          inst->opcode() == spv::Op::OpPtrAccessChain ||
456          inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
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() == spv::Op::OpPtrAccessChain ||
471       inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
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 spv::Op::OpTypeStruct: {
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 spv::Op::OpTypeArray:
505       case spv::Op::OpTypeRuntimeArray:
506       case spv::Op::OpTypeVector:
507       case spv::Op::OpTypeMatrix:
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() == spv::Op::OpCompositeExtract ||
543          (inst->opcode() == spv::Op::OpSpecConstantOp &&
544           spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
545               spv::Op::OpCompositeExtract));
546 
547   uint32_t first_operand = 0;
548   if (inst->opcode() == spv::Op::OpSpecConstantOp) {
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 spv::Op::OpTypeStruct:
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 spv::Op::OpTypeArray:
578       case spv::Op::OpTypeRuntimeArray:
579       case spv::Op::OpTypeVector:
580       case spv::Op::OpTypeMatrix:
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() == spv::Op::OpCompositeInsert ||
598          (inst->opcode() == spv::Op::OpSpecConstantOp &&
599           spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
600               spv::Op::OpCompositeInsert));
601 
602   uint32_t first_operand = 0;
603   if (inst->opcode() == spv::Op::OpSpecConstantOp) {
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 spv::Op::OpTypeStruct:
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 spv::Op::OpTypeArray:
639       case spv::Op::OpTypeRuntimeArray:
640       case spv::Op::OpTypeVector:
641       case spv::Op::OpTypeMatrix:
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