[flang] Continue conversion to value semantics
Original-commit: flang-compiler/f18@03fe266611 Reviewed-on: https://github.com/flang-compiler/f18/pull/101 Tree-same-pre-rewrite: false
This commit is contained in:
parent
c3daaf8e79
commit
7b15d8054b
|
|
@ -47,6 +47,8 @@ static constexpr Ordering Reverse(Ordering ordering) {
|
||||||
// To facilitate exhaustive testing of what would otherwise be more rare
|
// To facilitate exhaustive testing of what would otherwise be more rare
|
||||||
// edge cases, this class template may be configured to use other part
|
// edge cases, this class template may be configured to use other part
|
||||||
// types &/or partial fields in the parts.
|
// types &/or partial fields in the parts.
|
||||||
|
// Member functions that correspond to Fortran intrinsic functions are
|
||||||
|
// named accordingly.
|
||||||
template<int BITS, int PARTBITS = 32, typename PART = std::uint32_t,
|
template<int BITS, int PARTBITS = 32, typename PART = std::uint32_t,
|
||||||
typename BIGPART = std::uint64_t, bool LITTLE_ENDIAN = true>
|
typename BIGPART = std::uint64_t, bool LITTLE_ENDIAN = true>
|
||||||
class FixedPoint {
|
class FixedPoint {
|
||||||
|
|
@ -73,35 +75,72 @@ private:
|
||||||
static constexpr Part topPartMask{static_cast<Part>(~0) >> extraTopPartBits};
|
static constexpr Part topPartMask{static_cast<Part>(~0) >> extraTopPartBits};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Constructors and value-generating static functions
|
||||||
constexpr FixedPoint() { Clear(); } // default constructor: zero
|
constexpr FixedPoint() { Clear(); } // default constructor: zero
|
||||||
constexpr FixedPoint(const FixedPoint &) = default;
|
constexpr FixedPoint(const FixedPoint &) = default;
|
||||||
constexpr FixedPoint(std::uint64_t n) {
|
constexpr FixedPoint(std::uint64_t n) {
|
||||||
for (int j{0}; j + 1 < parts; ++j) {
|
for (int j{0}; j + 1 < parts; ++j) {
|
||||||
LEPart(j) = n & partMask;
|
SetLEPart(j, n);
|
||||||
if constexpr (partBits < 64) {
|
if constexpr (partBits < 64) {
|
||||||
n >>= partBits;
|
n >>= partBits;
|
||||||
} else {
|
} else {
|
||||||
n = 0;
|
n = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LEPart(parts - 1) = n & topPartMask;
|
SetLEPart(parts - 1, n);
|
||||||
}
|
}
|
||||||
constexpr FixedPoint(std::int64_t n) {
|
constexpr FixedPoint(std::int64_t n) {
|
||||||
std::int64_t signExtension{-(n < 0)};
|
std::int64_t signExtension{-(n < 0)};
|
||||||
signExtension <<= partBits;
|
signExtension <<= partBits;
|
||||||
for (int j{0}; j + 1 < parts; ++j) {
|
for (int j{0}; j + 1 < parts; ++j) {
|
||||||
LEPart(j) = n & partMask;
|
SetLEPart(j, n);
|
||||||
if constexpr (partBits < 64) {
|
if constexpr (partBits < 64) {
|
||||||
n = (n >> partBits) | signExtension;
|
n = (n >> partBits) | signExtension;
|
||||||
} else {
|
} else {
|
||||||
n = signExtension;
|
n = signExtension;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LEPart(parts - 1) = n & topPartMask;
|
SetLEPart(parts - 1, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right-justified mask (e.g., MASKR(1) == 1, MASKR(2) == 3, &c.)
|
||||||
|
static constexpr FixedPoint MASKR(int places) {
|
||||||
|
FixedPoint result{nullptr};
|
||||||
|
int j{0};
|
||||||
|
for (; j + 1 < parts && places >= partBits; ++j, places -= partBits) {
|
||||||
|
result.LEPart(j) = partMask;
|
||||||
|
}
|
||||||
|
if (places > 0) {
|
||||||
|
if (j + 1 < parts) {
|
||||||
|
result.LEPart(j++) = partMask >> (partBits - places);
|
||||||
|
} else if (j + 1 == parts) {
|
||||||
|
if (places >= topPartBits) {
|
||||||
|
result.LEPart(j++) = topPartMask;
|
||||||
|
} else {
|
||||||
|
result.LEPart(j++) = topPartMask >> (topPartBits - places);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (; j < parts; ++j) {
|
||||||
|
result.LEPart(j) = 0;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left-justified mask (e.g., MASKL(1) has only its sign bit set)
|
||||||
|
static constexpr FixedPoint MASKL(int places) {
|
||||||
|
if (places < 0) {
|
||||||
|
return {};
|
||||||
|
} else if (places >= bits) {
|
||||||
|
return MASKR(bits);
|
||||||
|
} else {
|
||||||
|
return MASKR(bits - places).NOT();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr FixedPoint &operator=(const FixedPoint &) = default;
|
constexpr FixedPoint &operator=(const FixedPoint &) = default;
|
||||||
|
|
||||||
|
// Predicates and comparisons
|
||||||
constexpr bool IsZero() const {
|
constexpr bool IsZero() const {
|
||||||
for (int j{0}; j < parts; ++j) {
|
for (int j{0}; j < parts; ++j) {
|
||||||
if (part_[j] != 0) {
|
if (part_[j] != 0) {
|
||||||
|
|
@ -162,27 +201,32 @@ public:
|
||||||
return signExtended;
|
return signExtended;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOT
|
// Ones'-complement (i.e., C's ~)
|
||||||
constexpr FixedPoint OnesComplement() const {
|
constexpr FixedPoint NOT() const {
|
||||||
FixedPoint result{nullptr};
|
FixedPoint result{nullptr};
|
||||||
for (int j{0}; j + 1 < parts; ++j) {
|
for (int j{0}; j < parts; ++j) {
|
||||||
result.LEPart(j) = ~LEPart(j) & partMask;
|
result.SetLEPart(j, ~LEPart(j));
|
||||||
}
|
}
|
||||||
result.LEPart(parts - 1) = ~LEPart(parts - 1) & topPartMask;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true on overflow (i.e., negating the most negative signed number)
|
// Two's-complement negation (-x = ~x + 1).
|
||||||
constexpr bool TwosComplement() {
|
struct ValueWithOverflow {
|
||||||
|
FixedPoint value;
|
||||||
|
bool overflow; // true when operand was MASKL(1), the most negative number
|
||||||
|
};
|
||||||
|
constexpr ValueWithOverflow Negate() const {
|
||||||
|
FixedPoint result;
|
||||||
Part carry{1};
|
Part carry{1};
|
||||||
for (int j{0}; j + 1 < parts; ++j) {
|
for (int j{0}; j + 1 < parts; ++j) {
|
||||||
Part newCarry{LEPart(j) == 0 && carry};
|
Part newCarry{LEPart(j) == 0 && carry};
|
||||||
LEPart(j) = (~LEPart(j) + carry) & partMask;
|
result.SetLEPart(j, ~LEPart(j) + carry);
|
||||||
carry = newCarry;
|
carry = newCarry;
|
||||||
}
|
}
|
||||||
Part before{LEPart(parts - 1)};
|
Part top{LEPart(parts - 1)};
|
||||||
LEPart(parts - 1) = (~before + carry) & topPartMask;
|
result.SetLEPart(parts - 1, ~top + carry);
|
||||||
return before != 0 && LEPart(parts - 1) == before;
|
bool overflow{top != 0 && result.LEPart(parts - 1) == top};
|
||||||
|
return {result, overflow};
|
||||||
}
|
}
|
||||||
|
|
||||||
// LEADZ intrinsic
|
// LEADZ intrinsic
|
||||||
|
|
@ -216,19 +260,18 @@ public:
|
||||||
int j{parts - 1};
|
int j{parts - 1};
|
||||||
if (bitShift == 0) {
|
if (bitShift == 0) {
|
||||||
for (; j >= shiftParts; --j) {
|
for (; j >= shiftParts; --j) {
|
||||||
LEPart(j) = LEPart(j - shiftParts) & PartMask(j);
|
SetLEPart(j, LEPart(j - shiftParts));
|
||||||
}
|
}
|
||||||
for (; j >= 0; --j) {
|
for (; j >= 0; --j) {
|
||||||
LEPart(j) = 0;
|
LEPart(j) = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (; j > shiftParts; --j) {
|
for (; j > shiftParts; --j) {
|
||||||
LEPart(j) = ((LEPart(j - shiftParts) << bitShift) |
|
SetLEPart(j, ((LEPart(j - shiftParts) << bitShift) |
|
||||||
(LEPart(j - shiftParts - 1) >> (partBits - bitShift))) &
|
(LEPart(j - shiftParts - 1) >> (partBits - bitShift))));
|
||||||
PartMask(j);
|
|
||||||
}
|
}
|
||||||
if (j == shiftParts) {
|
if (j == shiftParts) {
|
||||||
LEPart(j) = (LEPart(0) << bitShift) & PartMask(j);
|
SetLEPart(j, LEPart(0) << bitShift);
|
||||||
--j;
|
--j;
|
||||||
}
|
}
|
||||||
for (; j >= 0; --j) {
|
for (; j >= 0; --j) {
|
||||||
|
|
@ -259,9 +302,8 @@ public:
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (; j + shiftParts + 1 < parts; ++j) {
|
for (; j + shiftParts + 1 < parts; ++j) {
|
||||||
LEPart(j) = ((LEPart(j + shiftParts) >> bitShift) |
|
SetLEPart(j, (LEPart(j + shiftParts) >> bitShift) |
|
||||||
(LEPart(j + shiftParts + 1) << (partBits - bitShift))) &
|
(LEPart(j + shiftParts + 1) << (partBits - bitShift)));
|
||||||
partMask;
|
|
||||||
}
|
}
|
||||||
if (j + shiftParts + 1 == parts) {
|
if (j + shiftParts + 1 == parts) {
|
||||||
LEPart(j++) = LEPart(parts - 1) >> bitShift;
|
LEPart(j++) = LEPart(parts - 1) >> bitShift;
|
||||||
|
|
@ -282,9 +324,7 @@ public:
|
||||||
bool fill{IsNegative()};
|
bool fill{IsNegative()};
|
||||||
ShiftRightLogical(count);
|
ShiftRightLogical(count);
|
||||||
if (fill) {
|
if (fill) {
|
||||||
FixedPoint signs;
|
Or(MASKL(count));
|
||||||
signs.LeftMask(count);
|
|
||||||
Or(signs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -316,12 +356,12 @@ public:
|
||||||
for (int j{0}; j + 1 < parts; ++j) {
|
for (int j{0}; j + 1 < parts; ++j) {
|
||||||
carry += LEPart(j);
|
carry += LEPart(j);
|
||||||
carry += y.LEPart(j);
|
carry += y.LEPart(j);
|
||||||
LEPart(j) = carry & partMask;
|
SetLEPart(j, carry);
|
||||||
carry >>= partBits;
|
carry >>= partBits;
|
||||||
}
|
}
|
||||||
carry += LEPart(parts - 1);
|
carry += LEPart(parts - 1);
|
||||||
carry += y.LEPart(parts - 1);
|
carry += y.LEPart(parts - 1);
|
||||||
LEPart(parts - 1) = carry & topPartMask;
|
SetLEPart(parts - 1, carry);
|
||||||
return carry > topPartMask;
|
return carry > topPartMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,9 +377,7 @@ public:
|
||||||
constexpr bool SubtractSigned(const FixedPoint &y) {
|
constexpr bool SubtractSigned(const FixedPoint &y) {
|
||||||
bool isNegative{IsNegative()};
|
bool isNegative{IsNegative()};
|
||||||
bool sameSign{isNegative == y.IsNegative()};
|
bool sameSign{isNegative == y.IsNegative()};
|
||||||
FixedPoint minusy{y};
|
AddUnsigned(y.Negate().value);
|
||||||
minusy.TwosComplement();
|
|
||||||
AddUnsigned(minusy);
|
|
||||||
return !sameSign && IsNegative() != isNegative;
|
return !sameSign && IsNegative() != isNegative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,16 +415,16 @@ public:
|
||||||
bool yIsNegative{y.IsNegative()};
|
bool yIsNegative{y.IsNegative()};
|
||||||
FixedPoint yprime{y};
|
FixedPoint yprime{y};
|
||||||
if (yIsNegative) {
|
if (yIsNegative) {
|
||||||
yprime.TwosComplement();
|
yprime = y.Negate().value;
|
||||||
}
|
}
|
||||||
bool isNegative{IsNegative()};
|
bool isNegative{IsNegative()};
|
||||||
if (isNegative) {
|
if (isNegative) {
|
||||||
TwosComplement();
|
*this = Negate().value;
|
||||||
}
|
}
|
||||||
MultiplyUnsigned(yprime, upper);
|
MultiplyUnsigned(yprime, upper);
|
||||||
if (isNegative != yIsNegative) {
|
if (isNegative != yIsNegative) {
|
||||||
*this = OnesComplement();
|
*this = NOT();
|
||||||
upper = upper.OnesComplement();
|
upper = upper.NOT();
|
||||||
FixedPoint one{std::uint64_t{1}};
|
FixedPoint one{std::uint64_t{1}};
|
||||||
if (AddUnsigned(one)) {
|
if (AddUnsigned(one)) {
|
||||||
upper.AddUnsigned(one);
|
upper.AddUnsigned(one);
|
||||||
|
|
@ -399,7 +437,7 @@ public:
|
||||||
const FixedPoint &divisor, FixedPoint &remainder) {
|
const FixedPoint &divisor, FixedPoint &remainder) {
|
||||||
remainder.Clear();
|
remainder.Clear();
|
||||||
if (divisor.IsZero()) {
|
if (divisor.IsZero()) {
|
||||||
RightMask(bits);
|
*this = MASKR(bits);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
FixedPoint top{*this};
|
FixedPoint top{*this};
|
||||||
|
|
@ -428,10 +466,11 @@ public:
|
||||||
Ordering divisorOrdering{divisor.CompareToZeroSigned()};
|
Ordering divisorOrdering{divisor.CompareToZeroSigned()};
|
||||||
if (divisorOrdering == Ordering::Less) {
|
if (divisorOrdering == Ordering::Less) {
|
||||||
negateQuotient = !negateQuotient;
|
negateQuotient = !negateQuotient;
|
||||||
if (divisor.TwosComplement()) {
|
auto negated{divisor.Negate()};
|
||||||
|
if (negated.overflow) {
|
||||||
// divisor was (and is) the most negative number
|
// divisor was (and is) the most negative number
|
||||||
if (CompareUnsigned(divisor) == Ordering::Equal) {
|
if (CompareUnsigned(divisor) == Ordering::Equal) {
|
||||||
RightMask(1);
|
*this = MASKR(1);
|
||||||
remainder.Clear();
|
remainder.Clear();
|
||||||
return bits <= 1; // edge case: 1-bit signed numbers overflow on 1!
|
return bits <= 1; // edge case: 1-bit signed numbers overflow on 1!
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -440,18 +479,20 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
divisor = negated.value;
|
||||||
} else if (divisorOrdering == Ordering::Equal) {
|
} else if (divisorOrdering == Ordering::Equal) {
|
||||||
// division by zero
|
// division by zero
|
||||||
remainder.Clear();
|
remainder.Clear();
|
||||||
if (dividendIsNegative) {
|
if (dividendIsNegative) {
|
||||||
LeftMask(1); // most negative signed number
|
*this = MASKL(1); // most negative signed number
|
||||||
} else {
|
} else {
|
||||||
RightMask(bits - 1); // most positive signed number
|
*this = MASKR(bits - 1); // most positive signed number
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (dividendIsNegative) {
|
if (dividendIsNegative) {
|
||||||
if (TwosComplement()) {
|
auto negated{Negate()};
|
||||||
|
if (negated.overflow) {
|
||||||
// Dividend was (and remains) the most negative number.
|
// Dividend was (and remains) the most negative number.
|
||||||
// See whether the original divisor was -1 (if so, it's 1 now).
|
// See whether the original divisor was -1 (if so, it's 1 now).
|
||||||
if (divisorOrdering == Ordering::Less &&
|
if (divisorOrdering == Ordering::Less &&
|
||||||
|
|
@ -461,16 +502,18 @@ public:
|
||||||
remainder.Clear();
|
remainder.Clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
*this = negated.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Overflow is not possible, and both the dividend (*this) and divisor
|
// Overflow is not possible, and both the dividend (*this) and divisor
|
||||||
// are now positive.
|
// are now positive.
|
||||||
DivideUnsigned(divisor, remainder);
|
DivideUnsigned(divisor, remainder);
|
||||||
if (negateQuotient) {
|
if (negateQuotient) {
|
||||||
TwosComplement();
|
*this = Negate().value;
|
||||||
}
|
}
|
||||||
if (dividendIsNegative) {
|
if (dividendIsNegative) {
|
||||||
remainder.TwosComplement();
|
remainder = remainder.Negate().value;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -489,40 +532,6 @@ public:
|
||||||
return overflow;
|
return overflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// MASKR intrinsic
|
|
||||||
constexpr void RightMask(int places) {
|
|
||||||
int j{0};
|
|
||||||
for (; j + 1 < parts && places >= partBits; ++j, places -= partBits) {
|
|
||||||
LEPart(j) = partMask;
|
|
||||||
}
|
|
||||||
if (places > 0) {
|
|
||||||
if (j + 1 < parts) {
|
|
||||||
LEPart(j++) = partMask >> (partBits - places);
|
|
||||||
} else if (j + 1 == parts) {
|
|
||||||
if (places >= topPartBits) {
|
|
||||||
LEPart(j++) = topPartMask;
|
|
||||||
} else {
|
|
||||||
LEPart(j++) = topPartMask >> (topPartBits - places);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (; j < parts; ++j) {
|
|
||||||
LEPart(j) = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MASKL intrinsic
|
|
||||||
constexpr void LeftMask(int places) {
|
|
||||||
if (places < 0) {
|
|
||||||
Clear();
|
|
||||||
} else if (places >= bits) {
|
|
||||||
RightMask(bits);
|
|
||||||
} else {
|
|
||||||
RightMask(bits - places);
|
|
||||||
*this = OnesComplement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
constexpr FixedPoint(std::nullptr_t) {} // does not initialize
|
constexpr FixedPoint(std::nullptr_t) {} // does not initialize
|
||||||
|
|
||||||
|
|
@ -543,6 +552,10 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void SetLEPart(int part, Part x) {
|
||||||
|
LEPart(part) = x & PartMask(part);
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr Part PartMask(int part) {
|
static constexpr Part PartMask(int part) {
|
||||||
return part == parts - 1 ? topPartMask : partMask;
|
return part == parts - 1 ? topPartMask : partMask;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,24 +32,23 @@ template<int BITS, typename FP = FixedPoint<BITS>> void exhaustiveTesting() {
|
||||||
TEST(zero.IsZero())(desc);
|
TEST(zero.IsZero())(desc);
|
||||||
for (std::uint64_t x{0}; x <= maxUnsignedValue; ++x) {
|
for (std::uint64_t x{0}; x <= maxUnsignedValue; ++x) {
|
||||||
FP a{x};
|
FP a{x};
|
||||||
COMPARE(x, ==, a.ToUInt64())(desc);
|
MATCH(x, a.ToUInt64())(desc);
|
||||||
FP copy{a};
|
FP copy{a};
|
||||||
COMPARE(x, ==, copy.ToUInt64())(desc);
|
MATCH(x, copy.ToUInt64())(desc);
|
||||||
copy = a;
|
copy = a;
|
||||||
COMPARE(x, ==, copy.ToUInt64())(desc);
|
MATCH(x, copy.ToUInt64())(desc);
|
||||||
COMPARE(x == 0, ==, a.IsZero())("%s, x=0x%llx", desc, x);
|
MATCH(x == 0, a.IsZero())("%s, x=0x%llx", desc, x);
|
||||||
FP t{a.OnesComplement()};
|
FP t{a.NOT()};
|
||||||
COMPARE(x ^ maxUnsignedValue, ==, t.ToUInt64())("%s, x=0x%llx", desc, x);
|
MATCH(x ^ maxUnsignedValue, t.ToUInt64())("%s, x=0x%llx", desc, x);
|
||||||
copy = a;
|
auto negated{a.Negate()};
|
||||||
bool over{copy.TwosComplement()};
|
MATCH(x == std::uint64_t{1} << (BITS - 1), negated.overflow)
|
||||||
COMPARE(over, ==, x == std::uint64_t{1} << (BITS - 1))
|
("%s, x=0x%llx", desc, x);
|
||||||
("%s, x=0x%llx", desc, x);
|
MATCH(negated.value.ToUInt64(), -x & maxUnsignedValue)
|
||||||
COMPARE(-x & maxUnsignedValue, ==, copy.ToUInt64())
|
("%s, x=0x%llx", desc, x);
|
||||||
("%s, x=0x%llx", desc, x);
|
|
||||||
int lzbc{a.LeadingZeroBitCount()};
|
int lzbc{a.LeadingZeroBitCount()};
|
||||||
COMPARE(lzbc, >=, 0)("%s, x=0x%llx", desc, x);
|
COMPARE(lzbc, >=, 0)("%s, x=0x%llx", desc, x);
|
||||||
COMPARE(lzbc, <=, BITS)("%s, x=0x%llx", desc, x);
|
COMPARE(lzbc, <=, BITS)("%s, x=0x%llx", desc, x);
|
||||||
COMPARE(x == 0, ==, lzbc == BITS)("%s, x=0x%llx, lzbc=%d", desc, x, lzbc);
|
MATCH(x == 0, lzbc == BITS)("%s, x=0x%llx, lzbc=%d", desc, x, lzbc);
|
||||||
std::uint64_t lzcheck{std::uint64_t{1} << (BITS - lzbc)};
|
std::uint64_t lzcheck{std::uint64_t{1} << (BITS - lzbc)};
|
||||||
COMPARE(x, <, lzcheck)("%s, x=0x%llx, lzbc=%d", desc, x, lzbc);
|
COMPARE(x, <, lzcheck)("%s, x=0x%llx, lzbc=%d", desc, x, lzbc);
|
||||||
COMPARE(x + x + !x, >=, lzcheck)("%s, x=0x%llx, lzbc=%d", desc, x, lzbc);
|
COMPARE(x + x + !x, >=, lzcheck)("%s, x=0x%llx, lzbc=%d", desc, x, lzbc);
|
||||||
|
|
@ -74,26 +73,26 @@ template<int BITS, typename FP = FixedPoint<BITS>> void exhaustiveTesting() {
|
||||||
for (int count{0}; count <= BITS + 1; ++count) {
|
for (int count{0}; count <= BITS + 1; ++count) {
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.ShiftLeft(count);
|
copy.ShiftLeft(count);
|
||||||
COMPARE((x << count) & maxUnsignedValue, ==, copy.ToUInt64())
|
MATCH((x << count) & maxUnsignedValue, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, count=%d", desc, x, count);
|
("%s, x=0x%llx, count=%d", desc, x, count);
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.ShiftRightLogical(count);
|
copy.ShiftRightLogical(count);
|
||||||
COMPARE(x >> count, ==, copy.ToUInt64())
|
MATCH(x >> count, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, count=%d", desc, x, count);
|
("%s, x=0x%llx, count=%d", desc, x, count);
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.ShiftLeft(-count);
|
copy.ShiftLeft(-count);
|
||||||
COMPARE(x >> count, ==, copy.ToUInt64())
|
MATCH(x >> count, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, count=%d", desc, x, count);
|
("%s, x=0x%llx, count=%d", desc, x, count);
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.ShiftRightLogical(-count);
|
copy.ShiftRightLogical(-count);
|
||||||
COMPARE((x << count) & maxUnsignedValue, ==, copy.ToUInt64())
|
MATCH((x << count) & maxUnsignedValue, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, count=%d", desc, x, count);
|
("%s, x=0x%llx, count=%d", desc, x, count);
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.ShiftRightArithmetic(count);
|
copy.ShiftRightArithmetic(count);
|
||||||
std::uint64_t fill{-(x >> (BITS-1))};
|
std::uint64_t fill{-(x >> (BITS-1))};
|
||||||
std::uint64_t sra{count >= BITS ? fill : (x >> count) | (fill << (BITS-count))};
|
std::uint64_t sra{count >= BITS ? fill : (x >> count) | (fill << (BITS-count))};
|
||||||
COMPARE(sra, ==, copy.ToInt64())
|
MATCH(sra, copy.ToInt64())
|
||||||
("%s, x=0x%llx, count=%d", desc, x, count);
|
("%s, x=0x%llx, count=%d", desc, x, count);
|
||||||
}
|
}
|
||||||
for (std::uint64_t y{0}; y <= maxUnsignedValue; ++y) {
|
for (std::uint64_t y{0}; y <= maxUnsignedValue; ++y) {
|
||||||
std::int64_t sy = y;
|
std::int64_t sy = y;
|
||||||
|
|
@ -117,84 +116,82 @@ template<int BITS, typename FP = FixedPoint<BITS>> void exhaustiveTesting() {
|
||||||
ord = Ordering::Equal;
|
ord = Ordering::Equal;
|
||||||
}
|
}
|
||||||
TEST(a.CompareSigned(b) == ord)
|
TEST(a.CompareSigned(b) == ord)
|
||||||
("%s, x=0x%llx %lld %d, y=0x%llx %lld %d", desc, x, sx, a.IsNegative(), y,
|
("%s, x=0x%llx %lld %d, y=0x%llx %lld %d", desc, x, sx,
|
||||||
sy, b.IsNegative());
|
a.IsNegative(), y, sy, b.IsNegative());
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.And(b);
|
copy.And(b);
|
||||||
COMPARE(x & y, ==, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(x & y, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.Or(b);
|
copy.Or(b);
|
||||||
COMPARE(x | y, ==, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(x | y, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.Xor(b);
|
copy.Xor(b);
|
||||||
COMPARE(x ^ y, ==, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(x ^ y, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
copy = a;
|
copy = a;
|
||||||
bool carry{copy.AddUnsigned(b)};
|
bool carry{copy.AddUnsigned(b)};
|
||||||
COMPARE(x + y, ==, copy.ToUInt64() + (std::uint64_t{carry} << BITS))
|
COMPARE(x + y, ==, copy.ToUInt64() + (std::uint64_t{carry} << BITS))
|
||||||
("%s, x=0x%llx, y=0x%llx, carry=%d", desc, x, y, carry);
|
("%s, x=0x%llx, y=0x%llx, carry=%d", desc, x, y, carry);
|
||||||
copy = a;
|
copy = a;
|
||||||
over = copy.AddSigned(b);
|
bool over{copy.AddSigned(b)};
|
||||||
COMPARE((sx + sy) & maxUnsignedValue, ==, copy.ToUInt64())
|
MATCH((sx + sy) & maxUnsignedValue, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
COMPARE(over, ==,
|
MATCH(over,
|
||||||
sx + sy < mostNegativeSignedValue || sx + sy > maxPositiveSignedValue)
|
sx + sy < mostNegativeSignedValue || sx + sy > maxPositiveSignedValue)
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
copy = a;
|
copy = a;
|
||||||
over = copy.SubtractSigned(b);
|
over = copy.SubtractSigned(b);
|
||||||
COMPARE((sx - sy) & maxUnsignedValue, ==, copy.ToUInt64())
|
MATCH((sx - sy) & maxUnsignedValue, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
COMPARE(over, ==,
|
MATCH(over,
|
||||||
sx - sy < mostNegativeSignedValue || sx - sy > maxPositiveSignedValue)
|
sx - sy < mostNegativeSignedValue || sx - sy > maxPositiveSignedValue)
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
copy = a;
|
copy = a;
|
||||||
FP upper;
|
FP upper;
|
||||||
copy.MultiplyUnsigned(b, upper);
|
copy.MultiplyUnsigned(b, upper);
|
||||||
COMPARE(x * y, ==, (upper.ToUInt64() << BITS) ^ copy.ToUInt64())
|
MATCH(x * y, (upper.ToUInt64() << BITS) ^ copy.ToUInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx, lower=0x%llx, upper=0x%llx", desc, x, y,
|
("%s, x=0x%llx, y=0x%llx, lower=0x%llx, upper=0x%llx", desc, x, y,
|
||||||
copy.ToUInt64(), upper.ToUInt64());
|
copy.ToUInt64(), upper.ToUInt64());
|
||||||
copy = a;
|
copy = a;
|
||||||
copy.MultiplySigned(b, upper);
|
copy.MultiplySigned(b, upper);
|
||||||
COMPARE((sx * sy) & maxUnsignedValue, ==, copy.ToUInt64())
|
MATCH((sx * sy) & maxUnsignedValue, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
COMPARE(((sx * sy) >> BITS) & maxUnsignedValue, ==, upper.ToUInt64())
|
MATCH(((sx * sy) >> BITS) & maxUnsignedValue, upper.ToUInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
copy = a;
|
copy = a;
|
||||||
FP rem;
|
FP rem;
|
||||||
COMPARE(y == 0, ==, copy.DivideUnsigned(b, rem))
|
MATCH(y == 0, copy.DivideUnsigned(b, rem))
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
if (y == 0) {
|
if (y == 0) {
|
||||||
COMPARE(maxUnsignedValue, ==, copy.ToUInt64())
|
MATCH(maxUnsignedValue, copy.ToUInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
COMPARE(0, ==, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(0, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
} else {
|
} else {
|
||||||
COMPARE(x / y, ==, copy.ToUInt64())
|
MATCH(x / y, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(x % y, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
COMPARE(x % y, ==, rem.ToUInt64())
|
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
|
||||||
}
|
}
|
||||||
copy = a;
|
copy = a;
|
||||||
bool badCase{sx == mostNegativeSignedValue &&
|
bool badCase{sx == mostNegativeSignedValue &&
|
||||||
((sy == -1 && sx != sy) || (BITS == 1 && sx == sy))};
|
((sy == -1 && sx != sy) || (BITS == 1 && sx == sy))};
|
||||||
COMPARE(y == 0 || badCase, ==, copy.DivideSigned(b, rem))
|
MATCH(y == 0 || badCase, copy.DivideSigned(b, rem))
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
if (y == 0) {
|
if (y == 0) {
|
||||||
if (sx >= 0) {
|
if (sx >= 0) {
|
||||||
COMPARE(maxPositiveSignedValue, ==, copy.ToInt64())
|
MATCH(maxPositiveSignedValue, copy.ToInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
} else {
|
} else {
|
||||||
COMPARE(mostNegativeSignedValue, ==, copy.ToInt64())
|
MATCH(mostNegativeSignedValue, copy.ToInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
}
|
}
|
||||||
COMPARE(0, ==, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(0, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
} else if (badCase) {
|
} else if (badCase) {
|
||||||
COMPARE(x, ==, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(x, copy.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
COMPARE(0, ==, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
MATCH(0, rem.ToUInt64())("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
} else {
|
} else {
|
||||||
COMPARE(sx / sy, ==, copy.ToInt64())
|
MATCH(sx / sy, copy.ToInt64())
|
||||||
("%s, x=0x%llx %lld, y=0x%llx %lld; unsigned 0x%llx", desc, x, sx, y,
|
("%s, x=0x%llx %lld, y=0x%llx %lld; unsigned 0x%llx", desc, x, sx, y,
|
||||||
sy, copy.ToUInt64());
|
sy, copy.ToUInt64());
|
||||||
COMPARE(sx - sy * (sx / sy), ==, rem.ToInt64())
|
MATCH(sx - sy * (sx / sy), rem.ToInt64())
|
||||||
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
("%s, x=0x%llx, y=0x%llx", desc, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,19 @@ FailureDetailPrinter Test(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FailureDetailPrinter Match(const char *file, int line,
|
||||||
|
unsigned long long want, const char *gots, unsigned long long got) {
|
||||||
|
if (want == got) {
|
||||||
|
++passes;
|
||||||
|
return BitBucket;
|
||||||
|
} else {
|
||||||
|
++failures;
|
||||||
|
fprintf(stderr, "%s:%d: FAIL: %s == 0x%llx, not 0x%llx\n", file, line, gots,
|
||||||
|
got, want);
|
||||||
|
return PrintFailureDetails;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FailureDetailPrinter Compare(const char *file, int line, const char *xs,
|
FailureDetailPrinter Compare(const char *file, int line, const char *xs,
|
||||||
const char *rel, const char *ys, unsigned long long x,
|
const char *rel, const char *ys, unsigned long long x,
|
||||||
unsigned long long y) {
|
unsigned long long y) {
|
||||||
|
|
@ -77,7 +90,7 @@ FailureDetailPrinter Compare(const char *file, int line, const char *xs,
|
||||||
return BitBucket;
|
return BitBucket;
|
||||||
} else {
|
} else {
|
||||||
++failures;
|
++failures;
|
||||||
fprintf(stderr, "%s:%d: FAIL %s[0x%llx] %s %s[0x%llx]:\n", file, line, xs,
|
fprintf(stderr, "%s:%d: FAIL %s[0x%llx] %s %s[0x%llx]\n", file, line, xs,
|
||||||
x, rel, ys, y);
|
x, rel, ys, y);
|
||||||
return PrintFailureDetails;
|
return PrintFailureDetails;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ int Complete();
|
||||||
// will also print z after the usual failure message if x != y.
|
// will also print z after the usual failure message if x != y.
|
||||||
#define TEST(predicate) \
|
#define TEST(predicate) \
|
||||||
testing::Test(__FILE__, __LINE__, #predicate, (predicate))
|
testing::Test(__FILE__, __LINE__, #predicate, (predicate))
|
||||||
|
#define MATCH(want, got) \
|
||||||
|
testing::Match(__FILE__, __LINE__, (want), #got, (got))
|
||||||
#define COMPARE(x, rel, y) \
|
#define COMPARE(x, rel, y) \
|
||||||
testing::Compare(__FILE__, __LINE__, #x, #rel, #y, (x), (y))
|
testing::Compare(__FILE__, __LINE__, #x, #rel, #y, (x), (y))
|
||||||
|
|
||||||
|
|
@ -34,6 +36,8 @@ int Complete();
|
||||||
using FailureDetailPrinter = void (*)(const char *, ...);
|
using FailureDetailPrinter = void (*)(const char *, ...);
|
||||||
FailureDetailPrinter Test(
|
FailureDetailPrinter Test(
|
||||||
const char *file, int line, const char *predicate, bool pass);
|
const char *file, int line, const char *predicate, bool pass);
|
||||||
|
FailureDetailPrinter Match(const char *file, int line,
|
||||||
|
unsigned long long want, const char *gots, unsigned long long got);
|
||||||
FailureDetailPrinter Compare(const char *file, int line, const char *xs,
|
FailureDetailPrinter Compare(const char *file, int line, const char *xs,
|
||||||
const char *rel, const char *ys, unsigned long long x,
|
const char *rel, const char *ys, unsigned long long x,
|
||||||
unsigned long long y);
|
unsigned long long y);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue