tint/number: add CheckedMod functions

Will be used to implement const eval of binary modulo.

Bug: tint:1581
Change-Id: Ib3cb422b247d57932d0b7cfc0ea8588206c39671
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/112321
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
Antonio Maiorano 2022-11-30 14:18:51 +00:00 committed by Dawn LUCI CQ
parent 83e98c6ad9
commit cfea220927
2 changed files with 84 additions and 0 deletions

View File

@ -543,6 +543,38 @@ inline std::optional<FloatingPointT> CheckedDiv(FloatingPointT a, FloatingPointT
return result;
}
namespace detail {
/// @returns the remainder of e1 / e2
template <typename T>
inline T Mod(T e1, T e2) {
return e1 - e2 * static_cast<T>(std::trunc(e1 / e2));
}
} // namespace detail
/// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the AInt
inline std::optional<AInt> CheckedMod(AInt a, AInt b) {
if (b == 0) {
return {};
}
if (b == -1 && a == AInt::Lowest()) {
return {};
}
return AInt{detail::Mod(a.value, b.value)};
}
/// @returns the remainder of a / b, or an empty optional if the resulting value overflowed the
/// float value
template <typename FloatingPointT, typename = traits::EnableIf<IsFloatingPoint<FloatingPointT>>>
inline std::optional<FloatingPointT> CheckedMod(FloatingPointT a, FloatingPointT b) {
auto result = FloatingPointT{detail::Mod(a.value, b.value)};
if (!std::isfinite(result.value)) {
return {};
}
return 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

View File

@ -687,6 +687,58 @@ INSTANTIATE_TEST_SUITE_P(CheckedDivTest_Float,
CheckedDivTest_FloatCases<f32>(),
CheckedDivTest_FloatCases<f16>())));
using CheckedModTest_AInt = testing::TestWithParam<BinaryCheckedCase_AInt>;
TEST_P(CheckedModTest_AInt, Test) {
auto expect = std::get<0>(GetParam());
auto a = std::get<1>(GetParam());
auto b = std::get<2>(GetParam());
EXPECT_TRUE(CheckedMod(a, b) == expect) << std::hex << "0x" << a << " - 0x" << b;
}
INSTANTIATE_TEST_SUITE_P(
CheckedModTest_AInt,
CheckedModTest_AInt,
testing::ValuesIn(std::vector<BinaryCheckedCase_AInt>{
{AInt(0), AInt(0), AInt(1)},
{AInt(0), AInt(1), AInt(1)},
{AInt(1), AInt(10), AInt(3)},
{AInt(2), AInt(10), AInt(4)},
{AInt(0), AInt::Highest(), AInt::Highest()},
{AInt(0), AInt::Lowest(), AInt::Lowest()},
{OVERFLOW, AInt::Highest(), AInt(0)},
{OVERFLOW, AInt::Lowest(), AInt(0)},
////////////////////////////////////////////////////////////////////////
}));
using CheckedModTest_Float = testing::TestWithParam<BinaryCheckedCase_Float>;
TEST_P(CheckedModTest_Float, Test) {
auto& p = GetParam();
std::visit(
[&](auto&& lhs) {
using T = std::decay_t<decltype(lhs)>;
auto rhs = std::get<T>(std::get<2>(p));
auto expect = std::get<std::optional<T>>(std::get<0>(p));
EXPECT_TRUE(CheckedMod(lhs, rhs) == expect)
<< std::hex << "0x" << lhs << " / 0x" << rhs;
},
std::get<1>(p));
}
template <typename T>
std::vector<BinaryCheckedCase_Float> CheckedModTest_FloatCases() {
return {
{T(0.5), T(10.5), T(1)}, {T(0.5), T(10.5), T(2)},
{T(1.5), T(10.5), T(3)}, {T(2.5), T(10.5), T(4)},
{T(0.5), T(10.5), T(5)}, {T(0), T::Highest(), T::Highest()},
{T(0), T::Lowest(), T::Lowest()}, {Overflow<T>, T(123), T(0)},
{Overflow<T>, T(123), T(-0)}, {Overflow<T>, T(-123), T(0)},
{Overflow<T>, T(-123), T(-0)},
};
}
INSTANTIATE_TEST_SUITE_P(CheckedModTest_Float,
CheckedModTest_Float,
testing::ValuesIn(Concat(CheckedModTest_FloatCases<AFloat>(),
CheckedModTest_FloatCases<f32>(),
CheckedModTest_FloatCases<f16>())));
using TernaryCheckedCase = std::tuple<std::optional<AInt>, AInt, AInt, AInt>;
using CheckedMaddTest_AInt = testing::TestWithParam<TernaryCheckedCase>;