[spirv-reader] Support OpIEqual

Bug: tint:3
Change-Id: I8d87711dcc0ffd1de3cd4a4874a8bf225c676b30
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/19540
Reviewed-by: dan sinclair <dsinclair@google.com>
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 609542c..9a1077b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -327,6 +327,7 @@
     reader/spirv/fail_stream_test.cc
     reader/spirv/function_arithmetic_test.cc
     reader/spirv/function_decl_test.cc
+    reader/spirv/function_logical_test.cc
     reader/spirv/function_var_test.cc
     reader/spirv/function_memory_test.cc
     reader/spirv/namer_test.cc
diff --git a/src/reader/spirv/function.cc b/src/reader/spirv/function.cc
index e6eb339..e49ff51 100644
--- a/src/reader/spirv/function.cc
+++ b/src/reader/spirv/function.cc
@@ -48,6 +48,8 @@
       return ast::BinaryOp::kMultiply;
     case SpvOpFDiv:
       return ast::BinaryOp::kDivide;
+    case SpvOpIEqual:
+      return ast::BinaryOp::kEqual;
     default:
       break;
   }
diff --git a/src/reader/spirv/function_logical_test.cc b/src/reader/spirv/function_logical_test.cc
new file mode 100644
index 0000000..476cc00
--- /dev/null
+++ b/src/reader/spirv/function_logical_test.cc
@@ -0,0 +1,199 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <string>
+#include <vector>
+
+#include "gmock/gmock.h"
+#include "src/reader/spirv/function.h"
+#include "src/reader/spirv/parser_impl.h"
+#include "src/reader/spirv/parser_impl_test_helper.h"
+#include "src/reader/spirv/spirv_tools_helpers_test.h"
+
+namespace tint {
+namespace reader {
+namespace spirv {
+namespace {
+
+using ::testing::HasSubstr;
+
+std::string CommonTypes() {
+  return R"(
+  %void = OpTypeVoid
+  %voidfn = OpTypeFunction %void
+
+  %bool = OpTypeBool
+  %true = OpConstantTrue %bool
+  %false = OpConstantFalse %bool
+
+  %uint = OpTypeInt 32 0
+  %int = OpTypeInt 32 1
+  %float = OpTypeFloat 32
+
+  %uint_10 = OpConstant %uint 10
+  %uint_20 = OpConstant %uint 20
+  %int_30 = OpConstant %int 30
+  %int_40 = OpConstant %int 40
+  %float_50 = OpConstant %float 50
+  %float_60 = OpConstant %float 60
+
+  %ptr_uint = OpTypePointer Function %uint
+  %ptr_int = OpTypePointer Function %int
+  %ptr_float = OpTypePointer Function %float
+
+  %v2bool = OpTypeVector %bool 2
+  %v2uint = OpTypeVector %uint 2
+  %v2int = OpTypeVector %int 2
+  %v2float = OpTypeVector %float 2
+
+  %v2uint_10_20 = OpConstantComposite %v2uint %uint_10 %uint_20
+  %v2uint_20_10 = OpConstantComposite %v2uint %uint_20 %uint_10
+  %v2int_30_40 = OpConstantComposite %v2int %int_30 %int_40
+  %v2int_40_30 = OpConstantComposite %v2int %int_40 %int_30
+  %v2float_50_60 = OpConstantComposite %v2float %float_50 %float_60
+  %v2float_60_50 = OpConstantComposite %v2float %float_60 %float_50
+)";
+}
+
+// Returns the AST dump for a given SPIR-V assembly constant.
+std::string AstFor(std::string assembly) {
+  if (assembly == "v2bool_t_f") {
+    return R"(TypeConstructor{
+          __vec_2__bool
+          ScalarConstructor{true}
+          ScalarConstructor{false}
+        })";
+  }
+  if (assembly == "v2bool_f_t") {
+    return R"(TypeConstructor{
+          __vec_2__bool
+          ScalarConstructor{false}
+          ScalarConstructor{true}
+        })";
+  }
+  if (assembly == "v2uint_10_20") {
+    return R"(TypeConstructor{
+          __vec_2__u32
+          ScalarConstructor{10}
+          ScalarConstructor{20}
+        })";
+  }
+  if (assembly == "v2uint_20_10") {
+    return R"(TypeConstructor{
+          __vec_2__u32
+          ScalarConstructor{20}
+          ScalarConstructor{10}
+        })";
+  }
+  if (assembly == "v2int_30_40") {
+    return R"(TypeConstructor{
+          __vec_2__i32
+          ScalarConstructor{30}
+          ScalarConstructor{40}
+        })";
+  }
+  if (assembly == "v2int_40_30") {
+    return R"(TypeConstructor{
+          __vec_2__i32
+          ScalarConstructor{40}
+          ScalarConstructor{30}
+        })";
+  }
+  if (assembly == "v2float_50_60") {
+    return R"(TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{50.000000}
+          ScalarConstructor{60.000000}
+        })";
+  }
+  if (assembly == "v2float_60_50") {
+    return R"(TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{60.000000}
+          ScalarConstructor{50.000000}
+        })";
+  }
+  return "bad case";
+}
+
+struct BinaryData {
+  const std::string res_type;
+  const std::string lhs;
+  const std::string op;
+  const std::string rhs;
+  const std::string ast_type;
+  const std::string ast_lhs;
+  const std::string ast_op;
+  const std::string ast_rhs;
+};
+inline std::ostream& operator<<(std::ostream& out, BinaryData data) {
+  out << "BinaryData{" << data.res_type << "," << data.lhs << "," << data.op
+      << "," << data.rhs << "," << data.ast_type << "," << data.ast_lhs << ","
+      << data.ast_op << "," << data.ast_rhs << "}";
+  return out;
+}
+
+using SpvBinaryLogicalTest =
+    SpvParserTestBase<::testing::TestWithParam<BinaryData>>;
+
+TEST_P(SpvBinaryLogicalTest, EmitExpression) {
+  const auto assembly = CommonTypes() + R"(
+     %100 = OpFunction %void None %voidfn
+     %entry = OpLabel
+     %1 = )" + GetParam().op +
+                        " %" + GetParam().res_type + " %" + GetParam().lhs +
+                        " %" + GetParam().rhs + R"(
+     OpReturn
+     OpFunctionEnd
+  )";
+  auto p = parser(test::Assemble(assembly));
+  ASSERT_TRUE(p->BuildAndParseInternalModuleExceptFunctions())
+      << p->error() << "\n"
+      << assembly;
+  FunctionEmitter fe(p, *spirv_function(100));
+  EXPECT_TRUE(fe.EmitBody()) << p->error();
+  std::ostringstream ss;
+  ss << R"(Variable{
+    x_1
+    none
+    )"
+     << GetParam().ast_type << "\n    {\n      Binary{"
+     << "\n        " << GetParam().ast_lhs << "\n        " << GetParam().ast_op
+     << "\n        " << GetParam().ast_rhs;
+  EXPECT_THAT(ToString(fe.ast_body()), HasSubstr(ss.str())) << assembly;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    SpvParserTest_IEqual,
+    SpvBinaryLogicalTest,
+    ::testing::Values(
+        // Both uint
+        BinaryData{"bool", "uint_10", "OpIEqual", "uint_20", "__bool",
+                   "ScalarConstructor{10}", "equal", "ScalarConstructor{20}"},
+        // Both int
+        BinaryData{"bool", "int_30", "OpIEqual", "int_40", "__bool",
+                   "ScalarConstructor{30}", "equal", "ScalarConstructor{40}"},
+        // Both v2uint
+        BinaryData{"v2bool", "v2uint_10_20", "OpIEqual", "v2uint_20_10",
+                   "__vec_2__bool", AstFor("v2uint_10_20"), "equal",
+                   AstFor("v2uint_20_10")},
+        // Both v2int
+        BinaryData{"v2bool", "v2int_30_40", "OpIEqual", "v2int_40_30",
+                   "__vec_2__bool", AstFor("v2int_30_40"), "equal",
+                   AstFor("v2int_40_30")}));
+
+}  // namespace
+}  // namespace spirv
+}  // namespace reader
+}  // namespace tint