blob: 12e16c111b88f4cdf4a2bfedfb9df776ed61a2ab [file] [log] [blame]
// Copyright 2020 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_WRITER_SPIRV_BUILDER_H_
#define SRC_TINT_WRITER_SPIRV_BUILDER_H_
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "spirv/unified1/spirv.h"
#include "src/tint/ast/assignment_statement.h"
#include "src/tint/ast/bitcast_expression.h"
#include "src/tint/ast/break_statement.h"
#include "src/tint/ast/continue_statement.h"
#include "src/tint/ast/discard_statement.h"
#include "src/tint/ast/if_statement.h"
#include "src/tint/ast/interpolate_attribute.h"
#include "src/tint/ast/loop_statement.h"
#include "src/tint/ast/return_statement.h"
#include "src/tint/ast/switch_statement.h"
#include "src/tint/ast/unary_op_expression.h"
#include "src/tint/ast/variable_decl_statement.h"
#include "src/tint/program_builder.h"
#include "src/tint/scope_stack.h"
#include "src/tint/sem/builtin.h"
#include "src/tint/type/storage_texture.h"
#include "src/tint/writer/spirv/function.h"
#include "src/tint/writer/spirv/scalar_constant.h"
// Forward declarations
namespace tint::sem {
class Call;
class Load;
class TypeInitializer;
class TypeConversion;
} // namespace tint::sem
namespace tint::type {
class Reference;
} // namespace tint::type
namespace tint::writer::spirv {
/// Builder class to create SPIR-V instructions from a module.
class Builder {
public:
/// Contains information for generating accessor chains
struct AccessorInfo {
AccessorInfo();
~AccessorInfo();
/// The ID of the current chain source. The chain source may change as we
/// evaluate the access chain. The chain source always points to the ID
/// which we will use to evaluate the current set of accessors. This maybe
/// the original variable, or maybe an intermediary if we had to evaulate
/// the access chain early (in the case of a swizzle of an access chain).
uint32_t source_id;
/// The type of the current chain source. This type matches the deduced
/// result_type of the current source defined above.
const type::Type* source_type;
/// A list of access chain indices to emit. Note, we _only_ have access
/// chain indices if the source is reference.
std::vector<uint32_t> access_chain_indices;
};
/// Constructor
/// @param program the program
/// @param zero_initialize_workgroup_memory `true` to initialize all the
/// variables in the Workgroup address space with OpConstantNull
explicit Builder(const Program* program, bool zero_initialize_workgroup_memory = false);
~Builder();
/// Generates the SPIR-V instructions for the given program
/// @returns true if the SPIR-V was successfully built
bool Build();
/// @returns the error string or blank if no error was reported.
const std::string& error() const { return error_; }
/// @returns true if the builder encountered an error
bool has_error() const { return !error_.empty(); }
/// @returns the number of uint32_t's needed to make up the results
uint32_t total_size() const;
/// @returns the id bound for this program
uint32_t id_bound() const { return next_id_; }
/// @returns the next id to be used
uint32_t next_id() {
auto id = next_id_;
next_id_ += 1;
return id;
}
/// Iterates over all the instructions in the correct order and calls the
/// given callback
/// @param cb the callback to execute
void iterate(std::function<void(const Instruction&)> cb) const;
/// Adds an instruction to the list of capabilities, if the capability
/// hasn't already been added.
/// @param cap the capability to set
void push_capability(uint32_t cap);
/// @returns the capabilities
const InstructionList& capabilities() const { return capabilities_; }
/// Adds an instruction to the extensions
/// @param extension the name of the extension
void push_extension(const char* extension);
/// @returns the extensions
const InstructionList& extensions() const { return extensions_; }
/// Adds an instruction to the ext import
/// @param op the op to set
/// @param operands the operands for the instruction
void push_ext_import(spv::Op op, const OperandList& operands) {
ext_imports_.push_back(Instruction{op, operands});
}
/// @returns the ext imports
const InstructionList& ext_imports() const { return ext_imports_; }
/// Adds an instruction to the memory model
/// @param op the op to set
/// @param operands the operands for the instruction
void push_memory_model(spv::Op op, const OperandList& operands) {
memory_model_.push_back(Instruction{op, operands});
}
/// @returns the memory model
const InstructionList& memory_model() const { return memory_model_; }
/// Adds an instruction to the entry points
/// @param op the op to set
/// @param operands the operands for the instruction
void push_entry_point(spv::Op op, const OperandList& operands) {
entry_points_.push_back(Instruction{op, operands});
}
/// @returns the entry points
const InstructionList& entry_points() const { return entry_points_; }
/// Adds an instruction to the execution modes
/// @param op the op to set
/// @param operands the operands for the instruction
void push_execution_mode(spv::Op op, const OperandList& operands) {
execution_modes_.push_back(Instruction{op, operands});
}
/// @returns the execution modes
const InstructionList& execution_modes() const { return execution_modes_; }
/// Adds an instruction to the debug
/// @param op the op to set
/// @param operands the operands for the instruction
void push_debug(spv::Op op, const OperandList& operands) {
debug_.push_back(Instruction{op, operands});
}
/// @returns the debug instructions
const InstructionList& debug() const { return debug_; }
/// Adds an instruction to the types
/// @param op the op to set
/// @param operands the operands for the instruction
void push_type(spv::Op op, const OperandList& operands) {
types_.push_back(Instruction{op, operands});
}
/// @returns the type instructions
const InstructionList& types() const { return types_; }
/// Adds an instruction to the annotations
/// @param op the op to set
/// @param operands the operands for the instruction
void push_annot(spv::Op op, const OperandList& operands) {
annotations_.push_back(Instruction{op, operands});
}
/// @returns the annotations
const InstructionList& annots() const { return annotations_; }
/// Adds a function to the builder
/// @param func the function to add
void push_function(const Function& func) {
functions_.push_back(func);
current_label_id_ = func.label_id();
}
/// @returns the functions
const std::vector<Function>& functions() const { return functions_; }
/// Pushes an instruction to the current function. If we're outside
/// a function then issue an internal error and return false.
/// @param op the operation
/// @param operands the operands
/// @returns true if we succeeded
bool push_function_inst(spv::Op op, const OperandList& operands);
/// Pushes a variable to the current function
/// @param operands the variable operands
void push_function_var(const OperandList& operands) {
if (TINT_UNLIKELY(functions_.empty())) {
TINT_ICE(Writer, builder_.Diagnostics())
<< "push_function_var() called without a function";
}
functions_.back().push_var(operands);
}
/// @returns true if the current instruction insertion point is
/// inside a basic block.
bool InsideBasicBlock() const;
/// Converts a address space to a SPIR-V address space.
/// @param klass the address space to convert
/// @returns the SPIR-V address space or SpvStorageClassMax on error.
SpvStorageClass ConvertAddressSpace(type::AddressSpace klass) const;
/// Converts a builtin to a SPIR-V builtin and pushes a capability if needed.
/// @param builtin the builtin to convert
/// @param storage the address space that this builtin is being used with
/// @returns the SPIR-V builtin or SpvBuiltInMax on error.
SpvBuiltIn ConvertBuiltin(ast::BuiltinValue builtin, type::AddressSpace storage);
/// Converts an interpolate attribute to SPIR-V decorations and pushes a
/// capability if needed.
/// @param id the id to decorate
/// @param type the interpolation type
/// @param sampling the interpolation sampling
void AddInterpolationDecorations(uint32_t id,
ast::InterpolationType type,
ast::InterpolationSampling sampling);
/// Generates the enabling of an extension. Emits an error and returns false if the extension is
/// not supported.
/// @param ext the extension to generate
/// @returns true on success.
bool GenerateExtension(ast::Extension ext);
/// Generates a label for the given id. Emits an error and returns false if
/// we're currently outside a function.
/// @param id the id to use for the label
/// @returns true on success.
bool GenerateLabel(uint32_t id);
/// Generates an assignment statement
/// @param assign the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateAssignStatement(const ast::AssignmentStatement* assign);
/// Generates a block statement, wrapped in a push/pop scope
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateBlockStatement(const ast::BlockStatement* stmt);
/// Generates a block statement
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateBlockStatementWithoutScoping(const ast::BlockStatement* stmt);
/// Generates a break statement
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateBreakStatement(const ast::BreakStatement* stmt);
/// Generates a break-if statement
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateBreakIfStatement(const ast::BreakIfStatement* stmt);
/// Generates a continue statement
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateContinueStatement(const ast::ContinueStatement* stmt);
/// Generates a discard statement
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateDiscardStatement(const ast::DiscardStatement* stmt);
/// Generates an entry point instruction
/// @param func the function
/// @param id the id of the function
/// @returns true if the instruction was generated, false otherwise
bool GenerateEntryPoint(const ast::Function* func, uint32_t id);
/// Generates execution modes for an entry point
/// @param func the function
/// @param id the id of the function
/// @returns false on failure
bool GenerateExecutionModes(const ast::Function* func, uint32_t id);
/// Generates an expression
/// @param expr the expression to generate
/// @returns the resulting ID of the expression or 0 on error
uint32_t GenerateExpression(const sem::Expression* expr);
/// Generates an expression
/// @param expr the expression to generate
/// @returns the resulting ID of the expression or 0 on error
uint32_t GenerateExpression(const ast::Expression* expr);
/// Generates the instructions for a function
/// @param func the function to generate
/// @returns true if the instructions were generated
bool GenerateFunction(const ast::Function* func);
/// Generates a function type if not already created
/// @param func the function to generate for
/// @returns the ID to use for the function type. Returns 0 on failure.
uint32_t GenerateFunctionTypeIfNeeded(const sem::Function* func);
/// Generates access control annotations if needed
/// @param type the type to generate for
/// @param struct_id the struct id
/// @param member_idx the member index
void GenerateMemberAccessIfNeeded(const type::Type* type,
uint32_t struct_id,
uint32_t member_idx);
/// Generates a function variable
/// @param var the variable
/// @returns true if the variable was generated
bool GenerateFunctionVariable(const ast::Variable* var);
/// Generates a global variable
/// @param var the variable to generate
/// @returns true if the variable is emited.
bool GenerateGlobalVariable(const ast::Variable* var);
/// Generates an index accessor expression.
///
/// For more information on accessors see the "Pointer evaluation" section of
/// the WGSL specification.
///
/// @param expr the expresssion to generate
/// @returns the id of the expression or 0 on failure
uint32_t GenerateAccessorExpression(const ast::Expression* expr);
/// Generates an index accessor
/// @param expr the accessor to generate
/// @param info the current accessor information
/// @returns true if the accessor was generated successfully
bool GenerateIndexAccessor(const ast::IndexAccessorExpression* expr, AccessorInfo* info);
/// Generates a member accessor
/// @param expr the accessor to generate
/// @param info the current accessor information
/// @returns true if the accessor was generated successfully
bool GenerateMemberAccessor(const ast::MemberAccessorExpression* expr, AccessorInfo* info);
/// Generates an identifier expression
/// @param expr the expresssion to generate
/// @returns the id of the expression or 0 on failure
uint32_t GenerateIdentifierExpression(const ast::IdentifierExpression* expr);
/// Generates a unary op expression
/// @param expr the expression to generate
/// @returns the id of the expression or 0 on failure
uint32_t GenerateUnaryOpExpression(const ast::UnaryOpExpression* expr);
/// Generates an if statement
/// @param stmt the statement to generate
/// @returns true on success
bool GenerateIfStatement(const ast::IfStatement* stmt);
/// Generates an import instruction for the "GLSL.std.450" extended
/// instruction set, if one doesn't exist yet, and returns the import ID.
/// @returns the import ID, or 0 on error.
uint32_t GetGLSLstd450Import();
/// Generates a initializer expression
/// @param var the variable generated for, nullptr if no variable associated.
/// @param expr the expression to generate
/// @returns the ID of the expression or 0 on failure.
uint32_t GenerateInitializerExpression(const ast::Variable* var, const ast::Expression* expr);
/// Generates a literal constant if needed
/// @param lit the literal to generate
/// @returns the ID on success or 0 on failure
uint32_t GenerateLiteralIfNeeded(const ast::LiteralExpression* lit);
/// Generates a binary expression
/// @param expr the expression to generate
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateBinaryExpression(const ast::BinaryExpression* expr);
/// Generates a bitcast expression
/// @param expr the expression to generate
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateBitcastExpression(const ast::BitcastExpression* expr);
/// Generates a short circuting binary expression
/// @param expr the expression to generate
/// @returns teh expression ID on success or 0 otherwise
uint32_t GenerateShortCircuitBinaryExpression(const ast::BinaryExpression* expr);
/// Generates a call expression
/// @param expr the expression to generate
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateCallExpression(const ast::CallExpression* expr);
/// Handles generating a function call expression
/// @param call the call expression
/// @param function the function being called
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateFunctionCall(const sem::Call* call, const sem::Function* function);
/// Handles generating a builtin call expression
/// @param call the call expression
/// @param builtin the builtin being called
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateBuiltinCall(const sem::Call* call, const sem::Builtin* builtin);
/// Handles generating a type initializer or type conversion expression
/// @param call the call expression
/// @param var the variable that is being initialized. May be null.
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateTypeInitializerOrConversion(const sem::Call* call, const ast::Variable* var);
/// Generates a texture builtin call. Emits an error and returns false if
/// we're currently outside a function.
/// @param call the call expression
/// @param builtin the semantic information for the texture builtin
/// @param result_type result type operand of the texture instruction
/// @param result_id result identifier operand of the texture instruction
/// parameters
/// @returns true on success
bool GenerateTextureBuiltin(const sem::Call* call,
const sem::Builtin* builtin,
spirv::Operand result_type,
spirv::Operand result_id);
/// Generates a control barrier statement.
/// @param builtin the semantic information for the barrier builtin call
/// @returns true on success
bool GenerateControlBarrierBuiltin(const sem::Builtin* builtin);
/// Generates an atomic builtin call.
/// @param call the call expression
/// @param builtin the semantic information for the atomic builtin call
/// @param result_type result type operand of the texture instruction
/// @param result_id result identifier operand of the texture instruction
/// @returns true on success
bool GenerateAtomicBuiltin(const sem::Call* call,
const sem::Builtin* builtin,
Operand result_type,
Operand result_id);
/// Generates a sampled image
/// @param texture_type the texture type
/// @param texture_operand the texture operand
/// @param sampler_operand the sampler operand
/// @returns the expression ID
uint32_t GenerateSampledImage(const type::Type* texture_type,
Operand texture_operand,
Operand sampler_operand);
/// Generates a cast or object copy for the expression result,
/// or return the ID generated the expression if it is already
/// of the right type.
/// @param to_type the type we're casting too
/// @param from_expr the expression to cast
/// @param is_global_init if this is a global initializer
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateCastOrCopyOrPassthrough(const type::Type* to_type,
const ast::Expression* from_expr,
bool is_global_init);
/// Generates a loop statement
/// @param stmt the statement to generate
/// @returns true on successful generation
bool GenerateLoopStatement(const ast::LoopStatement* stmt);
/// Generates a return statement
/// @param stmt the statement to generate
/// @returns true on success, false otherwise
bool GenerateReturnStatement(const ast::ReturnStatement* stmt);
/// Generates a switch statement
/// @param stmt the statement to generate
/// @returns ture on success, false otherwise
bool GenerateSwitchStatement(const ast::SwitchStatement* stmt);
/// Generates a conditional section merge block
/// @param cond the condition
/// @param true_body the statements making up the true block
/// @param else_stmt the statement for the else block
/// @returns true on success, false on failure
bool GenerateConditionalBlock(const ast::Expression* cond,
const ast::BlockStatement* true_body,
const ast::Statement* else_stmt);
/// Generates a statement
/// @param stmt the statement to generate
/// @returns true if the statement was generated
bool GenerateStatement(const ast::Statement* stmt);
/// Generates an OpLoad of the given expression type
/// @param type the reference type of the expression
/// @param id the SPIR-V id of the expression
/// @returns the ID of the loaded value or 0 on failure.
uint32_t GenerateLoad(const type::Reference* type, uint32_t id);
/// Generates an OpLoad on the given ID if it has reference type in WGSL, otherwise return the
/// ID itself.
/// @param type the type of the expression
/// @param id the SPIR-V id of the expression
/// @returns the ID of the loaded value or `id` if type is not a reference
uint32_t GenerateLoadIfNeeded(const type::Type* type, uint32_t id);
/// Generates an OpStore. Emits an error and returns false if we're
/// currently outside a function.
/// @param to the ID to store too
/// @param from the ID to store from
/// @returns true on success
bool GenerateStore(uint32_t to, uint32_t from);
/// Generates a type if not already created
/// @param type the type to create
/// @returns the ID to use for the given type. Returns 0 on unknown type.
uint32_t GenerateTypeIfNeeded(const type::Type* type);
/// Generates a texture type declaration
/// @param texture the texture to generate
/// @param result the result operand
/// @returns true if the texture was successfully generated
bool GenerateTextureType(const type::Texture* texture, const Operand& result);
/// Generates an array type declaration
/// @param ary the array to generate
/// @param result the result operand
/// @returns true if the array was successfully generated
bool GenerateArrayType(const type::Array* ary, const Operand& result);
/// Generates a matrix type declaration
/// @param mat the matrix to generate
/// @param result the result operand
/// @returns true if the matrix was successfully generated
bool GenerateMatrixType(const type::Matrix* mat, const Operand& result);
/// Generates a pointer type declaration
/// @param ptr the pointer type to generate
/// @param result the result operand
/// @returns true if the pointer was successfully generated
bool GeneratePointerType(const type::Pointer* ptr, const Operand& result);
/// Generates a reference type declaration
/// @param ref the reference type to generate
/// @param result the result operand
/// @returns true if the reference was successfully generated
bool GenerateReferenceType(const type::Reference* ref, const Operand& result);
/// Generates a vector type declaration
/// @param struct_type the vector to generate
/// @param result the result operand
/// @returns true if the vector was successfully generated
bool GenerateStructType(const sem::Struct* struct_type, const Operand& result);
/// Generates a struct member
/// @param struct_id the id of the parent structure
/// @param idx the index of the member
/// @param member the member to generate
/// @returns the id of the struct member or 0 on error.
uint32_t GenerateStructMember(uint32_t struct_id,
uint32_t idx,
const sem::StructMember* member);
/// Generates a variable declaration statement
/// @param stmt the statement to generate
/// @returns true on successfull generation
bool GenerateVariableDeclStatement(const ast::VariableDeclStatement* stmt);
/// Generates a vector type declaration
/// @param vec the vector to generate
/// @param result the result operand
/// @returns true if the vector was successfully generated
bool GenerateVectorType(const type::Vector* vec, const Operand& result);
/// Generates instructions to splat `scalar_id` into a vector of type
/// `vec_type`
/// @param scalar_id scalar to splat
/// @param vec_type type of vector
/// @returns id of the new vector
uint32_t GenerateSplat(uint32_t scalar_id, const type::Type* vec_type);
/// Generates instructions to add or subtract two matrices
/// @param lhs_id id of multiplicand
/// @param rhs_id id of multiplier
/// @param type type of both matrices and of result
/// @param op one of `spv::Op::OpFAdd` or `spv::Op::OpFSub`
/// @returns id of the result matrix
uint32_t GenerateMatrixAddOrSub(uint32_t lhs_id,
uint32_t rhs_id,
const type::Matrix* type,
spv::Op op);
/// Converts TexelFormat to SPIR-V and pushes an appropriate capability.
/// @param format AST image format type
/// @returns SPIR-V image format type
SpvImageFormat convert_texel_format_to_spv(const type::TexelFormat format);
/// Determines if the given type initializer is created from constant values
/// @param expr the expression to check
/// @returns true if the initializer is constant
bool IsInitializerConst(const ast::Expression* expr);
private:
/// @returns an Operand with a new result ID in it. Increments the next_id_
/// automatically.
Operand result_op();
/// @returns the resolved type of the ast::Expression `expr`
/// @param expr the expression
const type::Type* TypeOf(const ast::Expression* expr) const { return builder_.TypeOf(expr); }
/// Generates a constant value if needed
/// @param constant the constant to generate.
/// @returns the ID on success or 0 on failure
uint32_t GenerateConstantIfNeeded(const constant::Value* constant);
/// Generates a scalar constant if needed
/// @param constant the constant to generate.
/// @returns the ID on success or 0 on failure
uint32_t GenerateConstantIfNeeded(const ScalarConstant& constant);
/// Generates a constant-null of the given type, if needed
/// @param type the type of the constant null to generate.
/// @returns the ID on success or 0 on failure
uint32_t GenerateConstantNullIfNeeded(const type::Type* type);
/// Generates a vector constant splat if needed
/// @param type the type of the vector to generate
/// @param value_id the ID of the scalar value to splat
/// @returns the ID on success or 0 on failure
uint32_t GenerateConstantVectorSplatIfNeeded(const type::Vector* type, uint32_t value_id);
/// Registers the semantic variable to the given SPIR-V ID
/// @param var the semantic variable
/// @param id the generated SPIR-V identifier for the variable
void RegisterVariable(const sem::Variable* var, uint32_t id);
/// Looks up the SPIR-V ID for the variable, which must have been registered
/// with a call to RegisterVariable()
/// @returns the SPIR-V ID, or 0 if the variable was not found
uint32_t LookupVariableID(const sem::Variable* var);
/// Pushes a new scope
void PushScope();
/// Pops the top-most scope
void PopScope();
ProgramBuilder builder_;
std::string error_;
uint32_t next_id_ = 1;
uint32_t current_label_id_ = 0;
InstructionList capabilities_;
InstructionList extensions_;
InstructionList ext_imports_;
InstructionList memory_model_;
InstructionList entry_points_;
InstructionList execution_modes_;
InstructionList debug_;
InstructionList types_;
InstructionList annotations_;
std::vector<Function> functions_;
// Scope holds per-block information
struct Scope {
Scope();
Scope(const Scope&);
~Scope();
std::unordered_map<OperandListKey, uint32_t> type_init_to_id_;
};
std::unordered_map<const sem::Variable*, uint32_t> var_to_id_;
std::unordered_map<uint32_t, const sem::Variable*> id_to_var_;
std::unordered_map<std::string, uint32_t> import_name_to_id_;
std::unordered_map<Symbol, uint32_t> func_symbol_to_id_;
std::unordered_map<sem::CallTargetSignature, uint32_t> func_sig_to_id_;
std::unordered_map<const type::Type*, uint32_t> type_to_id_;
std::unordered_map<ScalarConstant, uint32_t> const_to_id_;
std::unordered_map<const type::Type*, uint32_t> const_null_to_id_;
std::unordered_map<uint64_t, uint32_t> const_splat_to_id_;
std::unordered_map<const type::Type*, uint32_t> texture_type_to_sampled_image_type_id_;
std::vector<Scope> scope_stack_;
std::vector<uint32_t> merge_stack_;
std::vector<uint32_t> continue_stack_;
std::unordered_set<uint32_t> capability_set_;
bool zero_initialize_workgroup_memory_ = false;
struct ContinuingInfo {
ContinuingInfo(const ast::Statement* last_statement,
uint32_t loop_header_id,
uint32_t break_target_id);
// The last statement in the continiung block.
const ast::Statement* const last_statement = nullptr;
// The ID of the loop header
const uint32_t loop_header_id = 0u;
// The ID of the merge block for the loop.
const uint32_t break_target_id = 0u;
};
// Stack of nodes, where each is the last statement in a surrounding
// continuing block.
std::vector<ContinuingInfo> continuing_stack_;
// The instruction to emit as the backedge of a loop.
struct Backedge {
Backedge(spv::Op, OperandList);
Backedge(const Backedge&);
Backedge& operator=(const Backedge&);
~Backedge();
spv::Op opcode;
OperandList operands;
};
std::vector<Backedge> backedge_stack_;
};
} // namespace tint::writer::spirv
#endif // SRC_TINT_WRITER_SPIRV_BUILDER_H_