From 287f6f12eff63c20ae727665b89eda3f7d83daf6 Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 10 Dec 2020 15:22:51 +0000 Subject: [PATCH] spirv-reader: support more integer builtins Support: - SAbs - UMax, SMax - UMin, SMin Add tests for operand and result conversion for UClamp. SClamp was already tested. Bug: tint:3 Change-Id: I9b9278843ca5243991b330b27764756137da4ee4 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/35302 Reviewed-by: dan sinclair Commit-Queue: David Neto Auto-Submit: David Neto --- src/reader/spirv/function.cc | 10 +- .../spirv/function_glsl_std_450_test.cc | 552 +++++++++++++++++- 2 files changed, 556 insertions(+), 6 deletions(-) diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 6a0c855a6c..484fbba1a8 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -326,6 +326,7 @@ ast::BinaryOp NegatedFloatCompare(SpvOp opcode) { std::string GetGlslStd450FuncName(uint32_t ext_opcode) { switch (ext_opcode) { case GLSLstd450FAbs: + case GLSLstd450SAbs: return "abs"; case GLSLstd450Acos: return "acos"; @@ -374,9 +375,13 @@ std::string GetGlslStd450FuncName(uint32_t ext_opcode) { return "log2"; case GLSLstd450NMax: case GLSLstd450FMax: // FMax is less prescriptive about NaN operands + case GLSLstd450UMax: + case GLSLstd450SMax: return "max"; case GLSLstd450NMin: case GLSLstd450FMin: // FMin is less prescriptive about NaN operands + case GLSLstd450UMin: + case GLSLstd450SMin: return "min"; case GLSLstd450FMix: return "mix"; @@ -411,7 +416,6 @@ std::string GetGlslStd450FuncName(uint32_t ext_opcode) { // TODO(dneto) - The following are not implemented. // They are grouped semantically, as in GLSL.std.450.h. case GLSLstd450RoundEven: - case GLSLstd450SAbs: case GLSLstd450SSign: case GLSLstd450Radians: @@ -425,10 +429,6 @@ std::string GetGlslStd450FuncName(uint32_t ext_opcode) { case GLSLstd450Modf: case GLSLstd450ModfStruct: - case GLSLstd450UMin: - case GLSLstd450SMin: - case GLSLstd450UMax: - case GLSLstd450SMax: case GLSLstd450IMix: case GLSLstd450Frexp: diff --git a/src/reader/spirv/function_glsl_std_450_test.cc b/src/reader/spirv/function_glsl_std_450_test.cc index 203d357d85..4333ceeb4c 100644 --- a/src/reader/spirv/function_glsl_std_450_test.cc +++ b/src/reader/spirv/function_glsl_std_450_test.cc @@ -161,8 +161,14 @@ using SpvParserTest_GlslStd450_Floating_FloatingUinting = 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>; @@ -691,6 +697,128 @@ INSTANTIATE_TEST_SUITE_P( {"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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + VariableConst{ + x_1 + none + __i32 + { + Call[not set]{ + Identifier[not set]{)" + GetParam().wgsl_func + + R"(} + ( + Identifier[not set]{i1} + ) + } + } + })")) + << ToString(fe.ast_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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + VariableConst{ + x_1 + none + __vec_2__i32 + { + Call[not set]{ + Identifier[not set]{)" + GetParam().wgsl_func + + R"(} + ( + Identifier[not set]{v2i1} + ) + } + } + })")) + << ToString(fe.ast_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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + VariableConst{ + x_1 + none + __i32 + { + Call[not set]{ + Identifier[not set]{)" + GetParam().wgsl_func + + R"(} + ( + Identifier[not set]{i1} + Identifier[not set]{i2} + ) + } + } + })")) + << ToString(fe.ast_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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + VariableConst{ + x_1 + none + __vec_2__i32 + { + Call[not set]{ + Identifier[not set]{)" + GetParam().wgsl_func + + R"(} + ( + Identifier[not set]{v2i1} + Identifier[not set]{v2i2} + ) + } + } + })")) + << ToString(fe.ast_body()); +} + TEST_P(SpvParserTest_GlslStd450_Inting_IntingIntingInting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %int %glsl )" + @@ -755,10 +883,80 @@ TEST_P(SpvParserTest_GlslStd450_Inting_IntingIntingInting, Vector) { << ToString(fe.ast_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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + VariableConst{ + x_1 + none + __u32 + { + Call[not set]{ + Identifier[not set]{)" + GetParam().wgsl_func + + R"(} + ( + Identifier[not set]{u1} + Identifier[not set]{u2} + ) + } + } + })")) + << ToString(fe.ast_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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + VariableConst{ + x_1 + none + __vec_2__u32 + { + Call[not set]{ + Identifier[not set]{)" + GetParam().wgsl_func + + R"(} + ( + Identifier[not set]{v2u1} + Identifier[not set]{v2u2} + ) + } + } + })")) + << ToString(fe.ast_body()); +} + TEST_P(SpvParserTest_GlslStd450_Uinting_UintingUintingUinting, Scalar) { const auto assembly = Preamble() + R"( %1 = OpExtInst %uint %glsl )" + @@ -822,11 +1020,187 @@ TEST_P(SpvParserTest_GlslStd450_Uinting_UintingUintingUinting, Vector) { << ToString(fe.ast_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_F(SpvParserTest, RectifyOperandsAndResult_GLSLstd450SClamp) { +// 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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body = ToString(fe.ast_body()); + EXPECT_THAT(body, HasSubstr(R"( + VariableConst{ + x_1 + none + __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 + __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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body = ToString(fe.ast_body()); + EXPECT_THAT(body, HasSubstr(R"( + VariableConst{ + x_1 + none + __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 + __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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body = ToString(fe.ast_body()); + EXPECT_THAT(body, HasSubstr(R"( + VariableConst{ + x_1 + none + __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 + __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 @@ -886,6 +1260,182 @@ TEST_F(SpvParserTest, RectifyOperandsAndResult_GLSLstd450SClamp) { << 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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body = ToString(fe.ast_body()); + EXPECT_THAT(body, HasSubstr(R"( + VariableConst{ + x_1 + none + __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 + __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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body = ToString(fe.ast_body()); + EXPECT_THAT(body, HasSubstr(R"( + VariableConst{ + x_1 + none + __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 + __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()); + FunctionEmitter fe(p.get(), *spirv_function(p.get(), 100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + auto body = ToString(fe.ast_body()); + EXPECT_THAT(body, HasSubstr(R"( + VariableConst{ + x_1 + none + __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 + __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; +} + } // namespace } // namespace spirv } // namespace reader