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 #include "source/util/make_unique.h"
17
18 namespace spvtools {
19 namespace opt {
20 namespace analysis {
21
22 // Don't compact before we have a reasonable number of ids allocated (~32kb).
23 static const size_t kCompactThresholdMinTotalIds = (8 * 1024);
24 // Compact when fewer than this fraction of the storage is used (should be 2^n
25 // for performance).
26 static const size_t kCompactThresholdFractionFreeIds = 8;
27
DefUseManager()28 DefUseManager::DefUseManager() {
29 use_pool_ = MakeUnique<UseListPool>();
30 used_id_pool_ = MakeUnique<UsedIdListPool>();
31 }
32
AnalyzeInstDef(Instruction * inst)33 void DefUseManager::AnalyzeInstDef(Instruction* inst) {
34 const uint32_t def_id = inst->result_id();
35 if (def_id != 0) {
36 auto iter = id_to_def_.find(def_id);
37 if (iter != id_to_def_.end()) {
38 // Clear the original instruction that defining the same result id of the
39 // new instruction.
40 ClearInst(iter->second);
41 }
42 id_to_def_[def_id] = inst;
43 } else {
44 ClearInst(inst);
45 }
46 }
47
AnalyzeInstUse(Instruction * inst)48 void DefUseManager::AnalyzeInstUse(Instruction* inst) {
49 // It might have existed before.
50 EraseUseRecordsOfOperandIds(inst);
51
52 // Create entry for the given instruction. Note that the instruction may
53 // not have any in-operands. In such cases, we still need a entry for those
54 // instructions so this manager knows it has seen the instruction later.
55 UsedIdList& used_ids =
56 inst_to_used_id_.insert({inst, UsedIdList(used_id_pool_.get())})
57 .first->second;
58
59 for (uint32_t i = 0; i < inst->NumOperands(); ++i) {
60 switch (inst->GetOperand(i).type) {
61 // For any id type but result id type
62 case SPV_OPERAND_TYPE_ID:
63 case SPV_OPERAND_TYPE_TYPE_ID:
64 case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
65 case SPV_OPERAND_TYPE_SCOPE_ID: {
66 uint32_t use_id = inst->GetSingleWordOperand(i);
67 Instruction* def = GetDef(use_id);
68 assert(def && "Definition is not registered.");
69
70 // Add to inst's use records
71 used_ids.push_back(use_id);
72
73 // Add to the users, taking care to avoid adding duplicates. We know
74 // the duplicate for this instruction will always be at the tail.
75 UseList& list = inst_to_users_.insert({def, UseList(use_pool_.get())})
76 .first->second;
77 if (list.empty() || list.back() != inst) {
78 list.push_back(inst);
79 }
80 } break;
81 default:
82 break;
83 }
84 }
85 }
86
AnalyzeInstDefUse(Instruction * inst)87 void DefUseManager::AnalyzeInstDefUse(Instruction* inst) {
88 AnalyzeInstDef(inst);
89 AnalyzeInstUse(inst);
90 // Analyze lines last otherwise they will be cleared when inst is
91 // cleared by preceding two calls
92 for (auto& l_inst : inst->dbg_line_insts()) AnalyzeInstDefUse(&l_inst);
93 }
94
UpdateDefUse(Instruction * inst)95 void DefUseManager::UpdateDefUse(Instruction* inst) {
96 const uint32_t def_id = inst->result_id();
97 if (def_id != 0) {
98 auto iter = id_to_def_.find(def_id);
99 if (iter == id_to_def_.end()) {
100 AnalyzeInstDef(inst);
101 }
102 }
103 AnalyzeInstUse(inst);
104 }
105
GetDef(uint32_t id)106 Instruction* DefUseManager::GetDef(uint32_t id) {
107 auto iter = id_to_def_.find(id);
108 if (iter == id_to_def_.end()) return nullptr;
109 return iter->second;
110 }
111
GetDef(uint32_t id) const112 const Instruction* DefUseManager::GetDef(uint32_t id) const {
113 const auto iter = id_to_def_.find(id);
114 if (iter == id_to_def_.end()) return nullptr;
115 return iter->second;
116 }
117
WhileEachUser(const Instruction * def,const std::function<bool (Instruction *)> & f) const118 bool DefUseManager::WhileEachUser(
119 const Instruction* def, const std::function<bool(Instruction*)>& f) const {
120 // Ensure that |def| has been registered.
121 assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
122 "Definition is not registered.");
123 if (!def->HasResultId()) return true;
124
125 auto iter = inst_to_users_.find(def);
126 if (iter != inst_to_users_.end()) {
127 for (Instruction* user : iter->second) {
128 if (!f(user)) return false;
129 }
130 }
131 return true;
132 }
133
WhileEachUser(uint32_t id,const std::function<bool (Instruction *)> & f) const134 bool DefUseManager::WhileEachUser(
135 uint32_t id, const std::function<bool(Instruction*)>& f) const {
136 return WhileEachUser(GetDef(id), f);
137 }
138
ForEachUser(const Instruction * def,const std::function<void (Instruction *)> & f) const139 void DefUseManager::ForEachUser(
140 const Instruction* def, const std::function<void(Instruction*)>& f) const {
141 WhileEachUser(def, [&f](Instruction* user) {
142 f(user);
143 return true;
144 });
145 }
146
ForEachUser(uint32_t id,const std::function<void (Instruction *)> & f) const147 void DefUseManager::ForEachUser(
148 uint32_t id, const std::function<void(Instruction*)>& f) const {
149 ForEachUser(GetDef(id), f);
150 }
151
WhileEachUse(const Instruction * def,const std::function<bool (Instruction *,uint32_t)> & f) const152 bool DefUseManager::WhileEachUse(
153 const Instruction* def,
154 const std::function<bool(Instruction*, uint32_t)>& f) const {
155 // Ensure that |def| has been registered.
156 assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) &&
157 "Definition is not registered.");
158 if (!def->HasResultId()) return true;
159
160 auto iter = inst_to_users_.find(def);
161 if (iter != inst_to_users_.end()) {
162 for (Instruction* user : iter->second) {
163 for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) {
164 const Operand& op = user->GetOperand(idx);
165 if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) {
166 if (def->result_id() == op.words[0]) {
167 if (!f(user, idx)) return false;
168 }
169 }
170 }
171 }
172 }
173 return true;
174 }
175
WhileEachUse(uint32_t id,const std::function<bool (Instruction *,uint32_t)> & f) const176 bool DefUseManager::WhileEachUse(
177 uint32_t id, const std::function<bool(Instruction*, uint32_t)>& f) const {
178 return WhileEachUse(GetDef(id), f);
179 }
180
ForEachUse(const Instruction * def,const std::function<void (Instruction *,uint32_t)> & f) const181 void DefUseManager::ForEachUse(
182 const Instruction* def,
183 const std::function<void(Instruction*, uint32_t)>& f) const {
184 WhileEachUse(def, [&f](Instruction* user, uint32_t index) {
185 f(user, index);
186 return true;
187 });
188 }
189
ForEachUse(uint32_t id,const std::function<void (Instruction *,uint32_t)> & f) const190 void DefUseManager::ForEachUse(
191 uint32_t id, const std::function<void(Instruction*, uint32_t)>& f) const {
192 ForEachUse(GetDef(id), f);
193 }
194
NumUsers(const Instruction * def) const195 uint32_t DefUseManager::NumUsers(const Instruction* def) const {
196 uint32_t count = 0;
197 ForEachUser(def, [&count](Instruction*) { ++count; });
198 return count;
199 }
200
NumUsers(uint32_t id) const201 uint32_t DefUseManager::NumUsers(uint32_t id) const {
202 return NumUsers(GetDef(id));
203 }
204
NumUses(const Instruction * def) const205 uint32_t DefUseManager::NumUses(const Instruction* def) const {
206 uint32_t count = 0;
207 ForEachUse(def, [&count](Instruction*, uint32_t) { ++count; });
208 return count;
209 }
210
NumUses(uint32_t id) const211 uint32_t DefUseManager::NumUses(uint32_t id) const {
212 return NumUses(GetDef(id));
213 }
214
GetAnnotations(uint32_t id) const215 std::vector<Instruction*> DefUseManager::GetAnnotations(uint32_t id) const {
216 std::vector<Instruction*> annos;
217 const Instruction* def = GetDef(id);
218 if (!def) return annos;
219
220 ForEachUser(def, [&annos](Instruction* user) {
221 if (IsAnnotationInst(user->opcode())) {
222 annos.push_back(user);
223 }
224 });
225 return annos;
226 }
227
AnalyzeDefUse(Module * module)228 void DefUseManager::AnalyzeDefUse(Module* module) {
229 if (!module) return;
230 // Analyze all the defs before any uses to catch forward references.
231 module->ForEachInst(
232 std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1),
233 true);
234 module->ForEachInst(
235 std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1),
236 true);
237 }
238
ClearInst(Instruction * inst)239 void DefUseManager::ClearInst(Instruction* inst) {
240 if (inst_to_used_id_.find(inst) != inst_to_used_id_.end()) {
241 EraseUseRecordsOfOperandIds(inst);
242 uint32_t const result_id = inst->result_id();
243 if (result_id != 0) {
244 // For each using instruction, remove result_id from their used ids.
245 auto iter = inst_to_users_.find(inst);
246 if (iter != inst_to_users_.end()) {
247 for (Instruction* use : iter->second) {
248 inst_to_used_id_.at(use).remove_first(result_id);
249 }
250 inst_to_users_.erase(iter);
251 }
252 id_to_def_.erase(inst->result_id());
253 }
254 }
255 }
256
EraseUseRecordsOfOperandIds(const Instruction * inst)257 void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) {
258 // Go through all ids used by this instruction, remove this instruction's
259 // uses of them.
260 auto iter = inst_to_used_id_.find(inst);
261 if (iter != inst_to_used_id_.end()) {
262 const UsedIdList& used_ids = iter->second;
263 for (uint32_t def_id : used_ids) {
264 auto def_iter = inst_to_users_.find(GetDef(def_id));
265 if (def_iter != inst_to_users_.end()) {
266 def_iter->second.remove_first(const_cast<Instruction*>(inst));
267 }
268 }
269 inst_to_used_id_.erase(inst);
270
271 // If we're using only a fraction of the space in used_ids_, compact storage
272 // to prevent memory usage from being unbounded.
273 if (used_id_pool_->total_nodes() > kCompactThresholdMinTotalIds &&
274 used_id_pool_->used_nodes() <
275 used_id_pool_->total_nodes() / kCompactThresholdFractionFreeIds) {
276 CompactStorage();
277 }
278 }
279 }
280
CompactStorage()281 void DefUseManager::CompactStorage() {
282 CompactUseRecords();
283 CompactUsedIds();
284 }
285
CompactUseRecords()286 void DefUseManager::CompactUseRecords() {
287 std::unique_ptr<UseListPool> new_pool = MakeUnique<UseListPool>();
288 for (auto& iter : inst_to_users_) {
289 iter.second.move_nodes(new_pool.get());
290 }
291 use_pool_ = std::move(new_pool);
292 }
293
CompactUsedIds()294 void DefUseManager::CompactUsedIds() {
295 std::unique_ptr<UsedIdListPool> new_pool = MakeUnique<UsedIdListPool>();
296 for (auto& iter : inst_to_used_id_) {
297 iter.second.move_nodes(new_pool.get());
298 }
299 used_id_pool_ = std::move(new_pool);
300 }
301
CompareAndPrintDifferences(const DefUseManager & lhs,const DefUseManager & rhs)302 bool CompareAndPrintDifferences(const DefUseManager& lhs,
303 const DefUseManager& rhs) {
304 bool same = true;
305
306 if (lhs.id_to_def_ != rhs.id_to_def_) {
307 for (auto p : lhs.id_to_def_) {
308 if (rhs.id_to_def_.find(p.first) == rhs.id_to_def_.end()) {
309 printf("Diff in id_to_def: missing value in rhs\n");
310 }
311 }
312 for (auto p : rhs.id_to_def_) {
313 if (lhs.id_to_def_.find(p.first) == lhs.id_to_def_.end()) {
314 printf("Diff in id_to_def: missing value in lhs\n");
315 }
316 }
317 same = false;
318 }
319
320 for (const auto& l : lhs.inst_to_used_id_) {
321 std::set<uint32_t> ul, ur;
322 lhs.ForEachUse(l.first,
323 [&ul](Instruction*, uint32_t id) { ul.insert(id); });
324 rhs.ForEachUse(l.first,
325 [&ur](Instruction*, uint32_t id) { ur.insert(id); });
326 if (ul.size() != ur.size()) {
327 printf(
328 "Diff in inst_to_used_id_: different number of used ids (%zu != %zu)",
329 ul.size(), ur.size());
330 same = false;
331 } else if (ul != ur) {
332 printf("Diff in inst_to_used_id_: different used ids\n");
333 same = false;
334 }
335 }
336 for (const auto& r : rhs.inst_to_used_id_) {
337 auto iter_l = lhs.inst_to_used_id_.find(r.first);
338 if (r.second.empty() &&
339 !(iter_l == lhs.inst_to_used_id_.end() || iter_l->second.empty())) {
340 printf("Diff in inst_to_used_id_: unexpected instr in rhs\n");
341 same = false;
342 }
343 }
344
345 for (const auto& l : lhs.inst_to_users_) {
346 std::set<Instruction*> ul, ur;
347 lhs.ForEachUser(l.first, [&ul](Instruction* use) { ul.insert(use); });
348 rhs.ForEachUser(l.first, [&ur](Instruction* use) { ur.insert(use); });
349 if (ul.size() != ur.size()) {
350 printf("Diff in inst_to_users_: different number of users (%zu != %zu)",
351 ul.size(), ur.size());
352 same = false;
353 } else if (ul != ur) {
354 printf("Diff in inst_to_users_: different users\n");
355 same = false;
356 }
357 }
358 for (const auto& r : rhs.inst_to_users_) {
359 auto iter_l = lhs.inst_to_users_.find(r.first);
360 if (r.second.empty() &&
361 !(iter_l == lhs.inst_to_users_.end() || iter_l->second.empty())) {
362 printf("Diff in inst_to_users_: unexpected instr in rhs\n");
363 same = false;
364 }
365 }
366 return same;
367 }
368
369 } // namespace analysis
370 } // namespace opt
371 } // namespace spvtools
372