/src/spirv-tools/source/opt/ir_context.h
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 | | #ifndef SOURCE_OPT_IR_CONTEXT_H_ |
16 | | #define SOURCE_OPT_IR_CONTEXT_H_ |
17 | | |
18 | | #include <algorithm> |
19 | | #include <iostream> |
20 | | #include <limits> |
21 | | #include <map> |
22 | | #include <memory> |
23 | | #include <queue> |
24 | | #include <unordered_map> |
25 | | #include <unordered_set> |
26 | | #include <utility> |
27 | | #include <vector> |
28 | | |
29 | | #include "source/assembly_grammar.h" |
30 | | #include "source/enum_string_mapping.h" |
31 | | #include "source/opt/cfg.h" |
32 | | #include "source/opt/constants.h" |
33 | | #include "source/opt/debug_info_manager.h" |
34 | | #include "source/opt/decoration_manager.h" |
35 | | #include "source/opt/def_use_manager.h" |
36 | | #include "source/opt/dominator_analysis.h" |
37 | | #include "source/opt/feature_manager.h" |
38 | | #include "source/opt/fold.h" |
39 | | #include "source/opt/liveness.h" |
40 | | #include "source/opt/loop_descriptor.h" |
41 | | #include "source/opt/module.h" |
42 | | #include "source/opt/register_pressure.h" |
43 | | #include "source/opt/scalar_analysis.h" |
44 | | #include "source/opt/struct_cfg_analysis.h" |
45 | | #include "source/opt/type_manager.h" |
46 | | #include "source/opt/value_number_table.h" |
47 | | #include "source/util/make_unique.h" |
48 | | #include "source/util/string_utils.h" |
49 | | |
50 | | namespace spvtools { |
51 | | namespace opt { |
52 | | |
53 | | class IRContext { |
54 | | public: |
55 | | // Available analyses. |
56 | | // |
57 | | // When adding a new analysis: |
58 | | // |
59 | | // 1. Enum values should be powers of 2. These are cast into uint32_t |
60 | | // bitmasks, so we can have at most 31 analyses represented. |
61 | | // |
62 | | // 2. Make sure it gets invalidated or preserved by IRContext methods that add |
63 | | // or remove IR elements (e.g., KillDef, KillInst, ReplaceAllUsesWith). |
64 | | // |
65 | | // 3. Add handling code in BuildInvalidAnalyses and InvalidateAnalyses |
66 | | enum Analysis { |
67 | | kAnalysisNone = 0 << 0, |
68 | | kAnalysisBegin = 1 << 0, |
69 | | kAnalysisDefUse = kAnalysisBegin, |
70 | | kAnalysisInstrToBlockMapping = 1 << 1, |
71 | | kAnalysisDecorations = 1 << 2, |
72 | | kAnalysisCombinators = 1 << 3, |
73 | | kAnalysisCFG = 1 << 4, |
74 | | kAnalysisDominatorAnalysis = 1 << 5, |
75 | | kAnalysisLoopAnalysis = 1 << 6, |
76 | | kAnalysisNameMap = 1 << 7, |
77 | | kAnalysisScalarEvolution = 1 << 8, |
78 | | kAnalysisRegisterPressure = 1 << 9, |
79 | | kAnalysisValueNumberTable = 1 << 10, |
80 | | kAnalysisStructuredCFG = 1 << 11, |
81 | | kAnalysisBuiltinVarId = 1 << 12, |
82 | | kAnalysisIdToFuncMapping = 1 << 13, |
83 | | kAnalysisConstants = 1 << 14, |
84 | | kAnalysisTypes = 1 << 15, |
85 | | kAnalysisDebugInfo = 1 << 16, |
86 | | kAnalysisLiveness = 1 << 17, |
87 | | kAnalysisEnd = 1 << 18 |
88 | | }; |
89 | | |
90 | | using ProcessFunction = std::function<bool(Function*)>; |
91 | | |
92 | | friend inline Analysis operator|(Analysis lhs, Analysis rhs); |
93 | | friend inline Analysis& operator|=(Analysis& lhs, Analysis rhs); |
94 | | friend inline Analysis operator<<(Analysis a, int shift); |
95 | | friend inline Analysis& operator<<=(Analysis& a, int shift); |
96 | | |
97 | | // Creates an |IRContext| that contains an owned |Module| |
98 | | IRContext(spv_target_env env, MessageConsumer c) |
99 | | : syntax_context_(spvContextCreate(env)), |
100 | | grammar_(syntax_context_), |
101 | | unique_id_(0), |
102 | | module_(new Module()), |
103 | | consumer_(std::move(c)), |
104 | | def_use_mgr_(nullptr), |
105 | | feature_mgr_(nullptr), |
106 | | valid_analyses_(kAnalysisNone), |
107 | | constant_mgr_(nullptr), |
108 | | type_mgr_(nullptr), |
109 | | id_to_name_(nullptr), |
110 | | max_id_bound_(kDefaultMaxIdBound), |
111 | | preserve_bindings_(false), |
112 | 34.4k | preserve_spec_constants_(false) { |
113 | 34.4k | SetContextMessageConsumer(syntax_context_, consumer_); |
114 | 34.4k | module_->SetContext(this); |
115 | 34.4k | } |
116 | | |
117 | | IRContext(spv_target_env env, std::unique_ptr<Module>&& m, MessageConsumer c) |
118 | | : syntax_context_(spvContextCreate(env)), |
119 | | grammar_(syntax_context_), |
120 | | unique_id_(0), |
121 | | module_(std::move(m)), |
122 | | consumer_(std::move(c)), |
123 | | def_use_mgr_(nullptr), |
124 | | feature_mgr_(nullptr), |
125 | | valid_analyses_(kAnalysisNone), |
126 | | type_mgr_(nullptr), |
127 | | id_to_name_(nullptr), |
128 | | max_id_bound_(kDefaultMaxIdBound), |
129 | | preserve_bindings_(false), |
130 | 0 | preserve_spec_constants_(false) { |
131 | 0 | SetContextMessageConsumer(syntax_context_, consumer_); |
132 | 0 | module_->SetContext(this); |
133 | 0 | InitializeCombinators(); |
134 | 0 | } |
135 | | |
136 | 34.4k | ~IRContext() { spvContextDestroy(syntax_context_); } |
137 | | |
138 | 20.4M | Module* module() const { return module_.get(); } |
139 | | |
140 | | // Returns a vector of pointers to constant-creation instructions in this |
141 | | // context. |
142 | | inline std::vector<Instruction*> GetConstants(); |
143 | | inline std::vector<const Instruction*> GetConstants() const; |
144 | | |
145 | | // Iterators for annotation instructions contained in this context. |
146 | | inline Module::inst_iterator annotation_begin(); |
147 | | inline Module::inst_iterator annotation_end(); |
148 | | inline IteratorRange<Module::inst_iterator> annotations(); |
149 | | inline IteratorRange<Module::const_inst_iterator> annotations() const; |
150 | | |
151 | | // Iterators for capabilities instructions contained in this module. |
152 | | inline Module::inst_iterator capability_begin(); |
153 | | inline Module::inst_iterator capability_end(); |
154 | | inline IteratorRange<Module::inst_iterator> capabilities(); |
155 | | inline IteratorRange<Module::const_inst_iterator> capabilities() const; |
156 | | |
157 | | // Iterators for extensions instructions contained in this module. |
158 | | inline Module::inst_iterator extension_begin(); |
159 | | inline Module::inst_iterator extension_end(); |
160 | | inline IteratorRange<Module::inst_iterator> extensions(); |
161 | | inline IteratorRange<Module::const_inst_iterator> extensions() const; |
162 | | |
163 | | // Iterators for types, constants and global variables instructions. |
164 | | inline Module::inst_iterator types_values_begin(); |
165 | | inline Module::inst_iterator types_values_end(); |
166 | | inline IteratorRange<Module::inst_iterator> types_values(); |
167 | | inline IteratorRange<Module::const_inst_iterator> types_values() const; |
168 | | |
169 | | // Iterators for ext_inst import instructions contained in this module. |
170 | | inline Module::inst_iterator ext_inst_import_begin(); |
171 | | inline Module::inst_iterator ext_inst_import_end(); |
172 | | inline IteratorRange<Module::inst_iterator> ext_inst_imports(); |
173 | | inline IteratorRange<Module::const_inst_iterator> ext_inst_imports() const; |
174 | | |
175 | | // There are several kinds of debug instructions, according to where they can |
176 | | // appear in the logical layout of a module: |
177 | | // - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued |
178 | | // - Section 7b: OpName, OpMemberName |
179 | | // - Section 7c: OpModuleProcessed |
180 | | // - Mostly anywhere: OpLine and OpNoLine |
181 | | // |
182 | | |
183 | | // Iterators for debug 1 instructions (excluding OpLine & OpNoLine) contained |
184 | | // in this module. These are for layout section 7a. |
185 | | inline Module::inst_iterator debug1_begin(); |
186 | | inline Module::inst_iterator debug1_end(); |
187 | | inline IteratorRange<Module::inst_iterator> debugs1(); |
188 | | inline IteratorRange<Module::const_inst_iterator> debugs1() const; |
189 | | |
190 | | // Iterators for debug 2 instructions (excluding OpLine & OpNoLine) contained |
191 | | // in this module. These are for layout section 7b. |
192 | | inline Module::inst_iterator debug2_begin(); |
193 | | inline Module::inst_iterator debug2_end(); |
194 | | inline IteratorRange<Module::inst_iterator> debugs2(); |
195 | | inline IteratorRange<Module::const_inst_iterator> debugs2() const; |
196 | | |
197 | | // Iterators for debug 3 instructions (excluding OpLine & OpNoLine) contained |
198 | | // in this module. These are for layout section 7c. |
199 | | inline Module::inst_iterator debug3_begin(); |
200 | | inline Module::inst_iterator debug3_end(); |
201 | | inline IteratorRange<Module::inst_iterator> debugs3(); |
202 | | inline IteratorRange<Module::const_inst_iterator> debugs3() const; |
203 | | |
204 | | // Iterators for debug info instructions (excluding OpLine & OpNoLine) |
205 | | // contained in this module. These are OpExtInst & |
206 | | // OpExtInstWithForwardRefsKHR for DebugInfo extension placed between section |
207 | | // 9 and 10. |
208 | | inline Module::inst_iterator ext_inst_debuginfo_begin(); |
209 | | inline Module::inst_iterator ext_inst_debuginfo_end(); |
210 | | inline IteratorRange<Module::inst_iterator> ext_inst_debuginfo(); |
211 | | inline IteratorRange<Module::const_inst_iterator> ext_inst_debuginfo() const; |
212 | | |
213 | | // Add |capability| to the module, if it is not already enabled. |
214 | | inline void AddCapability(spv::Capability capability); |
215 | | // Appends a capability instruction to this module. |
216 | | inline void AddCapability(std::unique_ptr<Instruction>&& c); |
217 | | // Removes instruction declaring `capability` from this module. |
218 | | // Returns true if the capability was removed, false otherwise. |
219 | | bool RemoveCapability(spv::Capability capability); |
220 | | |
221 | | // Appends an extension instruction to this module. |
222 | | inline void AddExtension(const std::string& ext_name); |
223 | | inline void AddExtension(std::unique_ptr<Instruction>&& e); |
224 | | // Removes instruction declaring `extension` from this module. |
225 | | // Returns true if the extension was removed, false otherwise. |
226 | | bool RemoveExtension(Extension extension); |
227 | | |
228 | | // Appends an extended instruction set instruction to this module. |
229 | | inline void AddExtInstImport(const std::string& name); |
230 | | inline void AddExtInstImport(std::unique_ptr<Instruction>&& e); |
231 | | // Set the memory model for this module. |
232 | | inline void SetMemoryModel(std::unique_ptr<Instruction>&& m); |
233 | | // Get the memory model for this module. |
234 | | inline const Instruction* GetMemoryModel() const; |
235 | | // Appends an entry point instruction to this module. |
236 | | inline void AddEntryPoint(std::unique_ptr<Instruction>&& e); |
237 | | // Appends an execution mode instruction to this module. |
238 | | inline void AddExecutionMode(std::unique_ptr<Instruction>&& e); |
239 | | // Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module. |
240 | | // "debug 1" instructions are the ones in layout section 7.a), see section |
241 | | // 2.4 Logical Layout of a Module from the SPIR-V specification. |
242 | | inline void AddDebug1Inst(std::unique_ptr<Instruction>&& d); |
243 | | // Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module. |
244 | | // "debug 2" instructions are the ones in layout section 7.b), see section |
245 | | // 2.4 Logical Layout of a Module from the SPIR-V specification. |
246 | | inline void AddDebug2Inst(std::unique_ptr<Instruction>&& d); |
247 | | // Appends a debug 3 instruction (OpModuleProcessed) to this module. |
248 | | // This is due to decision by the SPIR Working Group, pending publication. |
249 | | inline void AddDebug3Inst(std::unique_ptr<Instruction>&& d); |
250 | | // Appends a OpExtInst for DebugInfo to this module. |
251 | | inline void AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d); |
252 | | // Appends an annotation instruction to this module. |
253 | | inline void AddAnnotationInst(std::unique_ptr<Instruction>&& a); |
254 | | // Appends a type-declaration instruction to this module. |
255 | | inline void AddType(std::unique_ptr<Instruction>&& t); |
256 | | // Appends a constant, global variable, or OpUndef instruction to this module. |
257 | | inline void AddGlobalValue(std::unique_ptr<Instruction>&& v); |
258 | | // Prepends a function declaration to this module. |
259 | | inline void AddFunctionDeclaration(std::unique_ptr<Function>&& f); |
260 | | // Appends a function to this module. |
261 | | inline void AddFunction(std::unique_ptr<Function>&& f); |
262 | | |
263 | | // Returns a pointer to a def-use manager. If the def-use manager is |
264 | | // invalid, it is rebuilt first. |
265 | 332M | analysis::DefUseManager* get_def_use_mgr() { |
266 | 332M | if (!AreAnalysesValid(kAnalysisDefUse)) { |
267 | 17.2k | BuildDefUseManager(); |
268 | 17.2k | } |
269 | 332M | return def_use_mgr_.get(); |
270 | 332M | } |
271 | | |
272 | | // Returns a pointer to a liveness manager. If the liveness manager is |
273 | | // invalid, it is rebuilt first. |
274 | 0 | analysis::LivenessManager* get_liveness_mgr() { |
275 | 0 | if (!AreAnalysesValid(kAnalysisLiveness)) { |
276 | 0 | BuildLivenessManager(); |
277 | 0 | } |
278 | 0 | return liveness_mgr_.get(); |
279 | 0 | } |
280 | | |
281 | | // Returns a pointer to a value number table. If the liveness analysis is |
282 | | // invalid, it is rebuilt first. |
283 | 6.86k | ValueNumberTable* GetValueNumberTable() { |
284 | 6.86k | if (!AreAnalysesValid(kAnalysisValueNumberTable)) { |
285 | 6.86k | BuildValueNumberTable(); |
286 | 6.86k | } |
287 | 6.86k | return vn_table_.get(); |
288 | 6.86k | } |
289 | | |
290 | | // Returns a pointer to a StructuredCFGAnalysis. If the analysis is invalid, |
291 | | // it is rebuilt first. |
292 | 18.9M | StructuredCFGAnalysis* GetStructuredCFGAnalysis() { |
293 | 18.9M | if (!AreAnalysesValid(kAnalysisStructuredCFG)) { |
294 | 42.6k | BuildStructuredCFGAnalysis(); |
295 | 42.6k | } |
296 | 18.9M | return struct_cfg_analysis_.get(); |
297 | 18.9M | } |
298 | | |
299 | | // Returns a pointer to a liveness analysis. If the liveness analysis is |
300 | | // invalid, it is rebuilt first. |
301 | 0 | LivenessAnalysis* GetLivenessAnalysis() { |
302 | 0 | if (!AreAnalysesValid(kAnalysisRegisterPressure)) { |
303 | 0 | BuildRegPressureAnalysis(); |
304 | 0 | } |
305 | 0 | return reg_pressure_.get(); |
306 | 0 | } |
307 | | |
308 | | // Returns the basic block for instruction |instr|. Re-builds the instruction |
309 | | // block map, if needed. |
310 | 218M | BasicBlock* get_instr_block(Instruction* instr) { |
311 | 218M | if (!AreAnalysesValid(kAnalysisInstrToBlockMapping)) { |
312 | 18.1k | BuildInstrToBlockMapping(); |
313 | 18.1k | } |
314 | 218M | auto entry = instr_to_block_.find(instr); |
315 | 218M | return (entry != instr_to_block_.end()) ? entry->second : nullptr; |
316 | 218M | } |
317 | | |
318 | | // Returns the basic block for |id|. Re-builds the instruction block map, if |
319 | | // needed. |
320 | | // |
321 | | // |id| must be a registered definition. |
322 | 26.8M | BasicBlock* get_instr_block(uint32_t id) { |
323 | 26.8M | Instruction* def = get_def_use_mgr()->GetDef(id); |
324 | 26.8M | return get_instr_block(def); |
325 | 26.8M | } |
326 | | |
327 | | // Sets the basic block for |inst|. Re-builds the mapping if it has become |
328 | | // invalid. |
329 | 7.14M | void set_instr_block(Instruction* inst, BasicBlock* block) { |
330 | 7.14M | if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { |
331 | 7.14M | instr_to_block_[inst] = block; |
332 | 7.14M | } |
333 | 7.14M | } |
334 | | |
335 | | // Returns a pointer the decoration manager. If the decoration manager is |
336 | | // invalid, it is rebuilt first. |
337 | 64.4M | analysis::DecorationManager* get_decoration_mgr() { |
338 | 64.4M | if (!AreAnalysesValid(kAnalysisDecorations)) { |
339 | 79.3k | BuildDecorationManager(); |
340 | 79.3k | } |
341 | 64.4M | return decoration_mgr_.get(); |
342 | 64.4M | } |
343 | | |
344 | | // Returns a pointer to the constant manager. If no constant manager has been |
345 | | // created yet, it creates one. NOTE: Once created, the constant manager |
346 | | // remains active and it is never re-built. |
347 | 64.0M | analysis::ConstantManager* get_constant_mgr() { |
348 | 64.0M | if (!AreAnalysesValid(kAnalysisConstants)) { |
349 | 18.0k | BuildConstantManager(); |
350 | 18.0k | } |
351 | 64.0M | return constant_mgr_.get(); |
352 | 64.0M | } |
353 | | |
354 | | // Returns a pointer to the type manager. If no type manager has been created |
355 | | // yet, it creates one. NOTE: Once created, the type manager remains active it |
356 | | // is never re-built. |
357 | 42.4M | analysis::TypeManager* get_type_mgr() { |
358 | 42.4M | if (!AreAnalysesValid(kAnalysisTypes)) { |
359 | 17.9k | BuildTypeManager(); |
360 | 17.9k | } |
361 | 42.4M | return type_mgr_.get(); |
362 | 42.4M | } |
363 | | |
364 | | // Returns a pointer to the debug information manager. If no debug |
365 | | // information manager has been created yet, it creates one. |
366 | | // NOTE: Once created, the debug information manager remains active |
367 | | // it is never re-built. |
368 | 53.4M | analysis::DebugInfoManager* get_debug_info_mgr() { |
369 | 53.4M | if (!AreAnalysesValid(kAnalysisDebugInfo)) { |
370 | 13.4k | BuildDebugInfoManager(); |
371 | 13.4k | } |
372 | 53.4M | return debug_info_mgr_.get(); |
373 | 53.4M | } |
374 | | |
375 | | // Returns a pointer to the scalar evolution analysis. If it is invalid it |
376 | | // will be rebuilt first. |
377 | 0 | ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() { |
378 | 0 | if (!AreAnalysesValid(kAnalysisScalarEvolution)) { |
379 | 0 | BuildScalarEvolutionAnalysis(); |
380 | 0 | } |
381 | 0 | return scalar_evolution_analysis_.get(); |
382 | 0 | } |
383 | | |
384 | | // Build the map from the ids to the OpName and OpMemberName instruction |
385 | | // associated with it. |
386 | | inline void BuildIdToNameMap(); |
387 | | |
388 | | // Returns a range of instrucions that contain all of the OpName and |
389 | | // OpMemberNames associated with the given id. |
390 | | inline IteratorRange<std::multimap<uint32_t, Instruction*>::iterator> |
391 | | GetNames(uint32_t id); |
392 | | |
393 | | // Returns an OpMemberName instruction that targets |struct_type_id| at |
394 | | // index |index|. Returns nullptr if no such instruction exists. |
395 | | // While the SPIR-V spec does not prohibit having multiple OpMemberName |
396 | | // instructions for the same structure member, it is hard to imagine a member |
397 | | // having more than one name. This method returns the first one it finds. |
398 | | inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index); |
399 | | |
400 | | // Copy names from |old_id| to |new_id|. Only copy member name if index is |
401 | | // less than |max_member_index|. |
402 | | inline void CloneNames(const uint32_t old_id, const uint32_t new_id, |
403 | | const uint32_t max_member_index = UINT32_MAX); |
404 | | |
405 | | // Sets the message consumer to the given |consumer|. |consumer| which will be |
406 | | // invoked every time there is a message to be communicated to the outside. |
407 | 0 | void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } |
408 | | |
409 | | // Returns the reference to the message consumer for this pass. |
410 | 17.9k | const MessageConsumer& consumer() const { return consumer_; } |
411 | | |
412 | | // Rebuilds the analyses in |set| that are invalid. |
413 | | void BuildInvalidAnalyses(Analysis set); |
414 | | |
415 | | // Invalidates all of the analyses except for those in |preserved_analyses|. |
416 | | void InvalidateAnalysesExceptFor(Analysis preserved_analyses); |
417 | | |
418 | | // Invalidates the analyses marked in |analyses_to_invalidate|. |
419 | | void InvalidateAnalyses(Analysis analyses_to_invalidate); |
420 | | |
421 | | // Deletes the instruction defining the given |id|. Returns true on |
422 | | // success, false if the given |id| is not defined at all. This method also |
423 | | // erases the name, decorations, and definition of |id|. |
424 | | // |
425 | | // Pointers and iterators pointing to the deleted instructions become invalid. |
426 | | // However other pointers and iterators are still valid. |
427 | | bool KillDef(uint32_t id); |
428 | | |
429 | | // Deletes the given instruction |inst|. This method erases the |
430 | | // information of the given instruction's uses of its operands. If |inst| |
431 | | // defines a result id, its name and decorations will also be deleted. |
432 | | // |
433 | | // Pointer and iterator pointing to the deleted instructions become invalid. |
434 | | // However other pointers and iterators are still valid. |
435 | | // |
436 | | // Note that if an instruction is not in an instruction list, the memory may |
437 | | // not be safe to delete, so the instruction is turned into a OpNop instead. |
438 | | // This can happen with OpLabel. |
439 | | // |
440 | | // Returns a pointer to the instruction after |inst| or |nullptr| if no such |
441 | | // instruction exists. |
442 | | Instruction* KillInst(Instruction* inst); |
443 | | |
444 | | // Deletes all the instruction in the range [`begin`; `end`[, for which the |
445 | | // unary predicate `condition` returned true. |
446 | | // Returns true if at least one instruction was removed, false otherwise. |
447 | | // |
448 | | // Pointer and iterator pointing to the deleted instructions become invalid. |
449 | | // However other pointers and iterators are still valid. |
450 | | bool KillInstructionIf(Module::inst_iterator begin, Module::inst_iterator end, |
451 | | std::function<bool(Instruction*)> condition); |
452 | | |
453 | | // Collects the non-semantic instruction tree that uses |inst|'s result id |
454 | | // to be killed later. |
455 | | void CollectNonSemanticTree(Instruction* inst, |
456 | | std::unordered_set<Instruction*>* to_kill); |
457 | | |
458 | | // Collect function reachable from |entryId|, returns |funcs| |
459 | | void CollectCallTreeFromRoots(unsigned entryId, |
460 | | std::unordered_set<uint32_t>* funcs); |
461 | | |
462 | | // Returns true if all of the given analyses are valid. |
463 | 1.17G | bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; } |
464 | | |
465 | | // Replaces all uses of |before| id with |after| id. Returns true if any |
466 | | // replacement happens. This method does not kill the definition of the |
467 | | // |before| id. If |after| is the same as |before|, does nothing and returns |
468 | | // false. |
469 | | // |
470 | | // |before| and |after| must be registered definitions in the DefUseManager. |
471 | | bool ReplaceAllUsesWith(uint32_t before, uint32_t after); |
472 | | |
473 | | // Replace all uses of |before| id with |after| id if those uses |
474 | | // (instruction) return true for |predicate|. Returns true if |
475 | | // any replacement happens. This method does not kill the definition of the |
476 | | // |before| id. If |after| is the same as |before|, does nothing and return |
477 | | // false. |
478 | | bool ReplaceAllUsesWithPredicate( |
479 | | uint32_t before, uint32_t after, |
480 | | const std::function<bool(Instruction*)>& predicate); |
481 | | |
482 | | // Returns true if all of the analyses that are suppose to be valid are |
483 | | // actually valid. |
484 | | bool IsConsistent(); |
485 | | |
486 | | // The IRContext will look at the def and uses of |inst| and update any valid |
487 | | // analyses will be updated accordingly. |
488 | | inline void AnalyzeDefUse(Instruction* inst); |
489 | | |
490 | | // Informs the IRContext that the uses of |inst| are going to change, and that |
491 | | // is should forget everything it know about the current uses. Any valid |
492 | | // analyses will be updated accordingly. |
493 | | void ForgetUses(Instruction* inst); |
494 | | |
495 | | // The IRContext will look at the uses of |inst| and update any valid analyses |
496 | | // will be updated accordingly. |
497 | | void AnalyzeUses(Instruction* inst); |
498 | | |
499 | | // Kill all name and decorate ops targeting |id|. |
500 | | void KillNamesAndDecorates(uint32_t id); |
501 | | |
502 | | // Kill all name and decorate ops targeting the result id of |inst|. |
503 | | void KillNamesAndDecorates(Instruction* inst); |
504 | | |
505 | | // Change operands of debug instruction to DebugInfoNone. |
506 | | void KillOperandFromDebugInstructions(Instruction* inst); |
507 | | |
508 | | // Returns the next unique id for use by an instruction. |
509 | 60.7M | inline uint32_t TakeNextUniqueId() { |
510 | 60.7M | assert(unique_id_ != std::numeric_limits<uint32_t>::max()); |
511 | | |
512 | | // Skip zero. |
513 | 60.7M | return ++unique_id_; |
514 | 60.7M | } |
515 | | |
516 | | // Returns true if |inst| is a combinator in the current context. |
517 | | // |combinator_ops_| is built if it has not been already. |
518 | 23.8M | inline bool IsCombinatorInstruction(const Instruction* inst) { |
519 | 23.8M | if (!AreAnalysesValid(kAnalysisCombinators)) { |
520 | 27.6k | InitializeCombinators(); |
521 | 27.6k | } |
522 | 23.8M | constexpr uint32_t kExtInstSetIdInIndx = 0; |
523 | 23.8M | constexpr uint32_t kExtInstInstructionInIndx = 1; |
524 | | |
525 | 23.8M | if (inst->opcode() != spv::Op::OpExtInst) { |
526 | 23.6M | return combinator_ops_[0].count(uint32_t(inst->opcode())) != 0; |
527 | 23.6M | } else { |
528 | 187k | uint32_t set = inst->GetSingleWordInOperand(kExtInstSetIdInIndx); |
529 | 187k | auto op = inst->GetSingleWordInOperand(kExtInstInstructionInIndx); |
530 | 187k | return combinator_ops_[set].count(op) != 0; |
531 | 187k | } |
532 | 23.8M | } |
533 | | |
534 | | // Returns a pointer to the CFG for all the functions in |module_|. |
535 | 51.3M | CFG* cfg() { |
536 | 51.3M | if (!AreAnalysesValid(kAnalysisCFG)) { |
537 | 47.8k | BuildCFG(); |
538 | 47.8k | } |
539 | 51.3M | return cfg_.get(); |
540 | 51.3M | } |
541 | | |
542 | | // Gets the loop descriptor for function |f|. |
543 | | LoopDescriptor* GetLoopDescriptor(const Function* f); |
544 | | |
545 | | // Gets the dominator analysis for function |f|. |
546 | | DominatorAnalysis* GetDominatorAnalysis(const Function* f); |
547 | | |
548 | | // Gets the postdominator analysis for function |f|. |
549 | | PostDominatorAnalysis* GetPostDominatorAnalysis(const Function* f); |
550 | | |
551 | | // Remove the dominator tree of |f| from the cache. |
552 | 3.36k | inline void RemoveDominatorAnalysis(const Function* f) { |
553 | 3.36k | dominator_trees_.erase(f); |
554 | 3.36k | } |
555 | | |
556 | | // Remove the postdominator tree of |f| from the cache. |
557 | 0 | inline void RemovePostDominatorAnalysis(const Function* f) { |
558 | 0 | post_dominator_trees_.erase(f); |
559 | 0 | } |
560 | | |
561 | | // Return the next available SSA id and increment it. Returns 0 if the |
562 | | // maximum SSA id has been reached. |
563 | 15.3M | inline uint32_t TakeNextId() { |
564 | 15.3M | uint32_t next_id = module()->TakeNextIdBound(); |
565 | 15.3M | if (next_id == 0) { |
566 | 0 | if (consumer()) { |
567 | 0 | std::string message = "ID overflow. Try running compact-ids."; |
568 | 0 | consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); |
569 | 0 | } |
570 | 0 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
571 | | // If TakeNextId returns 0, it is very likely that execution will |
572 | | // subsequently fail. Such failures are false alarms from a fuzzing point |
573 | | // of view: they are due to the fact that too many ids were used, rather |
574 | | // than being due to an actual bug. Thus, during a fuzzing build, it is |
575 | | // preferable to bail out when ID overflow occurs. |
576 | | // |
577 | | // A zero exit code is returned here because a non-zero code would cause |
578 | | // ClusterFuzz/OSS-Fuzz to regard the termination as a crash, and spurious |
579 | | // crash reports is what this guard aims to avoid. |
580 | 0 | exit(0); |
581 | 0 | #endif |
582 | 0 | } |
583 | 15.3M | return next_id; |
584 | 15.3M | } |
585 | | |
586 | 118M | FeatureManager* get_feature_mgr() { |
587 | 118M | if (!feature_mgr_.get()) { |
588 | 13.4k | AnalyzeFeatures(); |
589 | 13.4k | } |
590 | 118M | return feature_mgr_.get(); |
591 | 118M | } |
592 | | |
593 | 0 | void ResetFeatureManager() { feature_mgr_.reset(nullptr); } |
594 | | |
595 | | // Returns the grammar for this context. |
596 | 0 | const AssemblyGrammar& grammar() const { return grammar_; } |
597 | | |
598 | | // If |inst| has not yet been analysed by the def-use manager, then analyse |
599 | | // its definitions and uses. |
600 | | inline void UpdateDefUse(Instruction* inst); |
601 | | |
602 | 61.1M | const InstructionFolder& get_instruction_folder() { |
603 | 61.1M | if (!inst_folder_) { |
604 | 8.16k | inst_folder_ = MakeUnique<InstructionFolder>(this); |
605 | 8.16k | } |
606 | 61.1M | return *inst_folder_; |
607 | 61.1M | } |
608 | | |
609 | 15.3M | uint32_t max_id_bound() const { return max_id_bound_; } |
610 | 10.7k | void set_max_id_bound(uint32_t new_bound) { max_id_bound_ = new_bound; } |
611 | | |
612 | 168k | bool preserve_bindings() const { return preserve_bindings_; } |
613 | 10.7k | void set_preserve_bindings(bool should_preserve_bindings) { |
614 | 10.7k | preserve_bindings_ = should_preserve_bindings; |
615 | 10.7k | } |
616 | | |
617 | 168k | bool preserve_spec_constants() const { return preserve_spec_constants_; } |
618 | 10.7k | void set_preserve_spec_constants(bool should_preserve_spec_constants) { |
619 | 10.7k | preserve_spec_constants_ = should_preserve_spec_constants; |
620 | 10.7k | } |
621 | | |
622 | | // Return id of input variable only decorated with |builtin|, if in module. |
623 | | // Create variable and return its id otherwise. If builtin not currently |
624 | | // supported, return 0. |
625 | | uint32_t GetBuiltinInputVarId(uint32_t builtin); |
626 | | |
627 | | // Returns the function whose id is |id|, if one exists. Returns |nullptr| |
628 | | // otherwise. |
629 | 193k | Function* GetFunction(uint32_t id) { |
630 | 193k | if (!AreAnalysesValid(kAnalysisIdToFuncMapping)) { |
631 | 57.1k | BuildIdToFuncMapping(); |
632 | 57.1k | } |
633 | 193k | auto entry = id_to_func_.find(id); |
634 | 193k | return (entry != id_to_func_.end()) ? entry->second : nullptr; |
635 | 193k | } |
636 | | |
637 | 0 | Function* GetFunction(Instruction* inst) { |
638 | 0 | if (inst->opcode() != spv::Op::OpFunction) { |
639 | 0 | return nullptr; |
640 | 0 | } |
641 | 0 | return GetFunction(inst->result_id()); |
642 | 0 | } |
643 | | |
644 | | // Add to |todo| all ids of functions called directly from |func|. |
645 | | void AddCalls(const Function* func, std::queue<uint32_t>* todo); |
646 | | |
647 | | // Applies |pfn| to every function in the call trees that are rooted at the |
648 | | // entry points. Returns true if any call |pfn| returns true. By convention |
649 | | // |pfn| should return true if it modified the module. |
650 | | bool ProcessEntryPointCallTree(ProcessFunction& pfn); |
651 | | |
652 | | // Applies |pfn| to every function in the call trees rooted at the entry |
653 | | // points and exported functions. Returns true if any call |pfn| returns |
654 | | // true. By convention |pfn| should return true if it modified the module. |
655 | | bool ProcessReachableCallTree(ProcessFunction& pfn); |
656 | | |
657 | | // Applies |pfn| to every function in the call trees rooted at the elements of |
658 | | // |roots|. Returns true if any call to |pfn| returns true. By convention |
659 | | // |pfn| should return true if it modified the module. After returning |
660 | | // |roots| will be empty. |
661 | | bool ProcessCallTreeFromRoots(ProcessFunction& pfn, |
662 | | std::queue<uint32_t>* roots); |
663 | | |
664 | | // Emits a error message to the message consumer indicating the error |
665 | | // described by |message| occurred in |inst|. |
666 | | void EmitErrorMessage(std::string message, Instruction* inst); |
667 | | |
668 | | // Returns true if and only if there is a path to |bb| from the entry block of |
669 | | // the function that contains |bb|. |
670 | | bool IsReachable(const opt::BasicBlock& bb); |
671 | | |
672 | | // Return the stage of the module. Will generate error if entry points don't |
673 | | // all have the same stage. |
674 | | spv::ExecutionModel GetStage(); |
675 | | |
676 | | // Returns true of the current target environment is at least that of the |
677 | | // given environment. |
678 | 0 | bool IsTargetEnvAtLeast(spv_target_env env) { |
679 | | // A bit of a hack. We assume that the target environments are appended to |
680 | | // the enum, so that there is an appropriate order. |
681 | 0 | return syntax_context_->target_env >= env; |
682 | 0 | } |
683 | | |
684 | | // Return the target environment for the current context. |
685 | 0 | spv_target_env GetTargetEnv() const { return syntax_context_->target_env; } |
686 | | |
687 | | private: |
688 | | // Builds the def-use manager from scratch, even if it was already valid. |
689 | 17.2k | void BuildDefUseManager() { |
690 | 17.2k | def_use_mgr_ = MakeUnique<analysis::DefUseManager>(module()); |
691 | 17.2k | valid_analyses_ = valid_analyses_ | kAnalysisDefUse; |
692 | 17.2k | } |
693 | | |
694 | | // Builds the liveness manager from scratch, even if it was already valid. |
695 | 0 | void BuildLivenessManager() { |
696 | 0 | liveness_mgr_ = MakeUnique<analysis::LivenessManager>(this); |
697 | 0 | valid_analyses_ = valid_analyses_ | kAnalysisLiveness; |
698 | 0 | } |
699 | | |
700 | | // Builds the instruction-block map for the whole module. |
701 | 18.1k | void BuildInstrToBlockMapping() { |
702 | 18.1k | instr_to_block_.clear(); |
703 | 24.5k | for (auto& fn : *module_) { |
704 | 3.25M | for (auto& block : fn) { |
705 | 22.4M | block.ForEachInst([this, &block](Instruction* inst) { |
706 | 22.4M | instr_to_block_[inst] = █ |
707 | 22.4M | }); |
708 | 3.25M | } |
709 | 24.5k | } |
710 | 18.1k | valid_analyses_ = valid_analyses_ | kAnalysisInstrToBlockMapping; |
711 | 18.1k | } |
712 | | |
713 | | // Builds the instruction-function map for the whole module. |
714 | 57.1k | void BuildIdToFuncMapping() { |
715 | 57.1k | id_to_func_.clear(); |
716 | 82.5k | for (auto& fn : *module_) { |
717 | 82.5k | id_to_func_[fn.result_id()] = &fn; |
718 | 82.5k | } |
719 | 57.1k | valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping; |
720 | 57.1k | } |
721 | | |
722 | 79.3k | void BuildDecorationManager() { |
723 | 79.3k | decoration_mgr_ = MakeUnique<analysis::DecorationManager>(module()); |
724 | 79.3k | valid_analyses_ = valid_analyses_ | kAnalysisDecorations; |
725 | 79.3k | } |
726 | | |
727 | 51.0k | void BuildCFG() { |
728 | 51.0k | cfg_ = MakeUnique<CFG>(module()); |
729 | 51.0k | valid_analyses_ = valid_analyses_ | kAnalysisCFG; |
730 | 51.0k | } |
731 | | |
732 | 0 | void BuildScalarEvolutionAnalysis() { |
733 | 0 | scalar_evolution_analysis_ = MakeUnique<ScalarEvolutionAnalysis>(this); |
734 | 0 | valid_analyses_ = valid_analyses_ | kAnalysisScalarEvolution; |
735 | 0 | } |
736 | | |
737 | | // Builds the liveness analysis from scratch, even if it was already valid. |
738 | 0 | void BuildRegPressureAnalysis() { |
739 | 0 | reg_pressure_ = MakeUnique<LivenessAnalysis>(this); |
740 | 0 | valid_analyses_ = valid_analyses_ | kAnalysisRegisterPressure; |
741 | 0 | } |
742 | | |
743 | | // Builds the value number table analysis from scratch, even if it was already |
744 | | // valid. |
745 | 6.86k | void BuildValueNumberTable() { |
746 | 6.86k | vn_table_ = MakeUnique<ValueNumberTable>(this); |
747 | 6.86k | valid_analyses_ = valid_analyses_ | kAnalysisValueNumberTable; |
748 | 6.86k | } |
749 | | |
750 | | // Builds the structured CFG analysis from scratch, even if it was already |
751 | | // valid. |
752 | 42.6k | void BuildStructuredCFGAnalysis() { |
753 | 42.6k | struct_cfg_analysis_ = MakeUnique<StructuredCFGAnalysis>(this); |
754 | 42.6k | valid_analyses_ = valid_analyses_ | kAnalysisStructuredCFG; |
755 | 42.6k | } |
756 | | |
757 | | // Builds the constant manager from scratch, even if it was already |
758 | | // valid. |
759 | 18.0k | void BuildConstantManager() { |
760 | 18.0k | constant_mgr_ = MakeUnique<analysis::ConstantManager>(this); |
761 | 18.0k | valid_analyses_ = valid_analyses_ | kAnalysisConstants; |
762 | 18.0k | } |
763 | | |
764 | | // Builds the type manager from scratch, even if it was already |
765 | | // valid. |
766 | 17.9k | void BuildTypeManager() { |
767 | 17.9k | type_mgr_ = MakeUnique<analysis::TypeManager>(consumer(), this); |
768 | 17.9k | valid_analyses_ = valid_analyses_ | kAnalysisTypes; |
769 | 17.9k | } |
770 | | |
771 | | // Builds the debug information manager from scratch, even if it was |
772 | | // already valid. |
773 | 13.4k | void BuildDebugInfoManager() { |
774 | 13.4k | debug_info_mgr_ = MakeUnique<analysis::DebugInfoManager>(this); |
775 | 13.4k | valid_analyses_ = valid_analyses_ | kAnalysisDebugInfo; |
776 | 13.4k | } |
777 | | |
778 | | // Removes all computed dominator and post-dominator trees. This will force |
779 | | // the context to rebuild the trees on demand. |
780 | 33.6k | void ResetDominatorAnalysis() { |
781 | | // Clear the cache. |
782 | 33.6k | dominator_trees_.clear(); |
783 | 33.6k | post_dominator_trees_.clear(); |
784 | 33.6k | valid_analyses_ = valid_analyses_ | kAnalysisDominatorAnalysis; |
785 | 33.6k | } |
786 | | |
787 | | // Removes all computed loop descriptors. |
788 | 8.16k | void ResetLoopAnalysis() { |
789 | | // Clear the cache. |
790 | 8.16k | loop_descriptors_.clear(); |
791 | 8.16k | valid_analyses_ = valid_analyses_ | kAnalysisLoopAnalysis; |
792 | 8.16k | } |
793 | | |
794 | | // Removes all computed loop descriptors. |
795 | 0 | void ResetBuiltinAnalysis() { |
796 | | // Clear the cache. |
797 | 0 | builtin_var_id_map_.clear(); |
798 | 0 | valid_analyses_ = valid_analyses_ | kAnalysisBuiltinVarId; |
799 | 0 | } |
800 | | |
801 | | // Analyzes the features in the owned module. Builds the manager if required. |
802 | 13.4k | void AnalyzeFeatures() { |
803 | 13.4k | feature_mgr_ = |
804 | 13.4k | std::unique_ptr<FeatureManager>(new FeatureManager(grammar_)); |
805 | 13.4k | feature_mgr_->Analyze(module()); |
806 | 13.4k | } |
807 | | |
808 | | // Scans a module looking for it capabilities, and initializes combinator_ops_ |
809 | | // accordingly. |
810 | | void InitializeCombinators(); |
811 | | |
812 | | // Add the combinator opcode for the given capability to combinator_ops_. |
813 | | void AddCombinatorsForCapability(uint32_t capability); |
814 | | |
815 | | // Add the combinator opcode for the given extension to combinator_ops_. |
816 | | void AddCombinatorsForExtension(Instruction* extension); |
817 | | |
818 | | // Remove |inst| from |id_to_name_| if it is in map. |
819 | | void RemoveFromIdToName(const Instruction* inst); |
820 | | |
821 | | // Returns true if it is suppose to be valid but it is incorrect. Returns |
822 | | // true if the cfg is invalidated. |
823 | | bool CheckCFG(); |
824 | | |
825 | | // Return id of input variable only decorated with |builtin|, if in module. |
826 | | // Return 0 otherwise. |
827 | | uint32_t FindBuiltinInputVar(uint32_t builtin); |
828 | | |
829 | | // Add |var_id| to all entry points in module. |
830 | | void AddVarToEntryPoints(uint32_t var_id); |
831 | | |
832 | | // The SPIR-V syntax context containing grammar tables for opcodes and |
833 | | // operands. |
834 | | spv_context syntax_context_; |
835 | | |
836 | | // Auxiliary object for querying SPIR-V grammar facts. |
837 | | AssemblyGrammar grammar_; |
838 | | |
839 | | // An unique identifier for instructions in |module_|. Can be used to order |
840 | | // instructions in a container. |
841 | | // |
842 | | // This member is initialized to 0, but always issues this value plus one. |
843 | | // Therefore, 0 is not a valid unique id for an instruction. |
844 | | uint32_t unique_id_; |
845 | | |
846 | | // The module being processed within this IR context. |
847 | | std::unique_ptr<Module> module_; |
848 | | |
849 | | // A message consumer for diagnostics. |
850 | | MessageConsumer consumer_; |
851 | | |
852 | | // The def-use manager for |module_|. |
853 | | std::unique_ptr<analysis::DefUseManager> def_use_mgr_; |
854 | | |
855 | | // The instruction decoration manager for |module_|. |
856 | | std::unique_ptr<analysis::DecorationManager> decoration_mgr_; |
857 | | |
858 | | // The feature manager for |module_|. |
859 | | std::unique_ptr<FeatureManager> feature_mgr_; |
860 | | |
861 | | // A map from instructions to the basic block they belong to. This mapping is |
862 | | // built on-demand when get_instr_block() is called. |
863 | | // |
864 | | // NOTE: Do not traverse this map. Ever. Use the function and basic block |
865 | | // iterators to traverse instructions. |
866 | | std::unordered_map<Instruction*, BasicBlock*> instr_to_block_; |
867 | | |
868 | | // A map from ids to the function they define. This mapping is |
869 | | // built on-demand when GetFunction() is called. |
870 | | // |
871 | | // NOTE: Do not traverse this map. Ever. Use the function and basic block |
872 | | // iterators to traverse instructions. |
873 | | std::unordered_map<uint32_t, Function*> id_to_func_; |
874 | | |
875 | | // A bitset indicating which analyzes are currently valid. |
876 | | Analysis valid_analyses_; |
877 | | |
878 | | // Opcodes of shader capability core executable instructions |
879 | | // without side-effect. |
880 | | std::unordered_map<uint32_t, std::unordered_set<uint32_t>> combinator_ops_; |
881 | | |
882 | | // Opcodes of shader capability core executable instructions |
883 | | // without side-effect. |
884 | | std::unordered_map<uint32_t, uint32_t> builtin_var_id_map_; |
885 | | |
886 | | // The CFG for all the functions in |module_|. |
887 | | std::unique_ptr<CFG> cfg_; |
888 | | |
889 | | // Each function in the module will create its own dominator tree. We cache |
890 | | // the result so it doesn't need to be rebuilt each time. |
891 | | std::map<const Function*, DominatorAnalysis> dominator_trees_; |
892 | | std::map<const Function*, PostDominatorAnalysis> post_dominator_trees_; |
893 | | |
894 | | // Cache of loop descriptors for each function. |
895 | | std::unordered_map<const Function*, LoopDescriptor> loop_descriptors_; |
896 | | |
897 | | // Constant manager for |module_|. |
898 | | std::unique_ptr<analysis::ConstantManager> constant_mgr_; |
899 | | |
900 | | // Type manager for |module_|. |
901 | | std::unique_ptr<analysis::TypeManager> type_mgr_; |
902 | | |
903 | | // Debug information manager for |module_|. |
904 | | std::unique_ptr<analysis::DebugInfoManager> debug_info_mgr_; |
905 | | |
906 | | // A map from an id to its corresponding OpName and OpMemberName instructions. |
907 | | std::unique_ptr<std::multimap<uint32_t, Instruction*>> id_to_name_; |
908 | | |
909 | | // The cache scalar evolution analysis node. |
910 | | std::unique_ptr<ScalarEvolutionAnalysis> scalar_evolution_analysis_; |
911 | | |
912 | | // The liveness analysis |module_|. |
913 | | std::unique_ptr<LivenessAnalysis> reg_pressure_; |
914 | | |
915 | | std::unique_ptr<ValueNumberTable> vn_table_; |
916 | | |
917 | | std::unique_ptr<InstructionFolder> inst_folder_; |
918 | | |
919 | | std::unique_ptr<StructuredCFGAnalysis> struct_cfg_analysis_; |
920 | | |
921 | | // The liveness manager for |module_|. |
922 | | std::unique_ptr<analysis::LivenessManager> liveness_mgr_; |
923 | | |
924 | | // The maximum legal value for the id bound. |
925 | | uint32_t max_id_bound_; |
926 | | |
927 | | // Whether all bindings within |module_| should be preserved. |
928 | | bool preserve_bindings_; |
929 | | |
930 | | // Whether all specialization constants within |module_| |
931 | | // should be preserved. |
932 | | bool preserve_spec_constants_; |
933 | | }; |
934 | | |
935 | | inline IRContext::Analysis operator|(IRContext::Analysis lhs, |
936 | 1.06M | IRContext::Analysis rhs) { |
937 | 1.06M | return static_cast<IRContext::Analysis>(static_cast<int>(lhs) | |
938 | 1.06M | static_cast<int>(rhs)); |
939 | 1.06M | } |
940 | | |
941 | | inline IRContext::Analysis& operator|=(IRContext::Analysis& lhs, |
942 | 89.9k | IRContext::Analysis rhs) { |
943 | 89.9k | lhs = lhs | rhs; |
944 | 89.9k | return lhs; |
945 | 89.9k | } |
946 | | |
947 | 0 | inline IRContext::Analysis operator<<(IRContext::Analysis a, int shift) { |
948 | 0 | return static_cast<IRContext::Analysis>(static_cast<int>(a) << shift); |
949 | 0 | } |
950 | | |
951 | 0 | inline IRContext::Analysis& operator<<=(IRContext::Analysis& a, int shift) { |
952 | 0 | a = static_cast<IRContext::Analysis>(static_cast<int>(a) << shift); |
953 | 0 | return a; |
954 | 0 | } |
955 | | |
956 | 0 | std::vector<Instruction*> IRContext::GetConstants() { |
957 | 0 | return module()->GetConstants(); |
958 | 0 | } |
959 | | |
960 | 0 | std::vector<const Instruction*> IRContext::GetConstants() const { |
961 | 0 | return ((const Module*)module())->GetConstants(); |
962 | 0 | } |
963 | | |
964 | 0 | Module::inst_iterator IRContext::annotation_begin() { |
965 | 0 | return module()->annotation_begin(); |
966 | 0 | } |
967 | | |
968 | 0 | Module::inst_iterator IRContext::annotation_end() { |
969 | 0 | return module()->annotation_end(); |
970 | 0 | } |
971 | | |
972 | 199k | IteratorRange<Module::inst_iterator> IRContext::annotations() { |
973 | 199k | return module_->annotations(); |
974 | 199k | } |
975 | | |
976 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::annotations() const { |
977 | 0 | return ((const Module*)module_.get())->annotations(); |
978 | 0 | } |
979 | | |
980 | 0 | Module::inst_iterator IRContext::capability_begin() { |
981 | 0 | return module()->capability_begin(); |
982 | 0 | } |
983 | | |
984 | 0 | Module::inst_iterator IRContext::capability_end() { |
985 | 0 | return module()->capability_end(); |
986 | 0 | } |
987 | | |
988 | 15.1k | IteratorRange<Module::inst_iterator> IRContext::capabilities() { |
989 | 15.1k | return module()->capabilities(); |
990 | 15.1k | } |
991 | | |
992 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::capabilities() const { |
993 | 0 | return ((const Module*)module())->capabilities(); |
994 | 0 | } |
995 | | |
996 | 0 | Module::inst_iterator IRContext::extension_begin() { |
997 | 0 | return module()->extension_begin(); |
998 | 0 | } |
999 | | |
1000 | 0 | Module::inst_iterator IRContext::extension_end() { |
1001 | 0 | return module()->extension_end(); |
1002 | 0 | } |
1003 | | |
1004 | 0 | IteratorRange<Module::inst_iterator> IRContext::extensions() { |
1005 | 0 | return module()->extensions(); |
1006 | 0 | } |
1007 | | |
1008 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::extensions() const { |
1009 | 0 | return ((const Module*)module())->extensions(); |
1010 | 0 | } |
1011 | | |
1012 | 0 | Module::inst_iterator IRContext::types_values_begin() { |
1013 | 0 | return module()->types_values_begin(); |
1014 | 0 | } |
1015 | | |
1016 | 396k | Module::inst_iterator IRContext::types_values_end() { |
1017 | 396k | return module()->types_values_end(); |
1018 | 396k | } |
1019 | | |
1020 | 25.5k | IteratorRange<Module::inst_iterator> IRContext::types_values() { |
1021 | 25.5k | return module()->types_values(); |
1022 | 25.5k | } |
1023 | | |
1024 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::types_values() const { |
1025 | 0 | return ((const Module*)module_.get())->types_values(); |
1026 | 0 | } |
1027 | | |
1028 | 0 | Module::inst_iterator IRContext::ext_inst_import_begin() { |
1029 | 0 | return module()->ext_inst_import_begin(); |
1030 | 0 | } |
1031 | | |
1032 | 0 | Module::inst_iterator IRContext::ext_inst_import_end() { |
1033 | 0 | return module()->ext_inst_import_end(); |
1034 | 0 | } |
1035 | | |
1036 | 0 | IteratorRange<Module::inst_iterator> IRContext::ext_inst_imports() { |
1037 | 0 | return module()->ext_inst_imports(); |
1038 | 0 | } |
1039 | | |
1040 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::ext_inst_imports() const { |
1041 | 0 | return ((const Module*)module_.get())->ext_inst_imports(); |
1042 | 0 | } |
1043 | | |
1044 | 0 | Module::inst_iterator IRContext::debug1_begin() { |
1045 | 0 | return module()->debug1_begin(); |
1046 | 0 | } |
1047 | | |
1048 | 0 | Module::inst_iterator IRContext::debug1_end() { return module()->debug1_end(); } |
1049 | | |
1050 | 0 | IteratorRange<Module::inst_iterator> IRContext::debugs1() { |
1051 | 0 | return module()->debugs1(); |
1052 | 0 | } |
1053 | | |
1054 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::debugs1() const { |
1055 | 0 | return ((const Module*)module_.get())->debugs1(); |
1056 | 0 | } |
1057 | | |
1058 | 0 | Module::inst_iterator IRContext::debug2_begin() { |
1059 | 0 | return module()->debug2_begin(); |
1060 | 0 | } |
1061 | 0 | Module::inst_iterator IRContext::debug2_end() { return module()->debug2_end(); } |
1062 | | |
1063 | 42.5k | IteratorRange<Module::inst_iterator> IRContext::debugs2() { |
1064 | 42.5k | return module()->debugs2(); |
1065 | 42.5k | } |
1066 | | |
1067 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::debugs2() const { |
1068 | 0 | return ((const Module*)module_.get())->debugs2(); |
1069 | 0 | } |
1070 | | |
1071 | 0 | Module::inst_iterator IRContext::debug3_begin() { |
1072 | 0 | return module()->debug3_begin(); |
1073 | 0 | } |
1074 | | |
1075 | 0 | Module::inst_iterator IRContext::debug3_end() { return module()->debug3_end(); } |
1076 | | |
1077 | 0 | IteratorRange<Module::inst_iterator> IRContext::debugs3() { |
1078 | 0 | return module()->debugs3(); |
1079 | 0 | } |
1080 | | |
1081 | 0 | IteratorRange<Module::const_inst_iterator> IRContext::debugs3() const { |
1082 | 0 | return ((const Module*)module_.get())->debugs3(); |
1083 | 0 | } |
1084 | | |
1085 | 0 | Module::inst_iterator IRContext::ext_inst_debuginfo_begin() { |
1086 | 0 | return module()->ext_inst_debuginfo_begin(); |
1087 | 0 | } |
1088 | | |
1089 | 0 | Module::inst_iterator IRContext::ext_inst_debuginfo_end() { |
1090 | 0 | return module()->ext_inst_debuginfo_end(); |
1091 | 0 | } |
1092 | | |
1093 | 0 | IteratorRange<Module::inst_iterator> IRContext::ext_inst_debuginfo() { |
1094 | 0 | return module()->ext_inst_debuginfo(); |
1095 | 0 | } |
1096 | | |
1097 | | IteratorRange<Module::const_inst_iterator> IRContext::ext_inst_debuginfo() |
1098 | 0 | const { |
1099 | 0 | return ((const Module*)module_.get())->ext_inst_debuginfo(); |
1100 | 0 | } |
1101 | | |
1102 | 0 | void IRContext::AddCapability(spv::Capability capability) { |
1103 | 0 | if (!get_feature_mgr()->HasCapability(capability)) { |
1104 | 0 | std::unique_ptr<Instruction> capability_inst(new Instruction( |
1105 | 0 | this, spv::Op::OpCapability, 0, 0, |
1106 | 0 | {{SPV_OPERAND_TYPE_CAPABILITY, {static_cast<uint32_t>(capability)}}})); |
1107 | 0 | AddCapability(std::move(capability_inst)); |
1108 | 0 | } |
1109 | 0 | } |
1110 | | |
1111 | 0 | void IRContext::AddCapability(std::unique_ptr<Instruction>&& c) { |
1112 | 0 | AddCombinatorsForCapability(c->GetSingleWordInOperand(0)); |
1113 | 0 | if (feature_mgr_ != nullptr) { |
1114 | 0 | feature_mgr_->AddCapability( |
1115 | 0 | static_cast<spv::Capability>(c->GetSingleWordInOperand(0))); |
1116 | 0 | } |
1117 | 0 | if (AreAnalysesValid(kAnalysisDefUse)) { |
1118 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(c.get()); |
1119 | 0 | } |
1120 | 0 | module()->AddCapability(std::move(c)); |
1121 | 0 | } |
1122 | | |
1123 | 0 | void IRContext::AddExtension(const std::string& ext_name) { |
1124 | 0 | std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(ext_name); |
1125 | 0 | AddExtension(std::unique_ptr<Instruction>( |
1126 | 0 | new Instruction(this, spv::Op::OpExtension, 0u, 0u, |
1127 | 0 | {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); |
1128 | 0 | } |
1129 | | |
1130 | 0 | void IRContext::AddExtension(std::unique_ptr<Instruction>&& e) { |
1131 | 0 | if (AreAnalysesValid(kAnalysisDefUse)) { |
1132 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(e.get()); |
1133 | 0 | } |
1134 | 0 | if (feature_mgr_ != nullptr) { |
1135 | 0 | feature_mgr_->AddExtension(&*e); |
1136 | 0 | } |
1137 | 0 | module()->AddExtension(std::move(e)); |
1138 | 0 | } |
1139 | | |
1140 | 0 | void IRContext::AddExtInstImport(const std::string& name) { |
1141 | 0 | std::vector<uint32_t> ext_words = spvtools::utils::MakeVector(name); |
1142 | 0 | AddExtInstImport(std::unique_ptr<Instruction>( |
1143 | 0 | new Instruction(this, spv::Op::OpExtInstImport, 0u, TakeNextId(), |
1144 | 0 | {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); |
1145 | 0 | } |
1146 | | |
1147 | 0 | void IRContext::AddExtInstImport(std::unique_ptr<Instruction>&& e) { |
1148 | 0 | AddCombinatorsForExtension(e.get()); |
1149 | 0 | if (AreAnalysesValid(kAnalysisDefUse)) { |
1150 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(e.get()); |
1151 | 0 | } |
1152 | 0 | module()->AddExtInstImport(std::move(e)); |
1153 | 0 | if (feature_mgr_ != nullptr) { |
1154 | 0 | feature_mgr_->AddExtInstImportIds(module()); |
1155 | 0 | } |
1156 | 0 | } |
1157 | | |
1158 | 0 | void IRContext::SetMemoryModel(std::unique_ptr<Instruction>&& m) { |
1159 | 0 | module()->SetMemoryModel(std::move(m)); |
1160 | 0 | } |
1161 | | |
1162 | 0 | const Instruction* IRContext::GetMemoryModel() const { |
1163 | 0 | return module()->GetMemoryModel(); |
1164 | 0 | } |
1165 | | |
1166 | 0 | void IRContext::AddEntryPoint(std::unique_ptr<Instruction>&& e) { |
1167 | 0 | module()->AddEntryPoint(std::move(e)); |
1168 | 0 | } |
1169 | | |
1170 | 0 | void IRContext::AddExecutionMode(std::unique_ptr<Instruction>&& e) { |
1171 | 0 | module()->AddExecutionMode(std::move(e)); |
1172 | 0 | } |
1173 | | |
1174 | 0 | void IRContext::AddDebug1Inst(std::unique_ptr<Instruction>&& d) { |
1175 | 0 | module()->AddDebug1Inst(std::move(d)); |
1176 | 0 | } |
1177 | | |
1178 | 0 | void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) { |
1179 | 0 | if (AreAnalysesValid(kAnalysisNameMap)) { |
1180 | 0 | if (d->opcode() == spv::Op::OpName || |
1181 | 0 | d->opcode() == spv::Op::OpMemberName) { |
1182 | | // OpName and OpMemberName do not have result-ids. The target of the |
1183 | | // instruction is at InOperand index 0. |
1184 | 0 | id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()}); |
1185 | 0 | } |
1186 | 0 | } |
1187 | 0 | if (AreAnalysesValid(kAnalysisDefUse)) { |
1188 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(d.get()); |
1189 | 0 | } |
1190 | 0 | module()->AddDebug2Inst(std::move(d)); |
1191 | 0 | } |
1192 | | |
1193 | 0 | void IRContext::AddDebug3Inst(std::unique_ptr<Instruction>&& d) { |
1194 | 0 | module()->AddDebug3Inst(std::move(d)); |
1195 | 0 | } |
1196 | | |
1197 | 0 | void IRContext::AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d) { |
1198 | 0 | module()->AddExtInstDebugInfo(std::move(d)); |
1199 | 0 | } |
1200 | | |
1201 | 1.79k | void IRContext::AddAnnotationInst(std::unique_ptr<Instruction>&& a) { |
1202 | 1.79k | if (AreAnalysesValid(kAnalysisDecorations)) { |
1203 | 1.79k | get_decoration_mgr()->AddDecoration(a.get()); |
1204 | 1.79k | } |
1205 | 1.79k | if (AreAnalysesValid(kAnalysisDefUse)) { |
1206 | 1.79k | get_def_use_mgr()->AnalyzeInstDefUse(a.get()); |
1207 | 1.79k | } |
1208 | 1.79k | module()->AddAnnotationInst(std::move(a)); |
1209 | 1.79k | } |
1210 | | |
1211 | 5.01k | void IRContext::AddType(std::unique_ptr<Instruction>&& t) { |
1212 | 5.01k | module()->AddType(std::move(t)); |
1213 | 5.01k | if (AreAnalysesValid(kAnalysisDefUse)) { |
1214 | 4.95k | get_def_use_mgr()->AnalyzeInstDefUse(&*(--types_values_end())); |
1215 | 4.95k | } |
1216 | 5.01k | } |
1217 | | |
1218 | 0 | void IRContext::AddGlobalValue(std::unique_ptr<Instruction>&& v) { |
1219 | 0 | if (AreAnalysesValid(kAnalysisDefUse)) { |
1220 | 0 | get_def_use_mgr()->AnalyzeInstDefUse(&*v); |
1221 | 0 | } |
1222 | 0 | module()->AddGlobalValue(std::move(v)); |
1223 | 0 | } |
1224 | | |
1225 | 0 | void IRContext::AddFunctionDeclaration(std::unique_ptr<Function>&& f) { |
1226 | 0 | module()->AddFunctionDeclaration(std::move(f)); |
1227 | 0 | } |
1228 | | |
1229 | 0 | void IRContext::AddFunction(std::unique_ptr<Function>&& f) { |
1230 | 0 | module()->AddFunction(std::move(f)); |
1231 | 0 | } |
1232 | | |
1233 | 71.3k | void IRContext::AnalyzeDefUse(Instruction* inst) { |
1234 | 71.3k | if (AreAnalysesValid(kAnalysisDefUse)) { |
1235 | 71.3k | get_def_use_mgr()->AnalyzeInstDefUse(inst); |
1236 | 71.3k | } |
1237 | 71.3k | } |
1238 | | |
1239 | 4.77M | void IRContext::UpdateDefUse(Instruction* inst) { |
1240 | 4.77M | if (AreAnalysesValid(kAnalysisDefUse)) { |
1241 | 4.77M | get_def_use_mgr()->UpdateDefUse(inst); |
1242 | 4.77M | } |
1243 | 4.77M | } |
1244 | | |
1245 | 42.5k | void IRContext::BuildIdToNameMap() { |
1246 | 42.5k | id_to_name_ = MakeUnique<std::multimap<uint32_t, Instruction*>>(); |
1247 | 158k | for (Instruction& debug_inst : debugs2()) { |
1248 | 158k | if (debug_inst.opcode() == spv::Op::OpMemberName || |
1249 | 158k | debug_inst.opcode() == spv::Op::OpName) { |
1250 | 158k | id_to_name_->insert({debug_inst.GetSingleWordInOperand(0), &debug_inst}); |
1251 | 158k | } |
1252 | 158k | } |
1253 | 42.5k | valid_analyses_ = valid_analyses_ | kAnalysisNameMap; |
1254 | 42.5k | } |
1255 | | |
1256 | | IteratorRange<std::multimap<uint32_t, Instruction*>::iterator> |
1257 | 14.5M | IRContext::GetNames(uint32_t id) { |
1258 | 14.5M | if (!AreAnalysesValid(kAnalysisNameMap)) { |
1259 | 42.5k | BuildIdToNameMap(); |
1260 | 42.5k | } |
1261 | 14.5M | auto result = id_to_name_->equal_range(id); |
1262 | 14.5M | return make_range(std::move(result.first), std::move(result.second)); |
1263 | 14.5M | } |
1264 | | |
1265 | 0 | Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) { |
1266 | 0 | if (!AreAnalysesValid(kAnalysisNameMap)) { |
1267 | 0 | BuildIdToNameMap(); |
1268 | 0 | } |
1269 | 0 | auto result = id_to_name_->equal_range(struct_type_id); |
1270 | 0 | for (auto i = result.first; i != result.second; ++i) { |
1271 | 0 | auto* name_instr = i->second; |
1272 | 0 | if (name_instr->opcode() == spv::Op::OpMemberName && |
1273 | 0 | name_instr->GetSingleWordInOperand(1) == index) { |
1274 | 0 | return name_instr; |
1275 | 0 | } |
1276 | 0 | } |
1277 | 0 | return nullptr; |
1278 | 0 | } |
1279 | | |
1280 | | void IRContext::CloneNames(const uint32_t old_id, const uint32_t new_id, |
1281 | 0 | const uint32_t max_member_index) { |
1282 | 0 | std::vector<std::unique_ptr<Instruction>> names_to_add; |
1283 | 0 | auto names = GetNames(old_id); |
1284 | 0 | for (auto n : names) { |
1285 | 0 | Instruction* old_name_inst = n.second; |
1286 | 0 | if (old_name_inst->opcode() == spv::Op::OpMemberName) { |
1287 | 0 | auto midx = old_name_inst->GetSingleWordInOperand(1); |
1288 | 0 | if (midx >= max_member_index) continue; |
1289 | 0 | } |
1290 | 0 | std::unique_ptr<Instruction> new_name_inst(old_name_inst->Clone(this)); |
1291 | 0 | new_name_inst->SetInOperand(0, {new_id}); |
1292 | 0 | names_to_add.push_back(std::move(new_name_inst)); |
1293 | 0 | } |
1294 | | // We can't add the new names when we are iterating over name range above. |
1295 | | // We can add all the new names now. |
1296 | 0 | for (auto& new_name : names_to_add) AddDebug2Inst(std::move(new_name)); |
1297 | 0 | } |
1298 | | |
1299 | | } // namespace opt |
1300 | | } // namespace spvtools |
1301 | | |
1302 | | #endif // SOURCE_OPT_IR_CONTEXT_H_ |