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
35const uint32_t kNoDebugScope = 0;
36const uint32_t kNoInlinedAt = 0;
37
38namespace spvtools {
39namespace opt {
40
41class Function;
42class IRContext;
43class Module;
44class 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.
76struct 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
102inline 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.
112class 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.
150class 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).
545std::ostream& operator<<(std::ostream& str, const Instruction& inst);
546
547inline bool Instruction::operator==(const Instruction& other) const {
548 return unique_id() == other.unique_id();
549}
550
551inline bool Instruction::operator!=(const Instruction& other) const {
552 return !(*this == other);
553}
554
555inline bool Instruction::operator<(const Instruction& other) const {
556 return unique_id() < other.unique_id();
557}
558
559inline Operand& Instruction::GetOperand(uint32_t index) {
560 assert(index < operands_.size() && "operand index out of bound");
561 return operands_[index];
562}
563
564inline const Operand& Instruction::GetOperand(uint32_t index) const {
565 assert(index < operands_.size() && "operand index out of bound");
566 return operands_[index];
567}
568
569inline void Instruction::AddOperand(Operand&& operand) {
570 operands_.push_back(std::move(operand));
571}
572
573inline void Instruction::SetInOperand(uint32_t index,
574 Operand::OperandData&& data) {
575 SetOperand(index + TypeResultIdCount(), std::move(data));
576}
577
578inline 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
585inline 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
592inline 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
607inline 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
614inline 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
628inline bool Instruction::IsNop() const {
629 return opcode_ == SpvOpNop && !has_type_id_ && !has_result_id_ &&
630 operands_.empty();
631}
632
633inline void Instruction::ToNop() {
634 opcode_ = SpvOpNop;
635 has_type_id_ = false;
636 has_result_id_ = false;
637 operands_.clear();
638}
639
640inline 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
650inline 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
661inline 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
671inline 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
682inline 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
687inline 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
693inline 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
703inline 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
713inline 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
720inline 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
728inline 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
743inline 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
758inline 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
766inline 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
774inline 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
790bool Instruction::IsDecoration() const {
791 return spvOpcodeIsDecoration(opcode());
792}
793
794bool Instruction::IsLoad() const { return spvOpcodeIsLoad(opcode()); }
795
796bool Instruction::IsAtomicWithLoad() const {
797 return spvOpcodeIsAtomicWithLoad(opcode());
798}
799
800bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); }
801
802bool Instruction::IsConstant() const {
803 return IsCompileTimeConstantInst(opcode());
804}
805} // namespace opt
806} // namespace spvtools
807
808#endif // SOURCE_OPT_INSTRUCTION_H_
809