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