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/def_use_manager.cpp
Line
Count
Source
1
// Copyright (c) 2016 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/def_use_manager.h"
16
17
namespace spvtools {
18
namespace opt {
19
namespace analysis {
20
21
163M
void DefUseManager::AnalyzeInstDef(Instruction* inst) {
22
163M
  const uint32_t def_id = inst->result_id();
23
163M
  if (def_id != 0) {
24
110M
    auto iter = id_to_def_.find(def_id);
25
110M
    if (iter != id_to_def_.end()) {
26
      // Clear the original instruction that defining the same result id of the
27
      // new instruction.
28
12.6k
      ClearInst(iter->second);
29
12.6k
    }
30
110M
    id_to_def_[def_id] = inst;
31
110M
  } else {
32
53.2M
    ClearInst(inst);
33
53.2M
  }
34
163M
}
35
36
181M
void DefUseManager::AnalyzeInstUse(Instruction* inst) {
37
  // Create entry for the given instruction. Note that the instruction may
38
  // not have any in-operands. In such cases, we still need a entry for those
39
  // instructions so this manager knows it has seen the instruction later.
40
181M
  auto* used_ids = &inst_to_used_ids_[inst];
41
181M
  if (used_ids->size()) {
42
11.0M
    EraseUseRecordsOfOperandIds(inst);
43
11.0M
    used_ids = &inst_to_used_ids_[inst];
44
11.0M
  }
45
181M
  used_ids->clear();  // It might have existed before.
46
47
677M
  for (uint32_t i = 0; i < inst->NumOperands(); ++i) {
48
496M
    switch (inst->GetOperand(i).type) {
49
      // For any id type but result id type
50
239M
      case SPV_OPERAND_TYPE_ID:
51
328M
      case SPV_OPERAND_TYPE_TYPE_ID:
52
328M
      case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
53
328M
      case SPV_OPERAND_TYPE_SCOPE_ID: {
54
328M
        uint32_t use_id = inst->GetSingleWordOperand(i);
55
328M
        Instruction* def = GetDef(use_id);
56
328M
        assert(def && "Definition is not registered.");
57
328M
        id_to_users_.insert(UserEntry{def, inst});
58
328M
        used_ids->push_back(use_id);
59
328M
      } break;
60
167M
      default:
61
167M
        break;
62
496M
    }
63
496M
  }
64
181M
}
65
66
2.54M
void DefUseManager::AnalyzeInstDefUse(Instruction* inst) {
67
2.54M
  AnalyzeInstDef(inst);
68
2.54M
  AnalyzeInstUse(inst);
69
  // Analyze lines last otherwise they will be cleared when inst is
70
  // cleared by preceding two calls
71
2.54M
  for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst);
72
2.54M
}
73
74
2.21M
void DefUseManager::UpdateDefUse(Instruction* inst) {
75
2.21M
  const uint32_t def_id = inst->result_id();
76
2.21M
  if (def_id != 0) {
77
1.90M
    auto iter = id_to_def_.find(def_id);
78
1.90M
    if (iter == id_to_def_.end()) {
79
0
      AnalyzeInstDef(inst);
80
0
    }
81
1.90M
  }
82
2.21M
  AnalyzeInstUse(inst);
83
2.21M
}
84
85
568M
Instruction* DefUseManager::GetDef(uint32_t id) {
86
568M
  auto iter = id_to_def_.find(id);
87
568M
  if (iter == id_to_def_.end()) return nullptr;
88
556M
  return iter->second;
89
568M
}
90
91
31.0M
const Instruction* DefUseManager::GetDef(uint32_t id) const {
92
31.0M
  const auto iter = id_to_def_.find(id);
93
31.0M
  if (iter == id_to_def_.end()) return nullptr;
94
31.0M
  return iter->second;
95
31.0M
}
96
97
DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin(
98
24.9M
    const Instruction* def) const {
99
24.9M
  return id_to_users_.lower_bound(
100
24.9M
      UserEntry{const_cast<Instruction*>(def), nullptr});
101
24.9M
}
102
103
bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
104
                                const IdToUsersMap::const_iterator& cached_end,
105
63.6M
                                const Instruction* inst) const {
106
63.6M
  return (iter != cached_end && iter->def == inst);
107
63.6M
}
108
109
bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter,
110
0
                                const Instruction* inst) const {
111
0
  return UsersNotEnd(iter, id_to_users_.end(), inst);
112
0
}
113
114
bool DefUseManager::WhileEachUser(
115
9.55M
    const Instruction* def, const std::function<bool(Instruction*)>& f) const {
116
  // Ensure that |def| has been registered.
117
9.55M
  assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
118
9.55M
         "Definition is not registered.");
119
9.55M
  if (!def->HasResultId()) return true;
120
121
9.53M
  auto end = id_to_users_.end();
122
37.0M
  for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) {
123
27.8M
    if (!f(iter->user)) return false;
124
27.8M
  }
125
9.25M
  return true;
126
9.53M
}
127
128
bool DefUseManager::WhileEachUser(
129
1.63M
    uint32_t id, const std::function<bool(Instruction*)>& f) const {
130
1.63M
  return WhileEachUser(GetDef(id), f);
131
1.63M
}
132
133
void DefUseManager::ForEachUser(
134
7.46M
    const Instruction* def, const std::function<void(Instruction*)>& f) const {
135
15.2M
  WhileEachUser(def, [&f](Instruction* user) {
136
15.2M
    f(user);
137
15.2M
    return true;
138
15.2M
  });
139
7.46M
}
140
141
void DefUseManager::ForEachUser(
142
3.95M
    uint32_t id, const std::function<void(Instruction*)>& f) const {
143
3.95M
  ForEachUser(GetDef(id), f);
144
3.95M
}
145
146
bool DefUseManager::WhileEachUse(
147
    const Instruction* def,
148
8.21M
    const std::function<bool(Instruction*, uint32_t)>& f) const {
149
  // Ensure that |def| has been registered.
150
8.21M
  assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
151
8.21M
         "Definition is not registered.");
152
8.21M
  if (!def->HasResultId()) return true;
153
154
8.21M
  auto end = id_to_users_.end();
155
16.5M
  for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) {
156
8.48M
    Instruction* user = iter->user;
157
40.8M
    for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) {
158
32.4M
      const Operand& op = user->GetOperand(idx);
159
32.4M
      if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) {
160
27.1M
        if (def->result_id() == op.words[0]) {
161
8.63M
          if (!f(user, idx)) return false;
162
8.63M
        }
163
27.1M
      }
164
32.4M
    }
165
8.48M
  }
166
8.07M
  return true;
167
8.21M
}
168
169
bool DefUseManager::WhileEachUse(
170
3.68M
    uint32_t id, const std::function<bool(Instruction*, uint32_t)>& f) const {
171
3.68M
  return WhileEachUse(GetDef(id), f);
172
3.68M
}
173
174
void DefUseManager::ForEachUse(
175
    const Instruction* def,
176
4.52M
    const std::function<void(Instruction*, uint32_t)>& f) const {
177
5.68M
  WhileEachUse(def, [&f](Instruction* user, uint32_t index) {
178
5.68M
    f(user, index);
179
5.68M
    return true;
180
5.68M
  });
181
4.52M
}
182
183
void DefUseManager::ForEachUse(
184
3.99M
    uint32_t id, const std::function<void(Instruction*, uint32_t)>& f) const {
185
3.99M
  ForEachUse(GetDef(id), f);
186
3.99M
}
187
188
96.9k
uint32_t DefUseManager::NumUsers(const Instruction* def) const {
189
96.9k
  uint32_t count = 0;
190
285k
  ForEachUser(def, [&count](Instruction*) { ++count; });
191
96.9k
  return count;
192
96.9k
}
193
194
0
uint32_t DefUseManager::NumUsers(uint32_t id) const {
195
0
  return NumUsers(GetDef(id));
196
0
}
197
198
19.0k
uint32_t DefUseManager::NumUses(const Instruction* def) const {
199
19.0k
  uint32_t count = 0;
200
116k
  ForEachUse(def, [&count](Instruction*, uint32_t) { ++count; });
201
19.0k
  return count;
202
19.0k
}
203
204
0
uint32_t DefUseManager::NumUses(uint32_t id) const {
205
0
  return NumUses(GetDef(id));
206
0
}
207
208
0
std::vector<Instruction*> DefUseManager::GetAnnotations(uint32_t id) const {
209
0
  std::vector<Instruction*> annos;
210
0
  const Instruction* def = GetDef(id);
211
0
  if (!def) return annos;
212
213
0
  ForEachUser(def, [&annos](Instruction* user) {
214
0
    if (IsAnnotationInst(user->opcode())) {
215
0
      annos.push_back(user);
216
0
    }
217
0
  });
218
0
  return annos;
219
0
}
220
221
591k
void DefUseManager::AnalyzeDefUse(Module* module) {
222
591k
  if (!module) return;
223
  // Analyze all the defs before any uses to catch forward references.
224
591k
  module->ForEachInst(
225
591k
      std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1),
226
591k
      true);
227
591k
  module->ForEachInst(
228
591k
      std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1),
229
591k
      true);
230
591k
}
231
232
63.0M
void DefUseManager::ClearInst(Instruction* inst) {
233
63.0M
  auto iter = inst_to_used_ids_.find(inst);
234
63.0M
  if (iter != inst_to_used_ids_.end()) {
235
9.61M
    EraseUseRecordsOfOperandIds(inst);
236
9.61M
    if (inst->result_id() != 0) {
237
      // Remove all uses of this inst.
238
7.18M
      auto users_begin = UsersBegin(inst);
239
7.18M
      auto end = id_to_users_.end();
240
7.18M
      auto new_end = users_begin;
241
9.99M
      for (; UsersNotEnd(new_end, end, inst); ++new_end) {
242
2.80M
      }
243
7.18M
      id_to_users_.erase(users_begin, new_end);
244
7.18M
      id_to_def_.erase(inst->result_id());
245
7.18M
    }
246
9.61M
  }
247
63.0M
}
248
249
25.4M
void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) {
250
  // Go through all ids used by this instruction, remove this instruction's
251
  // uses of them.
252
25.4M
  auto iter = inst_to_used_ids_.find(inst);
253
25.4M
  if (iter != inst_to_used_ids_.end()) {
254
68.0M
    for (auto use_id : iter->second) {
255
68.0M
      id_to_users_.erase(
256
68.0M
          UserEntry{GetDef(use_id), const_cast<Instruction*>(inst)});
257
68.0M
    }
258
25.4M
    inst_to_used_ids_.erase(iter);
259
25.4M
  }
260
25.4M
}
261
262
bool CompareAndPrintDifferences(const DefUseManager& lhs,
263
554k
                                const DefUseManager& rhs) {
264
554k
  bool same = true;
265
266
554k
  if (lhs.id_to_def_ != rhs.id_to_def_) {
267
0
    for (auto p : lhs.id_to_def_) {
268
0
      if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) {
269
0
        printf("Diff in id_to_def: missing value in rhs\n");
270
0
      }
271
0
    }
272
0
    for (auto p : rhs.id_to_def_) {
273
0
      if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) {
274
0
        printf("Diff in id_to_def: missing value in lhs\n");
275
0
      }
276
0
    }
277
0
    same = false;
278
0
  }
279
280
554k
  if (lhs.id_to_users_ != rhs.id_to_users_) {
281
0
    for (auto p : lhs.id_to_users_) {
282
0
      if (rhs.id_to_users_.count(p) == 0) {
283
0
        printf("Diff in id_to_users: missing value in rhs\n");
284
0
      }
285
0
    }
286
0
    for (auto p : rhs.id_to_users_) {
287
0
      if (lhs.id_to_users_.count(p) == 0) {
288
0
        printf("Diff in id_to_users: missing value in lhs\n");
289
0
      }
290
0
    }
291
0
    same = false;
292
0
  }
293
294
554k
  if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) {
295
0
    for (auto p : lhs.inst_to_used_ids_) {
296
0
      if (rhs.inst_to_used_ids_.count(p.first) == 0) {
297
0
        printf("Diff in inst_to_used_ids: missing value in rhs\n");
298
0
      }
299
0
    }
300
0
    for (auto p : rhs.inst_to_used_ids_) {
301
0
      if (lhs.inst_to_used_ids_.count(p.first) == 0) {
302
0
        printf("Diff in inst_to_used_ids: missing value in lhs\n");
303
0
      }
304
0
    }
305
0
    same = false;
306
0
  }
307
308
554k
  return same;
309
554k
}
310
311
}  // namespace analysis
312
}  // namespace opt
313
}  // namespace spvtools