Coverage Report

Created: 2026-06-08 06:54

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