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