// 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. #include "gmock/gmock.h" #include "src/reader/spirv/function.h" #include "src/reader/spirv/parser_impl_test_helper.h" #include "src/reader/spirv/spirv_tools_helpers_test.h" namespace tint { namespace reader { namespace spirv { namespace { using ::testing::HasSubstr; std::string Preamble() { return R"( OpCapability Shader %glsl = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %100 "main" OpExecutionMode %100 LocalSize 1 1 1 OpName %u1 "u1" OpName %u2 "u2" OpName %u3 "u3" OpName %i1 "i1" OpName %i2 "i2" OpName %i3 "i3" OpName %f1 "f1" OpName %f2 "f2" OpName %f3 "f3" OpName %v2u1 "v2u1" OpName %v2u2 "v2u2" OpName %v2u3 "v2u3" OpName %v2i1 "v2i1" OpName %v2i2 "v2i2" OpName %v2i3 "v2i3" OpName %v2f1 "v2f1" OpName %v2f2 "v2f2" OpName %v2f3 "v2f3" OpName %v3f1 "v3f1" OpName %v3f2 "v3f2" OpName %v4f1 "v4f1" OpName %v4f2 "v4f2" %void = OpTypeVoid %voidfn = OpTypeFunction %void %uint = OpTypeInt 32 0 %int = OpTypeInt 32 1 %float = OpTypeFloat 32 %uint_10 = OpConstant %uint 10 %uint_15 = OpConstant %uint 15 %uint_20 = OpConstant %uint 20 %int_30 = OpConstant %int 30 %int_35 = OpConstant %int 35 %int_40 = OpConstant %int 40 %float_50 = OpConstant %float 50 %float_60 = OpConstant %float 60 %float_70 = OpConstant %float 70 %v2uint = OpTypeVector %uint 2 %v2int = OpTypeVector %int 2 %v2float = OpTypeVector %float 2 %v3float = OpTypeVector %float 3 %v4float = OpTypeVector %float 4 %v2uint_10_20 = OpConstantComposite %v2uint %uint_10 %uint_20 %v2uint_20_10 = OpConstantComposite %v2uint %uint_20 %uint_10 %v2uint_15_15 = OpConstantComposite %v2uint %uint_15 %uint_15 %v2int_30_40 = OpConstantComposite %v2int %int_30 %int_40 %v2int_40_30 = OpConstantComposite %v2int %int_40 %int_30 %v2int_35_35 = OpConstantComposite %v2int %int_35 %int_35 %v2float_50_60 = OpConstantComposite %v2float %float_50 %float_60 %v2float_60_50 = OpConstantComposite %v2float %float_60 %float_50 %v2float_70_70 = OpConstantComposite %v2float %float_70 %float_70 %v3float_50_60_70 = OpConstantComposite %v3float %float_50 %float_60 %float_70 %v3float_60_70_50 = OpConstantComposite %v3float %float_60 %float_70 %float_50 %v4float_50_50_50_50 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50 %100 = OpFunction %void None %voidfn %entry = OpLabel %u1 = OpCopyObject %uint %uint_10 %u2 = OpCopyObject %uint %uint_15 %u3 = OpCopyObject %uint %uint_20 %i1 = OpCopyObject %int %int_30 %i2 = OpCopyObject %int %int_35 %i3 = OpCopyObject %int %int_40 %f1 = OpCopyObject %float %float_50 %f2 = OpCopyObject %float %float_60 %f3 = OpCopyObject %float %float_70 %v2u1 = OpCopyObject %v2uint %v2uint_10_20 %v2u2 = OpCopyObject %v2uint %v2uint_20_10 %v2u3 = OpCopyObject %v2uint %v2uint_15_15 %v2i1 = OpCopyObject %v2int %v2int_30_40 %v2i2 = OpCopyObject %v2int %v2int_40_30 %v2i3 = OpCopyObject %v2int %v2int_35_35 %v2f1 = OpCopyObject %v2float %v2float_50_60 %v2f2 = OpCopyObject %v2float %v2float_60_50 %v2f3 = OpCopyObject %v2float %v2float_70_70 %v3f1 = OpCopyObject %v3float %v3float_50_60_70 %v3f2 = OpCopyObject %v3float %v3float_60_70_50 %v4f1 = OpCopyObject %v4float %v4float_50_50_50_50 %v4f2 = OpCopyObject %v4float %v4f1 )"; } struct GlslStd450Case { std::string opcode; std::string wgsl_func; }; inline std::ostream& operator<<(std::ostream& out, GlslStd450Case c) { out << "GlslStd450Case(" << c.opcode << " " << c.wgsl_func << ")"; return out; } // Nomenclature: // Float = scalar float // Floating = scalar float or vector-of-float // Float3 = 3-element vector of float // Int = scalar signed int // Inting = scalar int or vector-of-int // Uint = scalar unsigned int // Uinting = scalar unsigned or vector-of-unsigned using SpvParserTest_GlslStd450_Float_Floating = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Float_FloatingFloating = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Floating_Floating = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Floating_FloatingFloating = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Floating_FloatingInting = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Floating_FloatingUinting = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Float3_Float3Float3 = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Inting_Inting = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Inting_IntingInting = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Inting_IntingIntingInting = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Uinting_UintingUinting = SpvParserTestBase<::testing::TestWithParam>; using SpvParserTest_GlslStd450_Uinting_UintingUintingUinting = SpvParserTestBase<::testing::TestWithParam>; TEST_P(SpvParserTest_GlslStd450_Float_Floating, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{f1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Float_Floating, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %v2f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2f1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Float_FloatingFloating, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %f1 %f2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{f1} Identifier[not set]{f2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Float_FloatingFloating, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %v2f1 %v2f2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2f1} Identifier[not set]{v2f2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_Floating, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{f1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_Floating, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2float %glsl )" + GetParam().opcode + R"( %v2f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2f1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloating, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %f1 %f2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{f1} Identifier[not set]{f2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloating, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2float %glsl )" + GetParam().opcode + R"( %v2f1 %v2f2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2f1} Identifier[not set]{v2f2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %f1 %f2 %f3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{f1} Identifier[not set]{f2} Identifier[not set]{f3} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2float %glsl )" + GetParam().opcode + R"( %v2f1 %v2f2 %v2f3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2f1} Identifier[not set]{v2f2} Identifier[not set]{v2f3} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingUinting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %f1 %u1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{f1} Identifier[not set]{u1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingUinting, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2float %glsl )" + GetParam().opcode + R"( %v2f1 %v2u1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2f1} Identifier[not set]{v2u1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingInting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl )" + GetParam().opcode + R"( %f1 %i1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{f1} Identifier[not set]{i1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Floating_FloatingInting, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2float %glsl )" + GetParam().opcode + R"( %v2f1 %v2i1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2f1} Identifier[not set]{v2i1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Float3_Float3Float3, Samples) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v3float %glsl )" + GetParam().opcode + R"( %v3f1 %v3f2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_3__f32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v3f1} Identifier[not set]{v3f2} ) } } })")) << body; } INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Float_Floating, ::testing::Values(GlslStd450Case{"Length", "length"})); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Float_FloatingFloating, ::testing::Values(GlslStd450Case{"Distance", "distance"})); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Floating_Floating, ::testing::ValuesIn(std::vector{ {"Acos", "acos"}, {"Asin", "asin"}, {"Atan", "atan"}, {"Ceil", "ceil"}, {"Cos", "cos"}, {"Cosh", "cosh"}, {"Exp", "exp"}, {"Exp2", "exp2"}, {"FAbs", "abs"}, {"FSign", "sign"}, {"Floor", "floor"}, {"Fract", "fract"}, {"InverseSqrt", "inverseSqrt"}, {"Log", "log"}, {"Log2", "log2"}, {"Round", "round"}, {"RoundEven", "round"}, {"Sin", "sin"}, {"Sinh", "sinh"}, {"Sqrt", "sqrt"}, {"Tan", "tan"}, {"Tanh", "tanh"}, {"Trunc", "trunc"}, })); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Floating_FloatingFloating, ::testing::ValuesIn(std::vector{ {"Atan2", "atan2"}, {"NMax", "max"}, {"NMin", "min"}, {"FMax", "max"}, // WGSL max promises more for NaN {"FMin", "min"}, // WGSL min promises more for NaN {"Pow", "pow"}, {"Step", "step"}, })); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Floating_FloatingUinting, ::testing::Values(GlslStd450Case{"Ldexp", "ldexp"})); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Floating_FloatingInting, ::testing::Values(GlslStd450Case{"Ldexp", "ldexp"})); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Float3_Float3Float3, ::testing::Values(GlslStd450Case{"Cross", "cross"})); INSTANTIATE_TEST_SUITE_P( Samples, SpvParserTest_GlslStd450_Floating_FloatingFloatingFloating, ::testing::ValuesIn(std::vector{ {"NClamp", "clamp"}, {"FClamp", "clamp"}, // WGSL FClamp promises more for NaN {"Fma", "fma"}, {"FMix", "mix"}, {"SmoothStep", "smoothStep"}})); TEST_P(SpvParserTest_GlslStd450_Inting_Inting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %int %glsl )" + GetParam().opcode + R"( %i1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __i32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{i1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Inting_Inting, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2int %glsl )" + GetParam().opcode + R"( %v2i1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__i32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2i1} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Inting_IntingInting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %int %glsl )" + GetParam().opcode + R"( %i1 %i2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __i32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{i1} Identifier[not set]{i2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Inting_IntingInting, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2int %glsl )" + GetParam().opcode + R"( %v2i1 %v2i2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__i32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2i1} Identifier[not set]{v2i2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Inting_IntingIntingInting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %int %glsl )" + GetParam().opcode + R"( %i1 %i2 %i3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __i32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{i1} Identifier[not set]{i2} Identifier[not set]{i3} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Inting_IntingIntingInting, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2int %glsl )" + GetParam().opcode + R"( %v2i1 %v2i2 %v2i3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__i32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2i1} Identifier[not set]{v2i2} Identifier[not set]{v2i3} ) } } })")) << body; } INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Inting_Inting, ::testing::Values(GlslStd450Case{"SAbs", "abs"})); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Inting_IntingInting, ::testing::Values(GlslStd450Case{"SMax", "max"}, GlslStd450Case{"SMin", "min"})); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Inting_IntingIntingInting, ::testing::Values(GlslStd450Case{"SClamp", "clamp"})); TEST_P(SpvParserTest_GlslStd450_Uinting_UintingUinting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl )" + GetParam().opcode + R"( %u1 %u2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __u32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{u1} Identifier[not set]{u2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Uinting_UintingUinting, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2uint %glsl )" + GetParam().opcode + R"( %v2u1 %v2u2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__u32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2u1} Identifier[not set]{v2u2} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Uinting_UintingUintingUinting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl )" + GetParam().opcode + R"( %u1 %u2 %u3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __u32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{u1} Identifier[not set]{u2} Identifier[not set]{u3} ) } } })")) << body; } TEST_P(SpvParserTest_GlslStd450_Uinting_UintingUintingUinting, Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2uint %glsl )" + GetParam().opcode + R"( %v2u1 %v2u2 %v2u3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__u32 { Call[not set]{ Identifier[not set]{)" + GetParam().wgsl_func + R"(} ( Identifier[not set]{v2u1} Identifier[not set]{v2u2} Identifier[not set]{v2u3} ) } } })")) << body; } INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Uinting_UintingUinting, ::testing::Values(GlslStd450Case{"UMax", "max"}, GlslStd450Case{"UMin", "min"})); INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_Uinting_UintingUintingUinting, ::testing::Values(GlslStd450Case{"UClamp", "clamp"})); // Test Normalize. WGSL does not have a scalar form of the normalize builtin. // So we have to test it separately, as it does not fit the patterns tested // above. TEST_F(SpvParserTest, Normalize_Scalar) { // Scalar normalize always results in 1.0 const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl Normalize %f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __f32 { ScalarConstructor[not set]{1.000000} } })")) << body; } TEST_F(SpvParserTest, Normalize_Vector2) { // Scalar normalize always results in 1.0 const auto assembly = Preamble() + R"( %1 = OpExtInst %v2float %glsl Normalize %v2f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{normalize} ( Identifier[not set]{v2f1} ) } } })")) << body; } TEST_F(SpvParserTest, Normalize_Vector3) { // Scalar normalize always results in 1.0 const auto assembly = Preamble() + R"( %1 = OpExtInst %v3float %glsl Normalize %v3f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_3__f32 { Call[not set]{ Identifier[not set]{normalize} ( Identifier[not set]{v3f1} ) } } })")) << body; } TEST_F(SpvParserTest, Normalize_Vector4) { // Scalar normalize always results in 1.0 const auto assembly = Preamble() + R"( %1 = OpExtInst %v4float %glsl Normalize %v4f1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __vec_4__f32 { Call[not set]{ Identifier[not set]{normalize} ( Identifier[not set]{v4f1} ) } } })")) << body; } // Check that we convert signedness of operands and result type. // This is needed for each of the integer-based extended instructions. TEST_F(SpvParserTest, RectifyOperandsAndResult_SAbs) { const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl SAbs %u1 %2 = OpExtInst %v2uint %glsl SAbs %v2u1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __u32 { Bitcast[not set]<__u32>{ Call[not set]{ Identifier[not set]{abs} ( Bitcast[not set]<__i32>{ Identifier[not set]{u1} } ) } } } })")) << body; EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_2 none undefined __vec_2__u32 { Bitcast[not set]<__vec_2__u32>{ Call[not set]{ Identifier[not set]{abs} ( Bitcast[not set]<__vec_2__i32>{ Identifier[not set]{v2u1} } ) } } } })")) << body; } TEST_F(SpvParserTest, RectifyOperandsAndResult_SMax) { const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl SMax %u1 %u2 %2 = OpExtInst %v2uint %glsl SMax %v2u1 %v2u2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __u32 { Bitcast[not set]<__u32>{ Call[not set]{ Identifier[not set]{max} ( Bitcast[not set]<__i32>{ Identifier[not set]{u1} } Bitcast[not set]<__i32>{ Identifier[not set]{u2} } ) } } } })")) << body; EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_2 none undefined __vec_2__u32 { Bitcast[not set]<__vec_2__u32>{ Call[not set]{ Identifier[not set]{max} ( Bitcast[not set]<__vec_2__i32>{ Identifier[not set]{v2u1} } Bitcast[not set]<__vec_2__i32>{ Identifier[not set]{v2u2} } ) } } } })")) << body; } TEST_F(SpvParserTest, RectifyOperandsAndResult_SMin) { const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl SMin %u1 %u2 %2 = OpExtInst %v2uint %glsl SMin %v2u1 %v2u2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __u32 { Bitcast[not set]<__u32>{ Call[not set]{ Identifier[not set]{min} ( Bitcast[not set]<__i32>{ Identifier[not set]{u1} } Bitcast[not set]<__i32>{ Identifier[not set]{u2} } ) } } } })")) << body; EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_2 none undefined __vec_2__u32 { Bitcast[not set]<__vec_2__u32>{ Call[not set]{ Identifier[not set]{min} ( Bitcast[not set]<__vec_2__i32>{ Identifier[not set]{v2u1} } Bitcast[not set]<__vec_2__i32>{ Identifier[not set]{v2u2} } ) } } } })")) << body; } TEST_F(SpvParserTest, RectifyOperandsAndResult_SClamp) { const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl SClamp %u1 %i2 %u3 %2 = OpExtInst %v2uint %glsl SClamp %v2u1 %v2i2 %v2u3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __u32 { Bitcast[not set]<__u32>{ Call[not set]{ Identifier[not set]{clamp} ( Bitcast[not set]<__i32>{ Identifier[not set]{u1} } Identifier[not set]{i2} Bitcast[not set]<__i32>{ Identifier[not set]{u3} } ) } } } })")) << body; EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_2 none undefined __vec_2__u32 { Bitcast[not set]<__vec_2__u32>{ Call[not set]{ Identifier[not set]{clamp} ( Bitcast[not set]<__vec_2__i32>{ Identifier[not set]{v2u1} } Identifier[not set]{v2i2} Bitcast[not set]<__vec_2__i32>{ Identifier[not set]{v2u3} } ) } } } })")) << body; } TEST_F(SpvParserTest, RectifyOperandsAndResult_UMax) { const auto assembly = Preamble() + R"( %1 = OpExtInst %int %glsl UMax %i1 %i2 %2 = OpExtInst %v2int %glsl UMax %v2i1 %v2i2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __i32 { Bitcast[not set]<__i32>{ Call[not set]{ Identifier[not set]{max} ( Bitcast[not set]<__u32>{ Identifier[not set]{i1} } Bitcast[not set]<__u32>{ Identifier[not set]{i2} } ) } } } })")) << body; EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_2 none undefined __vec_2__i32 { Bitcast[not set]<__vec_2__i32>{ Call[not set]{ Identifier[not set]{max} ( Bitcast[not set]<__vec_2__u32>{ Identifier[not set]{v2i1} } Bitcast[not set]<__vec_2__u32>{ Identifier[not set]{v2i2} } ) } } } })")) << body; } TEST_F(SpvParserTest, RectifyOperandsAndResult_UMin) { const auto assembly = Preamble() + R"( %1 = OpExtInst %int %glsl UMin %i1 %i2 %2 = OpExtInst %v2int %glsl UMin %v2i1 %v2i2 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __i32 { Bitcast[not set]<__i32>{ Call[not set]{ Identifier[not set]{min} ( Bitcast[not set]<__u32>{ Identifier[not set]{i1} } Bitcast[not set]<__u32>{ Identifier[not set]{i2} } ) } } } })")) << body; EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_2 none undefined __vec_2__i32 { Bitcast[not set]<__vec_2__i32>{ Call[not set]{ Identifier[not set]{min} ( Bitcast[not set]<__vec_2__u32>{ Identifier[not set]{v2i1} } Bitcast[not set]<__vec_2__u32>{ Identifier[not set]{v2i2} } ) } } } })")) << body; } TEST_F(SpvParserTest, RectifyOperandsAndResult_UClamp) { const auto assembly = Preamble() + R"( %1 = OpExtInst %int %glsl UClamp %i1 %u2 %i3 %2 = OpExtInst %v2int %glsl UClamp %v2i1 %v2u2 %v2i3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __i32 { Bitcast[not set]<__i32>{ Call[not set]{ Identifier[not set]{clamp} ( Bitcast[not set]<__u32>{ Identifier[not set]{i1} } Identifier[not set]{u2} Bitcast[not set]<__u32>{ Identifier[not set]{i3} } ) } } } })")) << body; EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_2 none undefined __vec_2__i32 { Bitcast[not set]<__vec_2__i32>{ Call[not set]{ Identifier[not set]{clamp} ( Bitcast[not set]<__vec_2__u32>{ Identifier[not set]{v2i1} } Identifier[not set]{v2u2} Bitcast[not set]<__vec_2__u32>{ Identifier[not set]{v2i3} } ) } } } })")) << body; } struct DataPackingCase { std::string opcode; std::string wgsl_func; uint32_t vec_size; }; inline std::ostream& operator<<(std::ostream& out, DataPackingCase c) { out << "DataPacking(" << c.opcode << ")"; return out; } using SpvParserTest_GlslStd450_DataPacking = SpvParserTestBase<::testing::TestWithParam>; TEST_P(SpvParserTest_GlslStd450_DataPacking, Valid) { auto param = GetParam(); const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl )" + param.opcode + (param.vec_size == 2 ? " %v2f1" : " %v4f1") + R"( OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined __u32 { Call[not set]{ Identifier[not set]{)" + param.wgsl_func + R"(} ( Identifier[not set]{v)" + std::to_string(param.vec_size) + R"(f1} ) } } })")) << body; } INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_DataPacking, ::testing::ValuesIn(std::vector{ {"PackSnorm4x8", "pack4x8snorm", 4}, {"PackUnorm4x8", "pack4x8unorm", 4}, {"PackSnorm2x16", "pack2x16snorm", 2}, {"PackUnorm2x16", "pack2x16unorm", 2}, {"PackHalf2x16", "pack2x16float", 2}})); using SpvParserTest_GlslStd450_DataUnpacking = SpvParserTestBase<::testing::TestWithParam>; TEST_P(SpvParserTest_GlslStd450_DataUnpacking, Valid) { auto param = GetParam(); const auto assembly = Preamble() + R"( %1 = OpExtInst )" + (param.vec_size == 2 ? "%v2float" : "%v4float") + std::string(" %glsl ") + param.opcode + R"( %u1 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()) << assembly; auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); EXPECT_THAT(body, HasSubstr(R"( VariableConst{ x_1 none undefined )" + std::string(param.vec_size == 2 ? "__vec_2__f32" : "__vec_4__f32") + R"( { Call[not set]{ Identifier[not set]{)" + param.wgsl_func + R"(} ( Identifier[not set]{u1} ) } } })")) << body; } INSTANTIATE_TEST_SUITE_P(Samples, SpvParserTest_GlslStd450_DataUnpacking, ::testing::ValuesIn(std::vector{ {"UnpackSnorm4x8", "unpack4x8snorm", 4}, {"UnpackUnorm4x8", "unpack4x8unorm", 4}, {"UnpackSnorm2x16", "unpack2x16snorm", 2}, {"UnpackUnorm2x16", "unpack2x16unorm", 2}, {"UnpackHalf2x16", "unpack2x16float", 2}})); TEST_F(SpvParserTest, GlslStd450_Refract_Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl Refract %f1 %f2 %f3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableConst{ x_1 none undefined __f32 { MemberAccessor[not set]{ Call[not set]{ Identifier[not set]{refract} ( TypeConstructor[not set]{ __vec_2__f32 Identifier[not set]{f1} ScalarConstructor[not set]{0.000000} } TypeConstructor[not set]{ __vec_2__f32 Identifier[not set]{f2} ScalarConstructor[not set]{0.000000} } Identifier[not set]{f3} ) } Identifier[not set]{x} } } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } TEST_F(SpvParserTest, GlslStd450_Refract_Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v2float %glsl Refract %v2f1 %v2f2 %f3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{refract} ( Identifier[not set]{v2f1} Identifier[not set]{v2f2} Identifier[not set]{f3} ) } })"; EXPECT_THAT(body, HasSubstr(expected)); } TEST_F(SpvParserTest, GlslStd450_FaceForward_Scalar) { const auto assembly = Preamble() + R"( %99 = OpFAdd %float %f1 %f1 ; normal operand has only one use %1 = OpExtInst %float %glsl FaceForward %99 %f2 %f3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); // The %99 sum only has one use. Ensure it is evaluated only once by // making a let-declaration for it, since it is the normal operand to // the builtin function, and code generation uses it twice. const auto* expected = R"(VariableDeclStatement{ VariableConst{ x_99 none undefined __f32 { Binary[not set]{ Identifier[not set]{f1} add Identifier[not set]{f1} } } } } VariableDeclStatement{ VariableConst{ x_1 none undefined __f32 { Call[not set]{ Identifier[not set]{select} ( UnaryOp[not set]{ negation Identifier[not set]{x_99} } Identifier[not set]{x_99} Binary[not set]{ Binary[not set]{ Identifier[not set]{f2} multiply Identifier[not set]{f3} } less_than ScalarConstructor[not set]{0.000000} } ) } } } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } TEST_F(SpvParserTest, GlslStd450_FaceForward_Vector) { const auto assembly = Preamble() + R"( %99 = OpFAdd %v2float %v2f1 %v2f1 %1 = OpExtInst %v2float %glsl FaceForward %v2f1 %v2f2 %v2f3 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{faceForward} ( Identifier[not set]{v2f1} Identifier[not set]{v2f2} Identifier[not set]{v2f3} ) } })"; EXPECT_THAT(body, HasSubstr(expected)); } TEST_F(SpvParserTest, GlslStd450_Reflect_Scalar) { const auto assembly = Preamble() + R"( %98 = OpFAdd %float %f1 %f1 ; has only one use %99 = OpFAdd %float %f2 %f2 ; has only one use %1 = OpExtInst %float %glsl Reflect %98 %99 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); // The %99 sum only has one use. Ensure it is evaluated only once by // making a let-declaration for it, since it is the normal operand to // the builtin function, and code generation uses it twice. const auto* expected = R"(VariableDeclStatement{ VariableConst{ x_98 none undefined __f32 { Binary[not set]{ Identifier[not set]{f1} add Identifier[not set]{f1} } } } } VariableDeclStatement{ VariableConst{ x_99 none undefined __f32 { Binary[not set]{ Identifier[not set]{f2} add Identifier[not set]{f2} } } } } VariableDeclStatement{ VariableConst{ x_1 none undefined __f32 { Binary[not set]{ Identifier[not set]{x_98} subtract Binary[not set]{ ScalarConstructor[not set]{2.000000} multiply Binary[not set]{ Identifier[not set]{x_99} multiply Binary[not set]{ Identifier[not set]{x_99} multiply Identifier[not set]{x_98} } } } } } } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } TEST_F(SpvParserTest, GlslStd450_Reflect_Vector) { const auto assembly = Preamble() + R"( %98 = OpFAdd %v2float %v2f1 %v2f1 %99 = OpFAdd %v2float %v2f2 %v2f2 %1 = OpExtInst %v2float %glsl Reflect %98 %99 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableDeclStatement{ VariableConst{ x_98 none undefined __vec_2__f32 { Binary[not set]{ Identifier[not set]{v2f1} add Identifier[not set]{v2f1} } } } } VariableDeclStatement{ VariableConst{ x_99 none undefined __vec_2__f32 { Binary[not set]{ Identifier[not set]{v2f2} add Identifier[not set]{v2f2} } } } } VariableDeclStatement{ VariableConst{ x_1 none undefined __vec_2__f32 { Call[not set]{ Identifier[not set]{reflect} ( Identifier[not set]{x_98} Identifier[not set]{x_99} ) } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } TEST_F(SpvParserTest, GlslStd450_Degrees_Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl Degrees %float_50 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableDeclStatement{ VariableConst{ x_1 none undefined __f32 { Binary[not set]{ ScalarConstructor[not set]{50.000000} multiply ScalarConstructor[not set]{57.295780} } } } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } TEST_F(SpvParserTest, GlslStd450_Degrees_Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v3float %glsl Degrees %v3float_60_70_50 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableDeclStatement{ VariableConst{ x_1 none undefined __vec_3__f32 { Binary[not set]{ TypeConstructor[not set]{ __vec_3__f32 ScalarConstructor[not set]{60.000000} ScalarConstructor[not set]{70.000000} ScalarConstructor[not set]{50.000000} } multiply TypeConstructor[not set]{ __vec_3__f32 ScalarConstructor[not set]{57.295780} } } } } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } TEST_F(SpvParserTest, GlslStd450_Radians_Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %float %glsl Radians %float_50 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableDeclStatement{ VariableConst{ x_1 none undefined __f32 { Binary[not set]{ ScalarConstructor[not set]{50.000000} multiply ScalarConstructor[not set]{0.017453} } } } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } TEST_F(SpvParserTest, GlslStd450_Radians_Vector) { const auto assembly = Preamble() + R"( %1 = OpExtInst %v3float %glsl Radians %v3float_60_70_50 OpReturn OpFunctionEnd )"; auto p = parser(test::Assemble(assembly)); ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); auto fe = p->function_emitter(100); EXPECT_TRUE(fe.EmitBody()) << p->error(); const auto body = ToString(p->builder(), fe.ast_body()); const auto* expected = R"(VariableDeclStatement{ VariableConst{ x_1 none undefined __vec_3__f32 { Binary[not set]{ TypeConstructor[not set]{ __vec_3__f32 ScalarConstructor[not set]{60.000000} ScalarConstructor[not set]{70.000000} ScalarConstructor[not set]{50.000000} } multiply TypeConstructor[not set]{ __vec_3__f32 ScalarConstructor[not set]{0.017453} } } } } })"; EXPECT_THAT(body, HasSubstr(expected)) << body; } } // namespace } // namespace spirv } // namespace reader } // namespace tint