[spirv-reader] Support OpAny, OpAll, OpIsInf, OpIsNan

Bug: tint:3, tint:121
Change-Id: I1a6a2d45684c28a0e03ad4b208dc6b2f737da694
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/25422
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index 66f5c1c..3e547f9 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -191,6 +191,25 @@
   return false;
 }
 
+/// Converts a SPIR-V opcode for a WGSL builtin function, if there is a
+/// direct translation. Returns nullptr otherwise.
+/// @returns the WGSL builtin function name for the given opcode, or nullptr.
+const char* GetUnaryBuiltInFunctionName(SpvOp opcode) {
+  switch (opcode) {
+    case SpvOpAny:
+      return "any";
+    case SpvOpAll:
+      return "all";
+    case SpvOpIsNan:
+      return "is_nan";
+    case SpvOpIsInf:
+      return "is_inf";
+    default:
+      break;
+  }
+  return nullptr;
+}
+
 // Converts a SPIR-V opcode to its corresponding AST binary opcode, if any
 // @param opcode SPIR-V opcode
 // @returns the AST binary op for the given opcode, or kNone
@@ -2649,6 +2668,16 @@
                                                 arg0.type);
   }
 
+  const char* unary_builtin_name = GetUnaryBuiltInFunctionName(opcode);
+  if (unary_builtin_name != nullptr) {
+    ast::ExpressionList params;
+    params.emplace_back(MakeOperand(inst, 0).expr);
+    return {ast_type,
+            std::make_unique<ast::CallExpression>(
+                std::make_unique<ast::IdentifierExpression>(unary_builtin_name),
+                std::move(params))};
+  }
+
   if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) {
     return MakeAccessChain(inst);
   }
diff --git a/src/reader/spirv/function_logical_test.cc b/src/reader/spirv/function_logical_test.cc
index 8f4272c..24793d1 100644
--- a/src/reader/spirv/function_logical_test.cc
+++ b/src/reader/spirv/function_logical_test.cc
@@ -1279,10 +1279,198 @@
 })")) << 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
+using SpvLogicalTest = SpvParserTestBase<::testing::Test>;
+
+TEST_F(SpvLogicalTest, Any) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpAny %bool %v2bool_t_f
+     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"(VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __bool
+    {
+      Call{
+        Identifier{any}
+        (
+          TypeConstructor{
+            __vec_2__bool
+            ScalarConstructor{true}
+            ScalarConstructor{false}
+          }
+        )
+      }
+    }
+  }
+})")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvLogicalTest, All) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpAll %bool %v2bool_t_f
+     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"(VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __bool
+    {
+      Call{
+        Identifier{all}
+        (
+          TypeConstructor{
+            __vec_2__bool
+            ScalarConstructor{true}
+            ScalarConstructor{false}
+          }
+        )
+      }
+    }
+  }
+})")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvLogicalTest, IsNan_Scalar) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpIsNan %bool %float_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"(VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __bool
+    {
+      Call{
+        Identifier{is_nan}
+        (
+          ScalarConstructor{50.000000}
+        )
+      }
+    }
+  }
+})")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvLogicalTest, IsNan_Vector) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpIsNan %v2bool %v2float_50_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"(VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __vec_2__bool
+    {
+      Call{
+        Identifier{is_nan}
+        (
+          TypeConstructor{
+            __vec_2__f32
+            ScalarConstructor{50.000000}
+            ScalarConstructor{60.000000}
+          }
+        )
+      }
+    }
+  }
+})")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvLogicalTest, IsInf_Scalar) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpIsInf %bool %float_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"(VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __bool
+    {
+      Call{
+        Identifier{is_inf}
+        (
+          ScalarConstructor{50.000000}
+        )
+      }
+    }
+  }
+})")) << ToString(fe.ast_body());
+}
+
+TEST_F(SpvLogicalTest, IsInf_Vector) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = OpIsInf %v2bool %v2float_50_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"(VariableDeclStatement{
+  Variable{
+    x_1
+    none
+    __vec_2__bool
+    {
+      Call{
+        Identifier{is_inf}
+        (
+          TypeConstructor{
+            __vec_2__f32
+            ScalarConstructor{50.000000}
+            ScalarConstructor{60.000000}
+          }
+        )
+      }
+    }
+  }
+})")) << ToString(fe.ast_body());
+}
+
 // TODO(dneto): Kernel-guarded instructions.
 // TODO(dneto): OpSelect over more general types, as in SPIR-V 1.4