• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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