[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 <rharrison@chromium.org>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 9889e18..7140a0a 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -232,6 +232,31 @@
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 @@
std::make_unique<ast::AsExpression>(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<ast::BinaryExpression>(
+ negated_op, std::move(arg0.expr), std::move(arg1.expr));
+ auto negated_expr = std::make_unique<ast::UnaryOpExpression>(
+ 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 040a599..4a0f1df 100644
--- a/src/reader/spirv/function_logical_test.cc
+++ b/src/reader/spirv/function_logical_test.cc
@@ -682,21 +682,434 @@
"__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