[libc-commits] [libc] cb84721 - [libc] add division, modulo, and power to UInt

Michael Jones via libc-commits libc-commits at lists.llvm.org
Thu Sep 1 11:22:35 PDT 2022


Author: Michael Jones
Date: 2022-09-01T11:22:26-07:00
New Revision: cb84721c3c48778e42474c4f333225f1940337e0

URL: https://github.com/llvm/llvm-project/commit/cb84721c3c48778e42474c4f333225f1940337e0
DIFF: https://github.com/llvm/llvm-project/commit/cb84721c3c48778e42474c4f333225f1940337e0.diff

LOG: [libc] add division, modulo, and power to UInt

This adds division and power implementations to UInt. Modulo and
division are handled by the same function. These are necessary for some
higher order mathematics, often involving large floating point numbers.

Reviewed By: sivachandra, lntue

Differential Revision: https://reviews.llvm.org/D132184

Added: 
    

Modified: 
    libc/src/__support/UInt.h
    libc/test/src/__support/CMakeLists.txt
    libc/test/src/__support/uint128_test.cpp
    utils/bazel/llvm-project-overlay/libc/BUILD.bazel

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/UInt.h b/libc/src/__support/UInt.h
index 7ff920359a32b..ecaa696a1aabf 100644
--- a/libc/src/__support/UInt.h
+++ b/libc/src/__support/UInt.h
@@ -11,7 +11,9 @@
 
 #include "src/__support/CPP/array.h"
 #include "src/__support/CPP/limits.h"
+#include "src/__support/CPP/optional.h"
 #include "src/__support/CPP/type_traits.h"
+#include "src/__support/builtin_wrappers.h"
 
 #include <stddef.h> // For size_t
 #include <stdint.h>
@@ -38,6 +40,32 @@ template <size_t Bits> class UInt {
       val[i] = other.val[i];
   }
 
+  template <size_t OtherBits> constexpr UInt(const UInt<OtherBits> &other) {
+    if (OtherBits >= Bits) {
+      for (size_t i = 0; i < WordCount; ++i)
+        val[i] = other[i];
+    } else {
+      size_t i = 0;
+      for (; i < OtherBits / 64; ++i)
+        val[i] = other[i];
+      for (; i < WordCount; ++i)
+        val[i] = 0;
+    }
+  }
+
+  // Construct a UInt from a C array.
+  template <size_t N, enable_if_t<N <= WordCount, int> = 0>
+  constexpr UInt(const uint64_t (&nums)[N]) {
+    size_t min_wordcount = N < WordCount ? N : WordCount;
+    size_t i = 0;
+    for (; i < min_wordcount; ++i)
+      val[i] = nums[i];
+
+    // If nums doesn't completely fill val, then fill the rest with zeroes.
+    for (; i < WordCount; ++i)
+      val[i] = 0;
+  }
+
   // Initialize the first word to |v| and the rest to 0.
   constexpr UInt(uint64_t v) {
     val[0] = v;
@@ -209,6 +237,8 @@ template <size_t Bits> class UInt {
   constexpr UInt<Bits> operator*(const UInt<Bits> &other) const {
     UInt<Bits> result(0);
     for (size_t i = 0; i < WordCount; ++i) {
+      if (other[i] == 0)
+        continue;
       UInt<Bits> row_result(*this);
       row_result.mul(other[i]);
       row_result.shift_left(64 * i);
@@ -217,11 +247,83 @@ template <size_t Bits> class UInt {
     return result;
   }
 
+  // pow takes a power and sets this to its starting value to that power. Zero
+  // to the zeroth power returns 1.
+  constexpr void pow_n(uint64_t power) {
+    UInt<Bits> result = 1;
+    UInt<Bits> cur_power = *this;
+
+    while (power > 0) {
+      if ((power % 2) > 0) {
+        result = result * cur_power;
+      }
+      power = power >> 1;
+      cur_power *= cur_power;
+    }
+    *this = result;
+  }
+
+  // div takes another UInt of the same size and divides this by it. The value
+  // of this will be set to the quotient, and the return value is the remainder.
+  constexpr optional<UInt<Bits>> div(const UInt<Bits> &other) {
+    UInt<Bits> remainder(0);
+    if (*this < other) {
+      remainder = *this;
+      *this = UInt<Bits>(0);
+      return remainder;
+    }
+    if (other == 1) {
+      return remainder;
+    }
+    if (other == 0) {
+      return nullopt;
+    }
+
+    UInt<Bits> quotient(0);
+    UInt<Bits> subtractor = other;
+    int cur_bit = subtractor.clz() - this->clz();
+    subtractor.shift_left(cur_bit);
+
+    for (; cur_bit >= 0 && *this > 0; --cur_bit, subtractor.shift_right(1)) {
+      if (*this >= subtractor) {
+        this->sub(subtractor);
+        quotient = quotient | (UInt<Bits>(1) << cur_bit);
+      }
+    }
+    remainder = *this;
+    *this = quotient;
+    return remainder;
+  }
+
+  constexpr UInt<Bits> operator/(const UInt<Bits> &other) const {
+    UInt<Bits> result(*this);
+    result.div(other);
+    return result;
+  }
+
+  constexpr UInt<Bits> operator%(const UInt<Bits> &other) const {
+    UInt<Bits> result(*this);
+    return *result.div(other);
+  }
+
   constexpr UInt<Bits> &operator*=(const UInt<Bits> &other) {
     *this = *this * other;
     return *this;
   }
 
+  constexpr uint64_t clz() {
+    uint64_t leading_zeroes = 0;
+    for (size_t i = WordCount; i > 0; --i) {
+      if (val[i - 1] == 0) {
+        leading_zeroes += sizeof(uint64_t) * 8;
+      } else {
+        leading_zeroes += unsafe_clz(val[i - 1]);
+        break;
+      }
+    }
+    return leading_zeroes;
+  }
+
   constexpr void shift_left(size_t s) {
     const size_t drop = s / 64;  // Number of words to drop
     const size_t shift = s % 64; // Bits to shift in the remaining words.

diff  --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt
index 59b434ec1c57e..661f0839ea07d 100644
--- a/libc/test/src/__support/CMakeLists.txt
+++ b/libc/test/src/__support/CMakeLists.txt
@@ -71,6 +71,7 @@ add_libc_unittest(
     uint128_test.cpp
   DEPENDS
     libc.src.__support.uint
+    libc.src.__support.CPP.optional
 )
 
 add_libc_unittest(

diff  --git a/libc/test/src/__support/uint128_test.cpp b/libc/test/src/__support/uint128_test.cpp
index cbadd01a9e8c5..dcc844a08984d 100644
--- a/libc/test/src/__support/uint128_test.cpp
+++ b/libc/test/src/__support/uint128_test.cpp
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/optional.h"
 #include "src/__support/UInt.h"
 
 #include "utils/UnitTest/Test.h"
@@ -113,6 +114,151 @@ TEST(LlvmLibcUInt128ClassTest, MultiplicationTests) {
   EXPECT_EQ((val9 * val10), (val10 * val9));
 }
 
+TEST(LlvmLibcUInt128ClassTest, DivisionTests) {
+  LL_UInt128 val1({10, 0});
+  LL_UInt128 val2({5, 0});
+  LL_UInt128 result1({2, 0});
+  EXPECT_EQ((val1 / val2), result1);
+  EXPECT_EQ((val1 / result1), val2);
+
+  // Check that the division works accross the whole number
+  LL_UInt128 val3({0xffffffffffffffff, 0xffffffffffffffff});
+  LL_UInt128 val4({0xf, 0});
+  LL_UInt128 result2({0x1111111111111111, 0x1111111111111111});
+  EXPECT_EQ((val3 / val4), result2);
+  EXPECT_EQ((val3 / result2), val4);
+
+  // Check that division doesn't reorder the bits.
+  LL_UInt128 val5({0x26ae048cea62c840, 0x02468aceeca86420});
+  LL_UInt128 val6({2, 0});
+  LL_UInt128 result3({0x1357024675316420, 0x0123456776543210});
+  EXPECT_EQ((val5 / val6), result3);
+  EXPECT_EQ((val5 / result3), val6);
+
+  // Make sure that division handles inexact results correctly.
+  LL_UInt128 val7({1001, 0});
+  LL_UInt128 val8({10, 0});
+  LL_UInt128 result4({100, 0});
+  EXPECT_EQ((val7 / val8), result4);
+  EXPECT_EQ((val7 / result4), val8);
+
+  // Make sure that division handles divisors of one correctly.
+  LL_UInt128 val9({0x1234567812345678, 0x9abcdef09abcdef0});
+  LL_UInt128 val10({1, 0});
+  LL_UInt128 result5({0x1234567812345678, 0x9abcdef09abcdef0});
+  EXPECT_EQ((val9 / val10), result5);
+  EXPECT_EQ((val9 / result5), val10);
+
+  // Make sure that division handles results of slightly more than 1 correctly.
+  LL_UInt128 val11({1050, 0});
+  LL_UInt128 val12({1030, 0});
+  LL_UInt128 result6({1, 0});
+  EXPECT_EQ((val11 / val12), result6);
+
+  // Make sure that division handles dividing by zero correctly.
+  LL_UInt128 val13({1234, 0});
+  LL_UInt128 val14({0, 0});
+  EXPECT_FALSE(val13.div(val14).has_value());
+}
+
+TEST(LlvmLibcUInt128ClassTest, ModuloTests) {
+  LL_UInt128 val1({10, 0});
+  LL_UInt128 val2({5, 0});
+  LL_UInt128 result1({0, 0});
+  EXPECT_EQ((val1 % val2), result1);
+
+  LL_UInt128 val3({101, 0});
+  LL_UInt128 val4({10, 0});
+  LL_UInt128 result2({1, 0});
+  EXPECT_EQ((val3 % val4), result2);
+
+  LL_UInt128 val5({10000001, 0});
+  LL_UInt128 val6({10, 0});
+  LL_UInt128 result3({1, 0});
+  EXPECT_EQ((val5 % val6), result3);
+
+  LL_UInt128 val7({12345, 10});
+  LL_UInt128 val8({0, 1});
+  LL_UInt128 result4({12345, 0});
+  EXPECT_EQ((val7 % val8), result4);
+
+  LL_UInt128 val9({12345, 10});
+  LL_UInt128 val10({0, 11});
+  LL_UInt128 result5({12345, 10});
+  EXPECT_EQ((val9 % val10), result5);
+
+  LL_UInt128 val11({10, 10});
+  LL_UInt128 val12({10, 10});
+  LL_UInt128 result6({0, 0});
+  EXPECT_EQ((val11 % val12), result6);
+
+  LL_UInt128 val13({12345, 0});
+  LL_UInt128 val14({1, 0});
+  LL_UInt128 result7({0, 0});
+  EXPECT_EQ((val13 % val14), result7);
+
+  LL_UInt128 val15({0xffffffffffffffff, 0xffffffffffffffff});
+  LL_UInt128 val16({0x1111111111111111, 0x111111111111111});
+  LL_UInt128 result8({0xf, 0});
+  EXPECT_EQ((val15 % val16), result8);
+
+  LL_UInt128 val17({5076944270305263619, 54210108624}); // (10 ^ 30) + 3
+  LL_UInt128 val18({10, 0});
+  LL_UInt128 result9({3, 0});
+  EXPECT_EQ((val17 % val18), result9);
+}
+
+TEST(LlvmLibcUInt128ClassTest, PowerTests) {
+  LL_UInt128 val1({10, 0});
+  val1.pow_n(30);
+  LL_UInt128 result1({5076944270305263616, 54210108624}); // (10 ^ 30)
+  EXPECT_EQ(val1, result1);
+
+  LL_UInt128 val2({1, 0});
+  val2.pow_n(10);
+  LL_UInt128 result2({1, 0});
+  EXPECT_EQ(val2, result2);
+
+  LL_UInt128 val3({0, 0});
+  val3.pow_n(10);
+  LL_UInt128 result3({0, 0});
+  EXPECT_EQ(val3, result3);
+
+  LL_UInt128 val4({10, 0});
+  val4.pow_n(0);
+  LL_UInt128 result4({1, 0});
+  EXPECT_EQ(val4, result4);
+
+  // Test zero to the zero. Currently it returns 1, since that's the easiest
+  // result.
+  LL_UInt128 val5({0, 0});
+  val5.pow_n(0);
+  LL_UInt128 result5({1, 0});
+  EXPECT_EQ(val5, result5);
+
+  // Test a number that overflows. 100 ^ 20 is larger than 2 ^ 128.
+  LL_UInt128 val6({100, 0});
+  val6.pow_n(20);
+  LL_UInt128 result6({0xb9f5610000000000, 0x6329f1c35ca4bfab});
+  EXPECT_EQ(val6, result6);
+
+  // Test that both halves of the number are being used.
+  LL_UInt128 val7({1, 1});
+  val7.pow_n(2);
+  LL_UInt128 result7({1, 2});
+  EXPECT_EQ(val7, result7);
+
+  LL_UInt128 val_pow_two;
+  LL_UInt128 result_pow_two;
+  for (size_t i = 0; i < 128; ++i) {
+    val_pow_two = 2;
+    val_pow_two.pow_n(i);
+    result_pow_two = 1;
+    result_pow_two = result_pow_two << i;
+    EXPECT_EQ(val_pow_two, result_pow_two);
+  }
+}
+
 TEST(LlvmLibcUInt128ClassTest, ShiftLeftTests) {
   LL_UInt128 val1(0x0123456789abcdef);
   LL_UInt128 result1(0x123456789abcdef0);

diff  --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 0791a67230e63..bd39cf4733485 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -124,6 +124,8 @@ cc_library(
         "__support_cpp_array",
         "__support_cpp_limits",
         "__support_cpp_type_traits",
+        "__support_cpp_optional",
+        "__support_builtin_wrappers",
         ":libc_root",
     ],
 )


        


More information about the libc-commits mailing list