Coverage Report

Created: 2026-11-12 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/eliminate_dead_members_pass.cpp
Line
Count
Source
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
28
1.99k
Pass::Status EliminateDeadMembersPass::Process() {
29
1.99k
  if (!context()->get_feature_mgr()->HasCapability(spv::Capability::Shader)) {
30
0
    return Status::SuccessWithoutChange;
31
0
  }
32
33
1.99k
  FindLiveMembers();
34
1.99k
  return RemoveDeadMembers();
35
1.99k
}
36
37
1.99k
void EliminateDeadMembersPass::FindLiveMembers() {
38
  // Until we have implemented the rewriting of OpSpecConsantOp instructions,
39
  // we have to mark them as fully used just to be safe.
40
65.0k
  for (auto& inst : get_module()->types_values()) {
41
65.0k
    if (inst.opcode() == spv::Op::OpSpecConstantOp) {
42
0
      switch (spv::Op(inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
43
0
        case spv::Op::OpCompositeExtract:
44
0
          MarkMembersAsLiveForExtract(&inst);
45
0
          break;
46
0
        case spv::Op::OpCompositeInsert:
47
          // Nothing specific to do.
48
0
          break;
49
0
        case spv::Op::OpAccessChain:
50
0
        case spv::Op::OpInBoundsAccessChain:
51
0
        case spv::Op::OpPtrAccessChain:
52
0
        case spv::Op::OpInBoundsPtrAccessChain:
53
0
          assert(false && "Not implemented yet.");
54
0
          break;
55
0
        default:
56
0
          break;
57
0
      }
58
65.0k
    } else if (inst.opcode() == spv::Op::OpVariable) {
59
4.52k
      switch (spv::StorageClass(inst.GetSingleWordInOperand(0))) {
60
936
        case spv::StorageClass::Input:
61
2.70k
        case spv::StorageClass::Output:
62
2.70k
          MarkPointeeTypeAsFullUsed(inst.type_id());
63
2.70k
          break;
64
1.82k
        default:
65
          // Ignore structured buffers as layout(offset) qualifiers cannot be
66
          // applied to structure fields
67
1.82k
          if (inst.IsVulkanStorageBufferVariable())
68
46
            MarkPointeeTypeAsFullUsed(inst.type_id());
69
1.82k
          break;
70
4.52k
      }
71
60.5k
    } else if (inst.opcode() == spv::Op::OpTypePointer) {
72
9.88k
      uint32_t storage_class = inst.GetSingleWordInOperand(0);
73
9.88k
      if (storage_class == uint32_t(spv::StorageClass::PhysicalStorageBuffer)) {
74
0
        MarkTypeAsFullyUsed(inst.GetSingleWordInOperand(1));
75
0
      }
76
9.88k
    }
77
65.0k
  }
78
79
2.29k
  for (const Function& func : *get_module()) {
80
2.29k
    FindLiveMembers(func);
81
2.29k
  }
82
1.99k
}
83
84
2.29k
void EliminateDeadMembersPass::FindLiveMembers(const Function& function) {
85
2.29k
  function.ForEachInst(
86
556k
      [this](const Instruction* inst) { FindLiveMembers(inst); });
87
2.29k
}
88
89
556k
void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) {
90
556k
  switch (inst->opcode()) {
91
74.5k
    case spv::Op::OpStore:
92
74.5k
      MarkMembersAsLiveForStore(inst);
93
74.5k
      break;
94
282
    case spv::Op::OpCopyMemory:
95
282
    case spv::Op::OpCopyMemorySized:
96
282
      MarkMembersAsLiveForCopyMemory(inst);
97
282
      break;
98
32.2k
    case spv::Op::OpCompositeExtract:
99
32.2k
      MarkMembersAsLiveForExtract(inst);
100
32.2k
      break;
101
46.6k
    case spv::Op::OpAccessChain:
102
46.7k
    case spv::Op::OpInBoundsAccessChain:
103
46.7k
    case spv::Op::OpPtrAccessChain:
104
46.7k
    case spv::Op::OpInBoundsPtrAccessChain:
105
46.7k
      MarkMembersAsLiveForAccessChain(inst);
106
46.7k
      break;
107
282
    case spv::Op::OpReturnValue:
108
      // This should be an issue only if we are returning from the entry point.
109
      // However, for now I will keep it more conservative because functions are
110
      // often inlined leaving only the entry points.
111
282
      MarkOperandTypeAsFullyUsed(inst, 0);
112
282
      break;
113
0
    case spv::Op::OpArrayLength:
114
0
      MarkMembersAsLiveForArrayLength(inst);
115
0
      break;
116
56.6k
    case spv::Op::OpLoad:
117
67.8k
    case spv::Op::OpCompositeInsert:
118
76.1k
    case spv::Op::OpCompositeConstruct:
119
76.1k
      break;
120
325k
    default:
121
      // This path is here for safety.  All instructions that can reference
122
      // structs in a function body should be handled above.  However, this will
123
      // keep the pass valid, but not optimal, as new instructions get added
124
      // or if something was missed.
125
325k
      MarkStructOperandsAsFullyUsed(inst);
126
325k
      break;
127
556k
  }
128
556k
}
129
130
void EliminateDeadMembersPass::MarkMembersAsLiveForStore(
131
74.5k
    const Instruction* inst) {
132
  // We should only have to mark the members as live if the store is to
133
  // memory that is read outside of the shader.  Other passes can remove all
134
  // store to memory that is not visible outside of the shader, so we do not
135
  // complicate the code for now.
136
74.5k
  assert(inst->opcode() == spv::Op::OpStore);
137
74.5k
  uint32_t object_id = inst->GetSingleWordInOperand(1);
138
74.5k
  Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id);
139
74.5k
  uint32_t object_type_id = object_inst->type_id();
140
74.5k
  MarkTypeAsFullyUsed(object_type_id);
141
74.5k
}
142
143
503k
void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) {
144
503k
  Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
145
503k
  assert(type_inst != nullptr);
146
147
503k
  switch (type_inst->opcode()) {
148
3.44k
    case spv::Op::OpTypeStruct:
149
      // Mark every member and its type as fully used.
150
13.3k
      for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) {
151
9.89k
        used_members_[type_id].insert(i);
152
9.89k
        MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i));
153
9.89k
      }
154
3.44k
      break;
155
1.29k
    case spv::Op::OpTypeArray:
156
1.29k
    case spv::Op::OpTypeRuntimeArray:
157
1.29k
      MarkTypeAsFullyUsed(
158
1.29k
          type_inst->GetSingleWordInOperand(kArrayElementTypeIdx));
159
1.29k
      break;
160
498k
    default:
161
498k
      break;
162
503k
  }
163
503k
}
164
165
2.74k
void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) {
166
2.74k
  Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id);
167
2.74k
  assert(ptr_type_inst->opcode() == spv::Op::OpTypePointer);
168
2.74k
  MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1));
169
2.74k
}
170
171
void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory(
172
282
    const Instruction* inst) {
173
282
  uint32_t target_id = inst->GetSingleWordInOperand(0);
174
282
  Instruction* target_inst = get_def_use_mgr()->GetDef(target_id);
175
282
  uint32_t pointer_type_id = target_inst->type_id();
176
282
  Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
177
282
  uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
178
282
  MarkTypeAsFullyUsed(type_id);
179
282
}
180
181
void EliminateDeadMembersPass::MarkMembersAsLiveForExtract(
182
32.2k
    const Instruction* inst) {
183
32.2k
  assert(inst->opcode() == spv::Op::OpCompositeExtract ||
184
32.2k
         (inst->opcode() == spv::Op::OpSpecConstantOp &&
185
32.2k
          spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
186
32.2k
              spv::Op::OpCompositeExtract));
187
188
32.2k
  uint32_t first_operand =
189
32.2k
      (inst->opcode() == spv::Op::OpSpecConstantOp ? 1 : 0);
190
32.2k
  uint32_t composite_id = inst->GetSingleWordInOperand(first_operand);
191
32.2k
  Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
192
32.2k
  uint32_t type_id = composite_inst->type_id();
193
194
64.6k
  for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
195
32.4k
    Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
196
32.4k
    uint32_t member_idx = inst->GetSingleWordInOperand(i);
197
32.4k
    switch (type_inst->opcode()) {
198
1.72k
      case spv::Op::OpTypeStruct:
199
1.72k
        used_members_[type_id].insert(member_idx);
200
1.72k
        type_id = type_inst->GetSingleWordInOperand(member_idx);
201
1.72k
        break;
202
5.66k
      case spv::Op::OpTypeArray:
203
5.66k
      case spv::Op::OpTypeRuntimeArray:
204
30.6k
      case spv::Op::OpTypeVector:
205
30.6k
      case spv::Op::OpTypeMatrix:
206
30.6k
      case spv::Op::OpTypeCooperativeMatrixNV:
207
30.6k
      case spv::Op::OpTypeCooperativeMatrixKHR:
208
30.6k
      case spv::Op::OpTypeVectorIdEXT:
209
30.6k
        type_id = type_inst->GetSingleWordInOperand(0);
210
30.6k
        break;
211
0
      default:
212
0
        assert(false);
213
32.4k
    }
214
32.4k
  }
215
32.2k
}
216
217
void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain(
218
46.7k
    const Instruction* inst) {
219
46.7k
  assert(inst->opcode() == spv::Op::OpAccessChain ||
220
46.7k
         inst->opcode() == spv::Op::OpInBoundsAccessChain ||
221
46.7k
         inst->opcode() == spv::Op::OpPtrAccessChain ||
222
46.7k
         inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
223
224
46.7k
  uint32_t pointer_id = inst->GetSingleWordInOperand(0);
225
46.7k
  Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
226
46.7k
  uint32_t pointer_type_id = pointer_inst->type_id();
227
46.7k
  Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
228
46.7k
  uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
229
230
46.7k
  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
231
232
  // For a pointer access chain, we need to skip the |element| index.  It is not
233
  // a reference to the member of a struct, and it does not change the type.
234
46.7k
  uint32_t i = (inst->opcode() == spv::Op::OpAccessChain ||
235
113
                        inst->opcode() == spv::Op::OpInBoundsAccessChain
236
46.7k
                    ? 1
237
46.7k
                    : 2);
238
110k
  for (; i < inst->NumInOperands(); ++i) {
239
63.3k
    Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
240
63.3k
    switch (type_inst->opcode()) {
241
12.1k
      case spv::Op::OpTypeStruct: {
242
12.1k
        const analysis::IntConstant* member_idx =
243
12.1k
            const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
244
12.1k
                ->AsIntConstant();
245
12.1k
        assert(member_idx);
246
12.1k
        uint32_t index =
247
12.1k
            static_cast<uint32_t>(member_idx->GetZeroExtendedValue());
248
12.1k
        used_members_[type_id].insert(index);
249
12.1k
        type_id = type_inst->GetSingleWordInOperand(index);
250
12.1k
      } break;
251
22.4k
      case spv::Op::OpTypeArray:
252
22.4k
      case spv::Op::OpTypeRuntimeArray:
253
44.8k
      case spv::Op::OpTypeVector:
254
51.2k
      case spv::Op::OpTypeMatrix:
255
51.2k
      case spv::Op::OpTypeCooperativeMatrixNV:
256
51.2k
      case spv::Op::OpTypeCooperativeMatrixKHR:
257
51.2k
      case spv::Op::OpTypeVectorIdEXT:
258
51.2k
        type_id = type_inst->GetSingleWordInOperand(0);
259
51.2k
        break;
260
0
      default:
261
0
        assert(false);
262
63.3k
    }
263
63.3k
  }
264
46.7k
}
265
266
void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed(
267
282
    const Instruction* inst, uint32_t in_idx) {
268
282
  uint32_t op_id = inst->GetSingleWordInOperand(in_idx);
269
282
  Instruction* op_inst = get_def_use_mgr()->GetDef(op_id);
270
282
  MarkTypeAsFullyUsed(op_inst->type_id());
271
282
}
272
273
void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength(
274
0
    const Instruction* inst) {
275
0
  assert(inst->opcode() == spv::Op::OpArrayLength);
276
0
  uint32_t object_id = inst->GetSingleWordInOperand(0);
277
0
  Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
278
0
  uint32_t pointer_type_id = object_inst->type_id();
279
0
  Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
280
0
  uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
281
0
  used_members_[type_id].insert(inst->GetSingleWordInOperand(1));
282
0
}
283
284
1.99k
Pass::Status EliminateDeadMembersPass::RemoveDeadMembers() {
285
1.99k
  bool modified = false;
286
287
  // First update all of the OpTypeStruct instructions.
288
659k
  get_module()->ForEachInst([&modified, this](Instruction* inst) {
289
659k
    switch (inst->opcode()) {
290
1.92k
      case spv::Op::OpTypeStruct:
291
1.92k
        modified |= UpdateOpTypeStruct(inst);
292
1.92k
        break;
293
657k
      default:
294
657k
        break;
295
659k
    }
296
659k
  });
297
298
  // Now update all of the instructions that reference the OpTypeStructs.
299
1.99k
  Pass::Status status = Status::SuccessWithoutChange;
300
1.99k
  bool success = get_module()->WhileEachInst([&modified, &status,
301
659k
                                              this](Instruction* inst) {
302
659k
    switch (inst->opcode()) {
303
1.64k
      case spv::Op::OpMemberName:
304
1.64k
        modified |= UpdateOpMemberNameOrDecorate(inst);
305
1.64k
        break;
306
1.03k
      case spv::Op::OpMemberDecorate:
307
1.03k
        modified |= UpdateOpMemberNameOrDecorate(inst);
308
1.03k
        break;
309
0
      case spv::Op::OpGroupMemberDecorate:
310
0
        modified |= UpdateOpGroupMemberDecorate(inst);
311
0
        break;
312
0
      case spv::Op::OpSpecConstantComposite:
313
5.33k
      case spv::Op::OpConstantComposite:
314
13.7k
      case spv::Op::OpCompositeConstruct:
315
13.7k
        modified |= UpdateConstantComposite(inst);
316
13.7k
        break;
317
46.6k
      case spv::Op::OpAccessChain:
318
46.7k
      case spv::Op::OpInBoundsAccessChain:
319
46.7k
      case spv::Op::OpPtrAccessChain:
320
46.7k
      case spv::Op::OpInBoundsPtrAccessChain: {
321
46.7k
        Pass::Status s = UpdateAccessChain(inst);
322
46.7k
        if (s == Status::Failure) {
323
0
          status = Status::Failure;
324
0
          return false;
325
0
        }
326
46.7k
        if (s == Status::SuccessWithChange) {
327
82
          modified = true;
328
82
        }
329
46.7k
        break;
330
46.7k
      }
331
32.2k
      case spv::Op::OpCompositeExtract:
332
32.2k
        modified |= UpdateCompsiteExtract(inst);
333
32.2k
        break;
334
11.1k
      case spv::Op::OpCompositeInsert:
335
11.1k
        modified |= UpdateCompositeInsert(inst);
336
11.1k
        break;
337
0
      case spv::Op::OpArrayLength:
338
0
        modified |= UpdateOpArrayLength(inst);
339
0
        break;
340
0
      case spv::Op::OpSpecConstantOp:
341
0
        switch (spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx))) {
342
0
          case spv::Op::OpCompositeExtract:
343
0
            modified |= UpdateCompsiteExtract(inst);
344
0
            break;
345
0
          case spv::Op::OpCompositeInsert:
346
0
            modified |= UpdateCompositeInsert(inst);
347
0
            break;
348
0
          case spv::Op::OpAccessChain:
349
0
          case spv::Op::OpInBoundsAccessChain:
350
0
          case spv::Op::OpPtrAccessChain:
351
0
          case spv::Op::OpInBoundsPtrAccessChain:
352
0
            assert(false && "Not implemented yet.");
353
0
            break;
354
0
          default:
355
0
            break;
356
0
        }
357
0
        break;
358
552k
      default:
359
552k
        break;
360
659k
    }
361
659k
    return true;
362
659k
  });
363
364
1.99k
  if (!success || status == Status::Failure) {
365
0
    return Status::Failure;
366
0
  }
367
1.99k
  return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
368
1.99k
}
369
370
1.92k
bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) {
371
1.92k
  assert(inst->opcode() == spv::Op::OpTypeStruct);
372
373
1.92k
  const auto& live_members = used_members_[inst->result_id()];
374
1.92k
  if (live_members.size() == inst->NumInOperands()) {
375
1.88k
    return false;
376
1.88k
  }
377
378
42
  Instruction::OperandList new_operands;
379
42
  for (uint32_t idx : live_members) {
380
39
    new_operands.emplace_back(inst->GetInOperand(idx));
381
39
  }
382
383
42
  inst->SetInOperands(std::move(new_operands));
384
42
  context()->UpdateDefUse(inst);
385
42
  return true;
386
1.92k
}
387
388
2.68k
bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) {
389
2.68k
  assert(inst->opcode() == spv::Op::OpMemberName ||
390
2.68k
         inst->opcode() == spv::Op::OpMemberDecorate);
391
392
2.68k
  uint32_t type_id = inst->GetSingleWordInOperand(0);
393
2.68k
  auto live_members = used_members_.find(type_id);
394
2.68k
  if (live_members == used_members_.end()) {
395
0
    return false;
396
0
  }
397
398
2.68k
  uint32_t orig_member_idx = inst->GetSingleWordInOperand(1);
399
2.68k
  uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
400
401
2.68k
  if (new_member_idx == kRemovedMember) {
402
31
    context()->KillInst(inst);
403
31
    return true;
404
31
  }
405
406
2.65k
  if (new_member_idx == orig_member_idx) {
407
2.64k
    return false;
408
2.64k
  }
409
410
5
  inst->SetInOperand(1, {new_member_idx});
411
5
  return true;
412
2.65k
}
413
414
0
bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) {
415
0
  assert(inst->opcode() == spv::Op::OpGroupMemberDecorate);
416
417
0
  bool modified = false;
418
419
0
  Instruction::OperandList new_operands;
420
0
  new_operands.emplace_back(inst->GetInOperand(0));
421
0
  for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) {
422
0
    uint32_t type_id = inst->GetSingleWordInOperand(i);
423
0
    uint32_t member_idx = inst->GetSingleWordInOperand(i + 1);
424
0
    uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
425
426
0
    if (new_member_idx == kRemovedMember) {
427
0
      modified = true;
428
0
      continue;
429
0
    }
430
431
0
    new_operands.emplace_back(inst->GetOperand(i));
432
0
    if (new_member_idx != member_idx) {
433
0
      new_operands.emplace_back(
434
0
          Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
435
0
      modified = true;
436
0
    } else {
437
0
      new_operands.emplace_back(inst->GetOperand(i + 1));
438
0
    }
439
0
  }
440
441
0
  if (!modified) {
442
0
    return false;
443
0
  }
444
445
0
  if (new_operands.size() == 1) {
446
0
    context()->KillInst(inst);
447
0
    return true;
448
0
  }
449
450
0
  inst->SetInOperands(std::move(new_operands));
451
0
  context()->UpdateDefUse(inst);
452
0
  return true;
453
0
}
454
455
13.7k
bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) {
456
13.7k
  assert(inst->opcode() == spv::Op::OpSpecConstantComposite ||
457
13.7k
         inst->opcode() == spv::Op::OpConstantComposite ||
458
13.7k
         inst->opcode() == spv::Op::OpCompositeConstruct);
459
13.7k
  uint32_t type_id = inst->type_id();
460
461
13.7k
  bool modified = false;
462
13.7k
  Instruction::OperandList new_operands;
463
64.4k
  for (uint32_t i = 0; i < inst->NumInOperands(); ++i) {
464
50.7k
    uint32_t new_idx = GetNewMemberIndex(type_id, i);
465
50.7k
    if (new_idx == kRemovedMember) {
466
302
      modified = true;
467
50.4k
    } else {
468
50.4k
      new_operands.emplace_back(inst->GetInOperand(i));
469
50.4k
    }
470
50.7k
  }
471
13.7k
  inst->SetInOperands(std::move(new_operands));
472
13.7k
  context()->UpdateDefUse(inst);
473
13.7k
  return modified;
474
13.7k
}
475
476
46.7k
Pass::Status EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) {
477
46.7k
  assert(inst->opcode() == spv::Op::OpAccessChain ||
478
46.7k
         inst->opcode() == spv::Op::OpInBoundsAccessChain ||
479
46.7k
         inst->opcode() == spv::Op::OpPtrAccessChain ||
480
46.7k
         inst->opcode() == spv::Op::OpInBoundsPtrAccessChain);
481
482
46.7k
  uint32_t pointer_id = inst->GetSingleWordInOperand(0);
483
46.7k
  Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id);
484
46.7k
  uint32_t pointer_type_id = pointer_inst->type_id();
485
46.7k
  Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
486
46.7k
  uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
487
488
46.7k
  analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
489
46.7k
  Instruction::OperandList new_operands;
490
46.7k
  bool modified = false;
491
46.7k
  new_operands.emplace_back(inst->GetInOperand(0));
492
493
  // For pointer access chains we want to copy the element operand.
494
46.7k
  if (inst->opcode() == spv::Op::OpPtrAccessChain ||
495
46.7k
      inst->opcode() == spv::Op::OpInBoundsPtrAccessChain) {
496
0
    new_operands.emplace_back(inst->GetInOperand(1));
497
0
  }
498
499
46.7k
  for (uint32_t i = static_cast<uint32_t>(new_operands.size());
500
110k
       i < inst->NumInOperands(); ++i) {
501
63.3k
    Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
502
63.3k
    switch (type_inst->opcode()) {
503
12.1k
      case spv::Op::OpTypeStruct: {
504
12.1k
        const analysis::IntConstant* member_idx =
505
12.1k
            const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i))
506
12.1k
                ->AsIntConstant();
507
12.1k
        assert(member_idx);
508
12.1k
        uint32_t orig_member_idx =
509
12.1k
            static_cast<uint32_t>(member_idx->GetZeroExtendedValue());
510
12.1k
        uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx);
511
12.1k
        assert(new_member_idx != kRemovedMember);
512
12.1k
        if (orig_member_idx != new_member_idx) {
513
82
          InstructionBuilder ir_builder(
514
82
              context(), inst,
515
82
              IRContext::kAnalysisDefUse |
516
82
                  IRContext::kAnalysisInstrToBlockMapping);
517
82
          Instruction* const_inst = ir_builder.GetUintConstant(new_member_idx);
518
82
          if (!const_inst) {
519
0
            return Status::Failure;
520
0
          }
521
82
          uint32_t const_id = const_inst->result_id();
522
82
          new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}}));
523
82
          modified = true;
524
12.0k
        } else {
525
12.0k
          new_operands.emplace_back(inst->GetInOperand(i));
526
12.0k
        }
527
        // The type will have already been rewritten, so use the new member
528
        // index.
529
12.1k
        type_id = type_inst->GetSingleWordInOperand(new_member_idx);
530
12.1k
      } break;
531
22.4k
      case spv::Op::OpTypeArray:
532
22.4k
      case spv::Op::OpTypeRuntimeArray:
533
44.8k
      case spv::Op::OpTypeVector:
534
51.2k
      case spv::Op::OpTypeMatrix:
535
51.2k
      case spv::Op::OpTypeCooperativeMatrixNV:
536
51.2k
      case spv::Op::OpTypeCooperativeMatrixKHR:
537
51.2k
      case spv::Op::OpTypeVectorIdEXT:
538
51.2k
        new_operands.emplace_back(inst->GetInOperand(i));
539
51.2k
        type_id = type_inst->GetSingleWordInOperand(0);
540
51.2k
        break;
541
0
      default:
542
0
        assert(false);
543
0
        break;
544
63.3k
    }
545
63.3k
  }
546
547
46.7k
  if (!modified) {
548
46.7k
    return Status::SuccessWithoutChange;
549
46.7k
  }
550
82
  inst->SetInOperands(std::move(new_operands));
551
82
  context()->UpdateDefUse(inst);
552
82
  return Status::SuccessWithChange;
553
46.7k
}
554
555
uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id,
556
109k
                                                     uint32_t member_idx) {
557
109k
  auto live_members = used_members_.find(type_id);
558
109k
  if (live_members == used_members_.end()) {
559
88.6k
    return member_idx;
560
88.6k
  }
561
562
20.7k
  auto current_member = live_members->second.find(member_idx);
563
20.7k
  if (current_member == live_members->second.end()) {
564
334
    return kRemovedMember;
565
334
  }
566
567
20.4k
  return static_cast<uint32_t>(
568
20.4k
      std::distance(live_members->second.begin(), current_member));
569
20.7k
}
570
571
32.2k
bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) {
572
32.2k
  assert(inst->opcode() == spv::Op::OpCompositeExtract ||
573
32.2k
         (inst->opcode() == spv::Op::OpSpecConstantOp &&
574
32.2k
          spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
575
32.2k
              spv::Op::OpCompositeExtract));
576
577
32.2k
  uint32_t first_operand = 0;
578
32.2k
  if (inst->opcode() == spv::Op::OpSpecConstantOp) {
579
0
    first_operand = 1;
580
0
  }
581
32.2k
  uint32_t object_id = inst->GetSingleWordInOperand(first_operand);
582
32.2k
  Instruction* object_inst = get_def_use_mgr()->GetDef(object_id);
583
32.2k
  uint32_t type_id = object_inst->type_id();
584
585
32.2k
  Instruction::OperandList new_operands;
586
32.2k
  bool modified = false;
587
64.4k
  for (uint32_t i = 0; i < first_operand + 1; i++) {
588
32.2k
    new_operands.emplace_back(inst->GetInOperand(i));
589
32.2k
  }
590
64.6k
  for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) {
591
32.4k
    uint32_t member_idx = inst->GetSingleWordInOperand(i);
592
32.4k
    uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
593
32.4k
    assert(new_member_idx != kRemovedMember);
594
32.4k
    if (member_idx != new_member_idx) {
595
10
      modified = true;
596
10
    }
597
32.4k
    new_operands.emplace_back(
598
32.4k
        Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
599
600
32.4k
    Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
601
32.4k
    switch (type_inst->opcode()) {
602
1.72k
      case spv::Op::OpTypeStruct:
603
        // The type will have already been rewritten, so use the new member
604
        // index.
605
1.72k
        type_id = type_inst->GetSingleWordInOperand(new_member_idx);
606
1.72k
        break;
607
5.66k
      case spv::Op::OpTypeArray:
608
5.66k
      case spv::Op::OpTypeRuntimeArray:
609
30.6k
      case spv::Op::OpTypeVector:
610
30.6k
      case spv::Op::OpTypeMatrix:
611
30.6k
      case spv::Op::OpTypeCooperativeMatrixNV:
612
30.6k
      case spv::Op::OpTypeCooperativeMatrixKHR:
613
30.6k
      case spv::Op::OpTypeVectorIdEXT:
614
30.6k
        type_id = type_inst->GetSingleWordInOperand(0);
615
30.6k
        break;
616
0
      default:
617
0
        assert(false);
618
32.4k
    }
619
32.4k
  }
620
621
32.2k
  if (!modified) {
622
32.2k
    return false;
623
32.2k
  }
624
10
  inst->SetInOperands(std::move(new_operands));
625
10
  context()->UpdateDefUse(inst);
626
10
  return true;
627
32.2k
}
628
629
11.1k
bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) {
630
11.1k
  assert(inst->opcode() == spv::Op::OpCompositeInsert ||
631
11.1k
         (inst->opcode() == spv::Op::OpSpecConstantOp &&
632
11.1k
          spv::Op(inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) ==
633
11.1k
              spv::Op::OpCompositeInsert));
634
635
11.1k
  uint32_t first_operand = 0;
636
11.1k
  if (inst->opcode() == spv::Op::OpSpecConstantOp) {
637
0
    first_operand = 1;
638
0
  }
639
640
11.1k
  uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1);
641
11.1k
  Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id);
642
11.1k
  uint32_t type_id = composite_inst->type_id();
643
644
11.1k
  Instruction::OperandList new_operands;
645
11.1k
  bool modified = false;
646
647
33.4k
  for (uint32_t i = 0; i < first_operand + 2; ++i) {
648
22.3k
    new_operands.emplace_back(inst->GetInOperand(i));
649
22.3k
  }
650
22.6k
  for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) {
651
11.5k
    uint32_t member_idx = inst->GetSingleWordInOperand(i);
652
11.5k
    uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
653
11.5k
    if (new_member_idx == kRemovedMember) {
654
1
      context()->KillInst(inst);
655
1
      return true;
656
1
    }
657
658
11.5k
    if (member_idx != new_member_idx) {
659
1
      modified = true;
660
1
    }
661
11.5k
    new_operands.emplace_back(
662
11.5k
        Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}}));
663
664
11.5k
    Instruction* type_inst = get_def_use_mgr()->GetDef(type_id);
665
11.5k
    switch (type_inst->opcode()) {
666
974
      case spv::Op::OpTypeStruct:
667
        // The type will have already been rewritten, so use the new member
668
        // index.
669
974
        type_id = type_inst->GetSingleWordInOperand(new_member_idx);
670
974
        break;
671
3.76k
      case spv::Op::OpTypeArray:
672
3.76k
      case spv::Op::OpTypeRuntimeArray:
673
10.5k
      case spv::Op::OpTypeVector:
674
10.5k
      case spv::Op::OpTypeMatrix:
675
10.5k
      case spv::Op::OpTypeCooperativeMatrixNV:
676
10.5k
      case spv::Op::OpTypeCooperativeMatrixKHR:
677
10.5k
      case spv::Op::OpTypeVectorIdEXT:
678
10.5k
        type_id = type_inst->GetSingleWordInOperand(0);
679
10.5k
        break;
680
0
      default:
681
0
        assert(false);
682
11.5k
    }
683
11.5k
  }
684
685
11.1k
  if (!modified) {
686
11.1k
    return false;
687
11.1k
  }
688
1
  inst->SetInOperands(std::move(new_operands));
689
1
  context()->UpdateDefUse(inst);
690
1
  return true;
691
11.1k
}
692
693
0
bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) {
694
0
  uint32_t struct_id = inst->GetSingleWordInOperand(0);
695
0
  Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id);
696
0
  uint32_t pointer_type_id = struct_inst->type_id();
697
0
  Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id);
698
0
  uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1);
699
700
0
  uint32_t member_idx = inst->GetSingleWordInOperand(1);
701
0
  uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx);
702
0
  assert(new_member_idx != kRemovedMember);
703
704
0
  if (member_idx == new_member_idx) {
705
0
    return false;
706
0
  }
707
708
0
  inst->SetInOperand(1, {new_member_idx});
709
0
  context()->UpdateDefUse(inst);
710
0
  return true;
711
0
}
712
713
void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed(
714
325k
    const Instruction* inst) {
715
325k
  if (inst->type_id() != 0) {
716
143k
    MarkTypeAsFullyUsed(inst->type_id());
717
143k
  }
718
719
460k
  inst->ForEachInId([this](const uint32_t* id) {
720
460k
    Instruction* instruction = get_def_use_mgr()->GetDef(*id);
721
460k
    if (instruction->type_id() != 0) {
722
267k
      MarkTypeAsFullyUsed(instruction->type_id());
723
267k
    } else if (spvOpcodeGeneratesType(instruction->opcode())) {
724
      // Some instructions take a type as an operand, such as the Message Type
725
      // of OpAbortKHR or the Base Type of OpUntypedAccessChainKHR.
726
2.29k
      MarkTypeAsFullyUsed(instruction->result_id());
727
2.29k
    }
728
460k
  });
729
325k
}
730
}  // namespace opt
731
}  // namespace spvtools