blob: 8e43f1273e732f3108349ef178e8dc017748ef03 [file] [log] [blame]
// Copyright 2020 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "gmock/gmock.h"
#include "src/tint/lang/spirv/reader/ast_parser/helper_test.h"
#include "src/tint/lang/spirv/reader/ast_parser/spirv_tools_helpers_test.h"
namespace tint::spirv::reader::ast_parser {
namespace {
using ::testing::Eq;
std::string Preamble() {
return R"(
OpCapability Shader
OpMemoryModel Logical Simple
OpEntryPoint Fragment %main "x_100"
OpExecutionMode %main OriginUpperLeft
)";
}
std::string MainBody() {
return R"(
%void = OpTypeVoid
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%main_entry = OpLabel
OpReturn
OpFunctionEnd
)";
}
TEST_F(SpirvASTParserTest, ConvertType_PreservesExistingFailure) {
auto p = parser(std::vector<uint32_t>{});
p->Fail() << "boing";
auto* type = p->ConvertType(10);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("boing"));
}
TEST_F(SpirvASTParserTest, ConvertType_RequiresInternalRepresntation) {
auto p = parser(std::vector<uint32_t>{});
auto* type = p->ConvertType(10);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("ConvertType called when the internal module has not been built"));
}
TEST_F(SpirvASTParserTest, ConvertType_NotAnId) {
auto assembly = Preamble() + MainBody();
auto p = parser(test::Assemble(assembly));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(900);
EXPECT_EQ(type, nullptr);
EXPECT_EQ(nullptr, type);
EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 900"));
}
TEST_F(SpirvASTParserTest, ConvertType_IdExistsButIsNotAType) {
auto assembly = R"(
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical Simple
OpEntryPoint Fragment %main "x_100"
OpExecutionMode %main OriginUpperLeft
)" + MainBody();
auto p = parser(test::Assemble(assembly));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_EQ(nullptr, type);
EXPECT_THAT(p->error(), Eq("ID is not a SPIR-V type: 1"));
}
TEST_F(SpirvASTParserTest, ConvertType_UnhandledType) {
// Pipes are an OpenCL type. Tint doesn't support them.
auto p = parser(test::Assemble("%70 = OpTypePipe WriteOnly"));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(70);
EXPECT_EQ(nullptr, type);
EXPECT_THAT(p->error(), Eq("unknown SPIR-V type with ID 70: %70 = OpTypePipe WriteOnly"));
}
TEST_F(SpirvASTParserTest, ConvertType_Void) {
auto p = parser(test::Assemble(Preamble() + "%1 = OpTypeVoid" + R"(
%voidfn = OpTypeFunction %1
%main = OpFunction %1 None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)"));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_Bool) {
auto p = parser(test::Assemble(Preamble() + "%100 = OpTypeBool" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(100);
EXPECT_TRUE(type->Is<Bool>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_I32) {
auto p = parser(test::Assemble(Preamble() + "%2 = OpTypeInt 32 1" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(2);
EXPECT_TRUE(type->Is<I32>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_U32) {
auto p = parser(test::Assemble(Preamble() + "%3 = OpTypeInt 32 0" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<U32>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_F32) {
auto p = parser(test::Assemble(Preamble() + "%4 = OpTypeFloat 32" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(4);
EXPECT_TRUE(type->Is<F32>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_BadIntWidth) {
auto p = parser(test::Assemble(Preamble() + "%5 = OpTypeInt 17 1" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(5);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unhandled integer width: 17"));
}
TEST_F(SpirvASTParserTest, ConvertType_BadFloatWidth) {
auto p = parser(test::Assemble(Preamble() + "%6 = OpTypeFloat 19" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(6);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unhandled float width: 19"));
}
TEST_F(SpirvASTParserTest, DISABLED_ConvertType_InvalidVectorElement) {
auto p = parser(test::Assemble(Preamble() + R"(
%5 = OpTypePipe ReadOnly
%20 = OpTypeVector %5 2
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(20);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5"));
}
TEST_F(SpirvASTParserTest, ConvertType_VecOverF32) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%20 = OpTypeVector %float 2
%30 = OpTypeVector %float 3
%40 = OpTypeVector %float 4
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* v2xf32 = p->ConvertType(20);
EXPECT_TRUE(v2xf32->Is<Vector>());
EXPECT_TRUE(v2xf32->As<Vector>()->type->Is<F32>());
EXPECT_EQ(v2xf32->As<Vector>()->size, 2u);
auto* v3xf32 = p->ConvertType(30);
EXPECT_TRUE(v3xf32->Is<Vector>());
EXPECT_TRUE(v3xf32->As<Vector>()->type->Is<F32>());
EXPECT_EQ(v3xf32->As<Vector>()->size, 3u);
auto* v4xf32 = p->ConvertType(40);
EXPECT_TRUE(v4xf32->Is<Vector>());
EXPECT_TRUE(v4xf32->As<Vector>()->type->Is<F32>());
EXPECT_EQ(v4xf32->As<Vector>()->size, 4u);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_VecOverI32) {
auto p = parser(test::Assemble(Preamble() + R"(
%int = OpTypeInt 32 1
%20 = OpTypeVector %int 2
%30 = OpTypeVector %int 3
%40 = OpTypeVector %int 4
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* v2xi32 = p->ConvertType(20);
EXPECT_TRUE(v2xi32->Is<Vector>());
EXPECT_TRUE(v2xi32->As<Vector>()->type->Is<I32>());
EXPECT_EQ(v2xi32->As<Vector>()->size, 2u);
auto* v3xi32 = p->ConvertType(30);
EXPECT_TRUE(v3xi32->Is<Vector>());
EXPECT_TRUE(v3xi32->As<Vector>()->type->Is<I32>());
EXPECT_EQ(v3xi32->As<Vector>()->size, 3u);
auto* v4xi32 = p->ConvertType(40);
EXPECT_TRUE(v4xi32->Is<Vector>());
EXPECT_TRUE(v4xi32->As<Vector>()->type->Is<I32>());
EXPECT_EQ(v4xi32->As<Vector>()->size, 4u);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_VecOverU32) {
auto p = parser(test::Assemble(Preamble() + R"(
%uint = OpTypeInt 32 0
%20 = OpTypeVector %uint 2
%30 = OpTypeVector %uint 3
%40 = OpTypeVector %uint 4
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* v2xu32 = p->ConvertType(20);
EXPECT_TRUE(v2xu32->Is<Vector>());
EXPECT_TRUE(v2xu32->As<Vector>()->type->Is<U32>());
EXPECT_EQ(v2xu32->As<Vector>()->size, 2u);
auto* v3xu32 = p->ConvertType(30);
EXPECT_TRUE(v3xu32->Is<Vector>());
EXPECT_TRUE(v3xu32->As<Vector>()->type->Is<U32>());
EXPECT_EQ(v3xu32->As<Vector>()->size, 3u);
auto* v4xu32 = p->ConvertType(40);
EXPECT_TRUE(v4xu32->Is<Vector>());
EXPECT_TRUE(v4xu32->As<Vector>()->type->Is<U32>());
EXPECT_EQ(v4xu32->As<Vector>()->size, 4u);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, DISABLED_ConvertType_InvalidMatrixElement) {
auto p = parser(test::Assemble(Preamble() + R"(
%5 = OpTypePipe ReadOnly
%10 = OpTypeVector %5 2
%20 = OpTypeMatrix %10 2
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(20);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("unknown SPIR-V type: 5"));
}
TEST_F(SpirvASTParserTest, ConvertType_MatrixOverF32) {
// Matrices are only defined over floats.
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%v2 = OpTypeVector %float 2
%v3 = OpTypeVector %float 3
%v4 = OpTypeVector %float 4
; First digit is rows
; Second digit is columns
%22 = OpTypeMatrix %v2 2
%23 = OpTypeMatrix %v2 3
%24 = OpTypeMatrix %v2 4
%32 = OpTypeMatrix %v3 2
%33 = OpTypeMatrix %v3 3
%34 = OpTypeMatrix %v3 4
%42 = OpTypeMatrix %v4 2
%43 = OpTypeMatrix %v4 3
%44 = OpTypeMatrix %v4 4
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* m22 = p->ConvertType(22);
EXPECT_TRUE(m22->Is<Matrix>());
EXPECT_TRUE(m22->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m22->As<Matrix>()->rows, 2u);
EXPECT_EQ(m22->As<Matrix>()->columns, 2u);
auto* m23 = p->ConvertType(23);
EXPECT_TRUE(m23->Is<Matrix>());
EXPECT_TRUE(m23->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m23->As<Matrix>()->rows, 2u);
EXPECT_EQ(m23->As<Matrix>()->columns, 3u);
auto* m24 = p->ConvertType(24);
EXPECT_TRUE(m24->Is<Matrix>());
EXPECT_TRUE(m24->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m24->As<Matrix>()->rows, 2u);
EXPECT_EQ(m24->As<Matrix>()->columns, 4u);
auto* m32 = p->ConvertType(32);
EXPECT_TRUE(m32->Is<Matrix>());
EXPECT_TRUE(m32->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m32->As<Matrix>()->rows, 3u);
EXPECT_EQ(m32->As<Matrix>()->columns, 2u);
auto* m33 = p->ConvertType(33);
EXPECT_TRUE(m33->Is<Matrix>());
EXPECT_TRUE(m33->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m33->As<Matrix>()->rows, 3u);
EXPECT_EQ(m33->As<Matrix>()->columns, 3u);
auto* m34 = p->ConvertType(34);
EXPECT_TRUE(m34->Is<Matrix>());
EXPECT_TRUE(m34->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m34->As<Matrix>()->rows, 3u);
EXPECT_EQ(m34->As<Matrix>()->columns, 4u);
auto* m42 = p->ConvertType(42);
EXPECT_TRUE(m42->Is<Matrix>());
EXPECT_TRUE(m42->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m42->As<Matrix>()->rows, 4u);
EXPECT_EQ(m42->As<Matrix>()->columns, 2u);
auto* m43 = p->ConvertType(43);
EXPECT_TRUE(m43->Is<Matrix>());
EXPECT_TRUE(m43->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m43->As<Matrix>()->rows, 4u);
EXPECT_EQ(m43->As<Matrix>()->columns, 3u);
auto* m44 = p->ConvertType(44);
EXPECT_TRUE(m44->Is<Matrix>());
EXPECT_TRUE(m44->As<Matrix>()->type->Is<F32>());
EXPECT_EQ(m44->As<Matrix>()->rows, 4u);
EXPECT_EQ(m44->As<Matrix>()->columns, 4u);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_RuntimeArray) {
auto p = parser(test::Assemble(Preamble() + R"(
%uint = OpTypeInt 32 0
%10 = OpTypeRuntimeArray %uint
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->UnwrapAll()->Is<Array>());
auto* arr_type = type->UnwrapAll()->As<Array>();
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size, 0u);
EXPECT_EQ(arr_type->stride, 0u);
auto* elem_type = arr_type->type;
ASSERT_NE(elem_type, nullptr);
EXPECT_TRUE(elem_type->Is<U32>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_RuntimeArray_InvalidDecoration) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %10 Block
%uint = OpTypeInt 32 0
%10 = OpTypeRuntimeArray %uint
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(),
Eq("invalid array type ID 10: unknown decoration 2 with 1 total words"));
}
TEST_F(SpirvASTParserTest, ConvertType_RuntimeArray_ArrayStride_Valid) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %10 ArrayStride 64
%uint = OpTypeInt 32 0
%10 = OpTypeRuntimeArray %uint
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
auto* arr_type = type->UnwrapAll()->As<Array>();
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size, 0u);
EXPECT_EQ(arr_type->stride, 64u);
}
TEST_F(SpirvASTParserTest, ConvertType_RuntimeArray_ArrayStride_ZeroIsError) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %10 ArrayStride 0
%uint = OpTypeInt 32 0
%10 = OpTypeRuntimeArray %uint
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("invalid array type ID 10: ArrayStride can't be 0"));
}
TEST_F(SpirvASTParserTest, ConvertType_Array) {
auto p = parser(test::Assemble(Preamble() + R"(
%uint = OpTypeInt 32 0
%uint_42 = OpConstant %uint 42
%10 = OpTypeArray %uint %uint_42
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<Array>());
auto* arr_type = type->As<Array>();
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->size, 42u);
EXPECT_EQ(arr_type->stride, 0u);
auto* elem_type = arr_type->type;
ASSERT_NE(elem_type, nullptr);
EXPECT_TRUE(elem_type->Is<U32>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_ArrayBadLengthIsSpecConstantValue) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %uint_42 SpecId 12
%uint = OpTypeInt 32 0
%uint_42 = OpSpecConstant %uint 42
%10 = OpTypeArray %uint %uint_42
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("Array type 10 length is a specialization constant"));
}
TEST_F(SpirvASTParserTest, ConvertType_ArrayBadLengthIsSpecConstantExpr) {
auto p = parser(test::Assemble(Preamble() + R"(
%uint = OpTypeInt 32 0
%uint_42 = OpConstant %uint 42
%sum = OpSpecConstantOp %uint IAdd %uint_42 %uint_42
%10 = OpTypeArray %uint %sum
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("Array type 10 length is a specialization constant"));
}
// TODO(dneto): Maybe add a test where the length operand is not a constant.
// E.g. it's the ID of a type. That won't validate, and the SPIRV-Tools
// optimizer representation doesn't handle it and asserts out instead.
TEST_F(SpirvASTParserTest, ConvertType_ArrayBadTooBig) {
auto p = parser(test::Assemble(Preamble() + R"(
%uint64 = OpTypeInt 64 0
%uint64_big = OpConstant %uint64 5000000000
%10 = OpTypeArray %uint64 %uint64_big
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_EQ(type, nullptr);
// TODO(dneto): Right now it's rejected earlier in the flow because
// we can't even utter the uint64 type.
EXPECT_THAT(p->error(), Eq("unhandled integer width: 64"));
}
TEST_F(SpirvASTParserTest, ConvertType_Array_InvalidDecoration) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %10 Block
%uint = OpTypeInt 32 0
%uint_5 = OpConstant %uint 5
%10 = OpTypeArray %uint %uint_5
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(),
Eq("invalid array type ID 10: unknown decoration 2 with 1 total words"));
}
TEST_F(SpirvASTParserTest, ConvertType_ArrayStride_Valid) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %10 ArrayStride 8
%uint = OpTypeInt 32 0
%uint_5 = OpConstant %uint 5
%10 = OpTypeArray %uint %uint_5
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->UnwrapAll()->Is<Array>());
auto* arr_type = type->UnwrapAll()->As<Array>();
ASSERT_NE(arr_type, nullptr);
EXPECT_EQ(arr_type->stride, 8u);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_ArrayStride_ZeroIsError) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %10 ArrayStride 0
%uint = OpTypeInt 32 0
%uint_5 = OpConstant %uint 5
%10 = OpTypeArray %uint %uint_5
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
ASSERT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("invalid array type ID 10: ArrayStride can't be 0"));
}
TEST_F(SpirvASTParserTest, ConvertType_StructEmpty) {
auto p = parser(test::Assemble(Preamble() + R"(
%10 = OpTypeStruct
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(10);
EXPECT_EQ(type, nullptr);
EXPECT_EQ(p->error(),
"WGSL does not support empty structures. can't convert type: %10 = "
"OpTypeStruct");
}
TEST_F(SpirvASTParserTest, ConvertType_StructTwoMembers) {
auto p = parser(test::Assemble(Preamble() + R"(
%uint = OpTypeInt 32 0
%float = OpTypeFloat 32
%10 = OpTypeStruct %uint %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<Struct>());
auto str = type->Build(p->builder());
Program program = p->program();
EXPECT_EQ(test::ToString(program, str), "S");
}
TEST_F(SpirvASTParserTest, ConvertType_StructWithBlockDecoration) {
auto p = parser(test::Assemble(Preamble() + R"(
OpDecorate %10 Block
%uint = OpTypeInt 32 0
%10 = OpTypeStruct %uint
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<Struct>());
auto str = type->Build(p->builder());
Program program = p->program();
EXPECT_EQ(test::ToString(program, str), "S");
}
TEST_F(SpirvASTParserTest, ConvertType_StructWithMemberDecorations) {
auto p = parser(test::Assemble(Preamble() + R"(
OpMemberDecorate %10 0 Offset 0
OpMemberDecorate %10 1 Offset 8
OpMemberDecorate %10 2 Offset 16
%float = OpTypeFloat 32
%vec = OpTypeVector %float 2
%mat = OpTypeMatrix %vec 2
%10 = OpTypeStruct %float %vec %mat
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
EXPECT_TRUE(p->RegisterUserAndStructMemberNames());
auto* type = p->ConvertType(10);
ASSERT_NE(type, nullptr);
EXPECT_TRUE(type->Is<Struct>());
auto str = type->Build(p->builder());
Program program = p->program();
EXPECT_EQ(test::ToString(program, str), "S");
}
TEST_F(SpirvASTParserTest, ConvertType_Struct_NoDeduplication) {
// Prove that distinct SPIR-V structs map to distinct WGSL types.
auto p = parser(test::Assemble(Preamble() + R"(
%uint = OpTypeInt 32 0
%10 = OpTypeStruct %uint
%11 = OpTypeStruct %uint
)" + MainBody()));
EXPECT_TRUE(p->BuildAndParseInternalModule());
auto* type10 = p->ConvertType(10);
ASSERT_NE(type10, nullptr);
EXPECT_TRUE(type10->Is<Struct>());
auto* struct_type10 = type10->As<Struct>();
ASSERT_NE(struct_type10, nullptr);
EXPECT_EQ(struct_type10->members.size(), 1u);
EXPECT_TRUE(struct_type10->members[0]->Is<U32>());
auto* type11 = p->ConvertType(11);
ASSERT_NE(type11, nullptr);
EXPECT_TRUE(type11->Is<Struct>());
auto* struct_type11 = type11->As<Struct>();
ASSERT_NE(struct_type11, nullptr);
EXPECT_EQ(struct_type11->members.size(), 1u);
EXPECT_TRUE(struct_type11->members[0]->Is<U32>());
// They map to distinct types in WGSL
EXPECT_NE(type11, type10);
}
TEST_F(SpirvASTParserTest, ConvertType_Array_NoDeduplication) {
// Prove that distinct SPIR-V arrays map to distinct WGSL types.
auto assembly = Preamble() + R"(
%uint = OpTypeInt 32 0
%10 = OpTypeStruct %uint
%11 = OpTypeStruct %uint
%uint_1 = OpConstant %uint 1
%20 = OpTypeArray %10 %uint_1
%21 = OpTypeArray %11 %uint_1
)" + MainBody();
auto p = parser(test::Assemble(assembly));
EXPECT_TRUE(p->BuildAndParseInternalModule());
auto* type20 = p->ConvertType(20);
ASSERT_NE(type20, nullptr);
EXPECT_TRUE(type20->Is<Array>());
auto* type21 = p->ConvertType(21);
ASSERT_NE(type21, nullptr);
EXPECT_TRUE(type21->Is<Array>());
// They map to distinct types in WGSL
EXPECT_NE(type21, type20);
}
TEST_F(SpirvASTParserTest, ConvertType_RuntimeArray_NoDeduplication) {
// Prove that distinct SPIR-V runtime arrays map to distinct WGSL types.
// The implementation already de-duplicates them because it knows
// runtime-arrays normally have stride decorations.
auto assembly = Preamble() + R"(
%uint = OpTypeInt 32 0
%10 = OpTypeStruct %uint
%11 = OpTypeStruct %uint
%20 = OpTypeRuntimeArray %10
%21 = OpTypeRuntimeArray %11
%22 = OpTypeRuntimeArray %10
)" + MainBody();
auto p = parser(test::Assemble(assembly));
EXPECT_TRUE(p->BuildAndParseInternalModule());
auto* type20 = p->ConvertType(20);
ASSERT_NE(type20, nullptr);
EXPECT_TRUE(type20->Is<Alias>());
EXPECT_TRUE(type20->UnwrapAll()->Is<Array>());
EXPECT_EQ(type20->UnwrapAll()->As<Array>()->size, 0u);
auto* type21 = p->ConvertType(21);
ASSERT_NE(type21, nullptr);
EXPECT_TRUE(type21->Is<Alias>());
EXPECT_TRUE(type21->UnwrapAll()->Is<Array>());
EXPECT_EQ(type21->UnwrapAll()->As<Array>()->size, 0u);
auto* type22 = p->ConvertType(22);
ASSERT_NE(type22, nullptr);
EXPECT_TRUE(type22->Is<Alias>());
EXPECT_TRUE(type22->UnwrapAll()->Is<Array>());
EXPECT_EQ(type22->UnwrapAll()->As<Array>()->size, 0u);
// They map to distinct types in WGSL
EXPECT_NE(type21, type20);
EXPECT_NE(type22, type21);
EXPECT_NE(type22, type20);
}
// TODO(dneto): Demonstrate other member decorations. Blocked on
// crbug.com/tint/30
// TODO(dneto): Demonstrate multiple member deocrations. Blocked on
// crbug.com/tint/30
TEST_F(SpirvASTParserTest, ConvertType_InvalidPointeetype) {
// Disallow pointer-to-function
auto p = parser(test::Assemble(Preamble() + R"(
%void = OpTypeVoid
%42 = OpTypeFunction %void
%3 = OpTypePointer Input %42
%voidfn = OpTypeFunction %void
%main = OpFunction %void None %voidfn
%entry = OpLabel
OpReturn
OpFunctionEnd
)"));
EXPECT_TRUE(p->BuildInternalModule()) << p->error();
auto* type = p->ConvertType(3);
EXPECT_EQ(type, nullptr);
EXPECT_THAT(p->error(), Eq("SPIR-V pointer type with ID 3 has invalid pointee type 42"));
}
TEST_F(SpirvASTParserTest, DISABLED_ConvertType_InvalidAddressSpace) {
// Disallow invalid address space
auto p = parser(test::Assemble(Preamble() + R"(
%1 = OpTypeFloat 32
%3 = OpTypePointer !999 %1 ; Special syntax to inject 999 as the address space
)" + MainBody()));
// TODO(dneto): I can't get it past module building.
EXPECT_FALSE(p->BuildInternalModule()) << p->error();
}
TEST_F(SpirvASTParserTest, ConvertType_PointerInput) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%3 = OpTypePointer Input %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kPrivate);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_PointerOutput) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%3 = OpTypePointer Output %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kPrivate);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_PointerUniform) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%3 = OpTypePointer Uniform %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kUniform);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_PointerWorkgroup) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%3 = OpTypePointer Workgroup %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kWorkgroup);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_PointerStorageBuffer) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%3 = OpTypePointer StorageBuffer %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kStorage);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_PointerPrivate) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%3 = OpTypePointer Private %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kPrivate);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_PointerFunction) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%3 = OpTypePointer Function %float
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_TRUE(ptr_ty->type->Is<F32>());
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kFunction);
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_PointerToPointer) {
// FYI: The reader suports pointer-to-pointer even while WebGPU does not.
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%42 = OpTypePointer Output %float
%3 = OpTypePointer Input %42
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(3);
EXPECT_NE(type, nullptr);
EXPECT_TRUE(type->Is<Pointer>());
auto* ptr_ty = type->As<Pointer>();
EXPECT_NE(ptr_ty, nullptr);
EXPECT_EQ(ptr_ty->address_space, core::AddressSpace::kPrivate);
EXPECT_TRUE(ptr_ty->type->Is<Pointer>());
auto* ptr_ptr_ty = ptr_ty->type->As<Pointer>();
EXPECT_NE(ptr_ptr_ty, nullptr);
EXPECT_EQ(ptr_ptr_ty->address_space, core::AddressSpace::kPrivate);
EXPECT_TRUE(ptr_ptr_ty->type->Is<F32>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_Sampler_PretendVoid) {
// We fake the type suport for samplers, images, and sampled images.
auto p = parser(test::Assemble(Preamble() + R"(
%1 = OpTypeSampler
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_Image_PretendVoid) {
// We fake the type suport for samplers, images, and sampled images.
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%1 = OpTypeImage %float 2D 0 0 0 1 Unknown
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}
TEST_F(SpirvASTParserTest, ConvertType_SampledImage_PretendVoid) {
auto p = parser(test::Assemble(Preamble() + R"(
%float = OpTypeFloat 32
%im = OpTypeImage %float 2D 0 0 0 1 Unknown
%1 = OpTypeSampledImage %im
)" + MainBody()));
EXPECT_TRUE(p->BuildInternalModule());
auto* type = p->ConvertType(1);
EXPECT_TRUE(type->Is<Void>());
EXPECT_TRUE(p->error().empty());
}
} // namespace
} // namespace tint::spirv::reader::ast_parser