[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:
parent
4484fe128d
commit
9f7297c766
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue