[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>
This commit is contained in:
David Neto 2020-05-14 21:43:27 +00:00
parent 4484fe128d
commit 9f7297c766
2 changed files with 458 additions and 9 deletions

View File

@ -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<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

View File

@ -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