tint: Add binary-ops to the intrinsic table

• Declare all the binary ops in the intrinsics.def file.
• Reimplement Resolver::BinaryOpType() with the IntrinsicTable.

This will simplify maintenance of the operators, and will greatly
simplify the [AbstractInt -> i32|u32] [AbstractFloat -> f32|f16] logic.

Bug: tint:1504
Change-Id: Ie028602e05b59916c3f2168c92f200f10e402b96
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89027
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index 760813d..33142ea 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -1746,12 +1746,8 @@
     auto* lhs_ty = lhs->Type()->UnwrapRef();
     auto* rhs_ty = rhs->Type()->UnwrapRef();
 
-    auto* ty = BinaryOpType(lhs_ty, rhs_ty, expr->op);
+    auto* ty = intrinsic_table_->Lookup(expr->op, lhs_ty, rhs_ty, expr->source, false).result;
     if (!ty) {
-        AddError("Binary expression operand types are invalid for this operation: " +
-                     sem_.TypeNameOf(lhs_ty) + " " + FriendlyName(expr->op) + " " +
-                     sem_.TypeNameOf(rhs_ty),
-                 expr->source);
         return nullptr;
     }
 
@@ -1764,160 +1760,6 @@
     return sem;
 }
 
-const sem::Type* Resolver::BinaryOpType(const sem::Type* lhs_ty,
-                                        const sem::Type* rhs_ty,
-                                        ast::BinaryOp op) {
-    using Bool = sem::Bool;
-    using F32 = sem::F32;
-    using I32 = sem::I32;
-    using U32 = sem::U32;
-    using Matrix = sem::Matrix;
-    using Vector = sem::Vector;
-
-    auto* lhs_vec = lhs_ty->As<Vector>();
-    auto* lhs_vec_elem_type = lhs_vec ? lhs_vec->type() : nullptr;
-    auto* rhs_vec = rhs_ty->As<Vector>();
-    auto* rhs_vec_elem_type = rhs_vec ? rhs_vec->type() : nullptr;
-
-    const bool matching_vec_elem_types = lhs_vec_elem_type && rhs_vec_elem_type &&
-                                         (lhs_vec_elem_type == rhs_vec_elem_type) &&
-                                         (lhs_vec->Width() == rhs_vec->Width());
-
-    const bool matching_types = matching_vec_elem_types || (lhs_ty == rhs_ty);
-
-    // Binary logical expressions
-    if (op == ast::BinaryOp::kLogicalAnd || op == ast::BinaryOp::kLogicalOr) {
-        if (matching_types && lhs_ty->Is<Bool>()) {
-            return lhs_ty;
-        }
-    }
-    if (op == ast::BinaryOp::kOr || op == ast::BinaryOp::kAnd) {
-        if (matching_types && lhs_ty->Is<Bool>()) {
-            return lhs_ty;
-        }
-        if (matching_types && lhs_vec_elem_type && lhs_vec_elem_type->Is<Bool>()) {
-            return lhs_ty;
-        }
-    }
-
-    // Arithmetic expressions
-    if (ast::IsArithmetic(op)) {
-        // Binary arithmetic expressions over scalars
-        if (matching_types && lhs_ty->is_numeric_scalar()) {
-            return lhs_ty;
-        }
-
-        // Binary arithmetic expressions over vectors
-        if (matching_types && lhs_vec_elem_type && lhs_vec_elem_type->is_numeric_scalar()) {
-            return lhs_ty;
-        }
-
-        // Binary arithmetic expressions with mixed scalar and vector operands
-        if (lhs_vec_elem_type && (lhs_vec_elem_type == rhs_ty) && rhs_ty->is_numeric_scalar()) {
-            return lhs_ty;
-        }
-        if (rhs_vec_elem_type && (rhs_vec_elem_type == lhs_ty) && lhs_ty->is_numeric_scalar()) {
-            return rhs_ty;
-        }
-    }
-
-    // Matrix arithmetic
-    auto* lhs_mat = lhs_ty->As<Matrix>();
-    auto* lhs_mat_elem_type = lhs_mat ? lhs_mat->type() : nullptr;
-    auto* rhs_mat = rhs_ty->As<Matrix>();
-    auto* rhs_mat_elem_type = rhs_mat ? rhs_mat->type() : nullptr;
-    // Addition and subtraction of float matrices
-    if ((op == ast::BinaryOp::kAdd || op == ast::BinaryOp::kSubtract) && lhs_mat_elem_type &&
-        lhs_mat_elem_type->Is<F32>() && rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
-        (lhs_mat->columns() == rhs_mat->columns()) && (lhs_mat->rows() == rhs_mat->rows())) {
-        return rhs_ty;
-    }
-    if (op == ast::BinaryOp::kMultiply) {
-        // Multiplication of a matrix and a scalar
-        if (lhs_ty->Is<F32>() && rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>()) {
-            return rhs_ty;
-        }
-        if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() && rhs_ty->Is<F32>()) {
-            return lhs_ty;
-        }
-
-        // Vector times matrix
-        if (lhs_vec_elem_type && lhs_vec_elem_type->Is<F32>() && rhs_mat_elem_type &&
-            rhs_mat_elem_type->Is<F32>() && (lhs_vec->Width() == rhs_mat->rows())) {
-            return builder_->create<sem::Vector>(lhs_vec->type(), rhs_mat->columns());
-        }
-
-        // Matrix times vector
-        if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() && rhs_vec_elem_type &&
-            rhs_vec_elem_type->Is<F32>() && (lhs_mat->columns() == rhs_vec->Width())) {
-            return builder_->create<sem::Vector>(rhs_vec->type(), lhs_mat->rows());
-        }
-
-        // Matrix times matrix
-        if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() && rhs_mat_elem_type &&
-            rhs_mat_elem_type->Is<F32>() && (lhs_mat->columns() == rhs_mat->rows())) {
-            return builder_->create<sem::Matrix>(
-                builder_->create<sem::Vector>(lhs_mat_elem_type, lhs_mat->rows()),
-                rhs_mat->columns());
-        }
-    }
-
-    // Comparison expressions
-    if (ast::IsComparison(op)) {
-        if (matching_types) {
-            // Special case for bools: only == and !=
-            if (lhs_ty->Is<Bool>() &&
-                (op == ast::BinaryOp::kEqual || op == ast::BinaryOp::kNotEqual)) {
-                return builder_->create<sem::Bool>();
-            }
-
-            // For the rest, we can compare i32, u32, and f32
-            if (lhs_ty->IsAnyOf<I32, U32, F32>()) {
-                return builder_->create<sem::Bool>();
-            }
-        }
-
-        // Same for vectors
-        if (matching_vec_elem_types) {
-            if (lhs_vec_elem_type->Is<Bool>() &&
-                (op == ast::BinaryOp::kEqual || op == ast::BinaryOp::kNotEqual)) {
-                return builder_->create<sem::Vector>(builder_->create<sem::Bool>(),
-                                                     lhs_vec->Width());
-            }
-
-            if (lhs_vec_elem_type->is_numeric_scalar()) {
-                return builder_->create<sem::Vector>(builder_->create<sem::Bool>(),
-                                                     lhs_vec->Width());
-            }
-        }
-    }
-
-    // Binary bitwise operations
-    if (ast::IsBitwise(op)) {
-        if (matching_types && lhs_ty->is_integer_scalar_or_vector()) {
-            return lhs_ty;
-        }
-    }
-
-    // Bit shift expressions
-    if (ast::IsBitshift(op)) {
-        // Type validation rules are the same for left or right shift, despite
-        // differences in computation rules (i.e. right shift can be arithmetic or
-        // logical depending on lhs type).
-
-        if (lhs_ty->IsAnyOf<I32, U32>() && rhs_ty->Is<U32>()) {
-            return lhs_ty;
-        }
-
-        if (lhs_vec_elem_type && lhs_vec_elem_type->IsAnyOf<I32, U32>() && rhs_vec_elem_type &&
-            rhs_vec_elem_type->Is<U32>()) {
-            return lhs_ty;
-        }
-    }
-
-    return nullptr;
-}
-
 sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
     auto* expr = sem_.Get(unary->expr);
     auto* expr_ty = expr->Type();
@@ -2472,11 +2314,8 @@
 
         auto* lhs_ty = lhs->Type()->UnwrapRef();
         auto* rhs_ty = rhs->Type()->UnwrapRef();
-        auto* ty = BinaryOpType(lhs_ty, rhs_ty, stmt->op);
+        auto* ty = intrinsic_table_->Lookup(stmt->op, lhs_ty, rhs_ty, stmt->source, true).result;
         if (!ty) {
-            AddError("compound assignment operand types are invalid: " + sem_.TypeNameOf(lhs_ty) +
-                         " " + FriendlyName(stmt->op) + " " + sem_.TypeNameOf(rhs_ty),
-                     stmt->source);
             return false;
         }
         return validator_.Assignment(stmt, ty);