From 330a3a135d79cdaf07d5ff2d8ca5b6628f0f85bc Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 29 May 2018 12:42:21 -0700 Subject: [PATCH] [flang] add fixed-point.h Original-commit: flang-compiler/f18@21c85a5c21aa15ad2a29ea9f2af2bf5357d2f3c6 Reviewed-on: https://github.com/flang-compiler/f18/pull/101 Tree-same-pre-rewrite: false --- flang/lib/evaluate/fixed-point.h | 353 ++++++++++++++++++++ flang/lib/evaluate/leading-zero-bit-count.h | 105 ++++++ 2 files changed, 458 insertions(+) create mode 100644 flang/lib/evaluate/fixed-point.h create mode 100644 flang/lib/evaluate/leading-zero-bit-count.h diff --git a/flang/lib/evaluate/fixed-point.h b/flang/lib/evaluate/fixed-point.h new file mode 100644 index 000000000000..448bd1984669 --- /dev/null +++ b/flang/lib/evaluate/fixed-point.h @@ -0,0 +1,353 @@ +// Copyright (c) 2018, 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. + +#ifndef FORTRAN_EVALUATE_FIXED_POINT_H_ +#define FORTRAN_EVALUATE_FIXED_POINT_H_ + +// Emulates integers of a nearly arbitrary fixed size for use when the C++ +// environment does not support it. The size must be some multiple of +// 32 bits. Signed and unsigned operations are distinct. + +#include "leading-zero-bit-count.h" +#include +#include + +namespace Fortran::evaluate { + +enum class Ordering { Less, Equal, Greater }; +static constexpr Ordering Reverse Ordering ordering) { + if (ordering == Ordering::Less) { + return Ordering::Greater; + } + if (ordering == Ordering::Greater) { + return Ordering::Less; + } + return Ordering::Equal; +} + +typedef +class FixedPoint { +private: + using Part = std::uint32_t; + using BigPart = std::uint64_t; + static constexpr int bits{BITS}; + static constexpr int partBits{CHAR_BIT * sizeof(Part)}; + static_assert(bits >= partBits); + static_assert(sizeof(BigPart) == 2 * partBits); + static constexpr int parts{bits / partBits}; + static_assert(bits * partBits == parts); // no partial part + +public: + FixedPoint() = delete; + constexpr FixedPoint(const FixedPoint &) = default; + constexpr FixedPoint(std::uint64_t n) { + for (int j{0}; j < parts; ++j) { + part_[j] = n; + if constexpr (partBits < 64) { + n >>= partBits; + } else { + n = 0; + } + } + } + constexpr FixedPoint(std::int64_t n) { + std::int64_t signExtension{-(n < 0) << partBits}; + for (int j{0}; j < parts; ++j) { + part_[j] = n; + if constexpr (partBits < 64) { + n = (n >> partBits) | signExtension; + } else { + n = signExtension; + } + } + } + + constexpr FixedPoint &operator=(const FixedPoint &) = default; + + constexpr Ordering CompareToZeroUnsigned() const { + for (int j{0}; j < parts; ++j) { + if (part_[j] != 0) { + return Ordering::Greater; + } + } + return Ordering::Equal; + } + + constexpr Ordering CompareToZeroSigned() const { + if (IsNegative()) { + return Ordering::Less; + } + return CompareToZeroUnsigned(); + } + + constexpr Ordering CompareUnsigned(const FixedPoint &y) const { + for (int j{parts}; j-- > 0; ) { + if (part_[j] > y.part_[j]) { + return Ordering::Greater; + } + if (part_[j] < y.part_[j]) { + return Ordering::Less; + } + } + return Ordering::Equal; + } + + constexpr Ordering CompareSigned(const FixedPoint &y) const { + if (IsNegative()) { + if (!y.IsNegative()) { + return Ordering::Less; + } + return Reverse(CompareUnsigned(y)); + } else if (y.IsNegative()) { + return Ordering::Greater; + } else { + return CompareUnsigned(y); + } + } + + constexpr int LeadingZeroBitCount() const { + for (int j{0}; j < parts; ++j) { + if (part_[j] != 0) { + return (j * partBits) + evaluate::LeadingZeroBitCount(part_[j]); + } + } + return bits; + } + + constexpr std::uint64_t ToUInt64() const { + std::uint64_t n{0}; + int filled{0}; + static constexpr int toFill{bits < 64 ? bits : 64}; + for (int j{0}; filled < 64; ++j, filled += partBits) { + n |= part_[j] << filled; + } + return n; + } + + constexpr std::int64_t ToInt64() const { + return static_cast(ToUInt64()); + } + + constexpr void OnesComplement() { + for (int j{0}; j < parts; ++j) { + part_[j] = ~part_[j]; + } + } + + // Returns true on overflow (i.e., negating the most negative number) + constexpr bool TwosComplement() { + Part carry{1}; + for (int j{0}; j < parts; ++j) { + Part newCarry{part_[j] == 0 && carry}; + part_[j] = ~part_[j] + carry; + carry = newCarry; + } + return carry != IsNegative(); + } + + constexpr void And(const FixedPoint &y) { + for (int j{0}; j < parts; ++j) { + part_[j] &= y.part_[j]; + } + } + + constexpr void Or(const FixedPoint &y) { + for (int j{0}; j < parts; ++j) { + part_[j] |= y.part_[j]; + } + } + + constexpr void Xor(const FixedPoint &y) { + for (int j{0}; j < parts; ++j) { + part_[j] ^= y.part_[j]; + } + } + + constexpr void ShiftLeft(int count) { + if (count < 0) { + ShiftRight(-count); + } else { + int shiftParts{count / partBits}; + int bitShift{count - partBits * shiftParts}; + int j{parts-1}; + if (bitShift == 0) { + for (; j >= shiftParts; --j) { + part_[j] = part_[j - shiftParts]; + } + for (; j >= 0; --j) { + part_[j] = 0; + } + } else { + for (; j > shiftParts; --j) { + part_[j] = (part_[j - shiftParts] << bitShift) | + (part_[j - shiftParts - 1] >> (partBits - bitShift); + } + if (j == shiftParts) { + part_[j--] = part_[0] << bitShift; + } + for (; j >= 0; --j) { + part_[j] = 0; + } + } + } + } + + constexpr void ShiftRightLogical(int count) { // i.e., unsigned + if (count < 0) { + ShiftLeft(-count); + } else { + int shiftParts{count / partBits}; + int bitShift{count - partBits * shiftParts}; + int j{0}; + if (bitShift == 0) { + for (; j + shiftParts < parts; ++j) { + part_[j] = part_[j + shiftParts]; + } + for (; j < parts; ++j) { + part_[j] = 0; + } + } else { + for (; j + shiftParts + 1 < parts; ++j) { + part_[j] = (part_[j + shiftParts] >> bitShift) | + (part_[j + shiftParts + 1] << (partBits - bitShift); + } + if (j + shiftParts + 1 == parts) { + part_[j++] = part_[parts - 1] >> bitShift; + } + for (; j < parts; ++j) { + part_[j] = 0; + } + } + } + } + + // Returns carry out. + constexpr bool AddUnsigned(const FixedPoint &y, bool carryIn{false}) { + BigPart carry{carryIn}; + for (int j{0}; j < parts; ++j) { + carry += part_[j]; + part_[j] = carry += y.part_[j]; + carry >>= 32; + } + return carry != 0; + } + + // Returns true on overflow. + constexpr bool AddSigned(const FixedPoint &y) { + bool carry{AddUnsigned(y)}; + return carry != IsNegative(); + } + + // Returns true on overflow. + constexpr bool SubtractSigned(const FixedPoint &y) { + FixedPoint minusy{y}; + minusy.TwosComplement(); + return AddSigned(minusy); + } + + // Overwrites *this with lower half of full product. + constexpr void MultiplyUnsigned(const FixedPoint &y, FixedPoint &upper) { + Part product[2 * parts]{}; // little-endian full product + for (int j{0}; j < parts; ++j) { + if (part_[j] != 0) { + for (int k{0}; k < parts; ++k) { + if (y.part_[k] != 0) { + BigPart x{part_[j]}; + x *= y.part_[k]; + for (int to{j+k}; xy != 0; ++to) { + product[to] = xy += product[to]; + xy >>= partBits; + } + } + } + } + } + for (int j{0}; j < parts; ++j) { + part_[j] = product[j]; + upper.part_[j] = product[j + parts]; + } + } + + // Overwrites *this with lower half of full product. + constexpr void MultiplySigned(const FixedPoint &y, FixedPoint &upper) { + bool yIsNegative{y.IsNegative()}; + FixedPoint yprime{y}; + if (yIsNegative) { + yprime.TwosComplement(); + } + bool isNegative{IsNegative()}; + if (isNegative) { + TwosComplement(); + } + MultiplyUnsigned(yprime, upper); + if (isNegative != yIsNegative) { + OnesComplement(); + upper.OnesComplement(); + FixedPoint one{std::uint64_t{1}}; + if (AddUnsigned(one)) { + upper.AddUnsigned(one); + } + } + } + + // Overwrites *this with quotient. + constexpr void DivideUnsigned(const FixedPoint &divisor, FixedPoint &remainder) { + FixedPoint top{*this}; + *this = remainder = FixedPoint{0}; + int bitsDone{top.LeadingZeroBitCount()}; + top.ShiftLeft(bitsDone); + for (; bitsDone < bits; ++bitsDone) { + remainder.AddUnsigned(remainder, top.AddUnsigned(top)); + bool nextBit{remainder.CompareUnsigned(divisor) != Ordering::Less}; + quotient.AddUnsigned(quotient, nextBit); + if (nextBit) { + remainder.SubtractSigned(divisor); + } + } + } + + // Overwrites *this with quotient. Returns true on overflow (viz., + // the most negative value divided by -1) or division by zero. + constexpr bool DivideSigned(FixedPoint divisor, FixedPoint &remainder) { + bool negateQuotient{false}, negateRemainder{false}; + if (IsNegative()) { + negateQuotient = negateRemainder = true; + TwosComplement(); + } + Ordering divisorOrdering{divisor.CompareToZeroSigned()}; + bool overflow{divisorOrdering == Ordering::Equal}; + if (divisorOrdering == Ordering::Less) { + negateQuotient = !negateQuotient; + divisor.TwosComplement(); + } + DivideUnsigned(divisor, remainder); + overflow |= IsNegative(); + if (negateQuotient) { + TwosComplement(); + } + if (negateRemainder) { + remainder.TwosComplement(); + } + return overflow; + } + +private: + constexpr bool IsNegative() const { + return (part_[parts-1] >> (partBits - 1)) & 1; + } + + Part part_[parts]; // little-endian order: [parts-1] is most significant +}; +} // namespace Fortran::evaluate +#endif // FORTRAN_EVALUATE_FIXED_POINT_H_ diff --git a/flang/lib/evaluate/leading-zero-bit-count.h b/flang/lib/evaluate/leading-zero-bit-count.h new file mode 100644 index 000000000000..076a03e82839 --- /dev/null +++ b/flang/lib/evaluate/leading-zero-bit-count.h @@ -0,0 +1,105 @@ +// Copyright (c) 2018, 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. + +#ifndef FORTRAN_EVALUATE_LEADING_ZERO_BIT_COUNT_H_ +#define FORTRAN_EVALUATE_LEADING_ZERO_BIT_COUNT_H_ + +// A fast and portable function that counts the number of leading zero bits +// in an integer value. (If the most significant bit is set, the leading +// zero count is zero; if no bit is set, the leading zero count is the +// word size in bits; otherwise, it's the largest left shift count that +// doesn't reduce the number of bits in the word that are set.) + +#include + +namespace Fortran::evaluate { +namespace { +// The following magic constant is a binary deBruijn sequence. +// It has the remarkable property that if one extends it +// (virtually) on the right with 5 more zero bits, then all +// of the 64 contiguous framed blocks of six bits in the +// extended 69-bit sequence are distinct. Consequently, +// if one shifts it left by any shift count [0..63] with +// truncation and extracts the uppermost six bit field +// of the shifted value, each shift count maps to a distinct +// field value. That means that we can map those 64 field +// values back to the shift counts that produce them, +// and (the point) this means that we can shift this value +// by an unknown bit count in [0..63] and then figure out +// what that count must have been. +// 0 7 e d d 5 e 5 9 a 4 e 2 8 c 2 +// 0000011111101101110101011110010110011010010011100010100011000010 +static constexpr std::uint64_t deBruijn{0x07edd5e59a4e28c2}; +static constexpr std::uint8_t mapping[64]{ + 63, 0, 58, 1, 59, 47, 53, 2, 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, 44, 24, 15, 8, 23, 7, 6, 5 }; +} // namespace + +inline constexpr int LeadingZeroBitCount(std::uint64_t x) { + if (x == 0) { + return 64; + } else { + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + // All of the bits below the uppermost set bit are now also set. + x -= x >> 1; // All of the bits below the uppermost are now clear. + // x now has exactly one bit set, so it is a power of two, so + // multiplication by x is equivalent to a left shift by its + // base-2 logarithm. We calculate that unknown base-2 logarithm + // by shifting the deBruijn sequence and mapping the framed value. + int base2Log{mapping[(x * deBruijn) >> 58]}; + return 63 - base2Log; // convert to leading zero count + } +} + +inline constexpr int LeadingZeroBitCount(std::uint32_t x) { + return LeadingZeroBitCount(static_cast(x)) - 32; +} + +inline constexpr int LeadingZeroBitCount(std::uint16_t x) { + return LeadingZeroBitCount(static_cast(x)) - 48; +} + +namespace { +static constexpr std::uint8_t eightBitLeadingZeroBitCount[256]{ + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +} // namespace + +inline constexpr int LeadingZeroBitCount(std::uint8_t x) { + return eightBitLeadingZeroBitCount[x]; +} +} // namespace Fortran::evaluate +#endif // FORTRAN_EVALUATE_LEADING_ZERO_BIT_COUNT_H_