[spirv-writer] Adding binary equals generation

This CL adds generation for a == operator.

Bug: tint:5
Change-Id: Ib27836a42153f3732927234cfa9aed342d0f9ac1
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/19402
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/src/writer/spirv/builder.cc b/src/writer/spirv/builder.cc
index 21e8f42..a3e634d 100644
--- a/src/writer/spirv/builder.cc
+++ b/src/writer/spirv/builder.cc
@@ -547,38 +547,50 @@
 }
 
 uint32_t Builder::GenerateBinaryExpression(ast::BinaryExpression* expr) {
+  auto lhs_id = GenerateExpressionAndLoad(expr->lhs());
+  if (lhs_id == 0) {
+    return 0;
+  }
+  auto rhs_id = GenerateExpressionAndLoad(expr->rhs());
+  if (rhs_id == 0) {
+    return 0;
+  }
+
+  auto result = result_op();
+  auto result_id = result.to_i();
+
+  auto lhs_type = expr->lhs()->result_type();
+
+  auto expr_type = expr->result_type();
+  auto type_id = GenerateTypeIfNeeded(expr_type);
+  if (type_id == 0) {
+    return 0;
+  }
+
+  spv::Op op = spv::Op::OpNop;
   if (expr->IsAdd()) {
-    auto lhs_id = GenerateExpressionAndLoad(expr->lhs());
-    if (lhs_id == 0) {
-      return 0;
-    }
-    auto rhs_id = GenerateExpressionAndLoad(expr->rhs());
-    if (rhs_id == 0) {
-      return 0;
-    }
-
-    auto result = result_op();
-    auto result_id = result.to_i();
-
-    auto expr_type = expr->result_type();
-    auto type_id = GenerateTypeIfNeeded(expr_type);
-    if (type_id == 0) {
-      return 0;
-    }
-
     // This handles int and float and the vectors of those types. Other types
     // should have been rejected by validation.
-    spv::Op op = spv::Op::OpIAdd;
+    op = spv::Op::OpIAdd;
     if (expr_type->IsF32() ||
         (expr_type->IsVector() && expr_type->AsVector()->type()->IsF32())) {
       op = spv::Op::OpFAdd;
     }
-    push_function_inst(op, {Operand::Int(type_id), result, Operand::Int(lhs_id),
-                            Operand::Int(rhs_id)});
-
-    return result_id;
+  } else if (expr->IsEqual()) {
+    // This handles int and float and the vectors of those types. Other types
+    // should have been rejected by validation.
+    op = spv::Op::OpIEqual;
+    if (lhs_type->IsF32() ||
+        (lhs_type->IsVector() && lhs_type->AsVector()->type()->IsF32())) {
+      op = spv::Op::OpFOrdEqual;
+    }
+  } else {
+    return 0;
   }
-  return 0;
+
+  push_function_inst(op, {Operand::Int(type_id), result, Operand::Int(lhs_id),
+                          Operand::Int(rhs_id)});
+  return result_id;
 }
 
 bool Builder::GenerateReturnStatement(ast::ReturnStatement* stmt) {
diff --git a/src/writer/spirv/builder_binary_expression_test.cc b/src/writer/spirv/builder_binary_expression_test.cc
index 5827ef2..6b0848f 100644
--- a/src/writer/spirv/builder_binary_expression_test.cc
+++ b/src/writer/spirv/builder_binary_expression_test.cc
@@ -200,6 +200,168 @@
                          testing::Values(BinaryData{ast::BinaryOp::kAdd,
                                                     "OpFAdd"}));
 
+using BinaryCompareIntegerTest = testing::TestWithParam<BinaryData>;
+TEST_P(BinaryCompareIntegerTest, Scalar) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+
+  auto lhs = std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 3));
+  auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 4));
+
+  ast::BinaryExpression expr(param.op, std::move(lhs), std::move(rhs));
+
+  Context ctx;
+  TypeDeterminer td(&ctx);
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b;
+  b.push_function(Function{});
+
+  ASSERT_EQ(b.GenerateBinaryExpression(&expr), 4) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeInt 32 1
+%2 = OpConstant %1 3
+%3 = OpConstant %1 4
+%5 = OpTypeBool
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            "%4 = " + param.name + " %5 %2 %3\n");
+}
+
+TEST_P(BinaryCompareIntegerTest, Vector) {
+  auto param = GetParam();
+
+  ast::type::I32Type i32;
+  ast::type::VectorType vec3(&i32, 3);
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 1)));
+  auto lhs =
+      std::make_unique<ast::TypeConstructorExpression>(&vec3, std::move(vals));
+
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 1)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::IntLiteral>(&i32, 1)));
+  auto rhs =
+      std::make_unique<ast::TypeConstructorExpression>(&vec3, std::move(vals));
+
+  Context ctx;
+  TypeDeterminer td(&ctx);
+
+  ast::BinaryExpression expr(param.op, std::move(lhs), std::move(rhs));
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b;
+  b.push_function(Function{});
+
+  ASSERT_EQ(b.GenerateBinaryExpression(&expr), 5) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeInt 32 1
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstantComposite %1 %3 %3 %3
+%7 = OpTypeBool
+%6 = OpTypeVector %7 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            "%5 = " + param.name + " %6 %4 %4\n");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         BinaryCompareIntegerTest,
+                         testing::Values(BinaryData{ast::BinaryOp::kEqual,
+                                                    "OpIEqual"}));
+
+using BinaryCompareFloatTest = testing::TestWithParam<BinaryData>;
+TEST_P(BinaryCompareFloatTest, Scalar) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+
+  auto lhs = std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 3.2f));
+  auto rhs = std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 4.5f));
+
+  ast::BinaryExpression expr(param.op, std::move(lhs), std::move(rhs));
+
+  Context ctx;
+  TypeDeterminer td(&ctx);
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b;
+  b.push_function(Function{});
+
+  ASSERT_EQ(b.GenerateBinaryExpression(&expr), 4) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%1 = OpTypeFloat 32
+%2 = OpConstant %1 3.20000005
+%3 = OpConstant %1 4.5
+%5 = OpTypeBool
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            "%4 = " + param.name + " %5 %2 %3\n");
+}
+
+TEST_P(BinaryCompareFloatTest, Vector) {
+  auto param = GetParam();
+
+  ast::type::F32Type f32;
+  ast::type::VectorType vec3(&f32, 3);
+
+  ast::ExpressionList vals;
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  auto lhs =
+      std::make_unique<ast::TypeConstructorExpression>(&vec3, std::move(vals));
+
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  vals.push_back(std::make_unique<ast::ScalarConstructorExpression>(
+      std::make_unique<ast::FloatLiteral>(&f32, 1.f)));
+  auto rhs =
+      std::make_unique<ast::TypeConstructorExpression>(&vec3, std::move(vals));
+
+  Context ctx;
+  TypeDeterminer td(&ctx);
+
+  ast::BinaryExpression expr(param.op, std::move(lhs), std::move(rhs));
+
+  ASSERT_TRUE(td.DetermineResultType(&expr)) << td.error();
+
+  Builder b;
+  b.push_function(Function{});
+
+  ASSERT_EQ(b.GenerateBinaryExpression(&expr), 5) << b.error();
+  EXPECT_EQ(DumpInstructions(b.types()), R"(%2 = OpTypeFloat 32
+%1 = OpTypeVector %2 3
+%3 = OpConstant %2 1
+%4 = OpConstantComposite %1 %3 %3 %3
+%7 = OpTypeBool
+%6 = OpTypeVector %7 3
+)");
+  EXPECT_EQ(DumpInstructions(b.functions()[0].instructions()),
+            "%5 = " + param.name + " %6 %4 %4\n");
+}
+INSTANTIATE_TEST_SUITE_P(BuilderTest,
+                         BinaryCompareFloatTest,
+                         testing::Values(BinaryData{ast::BinaryOp::kEqual,
+                                                    "OpFOrdEqual"}));
+
 }  // namespace
 }  // namespace spirv
 }  // namespace writer