dan sinclair 7cac245abc [spirv-writer] Handle multi name swizzles.
This CL rebuilds the accessor code to allow generating multi item
swizzles. This requires being able to output the access chain in the
middle of the access chain and then work with the results of that access
chain.

Bug: tint:5
Change-Id: I0687509c9ddec6a2e13d9e3595f04a091ee9af7b
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/20623
Reviewed-by: David Neto <dneto@google.com>
2020-05-01 16:17:03 +00:00

372 lines
16 KiB
C++

// 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_WRITER_SPIRV_BUILDER_H_
#define SRC_WRITER_SPIRV_BUILDER_H_
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
#include "spirv/unified1/spirv.h"
#include "src/ast/builtin.h"
#include "src/ast/else_statement.h"
#include "src/ast/literal.h"
#include "src/ast/module.h"
#include "src/ast/struct_member.h"
#include "src/ast/type_constructor_expression.h"
#include "src/scope_stack.h"
#include "src/writer/spirv/function.h"
#include "src/writer/spirv/instruction.h"
namespace tint {
namespace writer {
namespace spirv {
/// Builder class to create SPIR-V instructions from a module.
class Builder {
public:
/// Contains information for generating accessor chains
struct 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.
ast::type::Type* source_type;
/// A list of access chain indices to emit. Note, we _only_ have access
/// chain indices if the source is pointer.
std::vector<uint32_t> access_chain_indices;
};
/// Constructor
/// @param mod the module to generate from
explicit Builder(ast::Module* mod);
~Builder();
/// Generates the SPIR-V instructions for the given module
/// @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 module
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;
}
/// Sets the id for a given function name
/// @param name the name to set
/// @param id the id to set
void set_func_name_to_id(const std::string& name, uint32_t id) {
func_name_to_id_[name] = id;
}
/// Retrives the id for the given function name
/// @param name the function name to search for
/// @returns the id for the given name or 0 on failure
uint32_t id_for_func_name(const std::string& name) {
if (func_name_to_id_.count(name) == 0) {
return 0;
}
return func_name_to_id_[name];
}
/// 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 preamble
/// @param op the op to set
/// @param operands the operands for the instruction
void push_preamble(spv::Op op, const std::vector<Operand>& operands) {
preamble_.push_back(Instruction{op, operands});
}
/// @returns the preamble
const std::vector<Instruction>& preamble() const { return preamble_; }
/// 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 std::vector<Operand>& operands) {
debug_.push_back(Instruction{op, operands});
}
/// @returns the debug instructions
const std::vector<Instruction>& 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 std::vector<Operand>& operands) {
types_.push_back(Instruction{op, operands});
}
/// @returns the type instructions
const std::vector<Instruction>& 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 std::vector<Operand>& operands) {
annotations_.push_back(Instruction{op, operands});
}
/// @returns the annotations
const std::vector<Instruction>& 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); }
/// @returns the functions
const std::vector<Function>& functions() const { return functions_; }
/// Pushes an instruction to the current function
/// @param op the operation
/// @param operands the operands
void push_function_inst(spv::Op op, const std::vector<Operand>& operands) {
functions_.back().push_inst(op, operands);
}
/// Pushes a variable to the current function
/// @param operands the variable operands
void push_function_var(const std::vector<Operand>& operands) {
functions_.back().push_var(operands);
}
/// Converts a storage class to a SPIR-V storage class.
/// @param klass the storage class to convert
/// @returns the SPIR-V storage class or SpvStorageClassMax on error.
SpvStorageClass ConvertStorageClass(ast::StorageClass klass) const;
/// Converts a builtin to a SPIR-V builtin
/// @param builtin the builtin to convert
/// @returns the SPIR-V builtin or SpvBuiltInMax on error.
SpvBuiltIn ConvertBuiltin(ast::Builtin builtin) const;
/// Generates a uint32_t literal.
/// @param val the value to generate
/// @returns the ID of the generated literal
uint32_t GenerateU32Literal(uint32_t val);
/// Generates an assignment statement
/// @param assign the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateAssignStatement(ast::AssignmentStatement* assign);
/// Generates a break statement
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateBreakStatement(ast::BreakStatement* stmt);
/// Generates a continue statement
/// @param stmt the statement to generate
/// @returns true if the statement was successfully generated
bool GenerateContinueStatement(ast::ContinueStatement* stmt);
/// Generates an entry point instruction
/// @param ep the entry point
/// @returns true if the instruction was generated, false otherwise
bool GenerateEntryPoint(ast::EntryPoint* ep);
/// Generates an expression
/// @param expr the expression to generate
/// @returns the resulting ID of the exp = {};ression or 0 on error
uint32_t GenerateExpression(ast::Expression* expr);
/// Generates the instructions for a function
/// @param func the function to generate
/// @returns true if the instructions were generated
bool GenerateFunction(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(ast::Function* func);
/// Generates a function variable
/// @param var the variable
/// @returns true if the variable was generated
bool GenerateFunctionVariable(ast::Variable* var);
/// Generates a global variable
/// @param var the variable to generate
/// @returns true if the variable is emited.
bool GenerateGlobalVariable(ast::Variable* var);
/// Generates an array 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(ast::Expression* expr);
/// Generates an array accessor
/// @param expr the accessor to generate
/// @param info the current accessor information
/// @returns true if the accessor was generated successfully
bool GenerateArrayAccessor(ast::ArrayAccessorExpression* 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(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(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(ast::UnaryOpExpression* expr);
/// Generates an if statement
/// @param stmt the statement to generate
/// @returns true on success
bool GenerateIfStatement(ast::IfStatement* stmt);
/// Generates an import instruction
/// @param imp the import
void GenerateImport(ast::Import* imp);
/// Generates a constructor expression
/// @param expr the expression to generate
/// @param is_global_init set true if this is a global variable constructor
/// @returns the ID of the expression or 0 on failure.
uint32_t GenerateConstructorExpression(ast::ConstructorExpression* expr,
bool is_global_init);
/// Generates a type constructor expression
/// @param init the expression to generate
/// @param is_global_init set true if this is a global variable constructor
/// @returns the ID of the expression or 0 on failure.
uint32_t GenerateTypeConstructorExpression(
ast::TypeConstructorExpression* init,
bool is_global_init);
/// Generates a literal constant if needed
/// @param lit the literal to generate
/// @returns the ID on success or 0 on failure
uint32_t GenerateLiteralIfNeeded(ast::Literal* lit);
/// Generates a binary expression
/// @param expr the expression to generate
/// @returns the expression ID on success or 0 otherwise
uint32_t GenerateBinaryExpression(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(ast::CallExpression* expr);
/// Generates a loop statement
/// @param stmt the statement to generate
/// @returns true on successful generation
bool GenerateLoopStatement(ast::LoopStatement* stmt);
/// Generates a return statement
/// @param stmt the statement to generate
/// @returns true on success, false otherwise
bool GenerateReturnStatement(ast::ReturnStatement* stmt);
/// Generates a conditional section merge block
/// @param cond the condition
/// @param true_body the statements making up the true block
/// @param cur_else_idx the index of the current else statement to process
/// @param else_stmts the list of all else statements
/// @returns true on success, false on failure
bool GenerateConditionalBlock(ast::Expression* cond,
const ast::StatementList& true_body,
size_t cur_else_idx,
const ast::ElseStatementList& else_stmts);
/// Generates a statement
/// @param stmt the statement to generate
/// @returns true if the statement was generated
bool GenerateStatement(ast::Statement* stmt);
/// Generates a list of statements
/// @param list the statement list to generate
/// @returns true on successful generation
bool GenerateStatementList(const ast::StatementList& list);
/// Geneates an OpLoad
/// @param type the type to load
/// @param id the variable id to load
/// @returns the ID of the loaded value or |id| if type is not a pointer
uint32_t GenerateLoadIfNeeded(ast::type::Type* type, uint32_t id);
/// Geneates an OpStore
/// @param to the ID to store too
/// @param from the ID to store from
void 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(ast::type::Type* type);
/// 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(ast::type::ArrayType* 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(ast::type::MatrixType* 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(ast::type::PointerType* ptr, 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(ast::type::StructType* 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,
ast::StructMember* member);
/// Generates a variable declaration statement
/// @param stmt the statement to generate
/// @returns true on successfull generation
bool GenerateVariableDeclStatement(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(ast::type::VectorType* vec, const Operand& result);
private:
/// @returns an Operand with a new result ID in it. Increments the next_id_
/// automatically.
Operand result_op();
ast::Module* mod_;
std::string error_;
uint32_t next_id_ = 1;
std::vector<Instruction> preamble_;
std::vector<Instruction> debug_;
std::vector<Instruction> types_;
std::vector<Instruction> annotations_;
std::vector<Function> functions_;
std::unordered_map<std::string, uint32_t> import_name_to_id_;
std::unordered_map<std::string, uint32_t> func_name_to_id_;
std::unordered_map<std::string, uint32_t> type_name_to_id_;
std::unordered_map<std::string, uint32_t> const_to_id_;
ScopeStack<uint32_t> scope_stack_;
std::unordered_map<uint32_t, ast::Variable*> spirv_id_to_variable_;
std::vector<uint32_t> merge_stack_;
std::vector<uint32_t> continue_stack_;
};
} // namespace spirv
} // namespace writer
} // namespace tint
#endif // SRC_WRITER_SPIRV_BUILDER_H_