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