| 1 | #define AVOID_NATIVE_UINT128_T 1 |
| 2 | #include "flang/Common/uint128.h" |
| 3 | #include "flang/Testing/testing.h" |
| 4 | #include "llvm/Support/raw_ostream.h" |
| 5 | #include <cinttypes> |
| 6 | |
| 7 | #if (defined __GNUC__ || defined __clang__) && defined __SIZEOF_INT128__ |
| 8 | #define HAS_NATIVE_UINT128_T 1 |
| 9 | #else |
| 10 | #undef HAS_NATIVE_UINT128_T |
| 11 | #endif |
| 12 | |
| 13 | using U128 = Fortran::common::UnsignedInt128; |
| 14 | |
| 15 | static void Test(std::uint64_t x) { |
| 16 | U128 n{x}; |
| 17 | MATCH(x, static_cast<std::uint64_t>(n)); |
| 18 | MATCH(~x, static_cast<std::uint64_t>(~n)); |
| 19 | MATCH(-x, static_cast<std::uint64_t>(-n)); |
| 20 | MATCH(!x, static_cast<std::uint64_t>(!n)); |
| 21 | TEST(n == n); |
| 22 | TEST(n + n == n * static_cast<U128>(2)); |
| 23 | TEST(n - n == static_cast<U128>(0)); |
| 24 | TEST(n + n == n << static_cast<U128>(1)); |
| 25 | TEST(n + n == n << static_cast<U128>(1)); |
| 26 | TEST((n + n) - n == n); |
| 27 | TEST(((n + n) >> static_cast<U128>(1)) == n); |
| 28 | if (x != 0) { |
| 29 | TEST(static_cast<U128>(0) / n == static_cast<U128>(0)); |
| 30 | TEST(static_cast<U128>(n - 1) / n == static_cast<U128>(0)); |
| 31 | TEST(static_cast<U128>(n) / n == static_cast<U128>(1)); |
| 32 | TEST(static_cast<U128>(n + n - 1) / n == static_cast<U128>(1)); |
| 33 | TEST(static_cast<U128>(n + n) / n == static_cast<U128>(2)); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | static void Test(std::uint64_t x, std::uint64_t y) { |
| 38 | U128 m{x}, n{y}; |
| 39 | MATCH(x, static_cast<std::uint64_t>(m)); |
| 40 | MATCH(y, static_cast<std::uint64_t>(n)); |
| 41 | MATCH(x & y, static_cast<std::uint64_t>(m & n)); |
| 42 | MATCH(x | y, static_cast<std::uint64_t>(m | n)); |
| 43 | MATCH(x ^ y, static_cast<std::uint64_t>(m ^ n)); |
| 44 | MATCH(x + y, static_cast<std::uint64_t>(m + n)); |
| 45 | MATCH(x - y, static_cast<std::uint64_t>(m - n)); |
| 46 | MATCH(x * y, static_cast<std::uint64_t>(m * n)); |
| 47 | if (n != 0) { |
| 48 | MATCH(x / y, static_cast<std::uint64_t>(m / n)); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | #if HAS_NATIVE_UINT128_T |
| 53 | static __uint128_t ToNative(U128 n) { |
| 54 | return static_cast<__uint128_t>(static_cast<std::uint64_t>(n >> 64)) << 64 | |
| 55 | static_cast<std::uint64_t>(n); |
| 56 | } |
| 57 | |
| 58 | static U128 FromNative(__uint128_t n) { |
| 59 | return U128{static_cast<std::uint64_t>(n >> 64)} << 64 | |
| 60 | U128{static_cast<std::uint64_t>(n)}; |
| 61 | } |
| 62 | |
| 63 | static void TestVsNative(__uint128_t x, __uint128_t y) { |
| 64 | U128 m{FromNative(x)}, n{FromNative(y)}; |
| 65 | TEST(ToNative(m) == x); |
| 66 | TEST(ToNative(n) == y); |
| 67 | TEST(ToNative(~m) == ~x); |
| 68 | TEST(ToNative(-m) == -x); |
| 69 | TEST(ToNative(!m) == !x); |
| 70 | TEST(ToNative(m < n) == (x < y)); |
| 71 | TEST(ToNative(m <= n) == (x <= y)); |
| 72 | TEST(ToNative(m == n) == (x == y)); |
| 73 | TEST(ToNative(m != n) == (x != y)); |
| 74 | TEST(ToNative(m >= n) == (x >= y)); |
| 75 | TEST(ToNative(m > n) == (x > y)); |
| 76 | TEST(ToNative(m & n) == (x & y)); |
| 77 | TEST(ToNative(m | n) == (x | y)); |
| 78 | TEST(ToNative(m ^ n) == (x ^ y)); |
| 79 | if (y < 128) { |
| 80 | TEST(ToNative(m << n) == (x << y)); |
| 81 | TEST(ToNative(m >> n) == (x >> y)); |
| 82 | } |
| 83 | TEST(ToNative(m + n) == (x + y)); |
| 84 | TEST(ToNative(m - n) == (x - y)); |
| 85 | TEST(ToNative(m * n) == (x * y)); |
| 86 | if (y > 0) { |
| 87 | TEST(ToNative(m / n) == (x / y)); |
| 88 | TEST(ToNative(m % n) == (x % y)); |
| 89 | TEST(ToNative(m - n * (m / n)) == (x % y)); |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | static void TestVsNative() { |
| 94 | for (int j{0}; j < 128; ++j) { |
| 95 | for (int k{0}; k < 128; ++k) { |
| 96 | __uint128_t m{1}, n{1}; |
| 97 | m <<= j, n <<= k; |
| 98 | TestVsNative(x: m, y: n); |
| 99 | TestVsNative(x: ~m, y: n); |
| 100 | TestVsNative(x: m, y: ~n); |
| 101 | TestVsNative(x: ~m, y: ~n); |
| 102 | TestVsNative(x: m ^ n, y: n); |
| 103 | TestVsNative(x: m, y: m ^ n); |
| 104 | TestVsNative(x: m ^ ~n, y: n); |
| 105 | TestVsNative(x: m, y: ~m ^ n); |
| 106 | TestVsNative(x: m ^ ~n, y: m ^ n); |
| 107 | TestVsNative(x: m ^ n, y: ~m ^ n); |
| 108 | TestVsNative(x: m ^ ~n, y: ~m ^ n); |
| 109 | Test(x: m, y: 10000000000000000); // important case for decimal conversion |
| 110 | Test(x: ~m, y: 10000000000000000); |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | #endif |
| 115 | |
| 116 | int main() { |
| 117 | for (std::uint64_t j{0}; j < 64; ++j) { |
| 118 | Test(x: j); |
| 119 | Test(x: ~j); |
| 120 | Test(x: std::uint64_t(1) << j); |
| 121 | for (std::uint64_t k{0}; k < 64; ++k) { |
| 122 | Test(x: j, y: k); |
| 123 | } |
| 124 | } |
| 125 | #if HAS_NATIVE_UINT128_T |
| 126 | llvm::outs() << "Environment has native __uint128_t\n" ; |
| 127 | TestVsNative(); |
| 128 | #else |
| 129 | llvm::outs() << "Environment lacks native __uint128_t\n" ; |
| 130 | #endif |
| 131 | return testing::Complete(); |
| 132 | } |
| 133 | |