Coverage Report

Created: 2026-03-31 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/opt/dead_variable_elimination.cpp
Line
Count
Source
1
// Copyright (c) 2017 Google Inc.
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/dead_variable_elimination.h"
16
17
#include <vector>
18
19
#include "source/opt/ir_context.h"
20
#include "source/opt/reflect.h"
21
22
namespace spvtools {
23
namespace opt {
24
25
// This optimization removes global variables that are not needed because they
26
// are definitely not accessed.
27
0
Pass::Status DeadVariableElimination::Process() {
28
  // The algorithm will compute the reference count for every global variable.
29
  // Anything with a reference count of 0 will then be deleted.  For variables
30
  // that might have references that are not explicit in this context, we use
31
  // the value kMustKeep as the reference count.
32
0
  std::vector<uint32_t> ids_to_remove;
33
34
  // Get the reference count for all of the global OpVariable instructions.
35
0
  for (auto& inst : context()->types_values()) {
36
0
    if (inst.opcode() != spv::Op::OpVariable) {
37
0
      continue;
38
0
    }
39
40
0
    size_t count = 0;
41
0
    uint32_t result_id = inst.result_id();
42
43
    // Check the linkage.  If it is exported, it could be reference somewhere
44
    // else, so we must keep the variable around.
45
0
    get_decoration_mgr()->ForEachDecoration(
46
0
        result_id, uint32_t(spv::Decoration::LinkageAttributes),
47
0
        [&count](const Instruction& linkage_instruction) {
48
0
          uint32_t last_operand = linkage_instruction.NumOperands() - 1;
49
0
          if (spv::LinkageType(linkage_instruction.GetSingleWordOperand(
50
0
                  last_operand)) == spv::LinkageType::Export) {
51
0
            count = kMustKeep;
52
0
          }
53
0
        });
54
55
0
    if (count != kMustKeep) {
56
      // If we don't have to keep the instruction for other reasons, then look
57
      // at the uses and count the number of real references.
58
0
      count = 0;
59
0
      get_def_use_mgr()->ForEachUser(result_id, [&count](Instruction* user) {
60
0
        if (!IsAnnotationInst(user->opcode()) &&
61
0
            user->opcode() != spv::Op::OpName) {
62
0
          ++count;
63
0
        }
64
0
      });
65
0
    }
66
0
    reference_count_[result_id] = count;
67
0
    if (count == 0) {
68
0
      ids_to_remove.push_back(result_id);
69
0
    }
70
0
  }
71
72
  // Remove all of the variables that have a reference count of 0.
73
0
  bool modified = false;
74
0
  if (!ids_to_remove.empty()) {
75
0
    modified = true;
76
0
    for (auto result_id : ids_to_remove) {
77
0
      DeleteVariable(result_id);
78
0
    }
79
0
  }
80
0
  return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange);
81
0
}
82
83
0
void DeadVariableElimination::DeleteVariable(uint32_t result_id) {
84
0
  Instruction* inst = get_def_use_mgr()->GetDef(result_id);
85
0
  assert(inst->opcode() == spv::Op::OpVariable &&
86
0
         "Should not be trying to delete anything other than an OpVariable.");
87
88
  // Look for an initializer that references another variable.  We need to know
89
  // if that variable can be deleted after the reference is removed.
90
0
  if (inst->NumOperands() == 4) {
91
0
    Instruction* initializer =
92
0
        get_def_use_mgr()->GetDef(inst->GetSingleWordOperand(3));
93
94
    // TODO: Handle OpSpecConstantOP which might be defined in terms of other
95
    // variables.  Will probably require a unified dead code pass that does all
96
    // instruction types.  (Issue 906)
97
0
    if (initializer->opcode() == spv::Op::OpVariable) {
98
0
      uint32_t initializer_id = initializer->result_id();
99
0
      size_t& count = reference_count_[initializer_id];
100
0
      if (count != kMustKeep) {
101
0
        --count;
102
0
      }
103
104
0
      if (count == 0) {
105
0
        DeleteVariable(initializer_id);
106
0
      }
107
0
    }
108
0
  }
109
0
  context()->KillDef(result_id);
110
0
}
111
}  // namespace opt
112
}  // namespace spvtools