Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -30,6 +30,7 @@ #include "clang/AST/TemplateName.h" #include "clang/AST/Type.h" #include "clang/Basic/AddressSpaces.h" +#include "clang/Basic/FixedPoint.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -1949,6 +1950,9 @@ unsigned char getFixedPointScale(QualType Ty) const; unsigned char getFixedPointIBits(QualType Ty) const; + FixedPointSemantics getFixedPointSema(QualType Ty) const; + APFixedPoint getFixedPointMax(QualType Ty) const; + APFixedPoint getFixedPointMin(QualType Ty) const; DeclarationNameInfo getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const; Index: include/clang/Basic/FixedPoint.h =================================================================== --- /dev/null +++ include/clang/Basic/FixedPoint.h @@ -0,0 +1,128 @@ +//===- FixedPoint.h - Fixed point constant handling -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines the fixed point number interface. +/// This is a class for abstracting various operations performed on fixed point +/// types described in ISO/IEC JTC1 SC22 WG14 N1169 starting at clause 4. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_FIXEDPOINT_H +#define LLVM_CLANG_BASIC_FIXEDPOINT_H + +#include "llvm/ADT/APSInt.h" + +namespace clang { + +class ASTContext; +class QualType; + +// The fixed point semantics work similarly to llvm::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 signed, the first bit +// in the value this represents is treaded 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"); + } + + inline unsigned getWidth() const { return Width; } + inline unsigned getScale() const { return Scale; } + inline bool isSigned() const { return IsSigned; } + inline bool isSaturated() const { return IsSaturated; } + inline bool hasUnsignedPadding() const { return HasUnsignedPadding; } + + void setSaturated(bool Saturated) { IsSaturated = Saturated; } + + unsigned getIntegralBits() const { + if (IsSigned || (!IsSigned && HasUnsignedPadding)) + return Width - Scale - 1; + else + return Width - Scale; + } + +private: + unsigned Width; + unsigned Scale; + bool IsSigned; + bool IsSaturated; + bool HasUnsignedPadding; +}; + +class APFixedPoint { + public: + APFixedPoint(const llvm::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(llvm::APInt(Sema.getWidth(), Val, Sema.isSigned()), + Sema) {} + + llvm::APSInt getValue() const { return llvm::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(); } + + // Convert this number to match the semantics provided. + APFixedPoint convert(const FixedPointSemantics &DstSema) const; + + APFixedPoint shr(unsigned Amt) const { + return APFixedPoint(Val >> Amt, Sema); + } + + APFixedPoint shl(unsigned Amt) const { + return APFixedPoint(Val << Amt, Sema); + } + + llvm::APSInt getIntPart() const { + if (Val < 0 && Val != -Val) // Cover the case when we have the min val + return -(-Val >> getScale()); + else + return Val >> getScale(); + } + + // 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); + +private: + llvm::APSInt Val; + FixedPointSemantics Sema; +}; + +} // namespace clang + +#endif Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -312,6 +312,14 @@ } } + /// In the event this target uses the same number of fractional bits for its + /// unsigned types as it does with its signed counterparts, there will be + /// exactly one bit of padding. + /// Return true if unsigned fixed point types have padding for this target. + bool doUnsignedFixedPointTypesHavePadding() const { + return PaddingOnUnsignedFixedPoint; + } + /// Return the width (in bits) of the specified integer type enum. /// /// For example, SignedInt -> getIntWidth(). Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -10282,3 +10282,22 @@ return 0; } } + +FixedPointSemantics ASTContext::getFixedPointSema(QualType Ty) const { + assert(Ty->isFixedPointType()); + bool isSigned = Ty->isSignedFixedPointType(); + return FixedPointSemantics( + static_cast(getTypeSize(Ty)), getFixedPointScale(Ty), isSigned, + Ty->isSaturatedFixedPointType(), + !isSigned && getTargetInfo().doUnsignedFixedPointTypesHavePadding()); +} + +APFixedPoint ASTContext::getFixedPointMax(QualType Ty) const { + assert(Ty->isFixedPointType()); + return APFixedPoint::getMax(getFixedPointSema(Ty)); +} + +APFixedPoint ASTContext::getFixedPointMin(QualType Ty) const { + assert(Ty->isFixedPointType()); + return APFixedPoint::getMin(getFixedPointSema(Ty)); +} Index: lib/Basic/CMakeLists.txt =================================================================== --- lib/Basic/CMakeLists.txt +++ lib/Basic/CMakeLists.txt @@ -54,6 +54,7 @@ DiagnosticOptions.cpp FileManager.cpp FileSystemStatCache.cpp + FixedPoint.cpp IdentifierTable.cpp LangOptions.cpp MemoryBufferCache.cpp Index: lib/Basic/FixedPoint.cpp =================================================================== --- /dev/null +++ lib/Basic/FixedPoint.cpp @@ -0,0 +1,124 @@ +//===- FixedPoint.cpp - Fixed point constant handling -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Defines the implementation for the fixed point number interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/Basic/FixedPoint.h" + +namespace clang { + +APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema) const { + llvm::APSInt NewVal = Val; + unsigned DstWidth = DstSema.getWidth(); + unsigned DstScale = DstSema.getScale(); + bool Upscaling = DstScale > getScale(); + + // Scaling and resizing + if (DstWidth > NewVal.getBitWidth()) + NewVal = NewVal.extend(DstWidth); + + if (Upscaling) { + NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale()); + NewVal <<= (DstScale - getScale()); + } else { + NewVal >>= (getScale() - DstScale); + } + + if (DstSema.isSaturated()) { + auto Mask = llvm::APInt::getBitsSetFrom( + NewVal.getBitWidth(), DstScale + DstSema.getIntegralBits()); + llvm::APInt Masked(NewVal & Mask); + if (!DstSema.isSigned() && NewVal.isNegative()) { + NewVal = 0; + } else if (!(Masked == Mask || Masked == 0)) { + // Change in the bits above the sign + if (NewVal.isNegative()) { + // Min val + if (DstSema.isSigned()) + NewVal = Mask; + else + NewVal = 0; + } else { + NewVal = ~Mask; // Max val + } + } + } + + NewVal = NewVal.extOrTrunc(DstWidth); + return APFixedPoint(NewVal, DstSema); +} + +int APFixedPoint::compare(const APFixedPoint &Other) const { + llvm::APSInt ThisVal = Val; + llvm::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 += std::abs(static_cast(getScale() - OtherScale)); + + 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 = llvm::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 = llvm::APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned()); + return APFixedPoint(Val, Sema); +} + +} // namespace clang Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -3351,16 +3351,14 @@ bool isSigned = !Literal.isUnsigned; unsigned scale = Context.getFixedPointScale(Ty); - unsigned ibits = Context.getFixedPointIBits(Ty); unsigned bit_width = Context.getTypeInfo(Ty).Width; llvm::APInt Val(bit_width, 0, isSigned); bool Overflowed = Literal.GetFixedPointValue(Val, scale); + bool ValIsZero = Val.isNullValue() && !Overflowed; - // Do not use bit_width since some types may have padding like _Fract or - // unsigned _Accums if PaddingOnUnsignedFixedPoint is set. - auto MaxVal = llvm::APInt::getMaxValue(ibits + scale).zextOrSelf(bit_width); - if (Literal.isFract && Val == MaxVal + 1) + auto MaxVal = Context.getFixedPointMax(Ty).getValue(); + if (Literal.isFract && Val == MaxVal + 1 && !ValIsZero) // Clause 6.4.4 - The value of a constant shall be in the range of // representable values for its type, with exception for constants of a // fract type with a value of exactly 1; such a constant shall denote Index: test/Frontend/fixed_point_declarations.c =================================================================== --- test/Frontend/fixed_point_declarations.c +++ test/Frontend/fixed_point_declarations.c @@ -1,5 +1,4 @@ // RUN: %clang -ffixed-point -S -emit-llvm %s -o - --target=x86_64-linux | FileCheck %s -// RUN: %clang -ffixed-point -S -emit-llvm %s -o - --target=x86_64-scei-ps4-ubuntu-fast | FileCheck %s // Primary fixed point types signed short _Accum s_short_accum; // CHECK-DAG: @s_short_accum = {{.*}}global i16 0, align 2 @@ -111,3 +110,18 @@ unsigned short _Fract u_short_fract_eps = 0x1p-8uhr; // CHECK-DAG: @u_short_fract_eps = {{.*}}global i8 1, align 1 unsigned _Fract u_fract_eps = 0x1p-16ur; // CHECK-DAG: @u_fract_eps = {{.*}}global i16 1, align 2 unsigned long _Fract u_long_fract_eps = 0x1p-32ulr; // CHECK-DAG: @u_long_fract_eps = {{.*}}global i32 1, align 4 + +// Zero +short _Accum short_accum_zero = 0.0hk; // CHECK-DAG: @short_accum_zero = {{.*}}global i16 0, align 2 + _Accum accum_zero = 0.0k; // CHECK-DAG: @accum_zero = {{.*}}global i32 0, align 4 +long _Accum long_accum_zero = 0.0lk; // CHECK-DAG: @long_accum_zero = {{.*}}global i64 0, align 8 +unsigned short _Accum u_short_accum_zero = 0.0uhk; // CHECK-DAG: @u_short_accum_zero = {{.*}}global i16 0, align 2 +unsigned _Accum u_accum_zero = 0.0uk; // CHECK-DAG: @u_accum_zero = {{.*}}global i32 0, align 4 +unsigned long _Accum u_long_accum_zero = 0.0ulk; // CHECK-DAG: @u_long_accum_zero = {{.*}}global i64 0, align 8 + +short _Fract short_fract_zero = 0.0hr; // CHECK-DAG: @short_fract_zero = {{.*}}global i8 0, align 1 + _Fract fract_zero = 0.0r; // CHECK-DAG: @fract_zero = {{.*}}global i16 0, align 2 +long _Fract long_fract_zero = 0.0lr; // CHECK-DAG: @long_fract_zero = {{.*}}global i32 0, align 4 +unsigned short _Fract u_short_fract_zero = 0.0uhr; // CHECK-DAG: @u_short_fract_zero = {{.*}}global i8 0, align 1 +unsigned _Fract u_fract_zero = 0.0ur; // CHECK-DAG: @u_fract_zero = {{.*}}global i16 0, align 2 +unsigned long _Fract u_long_fract_zero = 0.0ulr; // CHECK-DAG: @u_long_fract_zero = {{.*}}global i32 0, align 4 Index: unittests/Basic/CMakeLists.txt =================================================================== --- unittests/Basic/CMakeLists.txt +++ unittests/Basic/CMakeLists.txt @@ -6,6 +6,7 @@ CharInfoTest.cpp DiagnosticTest.cpp FileManagerTest.cpp + FixedPointTest.cpp MemoryBufferCacheTest.cpp SourceManagerTest.cpp VirtualFileSystemTest.cpp @@ -13,6 +14,7 @@ target_link_libraries(BasicTests PRIVATE + clangAST clangBasic clangLex ) Index: unittests/Basic/FixedPointTest.cpp =================================================================== --- /dev/null +++ unittests/Basic/FixedPointTest.cpp @@ -0,0 +1,633 @@ +//===- unittests/Basic/FixedPointTest.cpp -- fixed point number tests -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/FixedPoint.h" +#include "llvm/ADT/APSInt.h" +#include "gtest/gtest.h" + +using clang::APFixedPoint; +using clang::FixedPointSemantics; +using llvm::APInt; +using llvm::APSInt; + +namespace { + +FixedPointSemantics Saturated(FixedPointSemantics Sema) { + Sema.setSaturated(true); + return Sema; +} + +FixedPointSemantics getSAccSema() { + return FixedPointSemantics(/*width=*/16, /*scale=*/7, /*isSigned=*/true, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + +FixedPointSemantics getAccSema() { + return FixedPointSemantics(/*width=*/32, /*scale=*/15, /*isSigned=*/true, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + +FixedPointSemantics getLAccSema() { + 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 getUSAccSema() { + return FixedPointSemantics(/*width=*/16, /*scale=*/8, /*isSigned=*/false, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + +FixedPointSemantics getUAccSema() { + return FixedPointSemantics(/*width=*/32, /*scale=*/16, /*isSigned=*/false, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/false); +} + +FixedPointSemantics getULAccSema() { + 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 getPadUSAccSema() { + return FixedPointSemantics(/*width=*/16, /*scale=*/7, /*isSigned=*/false, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/true); +} + +FixedPointSemantics getPadUAccSema() { + return FixedPointSemantics(/*width=*/32, /*scale=*/15, /*isSigned=*/false, + /*isSaturated=*/false, + /*hasUnsignedPadding=*/true); +} + +FixedPointSemantics getPadULAccSema() { + 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(getSAccSema()); + CheckUnpaddedMax(getAccSema()); + CheckUnpaddedMax(getLAccSema()); + CheckUnpaddedMax(getUSAccSema()); + CheckUnpaddedMax(getUAccSema()); + CheckUnpaddedMax(getULAccSema()); + CheckUnpaddedMax(getSFractSema()); + CheckUnpaddedMax(getFractSema()); + CheckUnpaddedMax(getLFractSema()); + CheckUnpaddedMax(getUSFractSema()); + CheckUnpaddedMax(getUFractSema()); + CheckUnpaddedMax(getULFractSema()); + + CheckPaddedMax(getPadUSAccSema()); + CheckPaddedMax(getPadUAccSema()); + CheckPaddedMax(getPadULAccSema()); + CheckPaddedMax(getPadUSFractSema()); + CheckPaddedMax(getPadUFractSema()); + CheckPaddedMax(getPadULFractSema()); +} + +TEST(FixedPointTest, getMin) { + CheckMin(getSAccSema()); + CheckMin(getAccSema()); + CheckMin(getLAccSema()); + CheckMin(getUSAccSema()); + CheckMin(getUAccSema()); + CheckMin(getULAccSema()); + CheckMin(getSFractSema()); + CheckMin(getFractSema()); + CheckMin(getLFractSema()); + CheckMin(getUSFractSema()); + CheckMin(getUFractSema()); + CheckMin(getULFractSema()); + + CheckMin(getPadUSAccSema()); + CheckMin(getPadUAccSema()); + CheckMin(getPadULAccSema()); + 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(getSAccSema(), 2); + CheckIntPart(getAccSema(), 2); + CheckIntPart(getLAccSema(), 2); + CheckIntPart(getUSAccSema(), 2); + CheckIntPart(getUAccSema(), 2); + CheckIntPart(getULAccSema(), 2); + + // Zero + CheckIntPart(getSAccSema(), 0); + CheckIntPart(getAccSema(), 0); + CheckIntPart(getLAccSema(), 0); + CheckIntPart(getUSAccSema(), 0); + CheckIntPart(getUAccSema(), 0); + CheckIntPart(getULAccSema(), 0); + + CheckIntPart(getSFractSema(), 0); + CheckIntPart(getFractSema(), 0); + CheckIntPart(getLFractSema(), 0); + CheckIntPart(getUSFractSema(), 0); + CheckIntPart(getUFractSema(), 0); + CheckIntPart(getULFractSema(), 0); + + // Min + CheckIntPartMin(getSAccSema(), -256); + CheckIntPartMin(getAccSema(), -65536); + CheckIntPartMin(getLAccSema(), -4294967296); + + CheckIntPartMin(getSFractSema(), -1); + CheckIntPartMin(getFractSema(), -1); + CheckIntPartMin(getLFractSema(), -1); + + // Max + CheckIntPartMax(getSAccSema(), 255); + CheckIntPartMax(getAccSema(), 65535); + CheckIntPartMax(getLAccSema(), 4294967295); + CheckIntPartMax(getUSAccSema(), 255); + CheckIntPartMax(getUAccSema(), 65535); + CheckIntPartMax(getULAccSema(), 4294967295); + + CheckIntPartMax(getSFractSema(), 0); + CheckIntPartMax(getFractSema(), 0); + CheckIntPartMax(getLFractSema(), 0); + CheckIntPartMax(getUSFractSema(), 0); + CheckIntPartMax(getUFractSema(), 0); + CheckIntPartMax(getULFractSema(), 0); + + // Padded + // Normal Values + CheckIntPart(getPadUSAccSema(), 2); + CheckIntPart(getPadUAccSema(), 2); + CheckIntPart(getPadULAccSema(), 2); + + // Zero + CheckIntPart(getPadUSAccSema(), 0); + CheckIntPart(getPadUAccSema(), 0); + CheckIntPart(getPadULAccSema(), 0); + + CheckIntPart(getPadUSFractSema(), 0); + CheckIntPart(getPadUFractSema(), 0); + CheckIntPart(getPadULFractSema(), 0); + + // Max + CheckIntPartMax(getPadUSAccSema(), 255); + CheckIntPartMax(getPadUAccSema(), 65535); + CheckIntPartMax(getPadULAccSema(), 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, getSAccSema()), + APFixedPoint(81920, getAccSema())); + ASSERT_EQ(APFixedPoint(320, getSAccSema()), + APFixedPoint(5368709120, getLAccSema())); + ASSERT_EQ(APFixedPoint(0, getSAccSema()), APFixedPoint(0, getLAccSema())); + + // Across types (0.5) + ASSERT_EQ(APFixedPoint(64, getSAccSema()), APFixedPoint(64, getSFractSema())); + ASSERT_EQ(APFixedPoint(16384, getAccSema()), + APFixedPoint(16384, getFractSema())); + ASSERT_EQ(APFixedPoint(1073741824, getLAccSema()), + APFixedPoint(1073741824, getLFractSema())); + + // Across widths and types (0.5) + ASSERT_EQ(APFixedPoint(64, getSAccSema()), + APFixedPoint(16384, getFractSema())); + ASSERT_EQ(APFixedPoint(64, getSAccSema()), + APFixedPoint(1073741824, getLFractSema())); + + // Across saturation + ASSERT_EQ(APFixedPoint(320, getSAccSema()), + APFixedPoint(81920, Saturated(getAccSema()))); + + // Across signs + ASSERT_EQ(APFixedPoint(320, getSAccSema()), + APFixedPoint(640, getUSAccSema())); + ASSERT_EQ(APFixedPoint(-320, getSAccSema()), + APFixedPoint(-81920, getAccSema())); + + // Across padding + ASSERT_EQ(APFixedPoint(320, getSAccSema()), + APFixedPoint(320, getPadUSAccSema())); + ASSERT_EQ(APFixedPoint(640, getUSAccSema()), + APFixedPoint(320, getPadUSAccSema())); + + // Less than + ASSERT_LT(APFixedPoint(-1, getSAccSema()), APFixedPoint(0, getAccSema())); + ASSERT_LT(APFixedPoint(-1, getSAccSema()), APFixedPoint(0, getUAccSema())); + ASSERT_LT(APFixedPoint(0, getSAccSema()), APFixedPoint(1, getAccSema())); + ASSERT_LT(APFixedPoint(0, getSAccSema()), APFixedPoint(1, getUAccSema())); + ASSERT_LT(APFixedPoint(0, getUSAccSema()), APFixedPoint(1, getAccSema())); + ASSERT_LT(APFixedPoint(0, getUSAccSema()), APFixedPoint(1, getUAccSema())); + + // Greater than + ASSERT_GT(APFixedPoint(0, getAccSema()), APFixedPoint(-1, getSAccSema())); + ASSERT_GT(APFixedPoint(0, getUAccSema()), APFixedPoint(-1, getSAccSema())); + ASSERT_GT(APFixedPoint(1, getAccSema()), APFixedPoint(0, getSAccSema())); + ASSERT_GT(APFixedPoint(1, getUAccSema()), APFixedPoint(0, getSAccSema())); + ASSERT_GT(APFixedPoint(1, getAccSema()), APFixedPoint(0, getUSAccSema())); + ASSERT_GT(APFixedPoint(1, getUAccSema()), APFixedPoint(0, getUSAccSema())); +} + +// 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; + if (Dst.getScale() > Src.getScale()) { + ScaledVal <<= (Dst.getScale() - Src.getScale()); + } else { + ScaledVal >>= (Src.getScale() - Dst.getScale()); + } + + 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 + + // +Accs to Accs + CheckUnsaturatedConversion(Src, getSAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getLAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getUSAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getUAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getULAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getPadUSAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getPadUAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getPadULAccSema(), NormalVal); + + // -Accs to Accs + CheckUnsaturatedConversion(Src, getSAccSema(), -NormalVal); + CheckUnsaturatedConversion(Src, getAccSema(), -NormalVal); + CheckUnsaturatedConversion(Src, getLAccSema(), -NormalVal); + CheckSaturatedConversionMin(Src, Saturated(getUSAccSema()), -NormalVal); + CheckSaturatedConversionMin(Src, Saturated(getUAccSema()), -NormalVal); + CheckSaturatedConversionMin(Src, Saturated(getULAccSema()), -NormalVal); + CheckSaturatedConversionMin(Src, Saturated(getPadUSAccSema()), -NormalVal); + CheckSaturatedConversionMin(Src, Saturated(getPadUAccSema()), -NormalVal); + CheckSaturatedConversionMin(Src, Saturated(getPadULAccSema()), -NormalVal); + + // +Accs 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); + + // -Accs 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 Accs + CheckUnsaturatedConversion(Src, getSAccSema(), 0); + CheckUnsaturatedConversion(Src, getAccSema(), 0); + CheckUnsaturatedConversion(Src, getLAccSema(), 0); + CheckUnsaturatedConversion(Src, getUSAccSema(), 0); + CheckUnsaturatedConversion(Src, getUAccSema(), 0); + CheckUnsaturatedConversion(Src, getULAccSema(), 0); + CheckUnsaturatedConversion(Src, getPadUSAccSema(), 0); + CheckUnsaturatedConversion(Src, getPadUAccSema(), 0); + CheckUnsaturatedConversion(Src, getPadULAccSema(), 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 + + // +UAccs to Accs + CheckUnsaturatedConversion(Src, getSAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getLAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getUSAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getUAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getULAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getPadUSAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getPadUAccSema(), NormalVal); + CheckUnsaturatedConversion(Src, getPadULAccSema(), NormalVal); + + // +UAccs 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(getSAccSema(), 128); + CheckUnsignedAccumConversionsAgainstOthers(getUSAccSema(), 256); + CheckSignedAccumConversionsAgainstOthers(getAccSema(), 32768); + CheckUnsignedAccumConversionsAgainstOthers(getUAccSema(), 65536); + CheckSignedAccumConversionsAgainstOthers(getLAccSema(), 2147483648); + CheckUnsignedAccumConversionsAgainstOthers(getULAccSema(), 4294967296); + + CheckUnsignedAccumConversionsAgainstOthers(getPadUSAccSema(), 128); + CheckUnsignedAccumConversionsAgainstOthers(getPadUAccSema(), 32768); + CheckUnsignedAccumConversionsAgainstOthers(getPadULAccSema(), 2147483648); +} + +TEST(FixedPoint, AccConversionOverflow) { + // To SAcc max limit (65536) + CheckSaturatedConversionMax(getLAccSema(), Saturated(getAccSema()), + 140737488355328); + CheckSaturatedConversionMax(getLAccSema(), Saturated(getUAccSema()), + 140737488355328); + CheckSaturatedConversionMax(getLAccSema(), Saturated(getPadUAccSema()), + 140737488355328); + CheckSaturatedConversionMax(getULAccSema(), Saturated(getAccSema()), + 281474976710656); + CheckSaturatedConversionMax(getULAccSema(), Saturated(getUAccSema()), + 281474976710656); + CheckSaturatedConversionMax(getULAccSema(), Saturated(getPadUAccSema()), + 281474976710656); + + CheckSaturatedConversionMax(getPadULAccSema(), Saturated(getAccSema()), + 140737488355328); + CheckSaturatedConversionMax(getPadULAccSema(), Saturated(getUAccSema()), + 140737488355328); + CheckSaturatedConversionMax(getPadULAccSema(), Saturated(getPadUAccSema()), + 140737488355328); + + // To SAcc min limit (-65536) + CheckSaturatedConversionMin(getLAccSema(), Saturated(getAccSema()), + -140737488355328); + CheckSaturatedConversionMin(getLAccSema(), Saturated(getUAccSema()), + -140737488355328); + CheckSaturatedConversionMin(getLAccSema(), Saturated(getPadUAccSema()), + -140737488355328); +} + +TEST(FixedPoint, SAccConversionOverflow) { + // To SAcc max limit (256) + CheckSaturatedConversionMax(getAccSema(), Saturated(getSAccSema()), 8388608); + CheckSaturatedConversionMax(getAccSema(), Saturated(getUSAccSema()), 8388608); + CheckSaturatedConversionMax(getAccSema(), Saturated(getPadUSAccSema()), + 8388608); + CheckSaturatedConversionMax(getUAccSema(), Saturated(getSAccSema()), + 16777216); + CheckSaturatedConversionMax(getUAccSema(), Saturated(getUSAccSema()), + 16777216); + CheckSaturatedConversionMax(getUAccSema(), Saturated(getPadUSAccSema()), + 16777216); + CheckSaturatedConversionMax(getLAccSema(), Saturated(getSAccSema()), + 549755813888); + CheckSaturatedConversionMax(getLAccSema(), Saturated(getUSAccSema()), + 549755813888); + CheckSaturatedConversionMax(getLAccSema(), Saturated(getPadUSAccSema()), + 549755813888); + CheckSaturatedConversionMax(getULAccSema(), Saturated(getSAccSema()), + 1099511627776); + CheckSaturatedConversionMax(getULAccSema(), Saturated(getUSAccSema()), + 1099511627776); + CheckSaturatedConversionMax(getULAccSema(), Saturated(getPadUSAccSema()), + 1099511627776); + + CheckSaturatedConversionMax(getPadUAccSema(), Saturated(getSAccSema()), + 8388608); + CheckSaturatedConversionMax(getPadUAccSema(), Saturated(getUSAccSema()), + 8388608); + CheckSaturatedConversionMax(getPadUAccSema(), Saturated(getPadUSAccSema()), + 8388608); + CheckSaturatedConversionMax(getPadULAccSema(), Saturated(getSAccSema()), + 549755813888); + CheckSaturatedConversionMax(getPadULAccSema(), Saturated(getUSAccSema()), + 549755813888); + CheckSaturatedConversionMax(getPadULAccSema(), Saturated(getPadUSAccSema()), + 549755813888); + + // To SAcc min limit (-256) + CheckSaturatedConversionMin(getAccSema(), Saturated(getSAccSema()), -8388608); + CheckSaturatedConversionMin(getAccSema(), Saturated(getUSAccSema()), + -8388608); + CheckSaturatedConversionMin(getAccSema(), Saturated(getPadUSAccSema()), + -8388608); + CheckSaturatedConversionMin(getLAccSema(), Saturated(getSAccSema()), + -549755813888); + CheckSaturatedConversionMin(getLAccSema(), Saturated(getUSAccSema()), + -549755813888); + CheckSaturatedConversionMin(getLAccSema(), Saturated(getPadUSAccSema()), + -549755813888); +} + +void CheckSaturatedConversionToFractMax(FixedPointSemantics Src, + int64_t OneVal) { + CheckSaturatedConversionMax(Src, Saturated(getSFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getLFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getUSFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getUFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getPadULFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getPadUSFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getPadUFractSema()), OneVal); + CheckSaturatedConversionMax(Src, Saturated(getPadULFractSema()), OneVal); +} + +void CheckSaturatedConversionToFractMin(FixedPointSemantics Src, + int64_t MinusOneVal) { + CheckSaturatedConversionMin(Src, Saturated(getSFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getLFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getUSFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getUFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getPadULFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getPadUSFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getPadUFractSema()), MinusOneVal); + CheckSaturatedConversionMin(Src, Saturated(getPadULFractSema()), MinusOneVal); +} + +TEST(FixedPoint, OverflowConversionsToFract) { + CheckSaturatedConversionToFractMax(getSAccSema(), 128); + CheckSaturatedConversionToFractMin(getSAccSema(), -128); + CheckSaturatedConversionToFractMax(getAccSema(), 32768); + CheckSaturatedConversionToFractMin(getAccSema(), -32768); + CheckSaturatedConversionToFractMax(getLAccSema(), 2147483648); + CheckSaturatedConversionToFractMin(getLAccSema(), -2147483648); + + // Unsigned + CheckSaturatedConversionToFractMax(getUSAccSema(), 256); + CheckSaturatedConversionToFractMax(getUAccSema(), 65536); + CheckSaturatedConversionToFractMax(getULAccSema(), 4294967296); + + // Padded unsigned + CheckSaturatedConversionToFractMax(getPadUSAccSema(), 128); + CheckSaturatedConversionToFractMax(getPadUAccSema(), 32768); + CheckSaturatedConversionToFractMax(getPadULAccSema(), 2147483648); +} + +} // namespace