1 | // Copyright (c) 2016 Google Inc. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #ifndef SOURCE_OPT_INSTRUCTION_H_ |
16 | #define SOURCE_OPT_INSTRUCTION_H_ |
17 | |
18 | #include <cassert> |
19 | #include <functional> |
20 | #include <memory> |
21 | #include <string> |
22 | #include <utility> |
23 | #include <vector> |
24 | |
25 | #include "source/opcode.h" |
26 | #include "source/operand.h" |
27 | #include "source/util/ilist_node.h" |
28 | #include "source/util/small_vector.h" |
29 | |
30 | #include "source/latest_version_glsl_std_450_header.h" |
31 | #include "source/latest_version_spirv_header.h" |
32 | #include "source/opt/reflect.h" |
33 | #include "spirv-tools/libspirv.h" |
34 | |
35 | const uint32_t kNoDebugScope = 0; |
36 | const uint32_t kNoInlinedAt = 0; |
37 | |
38 | namespace spvtools { |
39 | namespace opt { |
40 | |
41 | class Function; |
42 | class IRContext; |
43 | class Module; |
44 | class InstructionList; |
45 | |
46 | // Relaxed logical addressing: |
47 | // |
48 | // In the logical addressing model, pointers cannot be stored or loaded. This |
49 | // is a useful assumption because it simplifies the aliasing significantly. |
50 | // However, for the purpose of legalizing code generated from HLSL, we will have |
51 | // to allow storing and loading of pointers to opaque objects and runtime |
52 | // arrays. This relaxation of the rule still implies that function and private |
53 | // scope variables do not have any aliasing, so we can treat them as before. |
54 | // This will be call the relaxed logical addressing model. |
55 | // |
56 | // This relaxation of the rule will be allowed by |GetBaseAddress|, but it will |
57 | // enforce that no other pointers are stored or loaded. |
58 | |
59 | // About operand: |
60 | // |
61 | // In the SPIR-V specification, the term "operand" is used to mean any single |
62 | // SPIR-V word following the leading wordcount-opcode word. Here, the term |
63 | // "operand" is used to mean a *logical* operand. A logical operand may consist |
64 | // of multiple SPIR-V words, which together make up the same component. For |
65 | // example, a logical operand of a 64-bit integer needs two words to express. |
66 | // |
67 | // Further, we categorize logical operands into *in* and *out* operands. |
68 | // In operands are operands actually serve as input to operations, while out |
69 | // operands are operands that represent ids generated from operations (result |
70 | // type id or result id). For example, for "OpIAdd %rtype %rid %inop1 %inop2", |
71 | // "%inop1" and "%inop2" are in operands, while "%rtype" and "%rid" are out |
72 | // operands. |
73 | |
74 | // A *logical* operand to a SPIR-V instruction. It can be the type id, result |
75 | // id, or other additional operands carried in an instruction. |
76 | struct Operand { |
77 | using OperandData = utils::SmallVector<uint32_t, 2>; |
78 | Operand(spv_operand_type_t t, OperandData&& w) |
79 | : type(t), words(std::move(w)) {} |
80 | |
81 | Operand(spv_operand_type_t t, const OperandData& w) : type(t), words(w) {} |
82 | |
83 | spv_operand_type_t type; // Type of this logical operand. |
84 | OperandData words; // Binary segments of this logical operand. |
85 | |
86 | // Returns a string operand as a C-style string. |
87 | const char* AsCString() const { |
88 | assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); |
89 | return reinterpret_cast<const char*>(words.data()); |
90 | } |
91 | |
92 | // Returns a string operand as a std::string. |
93 | std::string AsString() const { return AsCString(); } |
94 | |
95 | friend bool operator==(const Operand& o1, const Operand& o2) { |
96 | return o1.type == o2.type && o1.words == o2.words; |
97 | } |
98 | |
99 | // TODO(antiagainst): create fields for literal number kind, width, etc. |
100 | }; |
101 | |
102 | inline bool operator!=(const Operand& o1, const Operand& o2) { |
103 | return !(o1 == o2); |
104 | } |
105 | |
106 | // This structure is used to represent a DebugScope instruction from |
107 | // the OpenCL.100.DebugInfo extened instruction set. Note that we can |
108 | // ignore the result id of DebugScope instruction because it is not |
109 | // used for anything. We do not keep it to reduce the size of |
110 | // structure. |
111 | // TODO: Let validator check that the result id is not used anywhere. |
112 | class DebugScope { |
113 | public: |
114 | DebugScope(uint32_t lexical_scope, uint32_t inlined_at) |
115 | : lexical_scope_(lexical_scope), inlined_at_(inlined_at) {} |
116 | |
117 | inline bool operator!=(const DebugScope& d) const { |
118 | return lexical_scope_ != d.lexical_scope_ || inlined_at_ != d.inlined_at_; |
119 | } |
120 | |
121 | // Accessor functions for |lexical_scope_|. |
122 | uint32_t GetLexicalScope() const { return lexical_scope_; } |
123 | void SetLexicalScope(uint32_t scope) { lexical_scope_ = scope; } |
124 | |
125 | // Accessor functions for |inlined_at_|. |
126 | uint32_t GetInlinedAt() const { return inlined_at_; } |
127 | void SetInlinedAt(uint32_t at) { inlined_at_ = at; } |
128 | |
129 | // Pushes the binary segments for this DebugScope instruction into |
130 | // the back of *|binary|. |
131 | void ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set, |
132 | std::vector<uint32_t>* binary) const; |
133 | |
134 | private: |
135 | // The result id of the lexical scope in which this debug scope is |
136 | // contained. The value is kNoDebugScope if there is no scope. |
137 | uint32_t lexical_scope_; |
138 | |
139 | // The result id of DebugInlinedAt if instruction in this debug scope |
140 | // is inlined. The value is kNoInlinedAt if it is not inlined. |
141 | uint32_t inlined_at_; |
142 | }; |
143 | |
144 | // A SPIR-V instruction. It contains the opcode and any additional logical |
145 | // operand, including the result id (if any) and result type id (if any). It |
146 | // may also contain line-related debug instruction (OpLine, OpNoLine) directly |
147 | // appearing before this instruction. Note that the result id of an instruction |
148 | // should never change after the instruction being built. If the result id |
149 | // needs to change, the user should create a new instruction instead. |
150 | class Instruction : public utils::IntrusiveNodeBase<Instruction> { |
151 | public: |
152 | using OperandList = std::vector<Operand>; |
153 | using iterator = OperandList::iterator; |
154 | using const_iterator = OperandList::const_iterator; |
155 | |
156 | // Creates a default OpNop instruction. |
157 | // This exists solely for containers that can't do without. Should be removed. |
158 | Instruction() |
159 | : utils::IntrusiveNodeBase<Instruction>(), |
160 | context_(nullptr), |
161 | opcode_(SpvOpNop), |
162 | has_type_id_(false), |
163 | has_result_id_(false), |
164 | unique_id_(0), |
165 | dbg_scope_(kNoDebugScope, kNoInlinedAt) {} |
166 | |
167 | // Creates a default OpNop instruction. |
168 | Instruction(IRContext*); |
169 | // Creates an instruction with the given opcode |op| and no additional logical |
170 | // operands. |
171 | Instruction(IRContext*, SpvOp); |
172 | // Creates an instruction using the given spv_parsed_instruction_t |inst|. All |
173 | // the data inside |inst| will be copied and owned in this instance. And keep |
174 | // record of line-related debug instructions |dbg_line| ahead of this |
175 | // instruction, if any. |
176 | Instruction(IRContext* c, const spv_parsed_instruction_t& inst, |
177 | std::vector<Instruction>&& dbg_line = {}); |
178 | |
179 | Instruction(IRContext* c, const spv_parsed_instruction_t& inst, |
180 | const DebugScope& dbg_scope); |
181 | |
182 | // Creates an instruction with the given opcode |op|, type id: |ty_id|, |
183 | // result id: |res_id| and input operands: |in_operands|. |
184 | Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id, |
185 | const OperandList& in_operands); |
186 | |
187 | // TODO: I will want to remove these, but will first have to remove the use of |
188 | // std::vector<Instruction>. |
189 | Instruction(const Instruction&) = default; |
190 | Instruction& operator=(const Instruction&) = default; |
191 | |
192 | Instruction(Instruction&&); |
193 | Instruction& operator=(Instruction&&); |
194 | |
195 | virtual ~Instruction() = default; |
196 | |
197 | // Returns a newly allocated instruction that has the same operands, result, |
198 | // and type as |this|. The new instruction is not linked into any list. |
199 | // It is the responsibility of the caller to make sure that the storage is |
200 | // removed. It is the caller's responsibility to make sure that there is only |
201 | // one instruction for each result id. |
202 | Instruction* Clone(IRContext* c) const; |
203 | |
204 | IRContext* context() const { return context_; } |
205 | |
206 | SpvOp opcode() const { return opcode_; } |
207 | // Sets the opcode of this instruction to a specific opcode. Note this may |
208 | // invalidate the instruction. |
209 | // TODO(qining): Remove this function when instruction building and insertion |
210 | // is well implemented. |
211 | void SetOpcode(SpvOp op) { opcode_ = op; } |
212 | uint32_t type_id() const { |
213 | return has_type_id_ ? GetSingleWordOperand(0) : 0; |
214 | } |
215 | uint32_t result_id() const { |
216 | return has_result_id_ ? GetSingleWordOperand(has_type_id_ ? 1 : 0) : 0; |
217 | } |
218 | uint32_t unique_id() const { |
219 | assert(unique_id_ != 0); |
220 | return unique_id_; |
221 | } |
222 | // Returns the vector of line-related debug instructions attached to this |
223 | // instruction and the caller can directly modify them. |
224 | std::vector<Instruction>& dbg_line_insts() { return dbg_line_insts_; } |
225 | const std::vector<Instruction>& dbg_line_insts() const { |
226 | return dbg_line_insts_; |
227 | } |
228 | |
229 | // Clear line-related debug instructions attached to this instruction. |
230 | void clear_dbg_line_insts() { dbg_line_insts_.clear(); } |
231 | |
232 | // Same semantics as in the base class except the list the InstructionList |
233 | // containing |pos| will now assume ownership of |this|. |
234 | // inline void MoveBefore(Instruction* pos); |
235 | // inline void InsertAfter(Instruction* pos); |
236 | |
237 | // Begin and end iterators for operands. |
238 | iterator begin() { return operands_.begin(); } |
239 | iterator end() { return operands_.end(); } |
240 | const_iterator begin() const { return operands_.cbegin(); } |
241 | const_iterator end() const { return operands_.cend(); } |
242 | // Const begin and end iterators for operands. |
243 | const_iterator cbegin() const { return operands_.cbegin(); } |
244 | const_iterator cend() const { return operands_.cend(); } |
245 | |
246 | // Gets the number of logical operands. |
247 | uint32_t NumOperands() const { |
248 | return static_cast<uint32_t>(operands_.size()); |
249 | } |
250 | // Gets the number of SPIR-V words occupied by all logical operands. |
251 | uint32_t NumOperandWords() const { |
252 | return NumInOperandWords() + TypeResultIdCount(); |
253 | } |
254 | // Gets the |index|-th logical operand. |
255 | inline Operand& GetOperand(uint32_t index); |
256 | inline const Operand& GetOperand(uint32_t index) const; |
257 | // Adds |operand| to the list of operands of this instruction. |
258 | // It is the responsibility of the caller to make sure |
259 | // that the instruction remains valid. |
260 | inline void AddOperand(Operand&& operand); |
261 | // Gets the |index|-th logical operand as a single SPIR-V word. This method is |
262 | // not expected to be used with logical operands consisting of multiple SPIR-V |
263 | // words. |
264 | uint32_t GetSingleWordOperand(uint32_t index) const; |
265 | // Sets the |index|-th in-operand's data to the given |data|. |
266 | inline void SetInOperand(uint32_t index, Operand::OperandData&& data); |
267 | // Sets the |index|-th operand's data to the given |data|. |
268 | // This is for in-operands modification only, but with |index| expressed in |
269 | // terms of operand index rather than in-operand index. |
270 | inline void SetOperand(uint32_t index, Operand::OperandData&& data); |
271 | // Replace all of the in operands with those in |new_operands|. |
272 | inline void SetInOperands(OperandList&& new_operands); |
273 | // Sets the result type id. |
274 | inline void SetResultType(uint32_t ty_id); |
275 | // Sets the result id |
276 | inline void SetResultId(uint32_t res_id); |
277 | inline bool HasResultId() const { return has_result_id_; } |
278 | // Sets DebugScope. |
279 | inline void SetDebugScope(const DebugScope& scope); |
280 | inline const DebugScope& GetDebugScope() const { return dbg_scope_; } |
281 | // Remove the |index|-th operand |
282 | void RemoveOperand(uint32_t index) { |
283 | operands_.erase(operands_.begin() + index); |
284 | } |
285 | |
286 | // The following methods are similar to the above, but are for in operands. |
287 | uint32_t NumInOperands() const { |
288 | return static_cast<uint32_t>(operands_.size() - TypeResultIdCount()); |
289 | } |
290 | uint32_t NumInOperandWords() const; |
291 | Operand& GetInOperand(uint32_t index) { |
292 | return GetOperand(index + TypeResultIdCount()); |
293 | } |
294 | const Operand& GetInOperand(uint32_t index) const { |
295 | return GetOperand(index + TypeResultIdCount()); |
296 | } |
297 | uint32_t GetSingleWordInOperand(uint32_t index) const { |
298 | return GetSingleWordOperand(index + TypeResultIdCount()); |
299 | } |
300 | void RemoveInOperand(uint32_t index) { |
301 | operands_.erase(operands_.begin() + index + TypeResultIdCount()); |
302 | } |
303 | |
304 | // Returns true if this instruction is OpNop. |
305 | inline bool IsNop() const; |
306 | // Turns this instruction to OpNop. This does not clear out all preceding |
307 | // line-related debug instructions. |
308 | inline void ToNop(); |
309 | |
310 | // Runs the given function |f| on this instruction and optionally on the |
311 | // preceding debug line instructions. The function will always be run |
312 | // if this is itself a debug line instruction. |
313 | inline void ForEachInst(const std::function<void(Instruction*)>& f, |
314 | bool run_on_debug_line_insts = false); |
315 | inline void ForEachInst(const std::function<void(const Instruction*)>& f, |
316 | bool run_on_debug_line_insts = false) const; |
317 | |
318 | // Runs the given function |f| on this instruction and optionally on the |
319 | // preceding debug line instructions. The function will always be run |
320 | // if this is itself a debug line instruction. If |f| returns false, |
321 | // iteration is terminated and this function returns false. |
322 | inline bool WhileEachInst(const std::function<bool(Instruction*)>& f, |
323 | bool run_on_debug_line_insts = false); |
324 | inline bool WhileEachInst(const std::function<bool(const Instruction*)>& f, |
325 | bool run_on_debug_line_insts = false) const; |
326 | |
327 | // Runs the given function |f| on all operand ids. |
328 | // |
329 | // |f| should not transform an ID into 0, as 0 is an invalid ID. |
330 | inline void ForEachId(const std::function<void(uint32_t*)>& f); |
331 | inline void ForEachId(const std::function<void(const uint32_t*)>& f) const; |
332 | |
333 | // Runs the given function |f| on all "in" operand ids. |
334 | inline void ForEachInId(const std::function<void(uint32_t*)>& f); |
335 | inline void ForEachInId(const std::function<void(const uint32_t*)>& f) const; |
336 | |
337 | // Runs the given function |f| on all "in" operand ids. If |f| returns false, |
338 | // iteration is terminated and this function returns false. |
339 | inline bool WhileEachInId(const std::function<bool(uint32_t*)>& f); |
340 | inline bool WhileEachInId( |
341 | const std::function<bool(const uint32_t*)>& f) const; |
342 | |
343 | // Runs the given function |f| on all "in" operands. |
344 | inline void ForEachInOperand(const std::function<void(uint32_t*)>& f); |
345 | inline void ForEachInOperand( |
346 | const std::function<void(const uint32_t*)>& f) const; |
347 | |
348 | // Runs the given function |f| on all "in" operands. If |f| returns false, |
349 | // iteration is terminated and this function return false. |
350 | inline bool WhileEachInOperand(const std::function<bool(uint32_t*)>& f); |
351 | inline bool WhileEachInOperand( |
352 | const std::function<bool(const uint32_t*)>& f) const; |
353 | |
354 | // Returns true if any operands can be labels |
355 | inline bool HasLabels() const; |
356 | |
357 | // Pushes the binary segments for this instruction into the back of *|binary|. |
358 | void ToBinaryWithoutAttachedDebugInsts(std::vector<uint32_t>* binary) const; |
359 | |
360 | // Replaces the operands to the instruction with |new_operands|. The caller |
361 | // is responsible for building a complete and valid list of operands for |
362 | // this instruction. |
363 | void ReplaceOperands(const OperandList& new_operands); |
364 | |
365 | // Returns true if the instruction annotates an id with a decoration. |
366 | inline bool IsDecoration() const; |
367 | |
368 | // Returns true if the instruction is known to be a load from read-only |
369 | // memory. |
370 | bool IsReadOnlyLoad() const; |
371 | |
372 | // Returns the instruction that gives the base address of an address |
373 | // calculation. The instruction must be a load, as defined by |IsLoad|, |
374 | // store, copy, or access chain instruction. In logical addressing mode, will |
375 | // return an OpVariable or OpFunctionParameter instruction. For relaxed |
376 | // logical addressing, it would also return a load of a pointer to an opaque |
377 | // object. For physical addressing mode, could return other types of |
378 | // instructions. |
379 | Instruction* GetBaseAddress() const; |
380 | |
381 | // Returns true if the instruction loads from memory or samples an image, and |
382 | // stores the result into an id. It considers only core instructions. |
383 | // Memory-to-memory instructions are not considered loads. |
384 | inline bool IsLoad() const; |
385 | |
386 | // Returns true if the instruction declares a variable that is read-only. |
387 | bool IsReadOnlyVariable() const; |
388 | |
389 | // The following functions check for the various descriptor types defined in |
390 | // the Vulkan specification section 13.1. |
391 | |
392 | // Returns true if the instruction defines a pointer type that points to a |
393 | // storage image. |
394 | bool IsVulkanStorageImage() const; |
395 | |
396 | // Returns true if the instruction defines a pointer type that points to a |
397 | // sampled image. |
398 | bool IsVulkanSampledImage() const; |
399 | |
400 | // Returns true if the instruction defines a pointer type that points to a |
401 | // storage texel buffer. |
402 | bool IsVulkanStorageTexelBuffer() const; |
403 | |
404 | // Returns true if the instruction defines a pointer type that points to a |
405 | // storage buffer. |
406 | bool IsVulkanStorageBuffer() const; |
407 | |
408 | // Returns true if the instruction defines a pointer type that points to a |
409 | // uniform buffer. |
410 | bool IsVulkanUniformBuffer() const; |
411 | |
412 | // Returns true if the instruction is an atom operation that uses original |
413 | // value. |
414 | inline bool IsAtomicWithLoad() const; |
415 | |
416 | // Returns true if the instruction is an atom operation. |
417 | inline bool IsAtomicOp() const; |
418 | |
419 | // Returns true if this instruction is a branch or switch instruction (either |
420 | // conditional or not). |
421 | bool IsBranch() const { return spvOpcodeIsBranch(opcode()); } |
422 | |
423 | // Returns true if this instruction causes the function to finish execution |
424 | // and return to its caller |
425 | bool IsReturn() const { return spvOpcodeIsReturn(opcode()); } |
426 | |
427 | // Returns true if this instruction exits this function or aborts execution. |
428 | bool IsReturnOrAbort() const { return spvOpcodeIsReturnOrAbort(opcode()); } |
429 | |
430 | // Returns the id for the |element|'th subtype. If the |this| is not a |
431 | // composite type, this function returns 0. |
432 | uint32_t GetTypeComponent(uint32_t element) const; |
433 | |
434 | // Returns true if this instruction is a basic block terminator. |
435 | bool IsBlockTerminator() const { |
436 | return spvOpcodeIsBlockTerminator(opcode()); |
437 | } |
438 | |
439 | // Returns true if |this| is an instruction that define an opaque type. Since |
440 | // runtime array have similar characteristics they are included as opaque |
441 | // types. |
442 | bool IsOpaqueType() const; |
443 | |
444 | // Returns true if |this| is an instruction which could be folded into a |
445 | // constant value. |
446 | bool IsFoldable() const; |
447 | |
448 | // Returns true if |this| is an instruction which could be folded into a |
449 | // constant value by |FoldScalar|. |
450 | bool IsFoldableByFoldScalar() const; |
451 | |
452 | // Returns true if we are allowed to fold or otherwise manipulate the |
453 | // instruction that defines |id| in the given context. This includes not |
454 | // handling NaN values. |
455 | bool IsFloatingPointFoldingAllowed() const; |
456 | |
457 | inline bool operator==(const Instruction&) const; |
458 | inline bool operator!=(const Instruction&) const; |
459 | inline bool operator<(const Instruction&) const; |
460 | |
461 | // Takes ownership of the instruction owned by |i| and inserts it immediately |
462 | // before |this|. Returns the inserted instruction. |
463 | Instruction* InsertBefore(std::unique_ptr<Instruction>&& i); |
464 | // Takes ownership of the instructions in |list| and inserts them in order |
465 | // immediately before |this|. Returns the first inserted instruction. |
466 | // Assumes the list is non-empty. |
467 | Instruction* InsertBefore(std::vector<std::unique_ptr<Instruction>>&& list); |
468 | using utils::IntrusiveNodeBase<Instruction>::InsertBefore; |
469 | |
470 | // Returns true if |this| is an instruction defining a constant, but not a |
471 | // Spec constant. |
472 | inline bool IsConstant() const; |
473 | |
474 | // Returns true if |this| is an instruction with an opcode safe to move |
475 | bool IsOpcodeCodeMotionSafe() const; |
476 | |
477 | // Pretty-prints |inst|. |
478 | // |
479 | // Provides the disassembly of a specific instruction. Utilizes |inst|'s |
480 | // context to provide the correct interpretation of types, constants, etc. |
481 | // |
482 | // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
483 | // is always added to |options|. |
484 | std::string PrettyPrint(uint32_t options = 0u) const; |
485 | |
486 | // Returns true if the result can be a vector and the result of each component |
487 | // depends on the corresponding component of any vector inputs. |
488 | bool IsScalarizable() const; |
489 | |
490 | // Return true if the only effect of this instructions is the result. |
491 | bool IsOpcodeSafeToDelete() const; |
492 | |
493 | // Returns true if it is valid to use the result of |inst| as the base |
494 | // pointer for a load or store. In this case, valid is defined by the relaxed |
495 | // logical addressing rules when using logical addressing. Normal validation |
496 | // rules for physical addressing. |
497 | bool IsValidBasePointer() const; |
498 | |
499 | // Dump this instruction on stderr. Useful when running interactive |
500 | // debuggers. |
501 | void Dump() const; |
502 | |
503 | private: |
504 | // Returns the total count of result type id and result id. |
505 | uint32_t TypeResultIdCount() const { |
506 | if (has_type_id_ && has_result_id_) return 2; |
507 | if (has_type_id_ || has_result_id_) return 1; |
508 | return 0; |
509 | } |
510 | |
511 | // Returns true if the instruction declares a variable that is read-only. The |
512 | // first version assumes the module is a shader module. The second assumes a |
513 | // kernel. |
514 | bool IsReadOnlyVariableShaders() const; |
515 | bool IsReadOnlyVariableKernel() const; |
516 | |
517 | // Returns true if the result of |inst| can be used as the base image for an |
518 | // instruction that samples a image, reads an image, or writes to an image. |
519 | bool IsValidBaseImage() const; |
520 | |
521 | IRContext* context_; // IR Context |
522 | SpvOp opcode_; // Opcode |
523 | bool has_type_id_; // True if the instruction has a type id |
524 | bool has_result_id_; // True if the instruction has a result id |
525 | uint32_t unique_id_; // Unique instruction id |
526 | // All logical operands, including result type id and result id. |
527 | OperandList operands_; |
528 | // Opline and OpNoLine instructions preceding this instruction. Note that for |
529 | // Instructions representing OpLine or OpNonLine itself, this field should be |
530 | // empty. |
531 | std::vector<Instruction> dbg_line_insts_; |
532 | |
533 | // DebugScope that wraps this instruction. |
534 | DebugScope dbg_scope_; |
535 | |
536 | friend InstructionList; |
537 | }; |
538 | |
539 | // Pretty-prints |inst| to |str| and returns |str|. |
540 | // |
541 | // Provides the disassembly of a specific instruction. Utilizes |inst|'s context |
542 | // to provide the correct interpretation of types, constants, etc. |
543 | // |
544 | // Disassembly uses raw ids (not pretty printed names). |
545 | std::ostream& operator<<(std::ostream& str, const Instruction& inst); |
546 | |
547 | inline bool Instruction::operator==(const Instruction& other) const { |
548 | return unique_id() == other.unique_id(); |
549 | } |
550 | |
551 | inline bool Instruction::operator!=(const Instruction& other) const { |
552 | return !(*this == other); |
553 | } |
554 | |
555 | inline bool Instruction::operator<(const Instruction& other) const { |
556 | return unique_id() < other.unique_id(); |
557 | } |
558 | |
559 | inline Operand& Instruction::GetOperand(uint32_t index) { |
560 | assert(index < operands_.size() && "operand index out of bound" ); |
561 | return operands_[index]; |
562 | } |
563 | |
564 | inline const Operand& Instruction::GetOperand(uint32_t index) const { |
565 | assert(index < operands_.size() && "operand index out of bound" ); |
566 | return operands_[index]; |
567 | } |
568 | |
569 | inline void Instruction::AddOperand(Operand&& operand) { |
570 | operands_.push_back(std::move(operand)); |
571 | } |
572 | |
573 | inline void Instruction::SetInOperand(uint32_t index, |
574 | Operand::OperandData&& data) { |
575 | SetOperand(index + TypeResultIdCount(), std::move(data)); |
576 | } |
577 | |
578 | inline void Instruction::SetOperand(uint32_t index, |
579 | Operand::OperandData&& data) { |
580 | assert(index < operands_.size() && "operand index out of bound" ); |
581 | assert(index >= TypeResultIdCount() && "operand is not a in-operand" ); |
582 | operands_[index].words = std::move(data); |
583 | } |
584 | |
585 | inline void Instruction::SetInOperands(OperandList&& new_operands) { |
586 | // Remove the old in operands. |
587 | operands_.erase(operands_.begin() + TypeResultIdCount(), operands_.end()); |
588 | // Add the new in operands. |
589 | operands_.insert(operands_.end(), new_operands.begin(), new_operands.end()); |
590 | } |
591 | |
592 | inline void Instruction::SetResultId(uint32_t res_id) { |
593 | // TODO(dsinclair): Allow setting a result id if there wasn't one |
594 | // previously. Need to make room in the operands_ array to place the result, |
595 | // and update the has_result_id_ flag. |
596 | assert(has_result_id_); |
597 | |
598 | // TODO(dsinclair): Allow removing the result id. This needs to make sure, |
599 | // if there was a result id previously to remove it from the operands_ array |
600 | // and reset the has_result_id_ flag. |
601 | assert(res_id != 0); |
602 | |
603 | auto ridx = has_type_id_ ? 1 : 0; |
604 | operands_[ridx].words = {res_id}; |
605 | } |
606 | |
607 | inline void Instruction::SetDebugScope(const DebugScope& scope) { |
608 | dbg_scope_ = scope; |
609 | for (auto& i : dbg_line_insts_) { |
610 | i.dbg_scope_ = scope; |
611 | } |
612 | } |
613 | |
614 | inline void Instruction::SetResultType(uint32_t ty_id) { |
615 | // TODO(dsinclair): Allow setting a type id if there wasn't one |
616 | // previously. Need to make room in the operands_ array to place the result, |
617 | // and update the has_type_id_ flag. |
618 | assert(has_type_id_); |
619 | |
620 | // TODO(dsinclair): Allow removing the type id. This needs to make sure, |
621 | // if there was a type id previously to remove it from the operands_ array |
622 | // and reset the has_type_id_ flag. |
623 | assert(ty_id != 0); |
624 | |
625 | operands_.front().words = {ty_id}; |
626 | } |
627 | |
628 | inline bool Instruction::IsNop() const { |
629 | return opcode_ == SpvOpNop && !has_type_id_ && !has_result_id_ && |
630 | operands_.empty(); |
631 | } |
632 | |
633 | inline void Instruction::ToNop() { |
634 | opcode_ = SpvOpNop; |
635 | has_type_id_ = false; |
636 | has_result_id_ = false; |
637 | operands_.clear(); |
638 | } |
639 | |
640 | inline bool Instruction::WhileEachInst( |
641 | const std::function<bool(Instruction*)>& f, bool run_on_debug_line_insts) { |
642 | if (run_on_debug_line_insts) { |
643 | for (auto& dbg_line : dbg_line_insts_) { |
644 | if (!f(&dbg_line)) return false; |
645 | } |
646 | } |
647 | return f(this); |
648 | } |
649 | |
650 | inline bool Instruction::WhileEachInst( |
651 | const std::function<bool(const Instruction*)>& f, |
652 | bool run_on_debug_line_insts) const { |
653 | if (run_on_debug_line_insts) { |
654 | for (auto& dbg_line : dbg_line_insts_) { |
655 | if (!f(&dbg_line)) return false; |
656 | } |
657 | } |
658 | return f(this); |
659 | } |
660 | |
661 | inline void Instruction::ForEachInst(const std::function<void(Instruction*)>& f, |
662 | bool run_on_debug_line_insts) { |
663 | WhileEachInst( |
664 | [&f](Instruction* inst) { |
665 | f(inst); |
666 | return true; |
667 | }, |
668 | run_on_debug_line_insts); |
669 | } |
670 | |
671 | inline void Instruction::ForEachInst( |
672 | const std::function<void(const Instruction*)>& f, |
673 | bool run_on_debug_line_insts) const { |
674 | WhileEachInst( |
675 | [&f](const Instruction* inst) { |
676 | f(inst); |
677 | return true; |
678 | }, |
679 | run_on_debug_line_insts); |
680 | } |
681 | |
682 | inline void Instruction::ForEachId(const std::function<void(uint32_t*)>& f) { |
683 | for (auto& opnd : operands_) |
684 | if (spvIsIdType(opnd.type)) f(&opnd.words[0]); |
685 | } |
686 | |
687 | inline void Instruction::ForEachId( |
688 | const std::function<void(const uint32_t*)>& f) const { |
689 | for (const auto& opnd : operands_) |
690 | if (spvIsIdType(opnd.type)) f(&opnd.words[0]); |
691 | } |
692 | |
693 | inline bool Instruction::WhileEachInId( |
694 | const std::function<bool(uint32_t*)>& f) { |
695 | for (auto& opnd : operands_) { |
696 | if (spvIsInIdType(opnd.type)) { |
697 | if (!f(&opnd.words[0])) return false; |
698 | } |
699 | } |
700 | return true; |
701 | } |
702 | |
703 | inline bool Instruction::WhileEachInId( |
704 | const std::function<bool(const uint32_t*)>& f) const { |
705 | for (const auto& opnd : operands_) { |
706 | if (spvIsInIdType(opnd.type)) { |
707 | if (!f(&opnd.words[0])) return false; |
708 | } |
709 | } |
710 | return true; |
711 | } |
712 | |
713 | inline void Instruction::ForEachInId(const std::function<void(uint32_t*)>& f) { |
714 | WhileEachInId([&f](uint32_t* id) { |
715 | f(id); |
716 | return true; |
717 | }); |
718 | } |
719 | |
720 | inline void Instruction::ForEachInId( |
721 | const std::function<void(const uint32_t*)>& f) const { |
722 | WhileEachInId([&f](const uint32_t* id) { |
723 | f(id); |
724 | return true; |
725 | }); |
726 | } |
727 | |
728 | inline bool Instruction::WhileEachInOperand( |
729 | const std::function<bool(uint32_t*)>& f) { |
730 | for (auto& opnd : operands_) { |
731 | switch (opnd.type) { |
732 | case SPV_OPERAND_TYPE_RESULT_ID: |
733 | case SPV_OPERAND_TYPE_TYPE_ID: |
734 | break; |
735 | default: |
736 | if (!f(&opnd.words[0])) return false; |
737 | break; |
738 | } |
739 | } |
740 | return true; |
741 | } |
742 | |
743 | inline bool Instruction::WhileEachInOperand( |
744 | const std::function<bool(const uint32_t*)>& f) const { |
745 | for (const auto& opnd : operands_) { |
746 | switch (opnd.type) { |
747 | case SPV_OPERAND_TYPE_RESULT_ID: |
748 | case SPV_OPERAND_TYPE_TYPE_ID: |
749 | break; |
750 | default: |
751 | if (!f(&opnd.words[0])) return false; |
752 | break; |
753 | } |
754 | } |
755 | return true; |
756 | } |
757 | |
758 | inline void Instruction::ForEachInOperand( |
759 | const std::function<void(uint32_t*)>& f) { |
760 | WhileEachInOperand([&f](uint32_t* op) { |
761 | f(op); |
762 | return true; |
763 | }); |
764 | } |
765 | |
766 | inline void Instruction::ForEachInOperand( |
767 | const std::function<void(const uint32_t*)>& f) const { |
768 | WhileEachInOperand([&f](const uint32_t* op) { |
769 | f(op); |
770 | return true; |
771 | }); |
772 | } |
773 | |
774 | inline bool Instruction::HasLabels() const { |
775 | switch (opcode_) { |
776 | case SpvOpSelectionMerge: |
777 | case SpvOpBranch: |
778 | case SpvOpLoopMerge: |
779 | case SpvOpBranchConditional: |
780 | case SpvOpSwitch: |
781 | case SpvOpPhi: |
782 | return true; |
783 | break; |
784 | default: |
785 | break; |
786 | } |
787 | return false; |
788 | } |
789 | |
790 | bool Instruction::IsDecoration() const { |
791 | return spvOpcodeIsDecoration(opcode()); |
792 | } |
793 | |
794 | bool Instruction::IsLoad() const { return spvOpcodeIsLoad(opcode()); } |
795 | |
796 | bool Instruction::IsAtomicWithLoad() const { |
797 | return spvOpcodeIsAtomicWithLoad(opcode()); |
798 | } |
799 | |
800 | bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); } |
801 | |
802 | bool Instruction::IsConstant() const { |
803 | return IsCompileTimeConstantInst(opcode()); |
804 | } |
805 | } // namespace opt |
806 | } // namespace spvtools |
807 | |
808 | #endif // SOURCE_OPT_INSTRUCTION_H_ |
809 | |