Coverage Report

Created: 2025-07-23 06:18

/src/spirv-tools/source/opt/value_number_table.cpp
Line
Count
Source (jump to first uncovered line)
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/value_number_table.h"
16
17
#include <algorithm>
18
19
#include "source/opt/cfg.h"
20
#include "source/opt/ir_context.h"
21
22
namespace spvtools {
23
namespace opt {
24
25
4.14M
uint32_t ValueNumberTable::GetValueNumber(Instruction* inst) const {
26
4.14M
  assert(inst->result_id() != 0 &&
27
4.14M
         "inst must have a result id to get a value number.");
28
29
  // Check if this instruction already has a value.
30
4.14M
  auto result_id_to_val = id_to_value_.find(inst->result_id());
31
4.14M
  if (result_id_to_val != id_to_value_.end()) {
32
1.16M
    return result_id_to_val->second;
33
1.16M
  }
34
2.97M
  return 0;
35
4.14M
}
36
37
259k
uint32_t ValueNumberTable::GetValueNumber(uint32_t id) const {
38
259k
  return GetValueNumber(context()->get_def_use_mgr()->GetDef(id));
39
259k
}
40
41
2.67M
uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
42
  // If it already has a value return that.
43
2.67M
  uint32_t value = GetValueNumber(inst);
44
2.67M
  if (value != 0) {
45
0
    return value;
46
0
  }
47
48
2.67M
  auto assign_new_number = [this](Instruction* i) {
49
557k
    const auto new_value = TakeNextValueNumber();
50
557k
    id_to_value_[i->result_id()] = new_value;
51
557k
    return new_value;
52
557k
  };
53
54
  // If the instruction has other side effects, then it must
55
  // have its own value number.
56
2.67M
  if (!context()->IsCombinatorInstruction(inst) &&
57
2.67M
      !inst->IsCommonDebugInstr()) {
58
19.7k
    return assign_new_number(inst);
59
19.7k
  }
60
61
  // OpSampledImage and OpImage must remain in the same basic block in which
62
  // they are used, because of this we will assign each one it own value number.
63
2.65M
  switch (inst->opcode()) {
64
5
    case spv::Op::OpSampledImage:
65
5
    case spv::Op::OpImage:
66
130k
    case spv::Op::OpVariable:
67
130k
      return assign_new_number(inst);
68
2.52M
    default:
69
2.52M
      break;
70
2.65M
  }
71
72
  // A load that yields an image, sampler, or sampled image must remain in
73
  // the same basic block.  So assign it its own value number.
74
2.52M
  if (inst->IsLoad()) {
75
532k
    switch (context()->get_def_use_mgr()->GetDef(inst->type_id())->opcode()) {
76
122k
      case spv::Op::OpTypeSampledImage:
77
122k
      case spv::Op::OpTypeImage:
78
122k
      case spv::Op::OpTypeSampler:
79
122k
        return assign_new_number(inst);
80
410k
      default:
81
410k
        break;
82
532k
    }
83
532k
  }
84
85
  // If it is a load from memory that can be modified, we have to assume the
86
  // memory has been modified, so we give it a new value number.
87
  //
88
  // Note that this test will also handle volatile loads because they are not
89
  // read only.  However, if this is ever relaxed because we analyze stores, we
90
  // will have to add a new case for volatile loads.
91
2.40M
  if (inst->IsLoad() && !inst->IsReadOnlyLoad()) {
92
284k
    return assign_new_number(inst);
93
284k
  }
94
95
2.11M
  analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr();
96
97
  // When we copy an object, the value numbers should be the same.
98
2.11M
  if (inst->opcode() == spv::Op::OpCopyObject &&
99
2.11M
      dec_mgr->HaveTheSameDecorations(inst->result_id(),
100
11
                                      inst->GetSingleWordInOperand(0))) {
101
2
    value = GetValueNumber(inst->GetSingleWordInOperand(0));
102
2
    if (value != 0) {
103
2
      id_to_value_[inst->result_id()] = value;
104
2
      return value;
105
2
    }
106
2
  }
107
108
  // Phi nodes are a type of copy.  If all of the inputs have the same value
109
  // number, then we can assign the result of the phi the same value number.
110
2.11M
  if (inst->opcode() == spv::Op::OpPhi && inst->NumInOperands() > 0 &&
111
2.11M
      dec_mgr->HaveTheSameDecorations(inst->result_id(),
112
144k
                                      inst->GetSingleWordInOperand(0))) {
113
127k
    value = GetValueNumber(inst->GetSingleWordInOperand(0));
114
127k
    if (value != 0) {
115
135k
      for (uint32_t op = 2; op < inst->NumInOperands(); op += 2) {
116
131k
        if (value != GetValueNumber(inst->GetSingleWordInOperand(op))) {
117
123k
          value = 0;
118
123k
          break;
119
123k
        }
120
131k
      }
121
127k
      if (value != 0) {
122
4.23k
        id_to_value_[inst->result_id()] = value;
123
4.23k
        return value;
124
4.23k
      }
125
127k
    }
126
127k
  }
127
128
  // Replace all of the operands by their value number.  The sign bit will be
129
  // set to distinguish between an id and a value number.
130
2.11M
  Instruction value_ins(context(), inst->opcode(), inst->type_id(),
131
2.11M
                        inst->result_id(), {});
132
6.53M
  for (uint32_t o = 0; o < inst->NumInOperands(); ++o) {
133
4.42M
    const Operand& op = inst->GetInOperand(o);
134
4.42M
    if (spvIsIdType(op.type)) {
135
3.79M
      uint32_t id_value = op.words[0];
136
3.79M
      auto use_id_to_val = id_to_value_.find(id_value);
137
3.79M
      if (use_id_to_val != id_to_value_.end()) {
138
3.44M
        id_value = (1 << 31) | use_id_to_val->second;
139
3.44M
      }
140
3.79M
      value_ins.AddOperand(Operand(op.type, {id_value}));
141
3.79M
    } else {
142
626k
      value_ins.AddOperand(Operand(op.type, op.words));
143
626k
    }
144
4.42M
  }
145
146
  // TODO: Implement a normal form for opcodes that commute like integer
147
  // addition.  This will let us know that a+b is the same value as b+a.
148
149
  // Otherwise, we check if this value has been computed before.
150
2.11M
  auto value_iterator = instruction_to_value_.find(value_ins);
151
2.11M
  if (value_iterator != instruction_to_value_.end()) {
152
429k
    value = id_to_value_[value_iterator->first.result_id()];
153
429k
    id_to_value_[inst->result_id()] = value;
154
429k
    return value;
155
429k
  }
156
157
  // If not, assign it a new value number.
158
1.68M
  value = TakeNextValueNumber();
159
1.68M
  id_to_value_[inst->result_id()] = value;
160
1.68M
  instruction_to_value_[value_ins] = value;
161
1.68M
  return value;
162
2.11M
}
163
164
17.6k
void ValueNumberTable::BuildDominatorTreeValueNumberTable() {
165
  // First value number the headers.
166
1.86M
  for (auto& inst : context()->annotations()) {
167
1.86M
    if (inst.result_id() != 0) {
168
1.98k
      AssignValueNumber(&inst);
169
1.98k
    }
170
1.86M
  }
171
172
21.8k
  for (auto& inst : context()->capabilities()) {
173
21.8k
    if (inst.result_id() != 0) {
174
0
      AssignValueNumber(&inst);
175
0
    }
176
21.8k
  }
177
178
523k
  for (auto& inst : context()->types_values()) {
179
523k
    if (inst.result_id() != 0) {
180
523k
      AssignValueNumber(&inst);
181
523k
    }
182
523k
  }
183
184
17.6k
  for (auto& inst : context()->module()->ext_inst_imports()) {
185
9.51k
    if (inst.result_id() != 0) {
186
9.51k
      AssignValueNumber(&inst);
187
9.51k
    }
188
9.51k
  }
189
190
17.6k
  for (auto& inst : context()->module()->ext_inst_debuginfo()) {
191
0
    if (inst.result_id() != 0) {
192
0
      AssignValueNumber(&inst);
193
0
    }
194
0
  }
195
196
17.6k
  for (Function& func : *context()->module()) {
197
    // For best results we want to traverse the code in reverse post order.
198
    // This happens naturally because of the forward referencing rules.
199
911k
    for (BasicBlock& block : func) {
200
3.68M
      for (Instruction& inst : block) {
201
3.68M
        if (inst.result_id() != 0) {
202
2.14M
          AssignValueNumber(&inst);
203
2.14M
        }
204
3.68M
      }
205
911k
    }
206
17.3k
  }
207
17.6k
}
208
209
bool ComputeSameValue::operator()(const Instruction& lhs,
210
433k
                                  const Instruction& rhs) const {
211
433k
  if (lhs.result_id() == 0 || rhs.result_id() == 0) {
212
0
    return false;
213
0
  }
214
215
433k
  if (lhs.opcode() != rhs.opcode()) {
216
0
    return false;
217
0
  }
218
219
433k
  if (lhs.type_id() != rhs.type_id()) {
220
0
    return false;
221
0
  }
222
223
433k
  if (lhs.NumInOperands() != rhs.NumInOperands()) {
224
0
    return false;
225
0
  }
226
227
1.31M
  for (uint32_t i = 0; i < lhs.NumInOperands(); ++i) {
228
882k
    if (lhs.GetInOperand(i) != rhs.GetInOperand(i)) {
229
0
      return false;
230
0
    }
231
882k
  }
232
233
433k
  return lhs.context()->get_decoration_mgr()->HaveTheSameDecorations(
234
433k
      lhs.result_id(), rhs.result_id());
235
433k
}
236
237
3.79M
std::size_t ValueTableHash::operator()(const Instruction& inst) const {
238
  // We hash the opcode and in-operands, not the result, because we want
239
  // instructions that are the same except for the result to hash to the
240
  // same value.
241
3.79M
  std::u32string h;
242
3.79M
  h.push_back(uint32_t(inst.opcode()));
243
3.79M
  h.push_back(inst.type_id());
244
11.7M
  for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
245
7.96M
    const auto& opnd = inst.GetInOperand(i);
246
7.96M
    for (uint32_t word : opnd.words) {
247
7.96M
      h.push_back(word);
248
7.96M
    }
249
7.96M
  }
250
3.79M
  return std::hash<std::u32string>()(h);
251
3.79M
}
252
}  // namespace opt
253
}  // namespace spvtools