diff options
-rw-r--r-- | flang/lib/CMakeLists.txt | 2 | ||||
-rw-r--r-- | flang/lib/common/uint128.h | 54 | ||||
-rw-r--r-- | flang/lib/common/unsigned-const-division.h | 6 | ||||
-rw-r--r-- | flang/lib/decimal/big-radix-floating-point.h | 2 | ||||
-rw-r--r-- | flang/lib/decimal/binary-floating-point.h | 12 | ||||
-rw-r--r-- | flang/test/evaluate/CMakeLists.txt | 23 | ||||
-rw-r--r-- | flang/test/evaluate/uint128.cc | 145 |
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(); +} |