blob: c4b717fbb76cbe0d00eac354f8c4bfc6d3c8c1a0 [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_READER_SPIRV_PARSER_IMPL_H_
#define SRC_READER_SPIRV_PARSER_IMPL_H_
#include <cstdint>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "source/opt/constants.h"
#include "source/opt/decoration_manager.h"
#include "source/opt/ir_context.h"
#include "source/opt/module.h"
#include "source/opt/type_manager.h"
#include "source/opt/types.h"
#include "spirv-tools/libspirv.hpp"
#include "src/ast/expression.h"
#include "src/ast/import.h"
#include "src/ast/module.h"
#include "src/ast/struct_member_decoration.h"
#include "src/ast/type/alias_type.h"
#include "src/ast/type/type.h"
#include "src/reader/reader.h"
#include "src/reader/spirv/enum_converter.h"
#include "src/reader/spirv/fail_stream.h"
#include "src/reader/spirv/namer.h"
namespace tint {
namespace reader {
namespace spirv {
/// The binary representation of a SPIR-V decoration enum followed by its
/// operands, if any.
/// Example: { SpvDecorationBlock }
/// Example: { SpvDecorationArrayStride, 16 }
using Decoration = std::vector<uint32_t>;
using DecorationList = std::vector<Decoration>;
/// An AST expression with its type.
struct TypedExpression {
/// Dummy constructor
TypedExpression();
/// Constructor
/// @param t the type
/// @param e the expression
TypedExpression(ast::type::Type* t, std::unique_ptr<ast::Expression> e);
/// Move constructor
/// @param other the other typed expression
TypedExpression(TypedExpression&& other);
/// Destructor
~TypedExpression();
/// Takes values from another typed expression.
/// @param other the other typed expression
void reset(TypedExpression&& other);
/// The type
ast::type::Type* type;
/// The expression
std::unique_ptr<ast::Expression> expr;
};
/// Parser implementation for SPIR-V.
class ParserImpl : Reader {
public:
/// Creates a new parser
/// @param ctx the non-null context object
/// @param input the input data to parse
ParserImpl(Context* ctx, const std::vector<uint32_t>& input);
/// Destructor
~ParserImpl() override;
/// Run the parser
/// @returns true if the parse was successful, false otherwise.
bool Parse() override;
/// @returns the module. The module in the parser will be reset after this.
ast::Module module() override;
/// Returns a pointer to the module, without resetting it.
/// @returns the module
ast::Module& get_module() { return ast_module_; }
/// Logs failure, ands return a failure stream to accumulate diagnostic
/// messages. By convention, a failure should only be logged along with
/// a non-empty string diagnostic.
/// @returns the failure stream
FailStream& Fail() {
success_ = false;
return fail_stream_;
}
/// @returns the accumulated error string
const std::string error() { return errors_.str(); }
/// Builds an internal representation of the SPIR-V binary,
/// and parses it into a Tint AST module. Diagnostics are emitted
/// to the error stream.
/// @returns true if it was successful.
bool BuildAndParseInternalModule() {
return BuildInternalModule() && ParseInternalModule();
}
/// Builds an internal representation of the SPIR-V binary,
/// and parses the module, except functions, into a Tint AST module.
/// Diagnostics are emitted to the error stream.
/// @returns true if it was successful.
bool BuildAndParseInternalModuleExceptFunctions() {
return BuildInternalModule() && ParseInternalModuleExceptFunctions();
}
/// @returns the set of SPIR-V IDs for imports of the "GLSL.std.450"
/// extended instruction set.
const std::unordered_set<uint32_t>& glsl_std_450_imports() const {
return glsl_std_450_imports_;
}
/// @returns the import prefix to use for the GLSL.std.450 import.
std::string GlslStd450Prefix() const { return "std::glsl"; }
/// Converts a SPIR-V type to a Tint type, and saves it for fast lookup.
/// On failure, logs an error and returns null. This should only be called
/// after the internal representation of the module has been built.
/// @param type_id the SPIR-V ID of a type.
/// @returns a Tint type, or nullptr
ast::type::Type* ConvertType(uint32_t type_id);
/// Emits an alias type declaration for the given type, if necessary, and
/// also updates the mapping of the SPIR-V type ID to the alias type.
/// Do so for the types requiring user-specified names:
/// - struct types
/// - decorated arrays and runtime arrays
/// TODO(dneto): I expect images and samplers to require names as well.
/// This is a no-op if the parser has already failed.
/// @param type the type that might get an alias
void MaybeGenerateAlias(const spvtools::opt::analysis::Type* type);
/// @returns the fail stream object
FailStream& fail_stream() { return fail_stream_; }
/// @returns the namer object
Namer& namer() { return namer_; }
/// @returns a borrowed pointer to the internal representation of the module.
/// This is null until BuildInternalModule has been called.
spvtools::opt::IRContext* ir_context() { return ir_context_.get(); }
/// Gets the list of decorations for a SPIR-V result ID. Returns an empty
/// vector if the ID is not a result ID, or if no decorations target that ID.
/// The internal representation must have already been built.
/// @param id SPIR-V ID
/// @returns the list of decorations on the given ID
DecorationList GetDecorationsFor(uint32_t id) const;
/// Gets the list of decorations for the member of a struct. Returns an empty
/// list if the |id| is not the ID of a struct, or if the member index is out
/// of range, or if the target member has no decorations.
/// The internal representation must have already been built.
/// @param id SPIR-V ID of a struct
/// @param member_index the member within the struct
/// @returns the list of decorations on the member
DecorationList GetDecorationsForMember(uint32_t id,
uint32_t member_index) const;
/// Converts a SPIR-V decoration. On failure, emits a diagnostic and returns
/// nullptr.
/// @param decoration an encoded SPIR-V Decoration
/// @returns the corresponding ast::StructuMemberDecoration
std::unique_ptr<ast::StructMemberDecoration> ConvertMemberDecoration(
const Decoration& decoration);
/// Builds the internal representation of the SPIR-V module.
/// Assumes the module is somewhat well-formed. Normally you
/// would want to validate the SPIR-V module before attempting
/// to build this internal representation.
/// This is a no-op if the parser has already failed.
/// @returns true if the parser is still successful.
bool BuildInternalModule();
/// Walks the internal representation of the module to populate
/// the AST form of the module.
/// This is a no-op if the parser has already failed.
/// @returns true if the parser is still successful.
bool ParseInternalModule();
/// Walks the internal representation of the module, except for function
/// definitions, to populate the AST form of the module.
/// This is a no-op if the parser has already failed.
/// @returns true if the parser is still successful.
bool ParseInternalModuleExceptFunctions();
/// Destroys the internal representation of the SPIR-V module.
void ResetInternalModule();
/// Registers extended instruction imports. Only "GLSL.std.450" is supported.
/// This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool RegisterExtendedInstructionImports();
/// Registers user names for SPIR-V objects, from OpName, and OpMemberName.
/// Also synthesizes struct field names. Ensures uniqueness for names for
/// SPIR-V IDs, and uniqueness of names of fields within any single struct.
/// This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool RegisterUserAndStructMemberNames();
/// Emit entry point AST nodes.
/// This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool EmitEntryPoints();
/// Register Tint AST types for SPIR-V types, including type aliases as
/// needed. This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool RegisterTypes();
/// Emits module-scope variables.
/// This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool EmitModuleScopeVariables();
/// Emits functions, with callees preceding their callers.
/// This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool EmitFunctions();
/// Emits a single function, if it has a body.
/// This is a no-op if the parser has already failed.
/// @param f the function to emit
/// @returns true if parser is still successful.
bool EmitFunction(const spvtools::opt::Function& f);
/// Creates an AST Variable node for a SPIR-V ID, including any attached
/// decorations.
/// @param id the SPIR-V result ID
/// @param sc the storage class, which cannot be ast::StorageClass::kNone
/// @param type the type
/// @returns a new Variable node, or null in the error case
std::unique_ptr<ast::Variable> MakeVariable(uint32_t id,
ast::StorageClass sc,
ast::type::Type* type);
/// Creates an AST expression node for a SPIR-V constant.
/// @param id the SPIR-V ID of the constant
/// @returns a new expression
TypedExpression MakeConstantExpression(uint32_t id);
/// Creates an AST expression node for the null value for the given type.
/// @param type the AST type
/// @returns a new expression
std::unique_ptr<ast::Expression> MakeNullValue(ast::type::Type* type);
/// Converts a given expression to the signedness demanded for an operand
/// of the given SPIR-V opcode, if required. If the operation assumes
/// signed integer operands, and |expr| is unsigned, then return an
/// as-cast expression converting it to signed. Otherwise, return
/// |expr| itself. Similarly, convert as required from unsigned
/// to signed. Assumes all SPIR-V types have been mapped to AST types.
/// @param op the SPIR-V opcode
/// @param expr an expression
/// @returns expr, or a cast of expr
TypedExpression RectifyOperandSignedness(SpvOp op, TypedExpression&& expr);
/// Returns the "forced" result type for the given SPIR-V opcode.
/// If the WGSL result type for an operation has a more strict rule than
/// requried by SPIR-V, then we say the result type is "forced". This occurs
/// for signed integer division (OpSDiv), for example, where the result type
/// in WGSL must match the operand types.
/// @param op the SPIR-V opcode
/// @param first_operand_type the AST type for the first operand.
/// @returns the forced AST result type, or nullptr if no forcing is required.
ast::type::Type* ForcedResultType(SpvOp op,
ast::type::Type* first_operand_type);
/// Returns a signed integer scalar or vector type matching the shape (scalar,
/// vector, and component bit width) of another type, which itself is a
/// numeric scalar or vector. Returns null if the other type does not meet the
/// requirement.
/// @param other the type whose shape must be matched
/// @returns the signed scalar or vector type
ast::type::Type* GetSignedIntMatchingShape(ast::type::Type* other);
/// Returns a signed integer scalar or vector type matching the shape (scalar,
/// vector, and component bit width) of another type, which itself is a
/// numeric scalar or vector. Returns null if the other type does not meet the
/// requirement.
/// @param other the type whose shape must be matched
/// @returns the unsigned scalar or vector type
ast::type::Type* GetUnsignedIntMatchingShape(ast::type::Type* other);
/// Wraps the given expression in an as-cast to the given expression's type,
/// when the underlying operation produces a forced result type different
/// from the expression's result type. Otherwise, returns the given expression
/// unchanged.
/// @param expr the expression to pass through or to wrap
/// @param op the SPIR-V opcode
/// @param first_operand_type the AST type for the first operand.
/// @returns the forced AST result type, or nullptr if no forcing is required.
TypedExpression RectifyForcedResultType(TypedExpression expr,
SpvOp op,
ast::type::Type* first_operand_type);
/// @returns the registered boolean type.
ast::type::Type* BoolType() const { return bool_type_; }
private:
/// Converts a specific SPIR-V type to a Tint type. Integer case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Integer* int_ty);
/// Converts a specific SPIR-V type to a Tint type. Float case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Float* float_ty);
/// Converts a specific SPIR-V type to a Tint type. Vector case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Vector* vec_ty);
/// Converts a specific SPIR-V type to a Tint type. Matrix case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Matrix* mat_ty);
/// Converts a specific SPIR-V type to a Tint type. RuntimeArray case
ast::type::Type* ConvertType(
const spvtools::opt::analysis::RuntimeArray* rtarr_ty);
/// Converts a specific SPIR-V type to a Tint type. Array case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Array* arr_ty);
/// Converts a specific SPIR-V type to a Tint type. Struct case
ast::type::Type* ConvertType(
const spvtools::opt::analysis::Struct* struct_ty);
/// Converts a specific SPIR-V type to a Tint type. Pointer case
ast::type::Type* ConvertType(const spvtools::opt::analysis::Pointer* ptr_ty);
/// Applies SPIR-V decorations to the given array or runtime-array type.
/// @param spv_type the SPIR-V aray or runtime-array type.
/// @param ast_type non-null; the AST type to apply decorations to
/// @returns true on success.
bool ApplyArrayDecorations(const spvtools::opt::analysis::Type* spv_type,
ast::type::ArrayType* ast_type);
// The SPIR-V binary we're parsing
std::vector<uint32_t> spv_binary_;
// The resulting module in Tint AST form.
ast::Module ast_module_;
// Is the parse successful?
bool success_ = true;
// Collector for diagnostic messages.
std::stringstream errors_;
FailStream fail_stream_;
spvtools::MessageConsumer message_consumer_;
// The registered boolean type.
ast::type::Type* bool_type_;
// An object used to store and generate names for SPIR-V objects.
Namer namer_;
// An object used to convert SPIR-V enums to Tint enums
EnumConverter enum_converter_;
// The internal representation of the SPIR-V module and its context.
spvtools::Context tools_context_;
// All the state is owned by ir_context_.
std::unique_ptr<spvtools::opt::IRContext> ir_context_;
// The following are borrowed pointers to the internal state of ir_context_.
spvtools::opt::Module* module_ = nullptr;
spvtools::opt::analysis::DefUseManager* def_use_mgr_ = nullptr;
spvtools::opt::analysis::ConstantManager* constant_mgr_ = nullptr;
spvtools::opt::analysis::TypeManager* type_mgr_ = nullptr;
spvtools::opt::analysis::DecorationManager* deco_mgr_ = nullptr;
/// Maps a SPIR-V ID for an external instruction import to an AST import
std::unordered_map<uint32_t, ast::Import*> import_map_;
// The set of IDs that are imports of the GLSL.std.450 extended instruction
// sets.
std::unordered_set<uint32_t> glsl_std_450_imports_;
// Maps a SPIR-V type ID to the corresponding Tint type.
std::unordered_map<uint32_t, ast::type::Type*> id_to_type_;
// Maps an unsigned type corresponding to the given signed type.
std::unordered_map<ast::type::Type*, ast::type::Type*> signed_type_for_;
// Maps an signed type corresponding to the given unsigned type.
std::unordered_map<ast::type::Type*, ast::type::Type*> unsigned_type_for_;
};
} // namespace spirv
} // namespace reader
} // namespace tint
#endif // SRC_READER_SPIRV_PARSER_IMPL_H_