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; + enum 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,100 @@ +//===- 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; + +enum FixedPointSemantics { + Padding, + NoPadding, + SatPadding, + SatNoPadding, +}; + +llvm::APSInt ShrToZero(const llvm::APSInt &Val, unsigned Amt); + +bool IsPaddingSema(enum FixedPointSemantics Sema); +bool IsSaturatedSema(enum FixedPointSemantics Sema); + +class APFixedPoint { + public: + APFixedPoint(const llvm::APSInt &Val, unsigned Scale, + enum FixedPointSemantics Sema) + : Val(Val), Scale(Scale), Sema(Sema) {} + + llvm::APSInt getValue() const { return Val; } + unsigned getScale() const { return Scale; } + bool isSaturated() const { return IsSaturatedSema(Sema); } + + // Convert this number to match the scale, width, and sema of the + // provided fixed point type. + void convert(const ASTContext &Context, const QualType &Ty); + + // Change the width and scale of this fixed point number. + // Return true if overflow occurrd and false otherwise. + bool rescaleAndResize(unsigned DstWidth, unsigned DstScale); + + // Same as rescaleAndResize but handles overflow with saturation. + void saturatedRescaleAndResize(unsigned DstWidth, unsigned DstScale, + enum FixedPointSemantics DstSema); + + APFixedPoint shr(unsigned Amt) const { + return APFixedPoint(ShrToZero(Val, Amt), Scale, Sema); + } + + APFixedPoint shl(unsigned Amt) const { + return APFixedPoint(Val << Amt, Scale, Sema); + } + + llvm::APSInt getIntPart() const { return ShrToZero(Val, Scale); } + + // 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(unsigned NumBits, unsigned scale, bool Signed, + enum FixedPointSemantics Sema); + static APFixedPoint getMin(unsigned NumBits, unsigned scale, bool Signed, + enum FixedPointSemantics Sema); + + private: + llvm::APSInt Val; + unsigned Scale; + enum FixedPointSemantics Sema; +}; + +} // namespace clang + +#endif Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -311,6 +311,12 @@ } } + /// 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 SameFBits; } + /// 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,26 @@ return 0; } } + +enum FixedPointSemantics ASTContext::getFixedPointSema(QualType Ty) const { + assert(Ty->isFixedPointType()); + bool Saturated = Ty->isSaturatedFixedPointType(); + if (getTargetInfo().doUnsignedFixedPointTypesHavePadding()) + return Saturated ? SatPadding : Padding; + else + return Saturated ? SatNoPadding : NoPadding; +} + +APFixedPoint ASTContext::getFixedPointMax(QualType Ty) const { + assert(Ty->isFixedPointType()); + return APFixedPoint::getMax(getTypeSize(Ty), getFixedPointScale(Ty), + Ty->isSignedFixedPointType(), + getFixedPointSema(Ty)); +} + +APFixedPoint ASTContext::getFixedPointMin(QualType Ty) const { + assert(Ty->isFixedPointType()); + return APFixedPoint::getMin(getTypeSize(Ty), getFixedPointScale(Ty), + Ty->isSignedFixedPointType(), + 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,171 @@ +//===- 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 { + +llvm::APSInt ShrToZero(const llvm::APSInt &Val, unsigned Amt) { + if (Val < 0) + return -(-Val >> Amt); + else + return Val >> Amt; +} + +bool IsPaddingSema(enum FixedPointSemantics Sema) { + return Sema == Padding || Sema == SatPadding; +} + +bool IsSaturatedSema(enum FixedPointSemantics Sema) { + return Sema == SatPadding || Sema == SatNoPadding; +} + +void APFixedPoint::convert(const ASTContext &Context, const QualType &Ty) { + assert(Ty->isFixedPointType()); + + unsigned DstWidth = Context.getTypeSize(Ty); + unsigned DstScale = Context.getFixedPointScale(Ty); + enum FixedPointSemantics Sema = Context.getFixedPointSema(Ty); + saturatedRescaleAndResize(DstWidth, DstScale, Sema); +} + +bool APFixedPoint::rescaleAndResize(unsigned DstWidth, unsigned DstScale) { + assert(DstWidth > 0 && DstScale >= 0); + + if (DstWidth > Val.getBitWidth()) Val = Val.extend(DstWidth); + + bool Overflowed = false; + + if (DstScale > Scale) { + // We can overflow here + unsigned ShiftAmt = DstScale - Scale; + if (Val < 0 && Val.countLeadingOnes() >= ShiftAmt) + Overflowed = true; + else if (Val > 0 && Val.countLeadingZeros() >= ShiftAmt) + Overflowed = true; + + Val = Val.shl(ShiftAmt); + } else if (DstScale < Scale) { + Val = ShrToZero(Val, Scale - DstScale); + } + + // We can overflow here + if (DstWidth < Val.getBitWidth()) { + unsigned NumRemovedBits = Val.getBitWidth() - DstWidth; + if (Val < 0 && NumRemovedBits >= Val.countLeadingOnes()) { + Overflowed = true; + } else if (Val > 0 && NumRemovedBits >= Val.countLeadingZeros()) { + Overflowed = true; + } + + Val = Val.trunc(DstWidth); + } + + Scale = DstScale; + return Overflowed; +} + +void APFixedPoint::saturatedRescaleAndResize(unsigned DstWidth, + unsigned DstScale, + enum FixedPointSemantics DstSema) { + bool DstIsSaturated = IsSaturatedSema(DstSema); + bool IsNegative = Val < 0; + bool IsUnsigned = Val.isUnsigned(); + bool Overflowed = rescaleAndResize(DstWidth, DstScale); + + if (DstIsSaturated && Overflowed) { + if (IsNegative) { + // Use min + Val = llvm::APSInt::getMinValue(DstWidth, IsUnsigned); + } else { + // Use max + Val = llvm::APSInt::getMaxValue(DstWidth, IsUnsigned); + if (IsUnsigned && IsPaddingSema(DstSema)) Val = Val.lshr(1); + } + } + + 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 + if (Scale < OtherScale) + CommonWidth += OtherScale - Scale; + else if (Scale > OtherScale) + CommonWidth += Scale - OtherScale; + + ThisVal = ThisVal.extend(CommonWidth); + OtherVal = OtherVal.extend(CommonWidth); + + unsigned CommonScale = std::max(Scale, OtherScale); + if (Scale < CommonScale) ThisVal = ThisVal.shl(CommonScale - Scale); + if (OtherScale < CommonScale) + 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(unsigned NumBits, unsigned Scale, bool Signed, + enum FixedPointSemantics Sema) { + bool IsUnsigned = !Signed; + auto Val = llvm::APSInt::getMaxValue(NumBits, IsUnsigned); + if (IsUnsigned && IsPaddingSema(Sema)) Val = Val.lshr(1); + return APFixedPoint(Val, Scale, Sema); +} + +APFixedPoint APFixedPoint::getMin(unsigned NumBits, unsigned Scale, bool Signed, + enum FixedPointSemantics Sema) { + bool IsUnsigned = !Signed; + auto Val = llvm::APSInt::getMinValue(NumBits, IsUnsigned); + return APFixedPoint(Val, Scale, 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 SameFBits 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