tint: add CheckedDiv for abstract numbers

Bug: tint:1581
Change-Id: Iafed168a916ac3a1062bcf53299e8ff17fc389fb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/101041
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/tint/number.h b/src/tint/number.h
index 032844c..e96cfc7 100644
--- a/src/tint/number.h
+++ b/src/tint/number.h
@@ -479,6 +479,28 @@
     return AFloat{result};
 }
 
+/// @returns a / b, or an empty optional if the resulting value overflowed the AInt
+inline std::optional<AInt> CheckedDiv(AInt a, AInt b) {
+    if (b == 0) {
+        return {};
+    }
+
+    if (b == -1 && a == AInt::Lowest()) {
+        return {};
+    }
+
+    return AInt{a.value / b.value};
+}
+
+/// @returns a / b, or an empty optional if the resulting value overflowed the AFloat
+inline std::optional<AFloat> CheckedDiv(AFloat a, AFloat b) {
+    auto result = a.value / b.value;
+    if (!std::isfinite(result)) {
+        return {};
+    }
+    return AFloat{result};
+}
+
 /// @returns a * b + c, or an empty optional if the value overflowed the AInt
 inline std::optional<AInt> CheckedMadd(AInt a, AInt b, AInt c) {
     // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635
diff --git a/src/tint/number_test.cc b/src/tint/number_test.cc
index 3182ad3..15d96e7 100644
--- a/src/tint/number_test.cc
+++ b/src/tint/number_test.cc
@@ -537,6 +537,60 @@
         ////////////////////////////////////////////////////////////////////////
     }));
 
+using CheckedDivTest_AInt = testing::TestWithParam<BinaryCheckedCase_AInt>;
+TEST_P(CheckedDivTest_AInt, Test) {
+    auto expect = std::get<0>(GetParam());
+    auto a = std::get<1>(GetParam());
+    auto b = std::get<2>(GetParam());
+    EXPECT_EQ(CheckedDiv(a, b), expect) << std::hex << "0x" << a << " - 0x" << b;
+}
+INSTANTIATE_TEST_SUITE_P(
+    CheckedDivTest_AInt,
+    CheckedDivTest_AInt,
+    testing::ValuesIn(std::vector<BinaryCheckedCase_AInt>{
+        {AInt(0), AInt(0), AInt(1)},
+        {AInt(1), AInt(1), AInt(1)},
+        {AInt(1), AInt(1), AInt(1)},
+        {AInt(2), AInt(2), AInt(1)},
+        {AInt(2), AInt(4), AInt(2)},
+        {AInt::Highest(), AInt::Highest(), AInt(1)},
+        {AInt::Lowest(), AInt::Lowest(), AInt(1)},
+        {AInt(1), AInt::Highest(), AInt::Highest()},
+        {AInt(0), AInt(0), AInt::Highest()},
+        {AInt(0), AInt(0), AInt::Lowest()},
+        {OVERFLOW, AInt(123), AInt(0)},
+        {OVERFLOW, AInt(-123), AInt(0)},
+        ////////////////////////////////////////////////////////////////////////
+    }));
+
+using CheckedDivTest_AFloat = testing::TestWithParam<BinaryCheckedCase_AFloat>;
+TEST_P(CheckedDivTest_AFloat, Test) {
+    auto expect = std::get<0>(GetParam());
+    auto a = std::get<1>(GetParam());
+    auto b = std::get<2>(GetParam());
+    EXPECT_EQ(CheckedDiv(a, b), expect) << std::hex << "0x" << a << " - 0x" << b;
+}
+INSTANTIATE_TEST_SUITE_P(
+    CheckedDivTest_AFloat,
+    CheckedDivTest_AFloat,
+    testing::ValuesIn(std::vector<BinaryCheckedCase_AFloat>{
+        {AFloat(0), AFloat(0), AFloat(1)},
+        {AFloat(1), AFloat(1), AFloat(1)},
+        {AFloat(1), AFloat(1), AFloat(1)},
+        {AFloat(2), AFloat(2), AFloat(1)},
+        {AFloat(2), AFloat(4), AFloat(2)},
+        {AFloat::Highest(), AFloat::Highest(), AFloat(1)},
+        {AFloat::Lowest(), AFloat::Lowest(), AFloat(1)},
+        {AFloat(1), AFloat::Highest(), AFloat::Highest()},
+        {AFloat(0), AFloat(0), AFloat::Highest()},
+        {-AFloat(0), AFloat(0), AFloat::Lowest()},
+        {OVERFLOW, AFloat(123), AFloat(0)},
+        {OVERFLOW, AFloat(123), AFloat(-0)},
+        {OVERFLOW, AFloat(-123), AFloat(0)},
+        {OVERFLOW, AFloat(-123), AFloat(-0)},
+        ////////////////////////////////////////////////////////////////////////
+    }));
+
 using TernaryCheckedCase = std::tuple<std::optional<AInt>, AInt, AInt, AInt>;
 
 using CheckedMaddTest_AInt = testing::TestWithParam<TernaryCheckedCase>;