spirv-writer: support isFinite
Fixed: tint:157
Change-Id: I7b9af49edd44cbd8feb85fe637b82ac76a225e50
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/52420
Commit-Queue: David Neto <dneto@google.com>
Auto-Submit: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index a7af47f..7b51c94 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -1971,6 +1971,22 @@
return result_id;
}
+ // Generates the SPIR-V ID for the expression for the indexed call parameter,
+ // and loads it if necessary. Returns 0 on error.
+ auto get_param_as_value_id = [&](size_t i) -> uint32_t {
+ auto* arg = call->params()[i];
+ auto& param = intrinsic->Parameters()[i];
+ auto val_id = GenerateExpression(arg);
+ if (val_id == 0) {
+ return 0;
+ }
+
+ if (!param.type->Is<sem::Pointer>()) {
+ val_id = GenerateLoadIfNeeded(TypeOf(arg), val_id);
+ }
+ return val_id;
+ };
+
OperandList params = {Operand::Int(result_type_id), result};
spv::Op op = spv::Op::OpNop;
@@ -2054,6 +2070,32 @@
case IntrinsicType::kIsNan:
op = spv::Op::OpIsNan;
break;
+ case IntrinsicType::kIsFinite: {
+ // Implemented as: not(IsInf or IsNan)
+ auto val_id = get_param_as_value_id(0);
+ if (!val_id) {
+ return 0;
+ }
+ auto inf_result = result_op();
+ auto nan_result = result_op();
+ auto or_result = result_op();
+ if (push_function_inst(spv::Op::OpIsInf,
+ {Operand::Int(result_type_id), inf_result,
+ Operand::Int(val_id)}) &&
+ push_function_inst(spv::Op::OpIsNan,
+ {Operand::Int(result_type_id), nan_result,
+ Operand::Int(val_id)}) &&
+ push_function_inst(spv::Op::OpLogicalOr,
+ {Operand::Int(result_type_id), or_result,
+ Operand::Int(inf_result.to_i()),
+ Operand::Int(nan_result.to_i())}) &&
+ push_function_inst(spv::Op::OpLogicalNot,
+ {Operand::Int(result_type_id), result,
+ Operand::Int(or_result.to_i())})) {
+ return result_id;
+ }
+ return 0;
+ }
case IntrinsicType::kReverseBits:
op = spv::Op::OpBitReverse;
break;
@@ -2090,18 +2132,11 @@
}
for (size_t i = 0; i < call->params().size(); i++) {
- auto* arg = call->params()[i];
- auto& param = intrinsic->Parameters()[i];
- auto val_id = GenerateExpression(arg);
- if (val_id == 0) {
- return false;
+ if (auto val_id = get_param_as_value_id(i)) {
+ params.emplace_back(Operand::Int(val_id));
+ } else {
+ return 0;
}
-
- if (!param.type->Is<sem::Pointer>()) {
- val_id = GenerateLoadIfNeeded(TypeOf(arg), val_id);
- }
-
- params.emplace_back(Operand::Int(val_id));
}
if (!push_function_inst(op, params)) {
diff --git a/src/writer/spirv/builder_intrinsic_test.cc b/src/writer/spirv/builder_intrinsic_test.cc
index 3c40829..8599f1b 100644
--- a/src/writer/spirv/builder_intrinsic_test.cc
+++ b/src/writer/spirv/builder_intrinsic_test.cc
@@ -128,6 +128,62 @@
testing::Values(IntrinsicData{"isNan", "OpIsNan"},
IntrinsicData{"isInf", "OpIsInf"}));
+TEST_F(IntrinsicBuilderTest, IsFinite_Scalar) {
+ auto* var = Global("v", ty.f32(), ast::StorageClass::kPrivate);
+
+ auto* expr = Call("isFinite", "v");
+ WrapInFunction(expr);
+
+ spirv::Builder& b = Build();
+
+ b.push_function(Function{});
+ ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+
+ EXPECT_EQ(b.GenerateCallExpression(expr), 5u) << b.error();
+ EXPECT_EQ(DumpInstructions(b.types()), R"(%3 = OpTypeFloat 32
+%2 = OpTypePointer Private %3
+%4 = OpConstantNull %3
+%1 = OpVariable %2 Private %4
+%6 = OpTypeBool
+)");
+ EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+ R"(%7 = OpLoad %3 %1
+%8 = OpIsInf %6 %7
+%9 = OpIsNan %6 %7
+%10 = OpLogicalOr %6 %8 %9
+%5 = OpLogicalNot %6 %10
+)");
+}
+
+TEST_F(IntrinsicBuilderTest, IsFinite_Vector) {
+ auto* var = Global("v", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+
+ auto* expr = Call("isFinite", "v");
+ WrapInFunction(expr);
+
+ spirv::Builder& b = Build();
+
+ b.push_function(Function{});
+ ASSERT_TRUE(b.GenerateGlobalVariable(var)) << b.error();
+
+ EXPECT_EQ(b.GenerateCallExpression(expr), 6u) << b.error();
+ EXPECT_EQ(DumpInstructions(b.types()), R"(%4 = OpTypeFloat 32
+%3 = OpTypeVector %4 3
+%2 = OpTypePointer Private %3
+%5 = OpConstantNull %3
+%1 = OpVariable %2 Private %5
+%8 = OpTypeBool
+%7 = OpTypeVector %8 3
+)");
+ EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+ R"(%9 = OpLoad %3 %1
+%10 = OpIsInf %7 %9
+%11 = OpIsNan %7 %9
+%12 = OpLogicalOr %7 %10 %11
+%6 = OpLogicalNot %7 %12
+)");
+}
+
using IntrinsicIntTest = IntrinsicBuilderTestWithParam<IntrinsicData>;
TEST_P(IntrinsicIntTest, Call_SInt_Scalar) {
auto param = GetParam();