Coverage Report

Created: 2024-09-11 07:09

/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
10.0M
uint32_t ValueNumberTable::GetValueNumber(Instruction* inst) const {
26
10.0M
  assert(inst->result_id() != 0 &&
27
10.0M
         "inst must have a result id to get a value number.");
28
29
  // Check if this instruction already has a value.
30
10.0M
  auto result_id_to_val = id_to_value_.find(inst->result_id());
31
10.0M
  if (result_id_to_val != id_to_value_.end()) {
32
2.55M
    return result_id_to_val->second;
33
2.55M
  }
34
7.52M
  return 0;
35
10.0M
}
36
37
242k
uint32_t ValueNumberTable::GetValueNumber(uint32_t id) const {
38
242k
  return GetValueNumber(context()->get_def_use_mgr()->GetDef(id));
39
242k
}
40
41
7.25M
uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) {
42
  // If it already has a value return that.
43
7.25M
  uint32_t value = GetValueNumber(inst);
44
7.25M
  if (value != 0) {
45
0
    return value;
46
0
  }
47
48
  // If the instruction has other side effects, then it must
49
  // have its own value number.
50
  // OpSampledImage and OpImage must remain in the same basic block in which
51
  // they are used, because of this we will assign each one it own value number.
52
7.25M
  if (!context()->IsCombinatorInstruction(inst) &&
53
7.25M
      !inst->IsCommonDebugInstr()) {
54
15.4k
    value = TakeNextValueNumber();
55
15.4k
    id_to_value_[inst->result_id()] = value;
56
15.4k
    return value;
57
15.4k
  }
58
59
7.24M
  switch (inst->opcode()) {
60
1
    case spv::Op::OpSampledImage:
61
1
    case spv::Op::OpImage:
62
110k
    case spv::Op::OpVariable:
63
110k
      value = TakeNextValueNumber();
64
110k
      id_to_value_[inst->result_id()] = value;
65
110k
      return value;
66
7.13M
    default:
67
7.13M
      break;
68
7.24M
  }
69
70
  // If it is a load from memory that can be modified, we have to assume the
71
  // memory has been modified, so we give it a new value number.
72
  //
73
  // Note that this test will also handle volatile loads because they are not
74
  // read only.  However, if this is ever relaxed because we analyze stores, we
75
  // will have to add a new case for volatile loads.
76
7.13M
  if (inst->IsLoad() && !inst->IsReadOnlyLoad()) {
77
1.38M
    value = TakeNextValueNumber();
78
1.38M
    id_to_value_[inst->result_id()] = value;
79
1.38M
    return value;
80
1.38M
  }
81
82
5.74M
  analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr();
83
84
  // When we copy an object, the value numbers should be the same.
85
5.74M
  if (inst->opcode() == spv::Op::OpCopyObject &&
86
5.74M
      dec_mgr->HaveTheSameDecorations(inst->result_id(),
87
0
                                      inst->GetSingleWordInOperand(0))) {
88
0
    value = GetValueNumber(inst->GetSingleWordInOperand(0));
89
0
    if (value != 0) {
90
0
      id_to_value_[inst->result_id()] = value;
91
0
      return value;
92
0
    }
93
0
  }
94
95
  // Phi nodes are a type of copy.  If all of the inputs have the same value
96
  // number, then we can assign the result of the phi the same value number.
97
5.74M
  if (inst->opcode() == spv::Op::OpPhi && inst->NumInOperands() > 0 &&
98
5.74M
      dec_mgr->HaveTheSameDecorations(inst->result_id(),
99
129k
                                      inst->GetSingleWordInOperand(0))) {
100
120k
    value = GetValueNumber(inst->GetSingleWordInOperand(0));
101
120k
    if (value != 0) {
102
125k
      for (uint32_t op = 2; op < inst->NumInOperands(); op += 2) {
103
121k
        if (value != GetValueNumber(inst->GetSingleWordInOperand(op))) {
104
116k
          value = 0;
105
116k
          break;
106
116k
        }
107
121k
      }
108
120k
      if (value != 0) {
109
4.00k
        id_to_value_[inst->result_id()] = value;
110
4.00k
        return value;
111
4.00k
      }
112
120k
    }
113
120k
  }
114
115
  // Replace all of the operands by their value number.  The sign bit will be
116
  // set to distinguish between an id and a value number.
117
5.74M
  Instruction value_ins(context(), inst->opcode(), inst->type_id(),
118
5.74M
                        inst->result_id(), {});
119
17.5M
  for (uint32_t o = 0; o < inst->NumInOperands(); ++o) {
120
11.7M
    const Operand& op = inst->GetInOperand(o);
121
11.7M
    if (spvIsIdType(op.type)) {
122
10.7M
      uint32_t id_value = op.words[0];
123
10.7M
      auto use_id_to_val = id_to_value_.find(id_value);
124
10.7M
      if (use_id_to_val != id_to_value_.end()) {
125
10.4M
        id_value = (1 << 31) | use_id_to_val->second;
126
10.4M
      }
127
10.7M
      value_ins.AddOperand(Operand(op.type, {id_value}));
128
10.7M
    } else {
129
999k
      value_ins.AddOperand(Operand(op.type, op.words));
130
999k
    }
131
11.7M
  }
132
133
  // TODO: Implement a normal form for opcodes that commute like integer
134
  // addition.  This will let us know that a+b is the same value as b+a.
135
136
  // Otherwise, we check if this value has been computed before.
137
5.74M
  auto value_iterator = instruction_to_value_.find(value_ins);
138
5.74M
  if (value_iterator != instruction_to_value_.end()) {
139
2.60M
    value = id_to_value_[value_iterator->first.result_id()];
140
2.60M
    id_to_value_[inst->result_id()] = value;
141
2.60M
    return value;
142
2.60M
  }
143
144
  // If not, assign it a new value number.
145
3.13M
  value = TakeNextValueNumber();
146
3.13M
  id_to_value_[inst->result_id()] = value;
147
3.13M
  instruction_to_value_[value_ins] = value;
148
3.13M
  return value;
149
5.74M
}
150
151
15.1k
void ValueNumberTable::BuildDominatorTreeValueNumberTable() {
152
  // First value number the headers.
153
1.96M
  for (auto& inst : context()->annotations()) {
154
1.96M
    if (inst.result_id() != 0) {
155
1.41k
      AssignValueNumber(&inst);
156
1.41k
    }
157
1.96M
  }
158
159
21.3k
  for (auto& inst : context()->capabilities()) {
160
21.3k
    if (inst.result_id() != 0) {
161
0
      AssignValueNumber(&inst);
162
0
    }
163
21.3k
  }
164
165
602k
  for (auto& inst : context()->types_values()) {
166
602k
    if (inst.result_id() != 0) {
167
602k
      AssignValueNumber(&inst);
168
602k
    }
169
602k
  }
170
171
15.1k
  for (auto& inst : context()->module()->ext_inst_imports()) {
172
7.79k
    if (inst.result_id() != 0) {
173
7.79k
      AssignValueNumber(&inst);
174
7.79k
    }
175
7.79k
  }
176
177
15.1k
  for (auto& inst : context()->module()->ext_inst_debuginfo()) {
178
0
    if (inst.result_id() != 0) {
179
0
      AssignValueNumber(&inst);
180
0
    }
181
0
  }
182
183
15.1k
  for (Function& func : *context()->module()) {
184
    // For best results we want to traverse the code in reverse post order.
185
    // This happens naturally because of the forward referencing rules.
186
2.46M
    for (BasicBlock& block : func) {
187
10.7M
      for (Instruction& inst : block) {
188
10.7M
        if (inst.result_id() != 0) {
189
6.64M
          AssignValueNumber(&inst);
190
6.64M
        }
191
10.7M
      }
192
2.46M
    }
193
14.3k
  }
194
15.1k
}
195
196
bool ComputeSameValue::operator()(const Instruction& lhs,
197
2.61M
                                  const Instruction& rhs) const {
198
2.61M
  if (lhs.result_id() == 0 || rhs.result_id() == 0) {
199
0
    return false;
200
0
  }
201
202
2.61M
  if (lhs.opcode() != rhs.opcode()) {
203
0
    return false;
204
0
  }
205
206
2.61M
  if (lhs.type_id() != rhs.type_id()) {
207
0
    return false;
208
0
  }
209
210
2.61M
  if (lhs.NumInOperands() != rhs.NumInOperands()) {
211
0
    return false;
212
0
  }
213
214
7.63M
  for (uint32_t i = 0; i < lhs.NumInOperands(); ++i) {
215
5.02M
    if (lhs.GetInOperand(i) != rhs.GetInOperand(i)) {
216
0
      return false;
217
0
    }
218
5.02M
  }
219
220
2.61M
  return lhs.context()->get_decoration_mgr()->HaveTheSameDecorations(
221
2.61M
      lhs.result_id(), rhs.result_id());
222
2.61M
}
223
224
8.87M
std::size_t ValueTableHash::operator()(const Instruction& inst) const {
225
  // We hash the opcode and in-operands, not the result, because we want
226
  // instructions that are the same except for the result to hash to the
227
  // same value.
228
8.87M
  std::u32string h;
229
8.87M
  h.push_back(uint32_t(inst.opcode()));
230
8.87M
  h.push_back(inst.type_id());
231
27.4M
  for (uint32_t i = 0; i < inst.NumInOperands(); ++i) {
232
18.5M
    const auto& opnd = inst.GetInOperand(i);
233
18.5M
    for (uint32_t word : opnd.words) {
234
18.5M
      h.push_back(word);
235
18.5M
    }
236
18.5M
  }
237
8.87M
  return std::hash<std::u32string>()(h);
238
8.87M
}
239
}  // namespace opt
240
}  // namespace spvtools