/* * Copyright 2016-present Facebook, Inc. * * 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 #include #include #include #include #include #include #include using namespace folly; using namespace folly::detail; namespace { // Workaround for https://llvm.org/bugs/show_bug.cgi?id=16404, // issues with __int128 multiplication and UBSAN template T mul(T lhs, T rhs) { if (rhs < 0) { rhs = -rhs; lhs = -lhs; } T accum = 0; while (rhs != 0) { if ((rhs & 1) != 0) { accum += lhs; } lhs += lhs; rhs >>= 1; } return accum; } template T referenceDivFloor(T numer, T denom) { // rv = largest integral value <= numer / denom B n = numer; B d = denom; if (d < 0) { d = -d; n = -n; } B r = n / d; while (mul(r, d) > n) { --r; } while (mul(r + 1, d) <= n) { ++r; } T rv = static_cast(r); assert(static_cast(rv) == r); return rv; } template T referenceDivCeil(T numer, T denom) { // rv = smallest integral value >= numer / denom B n = numer; B d = denom; if (d < 0) { d = -d; n = -n; } B r = n / d; while (mul(r, d) < n) { ++r; } while (mul(r - 1, d) >= n) { --r; } T rv = static_cast(r); assert(static_cast(rv) == r); return rv; } template T referenceDivRoundAway(T numer, T denom) { if ((numer < 0) != (denom < 0)) { return referenceDivFloor(numer, denom); } else { return referenceDivCeil(numer, denom); } } template std::vector cornerValues() { std::vector rv; for (T i = 1; i < 24; ++i) { rv.push_back(i); rv.push_back(T(std::numeric_limits::max() / i)); rv.push_back(T(std::numeric_limits::max() - i)); rv.push_back(T(std::numeric_limits::max() / T(2) - i)); if (std::is_signed::value) { rv.push_back(-i); rv.push_back(T(std::numeric_limits::min() / i)); rv.push_back(T(std::numeric_limits::min() + i)); rv.push_back(T(std::numeric_limits::min() / T(2) + i)); } } return rv; } template void runDivTests() { using T = decltype(static_cast(1) / static_cast(1)); auto numers = cornerValues(); numers.push_back(0); auto denoms = cornerValues(); for (A n : numers) { for (B d : denoms) { if (std::is_signed::value && n == std::numeric_limits::min() && d == static_cast(-1)) { // n / d overflows in two's complement continue; } EXPECT_EQ(divCeil(n, d), (referenceDivCeil(n, d))) << n << "/" << d; EXPECT_EQ(divFloor(n, d), (referenceDivFloor(n, d))) << n << "/" << d; EXPECT_EQ(divTrunc(n, d), n / d) << n << "/" << d; EXPECT_EQ(divRoundAway(n, d), (referenceDivRoundAway(n, d))) << n << "/" << d; T nn = n; T dd = d; EXPECT_EQ(divCeilBranchless(nn, dd), divCeilBranchful(nn, dd)); EXPECT_EQ(divFloorBranchless(nn, dd), divFloorBranchful(nn, dd)); EXPECT_EQ(divRoundAwayBranchless(nn, dd), divRoundAwayBranchful(nn, dd)); } } } } // namespace TEST(Bits, divTestInt8) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestInt16) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestInt32) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } #if FOLLY_HAVE_INT128_T TEST(Bits, divTestInt64) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); } #endif TEST(Bits, divTestUint8) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestUint16) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } TEST(Bits, divTestUint32) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); #if FOLLY_HAVE_INT128_T runDivTests(); runDivTests(); #endif } #if FOLLY_HAVE_INT128_T TEST(Bits, divTestUint64) { runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); runDivTests(); } #endif