blob: d1b0e35f84f48e41098ee4c6bcd216f18425782e [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_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_
#define SRC_TINT_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "src/tint/utils/compiler_macros.h"
#if TINT_BUILD_SPV_READER
TINT_BEGIN_DISABLE_WARNING(NEWLINE_EOF);
TINT_BEGIN_DISABLE_WARNING(OLD_STYLE_CAST);
TINT_BEGIN_DISABLE_WARNING(SIGN_CONVERSION);
TINT_BEGIN_DISABLE_WARNING(WEAK_VTABLES);
#include "source/opt/ir_context.h"
TINT_END_DISABLE_WARNING(WEAK_VTABLES);
TINT_END_DISABLE_WARNING(SIGN_CONVERSION);
TINT_END_DISABLE_WARNING(OLD_STYLE_CAST);
TINT_END_DISABLE_WARNING(NEWLINE_EOF);
#endif
#include "gtest/gtest.h"
#include "src/tint/demangler.h"
#include "src/tint/reader/spirv/fail_stream.h"
#include "src/tint/reader/spirv/function.h"
#include "src/tint/reader/spirv/namer.h"
#include "src/tint/reader/spirv/parser_impl.h"
#include "src/tint/reader/spirv/spirv_tools_helpers_test.h"
#include "src/tint/reader/spirv/usage.h"
namespace tint::reader::spirv {
namespace test {
/// A test class that wraps ParseImpl
class ParserImplWrapperForTest {
public:
/// Constructor
/// @param input the input data to parse
explicit ParserImplWrapperForTest(const std::vector<uint32_t>& input);
/// Dumps SPIR-V if the conversion succeeded, then destroys the wrapper.
~ParserImplWrapperForTest();
/// Sets global state to force dumping of the assembly text of succesfully
/// SPIR-V.
static void DumpSuccessfullyConvertedSpirv() { dump_successfully_converted_spirv_ = true; }
/// Marks the test has having deliberately invalid SPIR-V
void DeliberatelyInvalidSpirv() { skip_dumping_spirv_ = true; }
/// Marks the test's SPIR-V as not being suitable for dumping, for a stated
/// reason.
void SkipDumpingPending(std::string) { skip_dumping_spirv_ = true; }
/// @returns a new function emitter for the given function ID.
/// Assumes ParserImpl::BuildInternalRepresentation has been run and
/// succeeded.
/// @param function_id the SPIR-V identifier of the function
FunctionEmitter function_emitter(uint32_t function_id) {
auto* spirv_function = impl_.ir_context()->GetFunction(function_id);
return FunctionEmitter(&impl_, *spirv_function);
}
/// Run the parser
/// @returns true if the parse was successful, false otherwise.
bool Parse() { return impl_.Parse(); }
/// @returns the program. The program builder in the parser will be reset
/// after this.
Program program() { return impl_.program(); }
/// @returns the namer object
Namer& namer() { return impl_.namer(); }
/// @returns a reference to the internal builder, without building the
/// program. To be used only for testing.
ProgramBuilder& builder() { return impl_.builder(); }
/// @returns the accumulated error string
const std::string error() { return impl_.error(); }
/// @return true if failure has not yet occurred
bool success() { return impl_.success(); }
/// 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() { return impl_.Fail(); }
/// @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 impl_.ir_context(); }
/// 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. Also computes a topological
/// ordering of the functions.
/// This is a no-op if the parser has already failed.
/// @returns true if the parser is still successful.
bool BuildInternalModule() { return impl_.BuildInternalModule(); }
/// 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 impl_.BuildAndParseInternalModuleExceptFunctions();
}
/// 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 impl_.BuildAndParseInternalModule(); }
/// 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() { return impl_.RegisterUserAndStructMemberNames(); }
/// 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() { return impl_.RegisterTypes(); }
/// Register sampler and texture usage for memory object declarations.
/// This must be called after we've registered line numbers for all
/// instructions. This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool RegisterHandleUsage() { return impl_.RegisterHandleUsage(); }
/// Emits module-scope variables.
/// This is a no-op if the parser has already failed.
/// @returns true if parser is still successful.
bool EmitModuleScopeVariables() { return impl_.EmitModuleScopeVariables(); }
/// @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 impl_.glsl_std_450_imports();
}
/// Converts a SPIR-V type to a Tint type, and saves it for fast lookup.
/// If the type is only used for builtins, then register that specially,
/// and return null. If the type is a sampler, image, or sampled image, then
/// return the Void type, because those opaque types are handled in a
/// different way.
/// On failure, logs an error and returns null. This should only be called
/// after the internal representation of the module has been built.
/// @param id the SPIR-V ID of a type.
/// @returns a Tint type, or nullptr
const Type* ConvertType(uint32_t id) { return impl_.ConvertType(id); }
/// 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 { return impl_.GetDecorationsFor(id); }
/// 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 {
return impl_.GetDecorationsForMember(id, member_index);
}
/// Converts a SPIR-V struct member decoration into a number of AST
/// decorations. If the decoration is recognized but deliberately dropped,
/// then returns an empty list without a diagnostic. On failure, emits a
/// diagnostic and returns an empty list.
/// @param struct_type_id the ID of the struct type
/// @param member_index the index of the member
/// @param member_ty the type of the member
/// @param decoration an encoded SPIR-V Decoration
/// @returns the AST decorations
auto ConvertMemberDecoration(uint32_t struct_type_id,
uint32_t member_index,
const Type* member_ty,
const Decoration& decoration) {
return impl_.ConvertMemberDecoration(struct_type_id, member_index, member_ty, decoration);
}
/// For a SPIR-V ID that might define a sampler, image, or sampled image
/// value, return the SPIR-V instruction that represents the memory object
/// declaration for the object. If we encounter an OpSampledImage along the
/// way, follow the image operand when follow_image is true; otherwise follow
/// the sampler operand. Returns nullptr if we can't trace back to a memory
/// object declaration. Emits an error and returns nullptr when the scan
/// fails due to a malformed module. This method can be used any time after
/// BuildInternalModule has been invoked.
/// @param id the SPIR-V ID of the sampler, image, or sampled image
/// @param follow_image indicates whether to follow the image operand of
/// OpSampledImage
/// @returns the memory object declaration for the handle, or nullptr
const spvtools::opt::Instruction* GetMemoryObjectDeclarationForHandle(uint32_t id,
bool follow_image) {
return impl_.GetMemoryObjectDeclarationForHandle(id, follow_image);
}
/// @param entry_point the SPIR-V ID of an entry point.
/// @returns the entry point info for the given ID
const std::vector<EntryPointInfo>& GetEntryPointInfo(uint32_t entry_point) {
return impl_.GetEntryPointInfo(entry_point);
}
/// Returns the handle usage for a memory object declaration.
/// @param id SPIR-V ID of a sampler or image OpVariable or
/// OpFunctionParameter
/// @returns the handle usage, or an empty usage object.
Usage GetHandleUsage(uint32_t id) const { return impl_.GetHandleUsage(id); }
/// Returns the SPIR-V instruction with the given ID, or nullptr.
/// @param id the SPIR-V result ID
/// @returns the instruction, or nullptr on error
const spvtools::opt::Instruction* GetInstructionForTest(uint32_t id) const {
return impl_.GetInstructionForTest(id);
}
/// @returns info about the gl_Position builtin variable.
const ParserImpl::BuiltInPositionInfo& GetBuiltInPositionInfo() {
return impl_.GetBuiltInPositionInfo();
}
/// Returns the source record for the SPIR-V instruction with the given
/// result ID.
/// @param id the SPIR-V result id.
/// @return the Source record, or a default one
Source GetSourceForResultIdForTest(uint32_t id) const {
return impl_.GetSourceForResultIdForTest(id);
}
private:
ParserImpl impl_;
/// When true, indicates the input SPIR-V module should not be emitted.
/// It's either deliberately invalid, or not supported for some pending
/// reason.
bool skip_dumping_spirv_ = false;
static bool dump_successfully_converted_spirv_;
};
// Sets global state to force dumping of the assembly text of succesfully
// SPIR-V.
inline void DumpSuccessfullyConvertedSpirv() {
ParserImplWrapperForTest::DumpSuccessfullyConvertedSpirv();
}
/// Returns the WGSL printed string of a program.
/// @param program the Program
/// @returns the WGSL printed string the program.
std::string ToString(const Program& program);
/// Returns the WGSL printed string of a statement list.
/// @param program the Program
/// @param stmts the statement list
/// @returns the WGSL printed string of a statement list.
std::string ToString(const Program& program, utils::VectorRef<const ast::Statement*> stmts);
/// Returns the WGSL printed string of an AST node.
/// @param program the Program
/// @param node the AST node
/// @returns the WGSL printed string of the AST node.
std::string ToString(const Program& program, const ast::Node* node);
} // namespace test
/// SPIR-V Parser test class
template <typename T>
class SpvParserTestBase : public T {
public:
SpvParserTestBase() = default;
~SpvParserTestBase() override = default;
/// Retrieves the parser from the helper
/// @param input the SPIR-V binary to parse
/// @returns a parser for the given binary
std::unique_ptr<test::ParserImplWrapperForTest> parser(const std::vector<uint32_t>& input) {
auto parser = std::make_unique<test::ParserImplWrapperForTest>(input);
// Don't run the Resolver when building the program.
// We're not interested in type information with these tests.
parser->builder().SetResolveOnBuild(false);
return parser;
}
};
/// SpvParserTest the the base class for SPIR-V reader unit tests.
/// Use this form when you don't need to template any further.
using SpvParserTest = SpvParserTestBase<::testing::Test>;
} // namespace tint::reader::spirv
#endif // SRC_TINT_READER_SPIRV_PARSER_IMPL_TEST_HELPER_H_