From 9f7297c76610b2d1fb737d6136d9981a54bf719a Mon Sep 17 00:00:00 2001 From: David Neto Date: Thu, 14 May 2020 21:43:27 +0000 Subject: [PATCH] [spirv-reader] Unordered float compares Unordered float compares are not supported directly by WGSL. Translate them as negated ordered compares. Bug: tint:3 Change-Id: I4fea7c924054cffc9a39a8be3b3d9f088d302114 Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/21540 Reviewed-by: Ryan Harrison --- src/reader/spirv/function.cc | 36 ++ src/reader/spirv/function_logical_test.cc | 431 +++++++++++++++++++++- 2 files changed, 458 insertions(+), 9 deletions(-) diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc index 9889e18180..7140a0a1ff 100644 --- a/src/reader/spirv/function.cc +++ b/src/reader/spirv/function.cc @@ -232,6 +232,31 @@ ast::BinaryOp ConvertBinaryOp(SpvOp opcode) { return ast::BinaryOp::kNone; } +// If the given SPIR-V opcode is a floating point unordered comparison, +// then returns the binary float comparison for which it is the negation. +// Othewrise returns BinaryOp::kNone. +// @param opcode SPIR-V opcode +// @returns operation corresponding to negated version of the SPIR-V opcode +ast::BinaryOp NegatedFloatCompare(SpvOp opcode) { + switch (opcode) { + case SpvOpFUnordEqual: + return ast::BinaryOp::kNotEqual; + case SpvOpFUnordNotEqual: + return ast::BinaryOp::kEqual; + case SpvOpFUnordLessThan: + return ast::BinaryOp::kGreaterThanEqual; + case SpvOpFUnordLessThanEqual: + return ast::BinaryOp::kGreaterThan; + case SpvOpFUnordGreaterThan: + return ast::BinaryOp::kLessThanEqual; + case SpvOpFUnordGreaterThanEqual: + return ast::BinaryOp::kLessThan; + default: + break; + } + return ast::BinaryOp::kNone; +} + // @returns the merge block ID for the given basic block, or 0 if there is none. uint32_t MergeFor(const spvtools::opt::BasicBlock& bb) { // Get the OpSelectionMerge or OpLoopMerge instruction, if any. @@ -1502,6 +1527,17 @@ TypedExpression FunctionEmitter::MaybeEmitCombinatorialValue( std::make_unique(target_ty, operand(0).expr)}; } + auto negated_op = NegatedFloatCompare(inst.opcode()); + if (negated_op != ast::BinaryOp::kNone) { + auto arg0 = operand(0); + auto arg1 = operand(1); + auto binary_expr = std::make_unique( + negated_op, std::move(arg0.expr), std::move(arg1.expr)); + auto negated_expr = std::make_unique( + ast::UnaryOp::kNot, std::move(binary_expr)); + return {ast_type, std::move(negated_expr)}; + } + // builtin readonly function // glsl.std.450 readonly function diff --git a/src/reader/spirv/function_logical_test.cc b/src/reader/spirv/function_logical_test.cc index 040a59977e..4a0f1df6d1 100644 --- a/src/reader/spirv/function_logical_test.cc +++ b/src/reader/spirv/function_logical_test.cc @@ -682,21 +682,434 @@ INSTANTIATE_TEST_SUITE_P( "__vec_2__bool", AstFor("v2int_30_40"), "less_than_equal", AstFor("cast_v2uint_20_10")})); +using SpvFUnordTest = SpvParserTestBase<::testing::Test>; + +TEST_F(SpvFUnordTest, FUnordEqual_Scalar) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordEqual %bool %float_50 %float_60 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + ScalarConstructor{50.000000} + not_equal + ScalarConstructor{60.000000} + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordEqual_Vector) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordEqual %bool %v2float_50_60 %v2float_60_50 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{50.000000} + ScalarConstructor{60.000000} + } + not_equal + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{60.000000} + ScalarConstructor{50.000000} + } + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordNotEqual_Scalar) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordNotEqual %bool %float_50 %float_60 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + ScalarConstructor{50.000000} + equal + ScalarConstructor{60.000000} + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordNotEqual_Vector) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordNotEqual %bool %v2float_50_60 %v2float_60_50 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{50.000000} + ScalarConstructor{60.000000} + } + equal + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{60.000000} + ScalarConstructor{50.000000} + } + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordLessThan_Scalar) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordLessThan %bool %float_50 %float_60 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + ScalarConstructor{50.000000} + greater_than_equal + ScalarConstructor{60.000000} + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordLessThan_Vector) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordLessThan %bool %v2float_50_60 %v2float_60_50 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{50.000000} + ScalarConstructor{60.000000} + } + greater_than_equal + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{60.000000} + ScalarConstructor{50.000000} + } + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordLessThanEqual_Scalar) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordLessThanEqual %bool %float_50 %float_60 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + ScalarConstructor{50.000000} + greater_than + ScalarConstructor{60.000000} + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordLessThanEqual_Vector) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordLessThanEqual %bool %v2float_50_60 %v2float_60_50 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{50.000000} + ScalarConstructor{60.000000} + } + greater_than + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{60.000000} + ScalarConstructor{50.000000} + } + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordGreaterThan_Scalar) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordGreaterThan %bool %float_50 %float_60 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + ScalarConstructor{50.000000} + less_than_equal + ScalarConstructor{60.000000} + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordGreaterThan_Vector) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordGreaterThan %bool %v2float_50_60 %v2float_60_50 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{50.000000} + ScalarConstructor{60.000000} + } + less_than_equal + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{60.000000} + ScalarConstructor{50.000000} + } + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordGreaterThanEqual_Scalar) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordGreaterThanEqual %bool %float_50 %float_60 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + ScalarConstructor{50.000000} + less_than + ScalarConstructor{60.000000} + } + } + } + })")) + << ToString(fe.ast_body()); +} + +TEST_F(SpvFUnordTest, FUnordGreaterThanEqual_Vector) { + const auto assembly = CommonTypes() + R"( + %100 = OpFunction %void None %voidfn + %entry = OpLabel + %1 = OpFUnordGreaterThanEqual %bool %v2float_50_60 %v2float_60_50 + OpReturn + OpFunctionEnd + )"; + auto* p = parser(test::Assemble(assembly)); + ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions()); + FunctionEmitter fe(p, *spirv_function(100)); + EXPECT_TRUE(fe.EmitBody()) << p->error(); + EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(R"( + Variable{ + x_1 + none + __bool + { + UnaryOp{ + not + Binary{ + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{50.000000} + ScalarConstructor{60.000000} + } + less_than + TypeConstructor{ + __vec_2__f32 + ScalarConstructor{60.000000} + ScalarConstructor{50.000000} + } + } + } + } + })")) + << ToString(fe.ast_body()); +} + // TODO(dneto): OpAny - likely builtin function TBD // TODO(dneto): OpAll - likely builtin function TBD // TODO(dneto): OpIsNan - likely builtin function TBD // TODO(dneto): OpIsInf - likely builtin function TBD // TODO(dneto): Kernel-guarded instructions. // TODO(dneto): OpSelect - likely builtin function TBD -// -// Unordered float inequalities: blocked pending the resolution of -// https://github.com/gpuweb/gpuweb/issues/706 -// TODO(dneto): OpFUnordEqual -// TODO(dneto): OpFUnordNotEqual -// TODO(dneto): OpFUnordLessThan -// TODO(dneto): OpFUnordGreaterThan -// TODO(dneto): OpFUnordLessThanEqual -// TODO(dneto): OpFUnordGreaterThanEqual } // namespace } // namespace spirv