aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flang/lib/CMakeLists.txt2
-rw-r--r--flang/lib/common/uint128.h54
-rw-r--r--flang/lib/common/unsigned-const-division.h6
-rw-r--r--flang/lib/decimal/big-radix-floating-point.h2
-rw-r--r--flang/lib/decimal/binary-floating-point.h12
-rw-r--r--flang/test/evaluate/CMakeLists.txt23
-rw-r--r--flang/test/evaluate/uint128.cc145
7 files changed, 207 insertions, 37 deletions
diff --git a/flang/lib/CMakeLists.txt b/flang/lib/CMakeLists.txt
index 786c24a..b3d35ac 100644
--- a/flang/lib/CMakeLists.txt
+++ b/flang/lib/CMakeLists.txt
@@ -14,7 +14,7 @@
add_subdirectory(burnside)
add_subdirectory(common)
-add_subdirectory(decimal)
add_subdirectory(evaluate)
+add_subdirectory(decimal)
add_subdirectory(parser)
add_subdirectory(semantics)
diff --git a/flang/lib/common/uint128.h b/flang/lib/common/uint128.h
index 3974982..93b827b 100644
--- a/flang/lib/common/uint128.h
+++ b/flang/lib/common/uint128.h
@@ -12,12 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Portable 128-bit unsigned integer arithmetic
+// Portable 128-bit unsigned integer arithmetic for use in impoverished
+// C++ implementations lacking __uint128_t.
#ifndef FORTRAN_COMMON_UINT128_H_
#define FORTRAN_COMMON_UINT128_H_
-#define AVOID_NATIVE_UINT128 1 // for testing purposes
+#ifndef AVOID_NATIVE_UINT128_T
+#define AVOID_NATIVE_UINT128_T 1 // for testing purposes (pmk!)
+#endif
#include "leading-zero-bit-count.h"
#include <cstdint>
@@ -29,8 +32,12 @@ class UnsignedInt128 {
public:
constexpr UnsignedInt128() {}
constexpr UnsignedInt128(std::uint64_t n) : low_{n} {}
- constexpr UnsignedInt128(std::int64_t n) : low_{static_cast<std::uint64_t>(n)}, high_{-static_cast<std::uint64_t>(n<0)} {}
- constexpr UnsignedInt128(int n) : low_{static_cast<std::uint64_t>(n)}, high_{-static_cast<std::uint64_t>(n<0)} {}
+ constexpr UnsignedInt128(std::int64_t n)
+ : low_{static_cast<std::uint64_t>(n)}, high_{-static_cast<std::uint64_t>(
+ n < 0)} {}
+ constexpr UnsignedInt128(int n)
+ : low_{static_cast<std::uint64_t>(n)}, high_{-static_cast<std::uint64_t>(
+ n < 0)} {}
constexpr UnsignedInt128(const UnsignedInt128 &) = default;
constexpr UnsignedInt128(UnsignedInt128 &&) = default;
constexpr UnsignedInt128 &operator=(const UnsignedInt128 &) = default;
@@ -79,6 +86,8 @@ public:
constexpr UnsignedInt128 operator<<(UnsignedInt128 that) const {
if (that >= 128) {
return {};
+ } else if (that == 0) {
+ return *this;
} else {
std::uint64_t n{that.low_};
if (n >= 64) {
@@ -91,6 +100,8 @@ public:
constexpr UnsignedInt128 operator>>(UnsignedInt128 that) const {
if (that >= 128) {
return {};
+ } else if (that == 0) {
+ return *this;
} else {
std::uint64_t n{that.low_};
if (n >= 64) {
@@ -119,18 +130,21 @@ public:
UnsignedInt128 x1y0{x1 * y0}, x1y1{x1 * y1};
return x0y0 + ((x0y1 + x1y0) << 32) + (x1y1 << 64);
} else {
- std::uint64_t x0{low_ & mask32}, x1{low_ >> 32}, x2{high_ & mask32}, x3{high_ >> 32};
- std::uint64_t y0{that.low_ & mask32}, y1{that.low_ >> 32}, y2{that.high_ & mask32}, y3{that.high_ >> 32};
+ std::uint64_t x0{low_ & mask32}, x1{low_ >> 32}, x2{high_ & mask32},
+ x3{high_ >> 32};
+ std::uint64_t y0{that.low_ & mask32}, y1{that.low_ >> 32},
+ y2{that.high_ & mask32}, y3{that.high_ >> 32};
UnsignedInt128 x0y0{x0 * y0}, x0y1{x0 * y1}, x0y2{x0 * y2}, x0y3{x0 * y3};
UnsignedInt128 x1y0{x1 * y0}, x1y1{x1 * y1}, x1y2{x1 * y2};
UnsignedInt128 x2y0{x2 * y0}, x2y1{x2 * y1};
UnsignedInt128 x3y0{x3 * y0};
- return x0y0 + ((x0y1 + x1y0) << 32) + ((x0y2 + x1y1 + x2y0) << 64) + ((x0y3 + x1y2 + x2y1 + x3y0) << 96);
+ return x0y0 + ((x0y1 + x1y0) << 32) + ((x0y2 + x1y1 + x2y0) << 64) +
+ ((x0y3 + x1y2 + x2y1 + x3y0) << 96);
}
}
constexpr UnsignedInt128 operator/(UnsignedInt128 that) const {
- int j{high_ == 0 ? 64 + LeadingZeroBitCount(low_) : LeadingZeroBitCount(high_)};
+ int j{LeadingZeroes()};
UnsignedInt128 bits{*this};
bits <<= j;
UnsignedInt128 numerator{};
@@ -151,7 +165,7 @@ public:
}
constexpr UnsignedInt128 operator%(UnsignedInt128 that) const {
- int j{high_ == 0 ? 64 + LeadingZeroBitCount(low_) : LeadingZeroBitCount(high_)};
+ int j{LeadingZeroes()};
UnsignedInt128 bits{*this};
bits <<= j;
UnsignedInt128 remainder{};
@@ -180,12 +194,8 @@ public:
constexpr bool operator!=(UnsignedInt128 that) const {
return !(*this == that);
}
- constexpr bool operator>=(UnsignedInt128 that) const {
- return that <= *this;
- }
- constexpr bool operator>(UnsignedInt128 that) const {
- return that < *this;
- }
+ constexpr bool operator>=(UnsignedInt128 that) const { return that <= *this; }
+ constexpr bool operator>(UnsignedInt128 that) const { return that < *this; }
constexpr UnsignedInt128 &operator&=(const UnsignedInt128 &that) {
*this = *this & that;
@@ -229,12 +239,22 @@ public:
}
private:
- constexpr UnsignedInt128(std::uint64_t hi, std::uint64_t lo) : low_{lo}, high_{hi} {}
+ constexpr UnsignedInt128(std::uint64_t hi, std::uint64_t lo)
+ : low_{lo}, high_{hi} {}
+ constexpr int LeadingZeroes() const {
+ if (high_ == 0) {
+ return 64 + LeadingZeroBitCount(low_);
+ } else {
+ return LeadingZeroBitCount(high_);
+ }
+ }
static constexpr std::uint64_t topBit{std::uint64_t{1} << 63};
std::uint64_t low_{0}, high_{0};
};
-#if (defined __GNUC__ || defined __clang__) && defined __SIZEOF_INT128__ && !AVOID_NATIVE_UINT128
+#if AVOID_NATIVE_UINT128_T
+using uint128_t = UnsignedInt128;
+#elif (defined __GNUC__ || defined __clang__) && defined __SIZEOF_INT128__
using uint128_t = __uint128_t;
#else
using uint128_t = UnsignedInt128;
diff --git a/flang/lib/common/unsigned-const-division.h b/flang/lib/common/unsigned-const-division.h
index 115d010..cf1c445 100644
--- a/flang/lib/common/unsigned-const-division.h
+++ b/flang/lib/common/unsigned-const-division.h
@@ -68,10 +68,10 @@ static_assert(FixedPointReciprocal<std::uint64_t>::For(10).Divide(
template<typename UINT, std::uint64_t DENOM>
inline constexpr UINT DivideUnsignedBy(UINT n) {
- if constexpr (!std::is_same_v<UINT, uint128_t>) {
- return FixedPointReciprocal<UINT>::For(DENOM).Divide(n);
- } else {
+ if constexpr (std::is_same_v<UINT, uint128_t>) {
return n / static_cast<UINT>(DENOM);
+ } else {
+ return FixedPointReciprocal<UINT>::For(DENOM).Divide(n);
}
}
}
diff --git a/flang/lib/decimal/big-radix-floating-point.h b/flang/lib/decimal/big-radix-floating-point.h
index 45658b1..d8756af 100644
--- a/flang/lib/decimal/big-radix-floating-point.h
+++ b/flang/lib/decimal/big-radix-floating-point.h
@@ -143,7 +143,7 @@ private:
return digits_ == digitLimit_ && digit_[digits_ - 1] >= radix / 10;
}
- // Set to an unsigned integer value.
+ // Sets *this to an unsigned integer value.
// Returns any remainder.
template<typename UINT> UINT SetTo(UINT n) {
static_assert(
diff --git a/flang/lib/decimal/binary-floating-point.h b/flang/lib/decimal/binary-floating-point.h
index 4eb7a7b..75d25c0 100644
--- a/flang/lib/decimal/binary-floating-point.h
+++ b/flang/lib/decimal/binary-floating-point.h
@@ -55,12 +55,14 @@ template<int PRECISION> struct BinaryFloatingPointNumber {
static constexpr int RANGE{static_cast<int>(
(exponentBias - 1) * ScaledLogBaseTenOfTwo / 1000000000000)};
- BinaryFloatingPointNumber() {} // zero
- BinaryFloatingPointNumber(const BinaryFloatingPointNumber &that) = default;
- BinaryFloatingPointNumber(BinaryFloatingPointNumber &&that) = default;
- BinaryFloatingPointNumber &operator=(
+ constexpr BinaryFloatingPointNumber() {} // zero
+ constexpr BinaryFloatingPointNumber(
const BinaryFloatingPointNumber &that) = default;
- BinaryFloatingPointNumber &operator=(
+ constexpr BinaryFloatingPointNumber(
+ BinaryFloatingPointNumber &&that) = default;
+ constexpr BinaryFloatingPointNumber &operator=(
+ const BinaryFloatingPointNumber &that) = default;
+ constexpr BinaryFloatingPointNumber &operator=(
BinaryFloatingPointNumber &&that) = default;
template<typename A> explicit constexpr BinaryFloatingPointNumber(A x) {
diff --git a/flang/test/evaluate/CMakeLists.txt b/flang/test/evaluate/CMakeLists.txt
index 018e069..a8b1758 100644
--- a/flang/test/evaluate/CMakeLists.txt
+++ b/flang/test/evaluate/CMakeLists.txt
@@ -23,8 +23,6 @@ add_executable(leading-zero-bit-count-test
target_link_libraries(leading-zero-bit-count-test
FortranEvaluateTesting
- FortranEvaluate
- FortranSemantics
)
add_executable(bit-population-count-test
@@ -33,10 +31,21 @@ add_executable(bit-population-count-test
target_link_libraries(bit-population-count-test
FortranEvaluateTesting
- FortranEvaluate
- FortranSemantics
)
+add_executable(uint128-test
+ uint128.cc
+)
+
+target_link_libraries(uint128-test
+ FortranEvaluateTesting
+)
+
+# These routines live in lib/common but we test them here.
+add_test(UINT128 uint128-test)
+add_test(Leadz leading-zero-bit-count-test)
+add_test(PopPar bit-population-count-test)
+
add_executable(expression-test
expression.cc
)
@@ -55,7 +64,6 @@ add_executable(integer-test
target_link_libraries(integer-test
FortranEvaluateTesting
FortranEvaluate
- FortranSemantics
)
add_executable(intrinsics-test
@@ -77,8 +85,6 @@ add_executable(logical-test
target_link_libraries(logical-test
FortranEvaluateTesting
- FortranEvaluate
- FortranSemantics
)
# GCC -fno-exceptions breaks the fenv.h interfaces needed to capture
@@ -141,10 +147,7 @@ set(FOLDING_TESTS
folding08.f90
)
-
add_test(Expression expression-test)
-add_test(Leadz leading-zero-bit-count-test)
-add_test(PopPar bit-population-count-test)
add_test(Integer integer-test)
add_test(Intrinsics intrinsics-test)
add_test(Logical logical-test)
diff --git a/flang/test/evaluate/uint128.cc b/flang/test/evaluate/uint128.cc
new file mode 100644
index 0000000..a9f4015
--- /dev/null
+++ b/flang/test/evaluate/uint128.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+//
+// 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.
+
+#define AVOID_NATIVE_UINT128_T 1
+#include "../../lib/common/uint128.h"
+#include "testing.h"
+#include <cinttypes>
+#include <iostream>
+
+#if (defined __GNUC__ || defined __clang__) && defined __SIZEOF_INT128__
+# define HAS_NATIVE_UINT128_T 1
+#else
+# undef HAS_NATIVE_UINT128_T
+#endif
+
+
+using U128 = Fortran::common::UnsignedInt128;
+
+static void Test(std::uint64_t x) {
+ U128 n{x};
+ MATCH(x, static_cast<std::uint64_t>(n));
+ MATCH(~x, static_cast<std::uint64_t>(~n));
+ MATCH(-x, static_cast<std::uint64_t>(-n));
+ MATCH(!x, static_cast<std::uint64_t>(!n));
+ TEST(n == n);
+ TEST(n + n == n * static_cast<U128>(2));
+ TEST(n - n == static_cast<U128>(0));
+ TEST(n + n == n << static_cast<U128>(1));
+ TEST(n + n == n << static_cast<U128>(1));
+ TEST((n + n) - n == n);
+ TEST(((n + n) >> static_cast<U128>(1)) == n);
+ if (x != 0) {
+ TEST(static_cast<U128>(0) / n == static_cast<U128>(0));
+ TEST(static_cast<U128>(n - 1) / n == static_cast<U128>(0));
+ TEST(static_cast<U128>(n) / n == static_cast<U128>(1));
+ TEST(static_cast<U128>(n + n - 1) / n == static_cast<U128>(1));
+ TEST(static_cast<U128>(n + n) / n == static_cast<U128>(2));
+ }
+}
+
+static void Test(std::uint64_t x, std::uint64_t y) {
+ U128 m{x}, n{y};
+ MATCH(x, static_cast<std::uint64_t>(m));
+ MATCH(y, static_cast<std::uint64_t>(n));
+ MATCH(x & y, static_cast<std::uint64_t>(m & n));
+ MATCH(x | y, static_cast<std::uint64_t>(m | n));
+ MATCH(x ^ y, static_cast<std::uint64_t>(m ^ n));
+ MATCH(x + y, static_cast<std::uint64_t>(m + n));
+ MATCH(x - y, static_cast<std::uint64_t>(m - n));
+ MATCH(x * y, static_cast<std::uint64_t>(m * n));
+ if (n != 0) {
+ MATCH(x / y, static_cast<std::uint64_t>(m / n));
+ }
+}
+
+#if HAS_NATIVE_UINT128_T
+static __uint128_t ToNative(U128 n) {
+ return static_cast<__uint128_t>(static_cast<std::uint64_t>(n >> 64)) << 64 | static_cast<std::uint64_t>(n);
+}
+
+static U128 FromNative(__uint128_t n) {
+ return U128{static_cast<std::uint64_t>(n >> 64)} << 64 | U128{static_cast<std::uint64_t>(n)};
+}
+
+static void TestVsNative(__uint128_t x, __uint128_t y) {
+ U128 m{FromNative(x)}, n{FromNative(y)};
+ TEST(ToNative(m) == x);
+ TEST(ToNative(n) == y);
+ TEST(ToNative(~m) == ~x);
+ TEST(ToNative(-m) == -x);
+ TEST(ToNative(!m) == !x);
+ TEST(ToNative(m < n) == (x < y));
+ TEST(ToNative(m <= n) == (x <= y));
+ TEST(ToNative(m == n) == (x == y));
+ TEST(ToNative(m != n) == (x != y));
+ TEST(ToNative(m >= n) == (x >= y));
+ TEST(ToNative(m > n) == (x > y));
+ TEST(ToNative(m & n) == (x & y));
+ TEST(ToNative(m | n) == (x | y));
+ TEST(ToNative(m ^ n) == (x ^ y));
+ if (y < 128) {
+ TEST(ToNative(m << n) == (x << y));
+ TEST(ToNative(m >> n) == (x >> y));
+ }
+ TEST(ToNative(m + n) == (x + y));
+ TEST(ToNative(m - n) == (x - y));
+ TEST(ToNative(m * n) == (x * y));
+ if (y > 0) {
+ TEST(ToNative(m / n) == (x / y));
+ TEST(ToNative(m % n) == (x % y));
+ TEST(ToNative(m - n * (m / n)) == (x / y));
+ }
+}
+
+static void TestVsNative() {
+ for (int j{0}; j < 128; ++j) {
+ for (int k{0}; k < 128; ++k) {
+ __uint128_t m{1}, n{1};
+ m <<= j, n <<= k;
+ TestVsNative(m, n);
+ TestVsNative(~m, n);
+ TestVsNative(m, ~n);
+ TestVsNative(~m, ~n);
+ TestVsNative(m ^ n, n);
+ TestVsNative(m, m ^ n);
+ TestVsNative(m ^ ~n, n);
+ TestVsNative(m, ~m ^ n);
+ TestVsNative(m ^ ~n, m ^ n);
+ TestVsNative(m ^ n, ~m ^ n);
+ TestVsNative(m ^ ~n, ~m ^ n);
+ Test(m, 10000000000000000); // important case for decimal conversion
+ Test(~m, 10000000000000000);
+ }
+ }
+}
+#endif
+
+int main() {
+ for (std::uint64_t j{0}; j < 64; ++j) {
+ Test(j);
+ Test(~j);
+ Test(std::uint64_t(1) << j);
+ for (std::uint64_t k{0}; k < 64; ++k) {
+ Test(j, k);
+ }
+ }
+#if HAS_NATIVE_UINT128_T
+ std::cout << "Environment has native __uint128_t\n";
+ TestVsNative();
+#else
+ std::cout << "Environment lacks native __uint128_t\n";
+#endif
+ testing::Complete();
+}