mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
[ADT] Move FixedPoint.h from Clang to LLVM.
This patch moves FixedPointSemantics and APFixedPoint from Clang to LLVM ADT. This will make it easier to use the fixed-point classes in LLVM for constructing an IR builder for fixed-point and for reusing the APFixedPoint class for constant evaluation purposes. RFC: http://lists.llvm.org/pipermail/llvm-dev/2020-August/144025.html Reviewed By: leonardchan, rjmccall Differential Revision: https://reviews.llvm.org/D85312
This commit is contained in:
parent
2c00c94c18
commit
9531c6209d
209
include/llvm/ADT/APFixedPoint.h
Normal file
209
include/llvm/ADT/APFixedPoint.h
Normal file
@ -0,0 +1,209 @@
|
||||
//===- APFixedPoint.h - Fixed point constant handling -----------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file
|
||||
/// Defines the fixed point number interface.
|
||||
/// This is a class for abstracting various operations performed on fixed point
|
||||
/// types.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_ADT_APFIXEDPOINT_H
|
||||
#define LLVM_ADT_APFIXEDPOINT_H
|
||||
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// The fixed point semantics work similarly to fltSemantics. The width
|
||||
/// specifies the whole bit width of the underlying scaled integer (with padding
|
||||
/// if any). The scale represents the number of fractional bits in this type.
|
||||
/// When HasUnsignedPadding is true and this type is unsigned, the first bit
|
||||
/// in the value this represents is treated as padding.
|
||||
class FixedPointSemantics {
|
||||
public:
|
||||
FixedPointSemantics(unsigned Width, unsigned Scale, bool IsSigned,
|
||||
bool IsSaturated, bool HasUnsignedPadding)
|
||||
: Width(Width), Scale(Scale), IsSigned(IsSigned),
|
||||
IsSaturated(IsSaturated), HasUnsignedPadding(HasUnsignedPadding) {
|
||||
assert(Width >= Scale && "Not enough room for the scale");
|
||||
assert(!(IsSigned && HasUnsignedPadding) &&
|
||||
"Cannot have unsigned padding on a signed type.");
|
||||
}
|
||||
|
||||
unsigned getWidth() const { return Width; }
|
||||
unsigned getScale() const { return Scale; }
|
||||
bool isSigned() const { return IsSigned; }
|
||||
bool isSaturated() const { return IsSaturated; }
|
||||
bool hasUnsignedPadding() const { return HasUnsignedPadding; }
|
||||
|
||||
void setSaturated(bool Saturated) { IsSaturated = Saturated; }
|
||||
|
||||
/// Return the number of integral bits represented by these semantics. These
|
||||
/// are separate from the fractional bits and do not include the sign or
|
||||
/// padding bit.
|
||||
unsigned getIntegralBits() const {
|
||||
if (IsSigned || (!IsSigned && HasUnsignedPadding))
|
||||
return Width - Scale - 1;
|
||||
else
|
||||
return Width - Scale;
|
||||
}
|
||||
|
||||
/// Return the FixedPointSemantics that allows for calculating the full
|
||||
/// precision semantic that can precisely represent the precision and ranges
|
||||
/// of both input values. This does not compute the resulting semantics for a
|
||||
/// given binary operation.
|
||||
FixedPointSemantics
|
||||
getCommonSemantics(const FixedPointSemantics &Other) const;
|
||||
|
||||
/// Return the FixedPointSemantics for an integer type.
|
||||
static FixedPointSemantics GetIntegerSemantics(unsigned Width,
|
||||
bool IsSigned) {
|
||||
return FixedPointSemantics(Width, /*Scale=*/0, IsSigned,
|
||||
/*IsSaturated=*/false,
|
||||
/*HasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned Width : 16;
|
||||
unsigned Scale : 13;
|
||||
unsigned IsSigned : 1;
|
||||
unsigned IsSaturated : 1;
|
||||
unsigned HasUnsignedPadding : 1;
|
||||
};
|
||||
|
||||
/// The APFixedPoint class works similarly to APInt/APSInt in that it is a
|
||||
/// functional replacement for a scaled integer. It is meant to replicate the
|
||||
/// fixed point types proposed in ISO/IEC JTC1 SC22 WG14 N1169. The class carries
|
||||
/// info about the fixed point type's width, sign, scale, and saturation, and
|
||||
/// provides different operations that would normally be performed on fixed point
|
||||
/// types.
|
||||
class APFixedPoint {
|
||||
public:
|
||||
APFixedPoint(const APInt &Val, const FixedPointSemantics &Sema)
|
||||
: Val(Val, !Sema.isSigned()), Sema(Sema) {
|
||||
assert(Val.getBitWidth() == Sema.getWidth() &&
|
||||
"The value should have a bit width that matches the Sema width");
|
||||
}
|
||||
|
||||
APFixedPoint(uint64_t Val, const FixedPointSemantics &Sema)
|
||||
: APFixedPoint(APInt(Sema.getWidth(), Val, Sema.isSigned()), Sema) {}
|
||||
|
||||
// Zero initialization.
|
||||
APFixedPoint(const FixedPointSemantics &Sema) : APFixedPoint(0, Sema) {}
|
||||
|
||||
APSInt getValue() const { return APSInt(Val, !Sema.isSigned()); }
|
||||
inline unsigned getWidth() const { return Sema.getWidth(); }
|
||||
inline unsigned getScale() const { return Sema.getScale(); }
|
||||
inline bool isSaturated() const { return Sema.isSaturated(); }
|
||||
inline bool isSigned() const { return Sema.isSigned(); }
|
||||
inline bool hasPadding() const { return Sema.hasUnsignedPadding(); }
|
||||
FixedPointSemantics getSemantics() const { return Sema; }
|
||||
|
||||
bool getBoolValue() const { return Val.getBoolValue(); }
|
||||
|
||||
// Convert this number to match the semantics provided. If the overflow
|
||||
// parameter is provided, set this value to true or false to indicate if this
|
||||
// operation results in an overflow.
|
||||
APFixedPoint convert(const FixedPointSemantics &DstSema,
|
||||
bool *Overflow = nullptr) const;
|
||||
|
||||
// Perform binary operations on a fixed point type. The resulting fixed point
|
||||
// value will be in the common, full precision semantics that can represent
|
||||
// the precision and ranges of both input values. See convert() for an
|
||||
// explanation of the Overflow parameter.
|
||||
APFixedPoint add(const APFixedPoint &Other, bool *Overflow = nullptr) const;
|
||||
APFixedPoint sub(const APFixedPoint &Other, bool *Overflow = nullptr) const;
|
||||
APFixedPoint mul(const APFixedPoint &Other, bool *Overflow = nullptr) const;
|
||||
APFixedPoint div(const APFixedPoint &Other, bool *Overflow = nullptr) const;
|
||||
|
||||
// Perform shift operations on a fixed point type. Unlike the other binary
|
||||
// operations, the resulting fixed point value will be in the original
|
||||
// semantic.
|
||||
APFixedPoint shl(unsigned Amt, bool *Overflow = nullptr) const;
|
||||
APFixedPoint shr(unsigned Amt, bool *Overflow = nullptr) const {
|
||||
// Right shift cannot overflow.
|
||||
if (Overflow)
|
||||
*Overflow = false;
|
||||
return APFixedPoint(Val >> Amt, Sema);
|
||||
}
|
||||
|
||||
/// Perform a unary negation (-X) on this fixed point type, taking into
|
||||
/// account saturation if applicable.
|
||||
APFixedPoint negate(bool *Overflow = nullptr) const;
|
||||
|
||||
/// Return the integral part of this fixed point number, rounded towards
|
||||
/// zero. (-2.5k -> -2)
|
||||
APSInt getIntPart() const {
|
||||
if (Val < 0 && Val != -Val) // Cover the case when we have the min val
|
||||
return -(-Val >> getScale());
|
||||
else
|
||||
return Val >> getScale();
|
||||
}
|
||||
|
||||
/// Return the integral part of this fixed point number, rounded towards
|
||||
/// zero. The value is stored into an APSInt with the provided width and sign.
|
||||
/// If the overflow parameter is provided, and the integral value is not able
|
||||
/// to be fully stored in the provided width and sign, the overflow parameter
|
||||
/// is set to true.
|
||||
///
|
||||
/// If the overflow parameter is provided, set this value to true or false to
|
||||
/// indicate if this operation results in an overflow.
|
||||
APSInt convertToInt(unsigned DstWidth, bool DstSign,
|
||||
bool *Overflow = nullptr) const;
|
||||
|
||||
void toString(SmallVectorImpl<char> &Str) const;
|
||||
std::string toString() const {
|
||||
SmallString<40> S;
|
||||
toString(S);
|
||||
return std::string(S.str());
|
||||
}
|
||||
|
||||
// If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1.
|
||||
int compare(const APFixedPoint &Other) const;
|
||||
bool operator==(const APFixedPoint &Other) const {
|
||||
return compare(Other) == 0;
|
||||
}
|
||||
bool operator!=(const APFixedPoint &Other) const {
|
||||
return compare(Other) != 0;
|
||||
}
|
||||
bool operator>(const APFixedPoint &Other) const { return compare(Other) > 0; }
|
||||
bool operator<(const APFixedPoint &Other) const { return compare(Other) < 0; }
|
||||
bool operator>=(const APFixedPoint &Other) const {
|
||||
return compare(Other) >= 0;
|
||||
}
|
||||
bool operator<=(const APFixedPoint &Other) const {
|
||||
return compare(Other) <= 0;
|
||||
}
|
||||
|
||||
static APFixedPoint getMax(const FixedPointSemantics &Sema);
|
||||
static APFixedPoint getMin(const FixedPointSemantics &Sema);
|
||||
|
||||
/// Create an APFixedPoint with a value equal to that of the provided integer,
|
||||
/// and in the same semantics as the provided target semantics. If the value
|
||||
/// is not able to fit in the specified fixed point semantics, and the
|
||||
/// overflow parameter is provided, it is set to true.
|
||||
static APFixedPoint getFromIntValue(const APSInt &Value,
|
||||
const FixedPointSemantics &DstFXSema,
|
||||
bool *Overflow = nullptr);
|
||||
|
||||
private:
|
||||
APSInt Val;
|
||||
FixedPointSemantics Sema;
|
||||
};
|
||||
|
||||
inline raw_ostream &operator<<(raw_ostream &OS, const APFixedPoint &FX) {
|
||||
OS << FX.toString();
|
||||
return OS;
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
428
lib/Support/APFixedPoint.cpp
Normal file
428
lib/Support/APFixedPoint.cpp
Normal file
@ -0,0 +1,428 @@
|
||||
//===- APFixedPoint.cpp - Fixed point constant handling ---------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
/// \file
|
||||
/// Defines the implementation for the fixed point number interface.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/APFixedPoint.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema,
|
||||
bool *Overflow) const {
|
||||
APSInt NewVal = Val;
|
||||
unsigned DstWidth = DstSema.getWidth();
|
||||
unsigned DstScale = DstSema.getScale();
|
||||
bool Upscaling = DstScale > getScale();
|
||||
if (Overflow)
|
||||
*Overflow = false;
|
||||
|
||||
if (Upscaling) {
|
||||
NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale());
|
||||
NewVal <<= (DstScale - getScale());
|
||||
} else {
|
||||
NewVal >>= (getScale() - DstScale);
|
||||
}
|
||||
|
||||
auto Mask = APInt::getBitsSetFrom(
|
||||
NewVal.getBitWidth(),
|
||||
std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth()));
|
||||
APInt Masked(NewVal & Mask);
|
||||
|
||||
// Change in the bits above the sign
|
||||
if (!(Masked == Mask || Masked == 0)) {
|
||||
// Found overflow in the bits above the sign
|
||||
if (DstSema.isSaturated())
|
||||
NewVal = NewVal.isNegative() ? Mask : ~Mask;
|
||||
else if (Overflow)
|
||||
*Overflow = true;
|
||||
}
|
||||
|
||||
// If the dst semantics are unsigned, but our value is signed and negative, we
|
||||
// clamp to zero.
|
||||
if (!DstSema.isSigned() && NewVal.isSigned() && NewVal.isNegative()) {
|
||||
// Found negative overflow for unsigned result
|
||||
if (DstSema.isSaturated())
|
||||
NewVal = 0;
|
||||
else if (Overflow)
|
||||
*Overflow = true;
|
||||
}
|
||||
|
||||
NewVal = NewVal.extOrTrunc(DstWidth);
|
||||
NewVal.setIsSigned(DstSema.isSigned());
|
||||
return APFixedPoint(NewVal, DstSema);
|
||||
}
|
||||
|
||||
int APFixedPoint::compare(const APFixedPoint &Other) const {
|
||||
APSInt ThisVal = getValue();
|
||||
APSInt OtherVal = Other.getValue();
|
||||
bool ThisSigned = Val.isSigned();
|
||||
bool OtherSigned = OtherVal.isSigned();
|
||||
unsigned OtherScale = Other.getScale();
|
||||
unsigned OtherWidth = OtherVal.getBitWidth();
|
||||
|
||||
unsigned CommonWidth = std::max(Val.getBitWidth(), OtherWidth);
|
||||
|
||||
// Prevent overflow in the event the widths are the same but the scales differ
|
||||
CommonWidth += getScale() >= OtherScale ? getScale() - OtherScale
|
||||
: OtherScale - getScale();
|
||||
|
||||
ThisVal = ThisVal.extOrTrunc(CommonWidth);
|
||||
OtherVal = OtherVal.extOrTrunc(CommonWidth);
|
||||
|
||||
unsigned CommonScale = std::max(getScale(), OtherScale);
|
||||
ThisVal = ThisVal.shl(CommonScale - getScale());
|
||||
OtherVal = OtherVal.shl(CommonScale - OtherScale);
|
||||
|
||||
if (ThisSigned && OtherSigned) {
|
||||
if (ThisVal.sgt(OtherVal))
|
||||
return 1;
|
||||
else if (ThisVal.slt(OtherVal))
|
||||
return -1;
|
||||
} else if (!ThisSigned && !OtherSigned) {
|
||||
if (ThisVal.ugt(OtherVal))
|
||||
return 1;
|
||||
else if (ThisVal.ult(OtherVal))
|
||||
return -1;
|
||||
} else if (ThisSigned && !OtherSigned) {
|
||||
if (ThisVal.isSignBitSet())
|
||||
return -1;
|
||||
else if (ThisVal.ugt(OtherVal))
|
||||
return 1;
|
||||
else if (ThisVal.ult(OtherVal))
|
||||
return -1;
|
||||
} else {
|
||||
// !ThisSigned && OtherSigned
|
||||
if (OtherVal.isSignBitSet())
|
||||
return 1;
|
||||
else if (ThisVal.ugt(OtherVal))
|
||||
return 1;
|
||||
else if (ThisVal.ult(OtherVal))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) {
|
||||
bool IsUnsigned = !Sema.isSigned();
|
||||
auto Val = APSInt::getMaxValue(Sema.getWidth(), IsUnsigned);
|
||||
if (IsUnsigned && Sema.hasUnsignedPadding())
|
||||
Val = Val.lshr(1);
|
||||
return APFixedPoint(Val, Sema);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
|
||||
auto Val = APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned());
|
||||
return APFixedPoint(Val, Sema);
|
||||
}
|
||||
|
||||
FixedPointSemantics FixedPointSemantics::getCommonSemantics(
|
||||
const FixedPointSemantics &Other) const {
|
||||
unsigned CommonScale = std::max(getScale(), Other.getScale());
|
||||
unsigned CommonWidth =
|
||||
std::max(getIntegralBits(), Other.getIntegralBits()) + CommonScale;
|
||||
|
||||
bool ResultIsSigned = isSigned() || Other.isSigned();
|
||||
bool ResultIsSaturated = isSaturated() || Other.isSaturated();
|
||||
bool ResultHasUnsignedPadding = false;
|
||||
if (!ResultIsSigned) {
|
||||
// Both are unsigned.
|
||||
ResultHasUnsignedPadding = hasUnsignedPadding() &&
|
||||
Other.hasUnsignedPadding() && !ResultIsSaturated;
|
||||
}
|
||||
|
||||
// If the result is signed, add an extra bit for the sign. Otherwise, if it is
|
||||
// unsigned and has unsigned padding, we only need to add the extra padding
|
||||
// bit back if we are not saturating.
|
||||
if (ResultIsSigned || ResultHasUnsignedPadding)
|
||||
CommonWidth++;
|
||||
|
||||
return FixedPointSemantics(CommonWidth, CommonScale, ResultIsSigned,
|
||||
ResultIsSaturated, ResultHasUnsignedPadding);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::add(const APFixedPoint &Other,
|
||||
bool *Overflow) const {
|
||||
auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
|
||||
APFixedPoint ConvertedThis = convert(CommonFXSema);
|
||||
APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
|
||||
APSInt ThisVal = ConvertedThis.getValue();
|
||||
APSInt OtherVal = ConvertedOther.getValue();
|
||||
bool Overflowed = false;
|
||||
|
||||
APSInt Result;
|
||||
if (CommonFXSema.isSaturated()) {
|
||||
Result = CommonFXSema.isSigned() ? ThisVal.sadd_sat(OtherVal)
|
||||
: ThisVal.uadd_sat(OtherVal);
|
||||
} else {
|
||||
Result = ThisVal.isSigned() ? ThisVal.sadd_ov(OtherVal, Overflowed)
|
||||
: ThisVal.uadd_ov(OtherVal, Overflowed);
|
||||
}
|
||||
|
||||
if (Overflow)
|
||||
*Overflow = Overflowed;
|
||||
|
||||
return APFixedPoint(Result, CommonFXSema);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::sub(const APFixedPoint &Other,
|
||||
bool *Overflow) const {
|
||||
auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
|
||||
APFixedPoint ConvertedThis = convert(CommonFXSema);
|
||||
APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
|
||||
APSInt ThisVal = ConvertedThis.getValue();
|
||||
APSInt OtherVal = ConvertedOther.getValue();
|
||||
bool Overflowed = false;
|
||||
|
||||
APSInt Result;
|
||||
if (CommonFXSema.isSaturated()) {
|
||||
Result = CommonFXSema.isSigned() ? ThisVal.ssub_sat(OtherVal)
|
||||
: ThisVal.usub_sat(OtherVal);
|
||||
} else {
|
||||
Result = ThisVal.isSigned() ? ThisVal.ssub_ov(OtherVal, Overflowed)
|
||||
: ThisVal.usub_ov(OtherVal, Overflowed);
|
||||
}
|
||||
|
||||
if (Overflow)
|
||||
*Overflow = Overflowed;
|
||||
|
||||
return APFixedPoint(Result, CommonFXSema);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::mul(const APFixedPoint &Other,
|
||||
bool *Overflow) const {
|
||||
auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
|
||||
APFixedPoint ConvertedThis = convert(CommonFXSema);
|
||||
APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
|
||||
APSInt ThisVal = ConvertedThis.getValue();
|
||||
APSInt OtherVal = ConvertedOther.getValue();
|
||||
bool Overflowed = false;
|
||||
|
||||
// Widen the LHS and RHS so we can perform a full multiplication.
|
||||
unsigned Wide = CommonFXSema.getWidth() * 2;
|
||||
if (CommonFXSema.isSigned()) {
|
||||
ThisVal = ThisVal.sextOrSelf(Wide);
|
||||
OtherVal = OtherVal.sextOrSelf(Wide);
|
||||
} else {
|
||||
ThisVal = ThisVal.zextOrSelf(Wide);
|
||||
OtherVal = OtherVal.zextOrSelf(Wide);
|
||||
}
|
||||
|
||||
// Perform the full multiplication and downscale to get the same scale.
|
||||
//
|
||||
// Note that the right shifts here perform an implicit downwards rounding.
|
||||
// This rounding could discard bits that would technically place the result
|
||||
// outside the representable range. We interpret the spec as allowing us to
|
||||
// perform the rounding step first, avoiding the overflow case that would
|
||||
// arise.
|
||||
APSInt Result;
|
||||
if (CommonFXSema.isSigned())
|
||||
Result = ThisVal.smul_ov(OtherVal, Overflowed)
|
||||
.ashr(CommonFXSema.getScale());
|
||||
else
|
||||
Result = ThisVal.umul_ov(OtherVal, Overflowed)
|
||||
.lshr(CommonFXSema.getScale());
|
||||
assert(!Overflowed && "Full multiplication cannot overflow!");
|
||||
Result.setIsSigned(CommonFXSema.isSigned());
|
||||
|
||||
// If our result lies outside of the representative range of the common
|
||||
// semantic, we either have overflow or saturation.
|
||||
APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()
|
||||
.extOrTrunc(Wide);
|
||||
APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()
|
||||
.extOrTrunc(Wide);
|
||||
if (CommonFXSema.isSaturated()) {
|
||||
if (Result < Min)
|
||||
Result = Min;
|
||||
else if (Result > Max)
|
||||
Result = Max;
|
||||
} else
|
||||
Overflowed = Result < Min || Result > Max;
|
||||
|
||||
if (Overflow)
|
||||
*Overflow = Overflowed;
|
||||
|
||||
return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),
|
||||
CommonFXSema);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::div(const APFixedPoint &Other,
|
||||
bool *Overflow) const {
|
||||
auto CommonFXSema = Sema.getCommonSemantics(Other.getSemantics());
|
||||
APFixedPoint ConvertedThis = convert(CommonFXSema);
|
||||
APFixedPoint ConvertedOther = Other.convert(CommonFXSema);
|
||||
APSInt ThisVal = ConvertedThis.getValue();
|
||||
APSInt OtherVal = ConvertedOther.getValue();
|
||||
bool Overflowed = false;
|
||||
|
||||
// Widen the LHS and RHS so we can perform a full division.
|
||||
unsigned Wide = CommonFXSema.getWidth() * 2;
|
||||
if (CommonFXSema.isSigned()) {
|
||||
ThisVal = ThisVal.sextOrSelf(Wide);
|
||||
OtherVal = OtherVal.sextOrSelf(Wide);
|
||||
} else {
|
||||
ThisVal = ThisVal.zextOrSelf(Wide);
|
||||
OtherVal = OtherVal.zextOrSelf(Wide);
|
||||
}
|
||||
|
||||
// Upscale to compensate for the loss of precision from division, and
|
||||
// perform the full division.
|
||||
ThisVal = ThisVal.shl(CommonFXSema.getScale());
|
||||
APSInt Result;
|
||||
if (CommonFXSema.isSigned()) {
|
||||
APInt Rem;
|
||||
APInt::sdivrem(ThisVal, OtherVal, Result, Rem);
|
||||
// If the quotient is negative and the remainder is nonzero, round
|
||||
// towards negative infinity by subtracting epsilon from the result.
|
||||
if (ThisVal.isNegative() != OtherVal.isNegative() && !Rem.isNullValue())
|
||||
Result = Result - 1;
|
||||
} else
|
||||
Result = ThisVal.udiv(OtherVal);
|
||||
Result.setIsSigned(CommonFXSema.isSigned());
|
||||
|
||||
// If our result lies outside of the representative range of the common
|
||||
// semantic, we either have overflow or saturation.
|
||||
APSInt Max = APFixedPoint::getMax(CommonFXSema).getValue()
|
||||
.extOrTrunc(Wide);
|
||||
APSInt Min = APFixedPoint::getMin(CommonFXSema).getValue()
|
||||
.extOrTrunc(Wide);
|
||||
if (CommonFXSema.isSaturated()) {
|
||||
if (Result < Min)
|
||||
Result = Min;
|
||||
else if (Result > Max)
|
||||
Result = Max;
|
||||
} else
|
||||
Overflowed = Result < Min || Result > Max;
|
||||
|
||||
if (Overflow)
|
||||
*Overflow = Overflowed;
|
||||
|
||||
return APFixedPoint(Result.sextOrTrunc(CommonFXSema.getWidth()),
|
||||
CommonFXSema);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const {
|
||||
APSInt ThisVal = Val;
|
||||
bool Overflowed = false;
|
||||
|
||||
// Widen the LHS.
|
||||
unsigned Wide = Sema.getWidth() * 2;
|
||||
if (Sema.isSigned())
|
||||
ThisVal = ThisVal.sextOrSelf(Wide);
|
||||
else
|
||||
ThisVal = ThisVal.zextOrSelf(Wide);
|
||||
|
||||
// Clamp the shift amount at the original width, and perform the shift.
|
||||
Amt = std::min(Amt, ThisVal.getBitWidth());
|
||||
APSInt Result = ThisVal << Amt;
|
||||
Result.setIsSigned(Sema.isSigned());
|
||||
|
||||
// If our result lies outside of the representative range of the
|
||||
// semantic, we either have overflow or saturation.
|
||||
APSInt Max = APFixedPoint::getMax(Sema).getValue().extOrTrunc(Wide);
|
||||
APSInt Min = APFixedPoint::getMin(Sema).getValue().extOrTrunc(Wide);
|
||||
if (Sema.isSaturated()) {
|
||||
if (Result < Min)
|
||||
Result = Min;
|
||||
else if (Result > Max)
|
||||
Result = Max;
|
||||
} else
|
||||
Overflowed = Result < Min || Result > Max;
|
||||
|
||||
if (Overflow)
|
||||
*Overflow = Overflowed;
|
||||
|
||||
return APFixedPoint(Result.sextOrTrunc(Sema.getWidth()), Sema);
|
||||
}
|
||||
|
||||
void APFixedPoint::toString(SmallVectorImpl<char> &Str) const {
|
||||
APSInt Val = getValue();
|
||||
unsigned Scale = getScale();
|
||||
|
||||
if (Val.isSigned() && Val.isNegative() && Val != -Val) {
|
||||
Val = -Val;
|
||||
Str.push_back('-');
|
||||
}
|
||||
|
||||
APSInt IntPart = Val >> Scale;
|
||||
|
||||
// Add 4 digits to hold the value after multiplying 10 (the radix)
|
||||
unsigned Width = Val.getBitWidth() + 4;
|
||||
APInt FractPart = Val.zextOrTrunc(Scale).zext(Width);
|
||||
APInt FractPartMask = APInt::getAllOnesValue(Scale).zext(Width);
|
||||
APInt RadixInt = APInt(Width, 10);
|
||||
|
||||
IntPart.toString(Str, /*Radix=*/10);
|
||||
Str.push_back('.');
|
||||
do {
|
||||
(FractPart * RadixInt)
|
||||
.lshr(Scale)
|
||||
.toString(Str, /*Radix=*/10, Val.isSigned());
|
||||
FractPart = (FractPart * RadixInt) & FractPartMask;
|
||||
} while (FractPart != 0);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::negate(bool *Overflow) const {
|
||||
if (!isSaturated()) {
|
||||
if (Overflow)
|
||||
*Overflow =
|
||||
(!isSigned() && Val != 0) || (isSigned() && Val.isMinSignedValue());
|
||||
return APFixedPoint(-Val, Sema);
|
||||
}
|
||||
|
||||
// We never overflow for saturation
|
||||
if (Overflow)
|
||||
*Overflow = false;
|
||||
|
||||
if (isSigned())
|
||||
return Val.isMinSignedValue() ? getMax(Sema) : APFixedPoint(-Val, Sema);
|
||||
else
|
||||
return APFixedPoint(Sema);
|
||||
}
|
||||
|
||||
APSInt APFixedPoint::convertToInt(unsigned DstWidth, bool DstSign,
|
||||
bool *Overflow) const {
|
||||
APSInt Result = getIntPart();
|
||||
unsigned SrcWidth = getWidth();
|
||||
|
||||
APSInt DstMin = APSInt::getMinValue(DstWidth, !DstSign);
|
||||
APSInt DstMax = APSInt::getMaxValue(DstWidth, !DstSign);
|
||||
|
||||
if (SrcWidth < DstWidth) {
|
||||
Result = Result.extend(DstWidth);
|
||||
} else if (SrcWidth > DstWidth) {
|
||||
DstMin = DstMin.extend(SrcWidth);
|
||||
DstMax = DstMax.extend(SrcWidth);
|
||||
}
|
||||
|
||||
if (Overflow) {
|
||||
if (Result.isSigned() && !DstSign) {
|
||||
*Overflow = Result.isNegative() || Result.ugt(DstMax);
|
||||
} else if (Result.isUnsigned() && DstSign) {
|
||||
*Overflow = Result.ugt(DstMax);
|
||||
} else {
|
||||
*Overflow = Result < DstMin || Result > DstMax;
|
||||
}
|
||||
}
|
||||
|
||||
Result.setIsSigned(DstSign);
|
||||
return Result.extOrTrunc(DstWidth);
|
||||
}
|
||||
|
||||
APFixedPoint APFixedPoint::getFromIntValue(const APSInt &Value,
|
||||
const FixedPointSemantics &DstFXSema,
|
||||
bool *Overflow) {
|
||||
FixedPointSemantics IntFXSema = FixedPointSemantics::GetIntegerSemantics(
|
||||
Value.getBitWidth(), Value.isSigned());
|
||||
return APFixedPoint(Value, IntFXSema).convert(DstFXSema, Overflow);
|
||||
}
|
||||
|
||||
} // namespace clang
|
@ -67,6 +67,7 @@ add_llvm_component_library(LLVMSupport
|
||||
ABIBreak.cpp
|
||||
ARMTargetParser.cpp
|
||||
AMDGPUMetadata.cpp
|
||||
APFixedPoint.cpp
|
||||
APFloat.cpp
|
||||
APInt.cpp
|
||||
APSInt.cpp
|
||||
|
644
unittests/ADT/APFixedPointTest.cpp
Normal file
644
unittests/ADT/APFixedPointTest.cpp
Normal file
@ -0,0 +1,644 @@
|
||||
//===- unittests/ADT/FixedPointTest.cpp -- fixed point number tests -----===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/APFixedPoint.h"
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using llvm::APFixedPoint;
|
||||
using llvm::FixedPointSemantics;
|
||||
using llvm::APInt;
|
||||
using llvm::APSInt;
|
||||
|
||||
namespace {
|
||||
|
||||
FixedPointSemantics Saturated(FixedPointSemantics Sema) {
|
||||
Sema.setSaturated(true);
|
||||
return Sema;
|
||||
}
|
||||
|
||||
FixedPointSemantics getSAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/16, /*scale=*/7, /*isSigned=*/true,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/32, /*scale=*/15, /*isSigned=*/true,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getLAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/64, /*scale=*/31, /*isSigned=*/true,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getSFractSema() {
|
||||
return FixedPointSemantics(/*width=*/8, /*scale=*/7, /*isSigned=*/true,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getFractSema() {
|
||||
return FixedPointSemantics(/*width=*/16, /*scale=*/15, /*isSigned=*/true,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getLFractSema() {
|
||||
return FixedPointSemantics(/*width=*/32, /*scale=*/31, /*isSigned=*/true,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getUSAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/16, /*scale=*/8, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getUAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/32, /*scale=*/16, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getULAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/64, /*scale=*/32, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getUSFractSema() {
|
||||
return FixedPointSemantics(/*width=*/8, /*scale=*/8, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getUFractSema() {
|
||||
return FixedPointSemantics(/*width=*/16, /*scale=*/16, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getULFractSema() {
|
||||
return FixedPointSemantics(/*width=*/32, /*scale=*/32, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/false);
|
||||
}
|
||||
|
||||
FixedPointSemantics getPadUSAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/16, /*scale=*/7, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/true);
|
||||
}
|
||||
|
||||
FixedPointSemantics getPadUAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/32, /*scale=*/15, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/true);
|
||||
}
|
||||
|
||||
FixedPointSemantics getPadULAccumSema() {
|
||||
return FixedPointSemantics(/*width=*/64, /*scale=*/31, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/true);
|
||||
}
|
||||
|
||||
FixedPointSemantics getPadUSFractSema() {
|
||||
return FixedPointSemantics(/*width=*/8, /*scale=*/7, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/true);
|
||||
}
|
||||
|
||||
FixedPointSemantics getPadUFractSema() {
|
||||
return FixedPointSemantics(/*width=*/16, /*scale=*/15, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/true);
|
||||
}
|
||||
|
||||
FixedPointSemantics getPadULFractSema() {
|
||||
return FixedPointSemantics(/*width=*/32, /*scale=*/31, /*isSigned=*/false,
|
||||
/*isSaturated=*/false,
|
||||
/*hasUnsignedPadding=*/true);
|
||||
}
|
||||
|
||||
void CheckUnpaddedMax(const FixedPointSemantics &Sema) {
|
||||
ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(),
|
||||
APSInt::getMaxValue(Sema.getWidth(), !Sema.isSigned()));
|
||||
}
|
||||
|
||||
void CheckPaddedMax(const FixedPointSemantics &Sema) {
|
||||
ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(),
|
||||
APSInt::getMaxValue(Sema.getWidth(), !Sema.isSigned()) >> 1);
|
||||
}
|
||||
|
||||
void CheckMin(const FixedPointSemantics &Sema) {
|
||||
ASSERT_EQ(APFixedPoint::getMin(Sema).getValue(),
|
||||
APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned()));
|
||||
}
|
||||
|
||||
TEST(FixedPointTest, getMax) {
|
||||
CheckUnpaddedMax(getSAccumSema());
|
||||
CheckUnpaddedMax(getAccumSema());
|
||||
CheckUnpaddedMax(getLAccumSema());
|
||||
CheckUnpaddedMax(getUSAccumSema());
|
||||
CheckUnpaddedMax(getUAccumSema());
|
||||
CheckUnpaddedMax(getULAccumSema());
|
||||
CheckUnpaddedMax(getSFractSema());
|
||||
CheckUnpaddedMax(getFractSema());
|
||||
CheckUnpaddedMax(getLFractSema());
|
||||
CheckUnpaddedMax(getUSFractSema());
|
||||
CheckUnpaddedMax(getUFractSema());
|
||||
CheckUnpaddedMax(getULFractSema());
|
||||
|
||||
CheckPaddedMax(getPadUSAccumSema());
|
||||
CheckPaddedMax(getPadUAccumSema());
|
||||
CheckPaddedMax(getPadULAccumSema());
|
||||
CheckPaddedMax(getPadUSFractSema());
|
||||
CheckPaddedMax(getPadUFractSema());
|
||||
CheckPaddedMax(getPadULFractSema());
|
||||
}
|
||||
|
||||
TEST(FixedPointTest, getMin) {
|
||||
CheckMin(getSAccumSema());
|
||||
CheckMin(getAccumSema());
|
||||
CheckMin(getLAccumSema());
|
||||
CheckMin(getUSAccumSema());
|
||||
CheckMin(getUAccumSema());
|
||||
CheckMin(getULAccumSema());
|
||||
CheckMin(getSFractSema());
|
||||
CheckMin(getFractSema());
|
||||
CheckMin(getLFractSema());
|
||||
CheckMin(getUSFractSema());
|
||||
CheckMin(getUFractSema());
|
||||
CheckMin(getULFractSema());
|
||||
|
||||
CheckMin(getPadUSAccumSema());
|
||||
CheckMin(getPadUAccumSema());
|
||||
CheckMin(getPadULAccumSema());
|
||||
CheckMin(getPadUSFractSema());
|
||||
CheckMin(getPadUFractSema());
|
||||
CheckMin(getPadULFractSema());
|
||||
}
|
||||
|
||||
void CheckIntPart(const FixedPointSemantics &Sema, int64_t IntPart) {
|
||||
unsigned Scale = Sema.getScale();
|
||||
|
||||
// Value with a fraction
|
||||
APFixedPoint ValWithFract(APInt(Sema.getWidth(),
|
||||
(IntPart << Scale) + (1ULL << (Scale - 1)),
|
||||
Sema.isSigned()),
|
||||
Sema);
|
||||
ASSERT_EQ(ValWithFract.getIntPart(), IntPart);
|
||||
|
||||
// Just fraction
|
||||
APFixedPoint JustFract(
|
||||
APInt(Sema.getWidth(), (1ULL << (Scale - 1)), Sema.isSigned()), Sema);
|
||||
ASSERT_EQ(JustFract.getIntPart(), 0);
|
||||
|
||||
// Whole number
|
||||
APFixedPoint WholeNum(
|
||||
APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema);
|
||||
ASSERT_EQ(WholeNum.getIntPart(), IntPart);
|
||||
|
||||
// Negative
|
||||
if (Sema.isSigned()) {
|
||||
APFixedPoint Negative(
|
||||
APInt(Sema.getWidth(), (IntPart << Scale), Sema.isSigned()), Sema);
|
||||
ASSERT_EQ(Negative.getIntPart(), IntPart);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckIntPartMin(const FixedPointSemantics &Sema, int64_t Expected) {
|
||||
ASSERT_EQ(APFixedPoint::getMin(Sema).getIntPart(), Expected);
|
||||
}
|
||||
|
||||
void CheckIntPartMax(const FixedPointSemantics &Sema, uint64_t Expected) {
|
||||
ASSERT_EQ(APFixedPoint::getMax(Sema).getIntPart(), Expected);
|
||||
}
|
||||
|
||||
TEST(FixedPoint, getIntPart) {
|
||||
// Normal values
|
||||
CheckIntPart(getSAccumSema(), 2);
|
||||
CheckIntPart(getAccumSema(), 2);
|
||||
CheckIntPart(getLAccumSema(), 2);
|
||||
CheckIntPart(getUSAccumSema(), 2);
|
||||
CheckIntPart(getUAccumSema(), 2);
|
||||
CheckIntPart(getULAccumSema(), 2);
|
||||
|
||||
// Zero
|
||||
CheckIntPart(getSAccumSema(), 0);
|
||||
CheckIntPart(getAccumSema(), 0);
|
||||
CheckIntPart(getLAccumSema(), 0);
|
||||
CheckIntPart(getUSAccumSema(), 0);
|
||||
CheckIntPart(getUAccumSema(), 0);
|
||||
CheckIntPart(getULAccumSema(), 0);
|
||||
|
||||
CheckIntPart(getSFractSema(), 0);
|
||||
CheckIntPart(getFractSema(), 0);
|
||||
CheckIntPart(getLFractSema(), 0);
|
||||
CheckIntPart(getUSFractSema(), 0);
|
||||
CheckIntPart(getUFractSema(), 0);
|
||||
CheckIntPart(getULFractSema(), 0);
|
||||
|
||||
// Min
|
||||
CheckIntPartMin(getSAccumSema(), -256);
|
||||
CheckIntPartMin(getAccumSema(), -65536);
|
||||
CheckIntPartMin(getLAccumSema(), -4294967296);
|
||||
|
||||
CheckIntPartMin(getSFractSema(), -1);
|
||||
CheckIntPartMin(getFractSema(), -1);
|
||||
CheckIntPartMin(getLFractSema(), -1);
|
||||
|
||||
// Max
|
||||
CheckIntPartMax(getSAccumSema(), 255);
|
||||
CheckIntPartMax(getAccumSema(), 65535);
|
||||
CheckIntPartMax(getLAccumSema(), 4294967295);
|
||||
CheckIntPartMax(getUSAccumSema(), 255);
|
||||
CheckIntPartMax(getUAccumSema(), 65535);
|
||||
CheckIntPartMax(getULAccumSema(), 4294967295);
|
||||
|
||||
CheckIntPartMax(getSFractSema(), 0);
|
||||
CheckIntPartMax(getFractSema(), 0);
|
||||
CheckIntPartMax(getLFractSema(), 0);
|
||||
CheckIntPartMax(getUSFractSema(), 0);
|
||||
CheckIntPartMax(getUFractSema(), 0);
|
||||
CheckIntPartMax(getULFractSema(), 0);
|
||||
|
||||
// Padded
|
||||
// Normal Values
|
||||
CheckIntPart(getPadUSAccumSema(), 2);
|
||||
CheckIntPart(getPadUAccumSema(), 2);
|
||||
CheckIntPart(getPadULAccumSema(), 2);
|
||||
|
||||
// Zero
|
||||
CheckIntPart(getPadUSAccumSema(), 0);
|
||||
CheckIntPart(getPadUAccumSema(), 0);
|
||||
CheckIntPart(getPadULAccumSema(), 0);
|
||||
|
||||
CheckIntPart(getPadUSFractSema(), 0);
|
||||
CheckIntPart(getPadUFractSema(), 0);
|
||||
CheckIntPart(getPadULFractSema(), 0);
|
||||
|
||||
// Max
|
||||
CheckIntPartMax(getPadUSAccumSema(), 255);
|
||||
CheckIntPartMax(getPadUAccumSema(), 65535);
|
||||
CheckIntPartMax(getPadULAccumSema(), 4294967295);
|
||||
|
||||
CheckIntPartMax(getPadUSFractSema(), 0);
|
||||
CheckIntPartMax(getPadUFractSema(), 0);
|
||||
CheckIntPartMax(getPadULFractSema(), 0);
|
||||
}
|
||||
|
||||
TEST(FixedPoint, compare) {
|
||||
// Equality
|
||||
// With fractional part (2.5)
|
||||
// Across sizes
|
||||
ASSERT_EQ(APFixedPoint(320, getSAccumSema()),
|
||||
APFixedPoint(81920, getAccumSema()));
|
||||
ASSERT_EQ(APFixedPoint(320, getSAccumSema()),
|
||||
APFixedPoint(5368709120, getLAccumSema()));
|
||||
ASSERT_EQ(APFixedPoint(0, getSAccumSema()), APFixedPoint(0, getLAccumSema()));
|
||||
|
||||
// Across types (0.5)
|
||||
ASSERT_EQ(APFixedPoint(64, getSAccumSema()),
|
||||
APFixedPoint(64, getSFractSema()));
|
||||
ASSERT_EQ(APFixedPoint(16384, getAccumSema()),
|
||||
APFixedPoint(16384, getFractSema()));
|
||||
ASSERT_EQ(APFixedPoint(1073741824, getLAccumSema()),
|
||||
APFixedPoint(1073741824, getLFractSema()));
|
||||
|
||||
// Across widths and types (0.5)
|
||||
ASSERT_EQ(APFixedPoint(64, getSAccumSema()),
|
||||
APFixedPoint(16384, getFractSema()));
|
||||
ASSERT_EQ(APFixedPoint(64, getSAccumSema()),
|
||||
APFixedPoint(1073741824, getLFractSema()));
|
||||
|
||||
// Across saturation
|
||||
ASSERT_EQ(APFixedPoint(320, getSAccumSema()),
|
||||
APFixedPoint(81920, Saturated(getAccumSema())));
|
||||
|
||||
// Across signs
|
||||
ASSERT_EQ(APFixedPoint(320, getSAccumSema()),
|
||||
APFixedPoint(640, getUSAccumSema()));
|
||||
ASSERT_EQ(APFixedPoint(-320, getSAccumSema()),
|
||||
APFixedPoint(-81920, getAccumSema()));
|
||||
|
||||
// Across padding
|
||||
ASSERT_EQ(APFixedPoint(320, getSAccumSema()),
|
||||
APFixedPoint(320, getPadUSAccumSema()));
|
||||
ASSERT_EQ(APFixedPoint(640, getUSAccumSema()),
|
||||
APFixedPoint(320, getPadUSAccumSema()));
|
||||
|
||||
// Less than
|
||||
ASSERT_LT(APFixedPoint(-1, getSAccumSema()), APFixedPoint(0, getAccumSema()));
|
||||
ASSERT_LT(APFixedPoint(-1, getSAccumSema()),
|
||||
APFixedPoint(0, getUAccumSema()));
|
||||
ASSERT_LT(APFixedPoint(0, getSAccumSema()), APFixedPoint(1, getAccumSema()));
|
||||
ASSERT_LT(APFixedPoint(0, getSAccumSema()), APFixedPoint(1, getUAccumSema()));
|
||||
ASSERT_LT(APFixedPoint(0, getUSAccumSema()), APFixedPoint(1, getAccumSema()));
|
||||
ASSERT_LT(APFixedPoint(0, getUSAccumSema()),
|
||||
APFixedPoint(1, getUAccumSema()));
|
||||
|
||||
// Greater than
|
||||
ASSERT_GT(APFixedPoint(0, getAccumSema()), APFixedPoint(-1, getSAccumSema()));
|
||||
ASSERT_GT(APFixedPoint(0, getUAccumSema()),
|
||||
APFixedPoint(-1, getSAccumSema()));
|
||||
ASSERT_GT(APFixedPoint(1, getAccumSema()), APFixedPoint(0, getSAccumSema()));
|
||||
ASSERT_GT(APFixedPoint(1, getUAccumSema()), APFixedPoint(0, getSAccumSema()));
|
||||
ASSERT_GT(APFixedPoint(1, getAccumSema()), APFixedPoint(0, getUSAccumSema()));
|
||||
ASSERT_GT(APFixedPoint(1, getUAccumSema()),
|
||||
APFixedPoint(0, getUSAccumSema()));
|
||||
}
|
||||
|
||||
// Check that a fixed point value in one sema is the same in another sema
|
||||
void CheckUnsaturatedConversion(FixedPointSemantics Src,
|
||||
FixedPointSemantics Dst, int64_t TestVal) {
|
||||
int64_t ScaledVal = TestVal;
|
||||
bool IsNegative = ScaledVal < 0;
|
||||
if (IsNegative)
|
||||
ScaledVal = -ScaledVal;
|
||||
|
||||
if (Dst.getScale() > Src.getScale()) {
|
||||
ScaledVal <<= (Dst.getScale() - Src.getScale());
|
||||
} else {
|
||||
ScaledVal >>= (Src.getScale() - Dst.getScale());
|
||||
}
|
||||
|
||||
if (IsNegative)
|
||||
ScaledVal = -ScaledVal;
|
||||
|
||||
APFixedPoint Fixed(TestVal, Src);
|
||||
APFixedPoint Expected(ScaledVal, Dst);
|
||||
ASSERT_EQ(Fixed.convert(Dst), Expected);
|
||||
}
|
||||
|
||||
// Check the value in a given fixed point sema overflows to the saturated min
|
||||
// for another sema
|
||||
void CheckSaturatedConversionMin(FixedPointSemantics Src,
|
||||
FixedPointSemantics Dst, int64_t TestVal) {
|
||||
APFixedPoint Fixed(TestVal, Src);
|
||||
ASSERT_EQ(Fixed.convert(Dst), APFixedPoint::getMin(Dst));
|
||||
}
|
||||
|
||||
// Check the value in a given fixed point sema overflows to the saturated max
|
||||
// for another sema
|
||||
void CheckSaturatedConversionMax(FixedPointSemantics Src,
|
||||
FixedPointSemantics Dst, int64_t TestVal) {
|
||||
APFixedPoint Fixed(TestVal, Src);
|
||||
ASSERT_EQ(Fixed.convert(Dst), APFixedPoint::getMax(Dst));
|
||||
}
|
||||
|
||||
// Check one signed _Accum sema converted to other sema for different values.
|
||||
void CheckSignedAccumConversionsAgainstOthers(FixedPointSemantics Src,
|
||||
int64_t OneVal) {
|
||||
int64_t NormalVal = (OneVal * 2) + (OneVal / 2); // 2.5
|
||||
int64_t HalfVal = (OneVal / 2); // 0.5
|
||||
|
||||
// +Accums to Accums
|
||||
CheckUnsaturatedConversion(Src, getSAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getLAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getUSAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getUAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getULAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUSAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getPadULAccumSema(), NormalVal);
|
||||
|
||||
// -Accums to Accums
|
||||
CheckUnsaturatedConversion(Src, getSAccumSema(), -NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getAccumSema(), -NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getLAccumSema(), -NormalVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getUSAccumSema()), -NormalVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getUAccumSema()), -NormalVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getULAccumSema()), -NormalVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getPadUSAccumSema()), -NormalVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getPadUAccumSema()), -NormalVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getPadULAccumSema()), -NormalVal);
|
||||
|
||||
// +Accums to Fracts
|
||||
CheckUnsaturatedConversion(Src, getSFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getLFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getUSFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getUFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getULFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUSFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getPadULFractSema(), HalfVal);
|
||||
|
||||
// -Accums to Fracts
|
||||
CheckUnsaturatedConversion(Src, getSFractSema(), -HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getFractSema(), -HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getLFractSema(), -HalfVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getUSFractSema()), -HalfVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getUFractSema()), -HalfVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getULFractSema()), -HalfVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getPadUSFractSema()), -HalfVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getPadUFractSema()), -HalfVal);
|
||||
CheckSaturatedConversionMin(Src, Saturated(getPadULFractSema()), -HalfVal);
|
||||
|
||||
// 0 to Accums
|
||||
CheckUnsaturatedConversion(Src, getSAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getLAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getUSAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getUAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getULAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getPadUSAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getPadUAccumSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getPadULAccumSema(), 0);
|
||||
|
||||
// 0 to Fracts
|
||||
CheckUnsaturatedConversion(Src, getSFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getLFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getUSFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getUFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getULFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getPadUSFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getPadUFractSema(), 0);
|
||||
CheckUnsaturatedConversion(Src, getPadULFractSema(), 0);
|
||||
}
|
||||
|
||||
// Check one unsigned _Accum sema converted to other sema for different
|
||||
// values.
|
||||
void CheckUnsignedAccumConversionsAgainstOthers(FixedPointSemantics Src,
|
||||
int64_t OneVal) {
|
||||
int64_t NormalVal = (OneVal * 2) + (OneVal / 2); // 2.5
|
||||
int64_t HalfVal = (OneVal / 2); // 0.5
|
||||
|
||||
// +UAccums to Accums
|
||||
CheckUnsaturatedConversion(Src, getSAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getLAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getUSAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getUAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getULAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUSAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUAccumSema(), NormalVal);
|
||||
CheckUnsaturatedConversion(Src, getPadULAccumSema(), NormalVal);
|
||||
|
||||
// +UAccums to Fracts
|
||||
CheckUnsaturatedConversion(Src, getSFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getLFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getUSFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getUFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getULFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUSFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getPadUFractSema(), HalfVal);
|
||||
CheckUnsaturatedConversion(Src, getPadULFractSema(), HalfVal);
|
||||
}
|
||||
|
||||
TEST(FixedPoint, AccumConversions) {
|
||||
// Normal conversions
|
||||
CheckSignedAccumConversionsAgainstOthers(getSAccumSema(), 128);
|
||||
CheckUnsignedAccumConversionsAgainstOthers(getUSAccumSema(), 256);
|
||||
CheckSignedAccumConversionsAgainstOthers(getAccumSema(), 32768);
|
||||
CheckUnsignedAccumConversionsAgainstOthers(getUAccumSema(), 65536);
|
||||
CheckSignedAccumConversionsAgainstOthers(getLAccumSema(), 2147483648);
|
||||
CheckUnsignedAccumConversionsAgainstOthers(getULAccumSema(), 4294967296);
|
||||
|
||||
CheckUnsignedAccumConversionsAgainstOthers(getPadUSAccumSema(), 128);
|
||||
CheckUnsignedAccumConversionsAgainstOthers(getPadUAccumSema(), 32768);
|
||||
CheckUnsignedAccumConversionsAgainstOthers(getPadULAccumSema(), 2147483648);
|
||||
}
|
||||
|
||||
TEST(FixedPoint, AccumConversionOverflow) {
|
||||
// To SAccum max limit (65536)
|
||||
CheckSaturatedConversionMax(getLAccumSema(), Saturated(getAccumSema()),
|
||||
140737488355328);
|
||||
CheckSaturatedConversionMax(getLAccumSema(), Saturated(getUAccumSema()),
|
||||
140737488355328);
|
||||
CheckSaturatedConversionMax(getLAccumSema(), Saturated(getPadUAccumSema()),
|
||||
140737488355328);
|
||||
CheckSaturatedConversionMax(getULAccumSema(), Saturated(getAccumSema()),
|
||||
281474976710656);
|
||||
CheckSaturatedConversionMax(getULAccumSema(), Saturated(getUAccumSema()),
|
||||
281474976710656);
|
||||
CheckSaturatedConversionMax(getULAccumSema(), Saturated(getPadUAccumSema()),
|
||||
281474976710656);
|
||||
|
||||
CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getAccumSema()),
|
||||
140737488355328);
|
||||
CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getUAccumSema()),
|
||||
140737488355328);
|
||||
CheckSaturatedConversionMax(getPadULAccumSema(),
|
||||
Saturated(getPadUAccumSema()), 140737488355328);
|
||||
|
||||
// To SAccum min limit (-65536)
|
||||
CheckSaturatedConversionMin(getLAccumSema(), Saturated(getAccumSema()),
|
||||
-140737488355328);
|
||||
CheckSaturatedConversionMin(getLAccumSema(), Saturated(getUAccumSema()),
|
||||
-140737488355328);
|
||||
CheckSaturatedConversionMin(getLAccumSema(), Saturated(getPadUAccumSema()),
|
||||
-140737488355328);
|
||||
}
|
||||
|
||||
TEST(FixedPoint, SAccumConversionOverflow) {
|
||||
// To SAccum max limit (256)
|
||||
CheckSaturatedConversionMax(getAccumSema(), Saturated(getSAccumSema()),
|
||||
8388608);
|
||||
CheckSaturatedConversionMax(getAccumSema(), Saturated(getUSAccumSema()),
|
||||
8388608);
|
||||
CheckSaturatedConversionMax(getAccumSema(), Saturated(getPadUSAccumSema()),
|
||||
8388608);
|
||||
CheckSaturatedConversionMax(getUAccumSema(), Saturated(getSAccumSema()),
|
||||
16777216);
|
||||
CheckSaturatedConversionMax(getUAccumSema(), Saturated(getUSAccumSema()),
|
||||
16777216);
|
||||
CheckSaturatedConversionMax(getUAccumSema(), Saturated(getPadUSAccumSema()),
|
||||
16777216);
|
||||
CheckSaturatedConversionMax(getLAccumSema(), Saturated(getSAccumSema()),
|
||||
549755813888);
|
||||
CheckSaturatedConversionMax(getLAccumSema(), Saturated(getUSAccumSema()),
|
||||
549755813888);
|
||||
CheckSaturatedConversionMax(getLAccumSema(), Saturated(getPadUSAccumSema()),
|
||||
549755813888);
|
||||
CheckSaturatedConversionMax(getULAccumSema(), Saturated(getSAccumSema()),
|
||||
1099511627776);
|
||||
CheckSaturatedConversionMax(getULAccumSema(), Saturated(getUSAccumSema()),
|
||||
1099511627776);
|
||||
CheckSaturatedConversionMax(getULAccumSema(), Saturated(getPadUSAccumSema()),
|
||||
1099511627776);
|
||||
|
||||
CheckSaturatedConversionMax(getPadUAccumSema(), Saturated(getSAccumSema()),
|
||||
8388608);
|
||||
CheckSaturatedConversionMax(getPadUAccumSema(), Saturated(getUSAccumSema()),
|
||||
8388608);
|
||||
CheckSaturatedConversionMax(getPadUAccumSema(),
|
||||
Saturated(getPadUSAccumSema()), 8388608);
|
||||
CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getSAccumSema()),
|
||||
549755813888);
|
||||
CheckSaturatedConversionMax(getPadULAccumSema(), Saturated(getUSAccumSema()),
|
||||
549755813888);
|
||||
CheckSaturatedConversionMax(getPadULAccumSema(),
|
||||
Saturated(getPadUSAccumSema()), 549755813888);
|
||||
|
||||
// To SAccum min limit (-256)
|
||||
CheckSaturatedConversionMin(getAccumSema(), Saturated(getSAccumSema()),
|
||||
-8388608);
|
||||
CheckSaturatedConversionMin(getAccumSema(), Saturated(getUSAccumSema()),
|
||||
-8388608);
|
||||
CheckSaturatedConversionMin(getAccumSema(), Saturated(getPadUSAccumSema()),
|
||||
-8388608);
|
||||
CheckSaturatedConversionMin(getLAccumSema(), Saturated(getSAccumSema()),
|
||||
-549755813888);
|
||||
CheckSaturatedConversionMin(getLAccumSema(), Saturated(getUSAccumSema()),
|
||||
-549755813888);
|
||||
CheckSaturatedConversionMin(getLAccumSema(), Saturated(getPadUSAccumSema()),
|
||||
-549755813888);
|
||||
}
|
||||
|
||||
TEST(FixedPoint, GetValueSignAfterConversion) {
|
||||
APFixedPoint Fixed(255 << 7, getSAccumSema());
|
||||
ASSERT_TRUE(Fixed.getValue().isSigned());
|
||||
APFixedPoint UFixed = Fixed.convert(getUSAccumSema());
|
||||
ASSERT_TRUE(UFixed.getValue().isUnsigned());
|
||||
ASSERT_EQ(UFixed.getValue(), APSInt::getUnsigned(255 << 8).extOrTrunc(16));
|
||||
}
|
||||
|
||||
TEST(FixedPoint, ModularWrapAround) {
|
||||
// Positive to negative
|
||||
APFixedPoint Val = APFixedPoint(1ULL << 7, getSAccumSema());
|
||||
ASSERT_EQ(Val.convert(getLFractSema()).getValue(), -(1ULL << 31));
|
||||
|
||||
Val = APFixedPoint(1ULL << 23, getAccumSema());
|
||||
ASSERT_EQ(Val.convert(getSAccumSema()).getValue(), -(1ULL << 15));
|
||||
|
||||
Val = APFixedPoint(1ULL << 47, getLAccumSema());
|
||||
ASSERT_EQ(Val.convert(getAccumSema()).getValue(), -(1ULL << 31));
|
||||
|
||||
// Negative to positive
|
||||
Val = APFixedPoint(/*-1.5*/ -192, getSAccumSema());
|
||||
ASSERT_EQ(Val.convert(getLFractSema()).getValue(), 1ULL << 30);
|
||||
|
||||
Val = APFixedPoint(-(257 << 15), getAccumSema());
|
||||
ASSERT_EQ(Val.convert(getSAccumSema()).getValue(), 255 << 7);
|
||||
|
||||
Val = APFixedPoint(-(65537ULL << 31), getLAccumSema());
|
||||
ASSERT_EQ(Val.convert(getAccumSema()).getValue(), 65535 << 15);
|
||||
|
||||
// Signed to unsigned
|
||||
Val = APFixedPoint(-(1 << 7), getSAccumSema());
|
||||
ASSERT_EQ(Val.convert(getUSAccumSema()).getValue(), 255 << 8);
|
||||
|
||||
Val = APFixedPoint(-(1 << 15), getAccumSema());
|
||||
ASSERT_EQ(Val.convert(getUAccumSema()).getValue(), 65535ULL << 16);
|
||||
|
||||
Val = APFixedPoint(-(1ULL << 31), getLAccumSema());
|
||||
ASSERT_EQ(Val.convert(getULAccumSema()).getValue().getZExtValue(),
|
||||
4294967295ULL << 32);
|
||||
}
|
||||
|
||||
} // namespace
|
@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
|
||||
add_llvm_unittest(ADTTests
|
||||
AnyTest.cpp
|
||||
APFixedPointTest.cpp
|
||||
APFloatTest.cpp
|
||||
APIntTest.cpp
|
||||
APSIntTest.cpp
|
||||
|
Loading…
x
Reference in New Issue
Block a user