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; + struct 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,111 @@ +//===- 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. +struct fixedPointSemantics { + unsigned width; + unsigned scale; + bool isSigned; + bool isSaturated; + bool hasUnsignedPadding; + + unsigned IntegralBits() const { + if (isSigned || (!isSigned && hasUnsignedPadding)) + return width - scale - 1; + else + return width - scale; + } +}; + +class APFixedPoint { + public: + APFixedPoint(const llvm::APInt &Val, const struct fixedPointSemantics &Sema) + : Val(Val, !Sema.isSigned), Sema(Sema) { + assert(Sema.width >= Sema.scale && "Not enough room for the scale"); + assert(Val.getBitWidth() == Sema.width && + "The value should have a bit width that matches the Sema width"); + } + + APFixedPoint(uint64_t Val, const struct fixedPointSemantics &Sema) + : APFixedPoint(llvm::APInt(Sema.width, Val, Sema.isSigned), Sema) {} + + llvm::APSInt getValue() const { return llvm::APSInt(Val, !Sema.isSigned); } + inline unsigned getWidth() const { return Sema.width; } + inline unsigned getScale() const { return Sema.scale; } + 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. + void convert(const struct fixedPointSemantics &DstSema); + + 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 struct fixedPointSemantics &Sema); + static APFixedPoint getMin(const struct fixedPointSemantics &Sema); + + private: + llvm::APSInt Val; + struct 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,21 @@ return 0; } } + +struct fixedPointSemantics ASTContext::getFixedPointSema(QualType Ty) const { + assert(Ty->isFixedPointType()); + bool isSigned = Ty->isSignedFixedPointType(); + return {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,144 @@ +//===- 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 { + +void APFixedPoint::convert(const struct fixedPointSemantics &DstSema) { + unsigned DstWidth = DstSema.width; + unsigned DstScale = DstSema.scale; + bool Upscaling = DstScale > getScale(); + + // Exit early + if (getWidth() == DstWidth && getScale() == DstScale && + isSigned() == DstSema.isSigned) { + if (!isSigned()) { + if (hasPadding() && !DstSema.hasUnsignedPadding) + Val = Val.shl(1); + else if (!hasPadding() && DstSema.hasUnsignedPadding) + Val = Val.lshr(1); + } + Sema = DstSema; + return; + } + + // Scaling and resizing + if (DstWidth > Val.getBitWidth()) + Val = Val.extend(DstWidth); + if (Upscaling) + Val = Val.extend(Val.getBitWidth() - getScale() + + std::max(getScale(), DstScale)); + + if (Upscaling) { + Val = Val.shl(DstScale - getScale()); + } else { + if (isSigned()) + Val = Val.ashr(getScale() - DstScale); + else + Val = Val.lshr(getScale() - DstScale); + } + + auto Mask = llvm::APInt::getBitsSetFrom(Val.getBitWidth(), + DstScale + DstSema.IntegralBits()); + llvm::APInt Masked(Val & Mask); + if (!DstSema.isSigned && Val.isNegative()) { + Val = 0; + } else if (!(Masked == Mask || Masked == 0)) { + // Change in the bits above the sign + if (Val.isNegative()) { + // Min val + if (DstSema.isSigned) + Val = Mask; + else + Val = 0; + } else { + Val = ~Mask; // Max val + } + } + + if (DstWidth < Val.getBitWidth()) + Val = Val.trunc(DstWidth); + + Val.setIsSigned(DstSema.isSigned); + Sema = 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)); + + if (getWidth() < CommonWidth) + ThisVal = ThisVal.extend(CommonWidth); + if (OtherWidth < CommonWidth) + OtherVal = OtherVal.extend(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 struct fixedPointSemantics &Sema) { + bool IsUnsigned = !Sema.isSigned; + auto Val = llvm::APSInt::getMaxValue(Sema.width, IsUnsigned); + if (IsUnsigned && Sema.hasUnsignedPadding) + Val = Val.lshr(1); + return APFixedPoint(Val, Sema); +} + +APFixedPoint APFixedPoint::getMin(const struct fixedPointSemantics &Sema) { + auto Val = llvm::APSInt::getMinValue(Sema.width, !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,599 @@ +//===- 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 { + +struct fixedPointSemantics Saturated(struct fixedPointSemantics Sema) { + Sema.isSaturated = true; + return Sema; +} + +struct fixedPointSemantics getSAccSema() { + return {/*width=*/16, /*scale=*/7, /*isSigned=*/true, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getAccSema() { + return {/*width=*/32, /*scale=*/15, /*isSigned=*/true, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getLAccSema() { + return {/*width=*/64, /*scale=*/31, /*isSigned=*/true, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getSFractSema() { + return {/*width=*/8, /*scale=*/7, /*isSigned=*/true, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getFractSema() { + return {/*width=*/16, /*scale=*/15, /*isSigned=*/true, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getLFractSema() { + return {/*width=*/32, /*scale=*/31, /*isSigned=*/true, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getUSAccSema() { + return {/*width=*/16, /*scale=*/8, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getUAccSema() { + return {/*width=*/32, /*scale=*/16, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getULAccSema() { + return {/*width=*/64, /*scale=*/32, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getUSFractSema() { + return {/*width=*/8, /*scale=*/8, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getUFractSema() { + return {/*width=*/16, /*scale=*/16, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getULFractSema() { + return {/*width=*/32, /*scale=*/32, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/false}; +} + +struct fixedPointSemantics getPadUSAccSema() { + return {/*width=*/16, /*scale=*/7, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/true}; +} + +struct fixedPointSemantics getPadUAccSema() { + return {/*width=*/32, /*scale=*/15, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/true}; +} + +struct fixedPointSemantics getPadULAccSema() { + return {/*width=*/64, /*scale=*/31, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/true}; +} + +struct fixedPointSemantics getPadUSFractSema() { + return {/*width=*/8, /*scale=*/7, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/true}; +} + +struct fixedPointSemantics getPadUFractSema() { + return {/*width=*/16, /*scale=*/15, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/true}; +} + +struct fixedPointSemantics getPadULFractSema() { + return {/*width=*/32, /*scale=*/31, /*isSigned=*/false, + /*isSaturated=*/false, /*hasUnsignedPadding=*/true}; +} + +void CheckUnpaddedMax(const fixedPointSemantics &Sema) { + ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(), + APSInt::getMaxValue(Sema.width, !Sema.isSigned)); +} + +void CheckPaddedMax(const fixedPointSemantics &Sema) { + ASSERT_EQ(APFixedPoint::getMax(Sema).getValue(), + APSInt::getMaxValue(Sema.width, !Sema.isSigned) >> 1); +} + +void CheckMin(const fixedPointSemantics &Sema) { + ASSERT_EQ(APFixedPoint::getMin(Sema).getValue(), + APSInt::getMinValue(Sema.width, !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.scale; + + // Value with a fraction + APFixedPoint ValWithFract(APInt(Sema.width, + (IntPart << Scale) + (1ULL << (Scale - 1)), + Sema.isSigned), + Sema); + ASSERT_EQ(ValWithFract.getIntPart(), IntPart); + + // Just fraction + APFixedPoint JustFract( + APInt(Sema.width, (1ULL << (Scale - 1)), Sema.isSigned), Sema); + ASSERT_EQ(JustFract.getIntPart(), 0); + + // Whole number + APFixedPoint WholeNum(APInt(Sema.width, (IntPart << Scale), Sema.isSigned), + Sema); + ASSERT_EQ(WholeNum.getIntPart(), IntPart); + + // Negative + if (Sema.isSigned) { + APFixedPoint Negative(APInt(Sema.width, (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(struct fixedPointSemantics Src, + struct fixedPointSemantics Dst, + int64_t TestVal) { + int64_t ScaledVal = TestVal; + if (Dst.scale > Src.scale) { + ScaledVal <<= (Dst.scale - Src.scale); + } else { + ScaledVal >>= (Src.scale - Dst.scale); + } + + APFixedPoint Fixed(TestVal, Src); + Fixed.convert(Dst); + APFixedPoint Expected(ScaledVal, Dst); + ASSERT_EQ(Fixed, Expected); +} + +// Check the value in a given fixed point sema overflows to the saturated min +// for another sema +void CheckSaturatedConversionMin(struct fixedPointSemantics Src, + struct fixedPointSemantics Dst, + int64_t TestVal) { + APFixedPoint Fixed(TestVal, Src); + Fixed.convert(Dst); + ASSERT_EQ(Fixed, APFixedPoint::getMin(Dst)); +} + +// Check the value in a given fixed point sema overflows to the saturated max +// for another sema +void CheckSaturatedConversionMax(struct fixedPointSemantics Src, + struct fixedPointSemantics Dst, + int64_t TestVal) { + APFixedPoint Fixed(TestVal, Src); + Fixed.convert(Dst); + ASSERT_EQ(Fixed, APFixedPoint::getMax(Dst)); +} + +// Check one signed _Accum sema converted to other sema for different values. +void CheckSignedAccumConversionsAgainstOthers(struct 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); +} + +// Check one unsigned _Accum sema converted to other sema for different values. +void CheckUnsignedAccumConversionsAgainstOthers(struct 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(struct 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(struct 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