leonardchan created this revision. leonardchan added reviewers: phosek, mcgrathr, jakehehrlich, rsmith, ebevhan, rjmccall. leonardchan added a project: clang.
This patch proposes an abstract type that represents fixed point numbers, similar to APInt or APSInt that was discussed in https://reviews.llvm.org/D48456#inline-425585. This type holds a value, scale, and saturation and is meant to perform intermediate calculations on constant fixed point values. Currently this class is used as a way for handling the conversions between fixed point numbers with different sizes and radixes. For example, if I'm casting from a signed _Accum to a saturated unsigned short _Accum, I will need to check the value of the signed _Accum to see if it fits into the short _Accum which involves getting and comparing against the max/min values of the short _Accum. The FixedPointNumber class currently handles the radix shifting and extension when converting to a signed _Accum. Tests will be added next. Just wanted to expose the type now in case anyone has any early comments. Repository: rC Clang https://reviews.llvm.org/D48661 Files: include/clang/AST/ASTContext.h include/clang/Basic/FixedPoint.h include/clang/Basic/TargetInfo.h lib/AST/ASTContext.cpp lib/Sema/SemaExpr.cpp test/Frontend/fixed_point_declarations.c
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: 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: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -10274,3 +10274,106 @@ return 0; } } + +FixedPointNumber ASTContext::getFixedPointMax(QualType Ty) const { + assert(Ty->isFixedPointType()); + unsigned NumBits = getTypeSize(Ty); + bool IsUnsigned = Ty->isUnsignedFixedPointType(); + auto Val = llvm::APSInt::getMaxValue(NumBits, IsUnsigned); + FixedPointNumber Fixed(Val, getFixedPointScale(Ty), + Ty->isSaturatedFixedPointType()); + if (IsUnsigned && getTargetInfo().unsignedFixedPointTypesHavePadding()) + Fixed = Fixed.shr(1); + return Fixed; +} + +FixedPointNumber ASTContext::getFixedPointMin(QualType Ty) const { + assert(Ty->isFixedPointType()); + unsigned NumBits = getTypeSize(Ty); + bool IsUnsigned = Ty->isUnsignedFixedPointType(); + auto Val = llvm::APSInt::getMinValue(NumBits, IsUnsigned); + return FixedPointNumber(Val, getFixedPointScale(Ty), + Ty->isSaturatedFixedPointType()); +} + +void FixedPointNumber::convert(const ASTContext &Context, const QualType &Ty) { + assert(Ty->isFixedPointType()); + + if (Ty->isSaturatedFixedPointType()) { + FixedPointNumber MaxVal = Context.getFixedPointMax(Ty); + FixedPointNumber MinVal = Context.getFixedPointMin(Ty); + if (*this > MaxVal) { + *this = MaxVal; + return; + } + if (*this < MinVal) { + *this = MinVal; + return; + } + } + + unsigned DstWidth = Context.getTypeSize(Ty); + unsigned DstScale = Context.getFixedPointScale(Ty); + unsigned SrcWidth = Val_.getBitWidth(); + bool SrcIsSigned = Val_.isSigned(); + + if (DstWidth > SrcWidth) Val_ = Val_.extend(DstWidth); + + if (DstScale > Scale_) { + Val_ = Val_.shl(DstScale - Scale_); + } else if (DstScale < Scale_) { + Val_ = SrcIsSigned ? Val_.ashr(Scale_ - DstScale) + : Val_.lshr(Scale_ - DstScale); + } + + if (DstWidth < SrcWidth) Val_ = Val_.trunc(DstWidth); + + Saturated_ = Ty->isSaturatedFixedPointType(); +} + +int FixedPointNumber::compare(const FixedPointNumber &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); + 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; +} Index: include/clang/Basic/TargetInfo.h =================================================================== --- include/clang/Basic/TargetInfo.h +++ include/clang/Basic/TargetInfo.h @@ -311,6 +311,13 @@ } } + /// 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. + /// False otherwise. + bool unsignedFixedPointTypesHavePadding() const { return SameFBits; } + /// Return the width (in bits) of the specified integer type enum. /// /// For example, SignedInt -> getIntWidth(). Index: include/clang/Basic/FixedPoint.h =================================================================== --- /dev/null +++ include/clang/Basic/FixedPoint.h @@ -0,0 +1,88 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_FIXEDPOINT_H +#define LLVM_CLANG_BASIC_FIXEDPOINT_H + +#include "clang/AST/ASTContext.h" +#include "llvm/ADT/APSInt.h" + +namespace clang { + +class FixedPointNumber { + public: + FixedPointNumber(const llvm::APSInt &Val, unsigned Scale, + bool Saturated = false) + : Val_(Val), Scale_(Scale), Saturated_(Saturated) {} + + llvm::APSInt getValue() const { return Val_; } + unsigned getScale() const { return Scale_; } + bool isSaturated() const { return Saturated_; } + + // Convert this number to match the scale, width, and saturation of the + // provided fixed point type. + void convert(const ASTContext &Context, const QualType &Ty); + + FixedPointNumber shr(unsigned Amt) const { + return FixedPointNumber(Val_ >> Amt, Scale_, Saturated_); + } + + FixedPointNumber shl(unsigned Amt) const { + return FixedPointNumber(Val_ << Amt, Scale_, Saturated_); + } + + FixedPointNumber extend(unsigned Width) const { + llvm::APSInt ValCpy = Val_; + if (ValCpy.getBitWidth() < Width) ValCpy = ValCpy.extend(Width); + return FixedPointNumber(ValCpy, Scale_, Saturated_); + } + + FixedPointNumber trunc(unsigned Width) const { + llvm::APSInt ValCpy = Val_; + if (ValCpy.getBitWidth() > Width) ValCpy = ValCpy.trunc(Width); + return FixedPointNumber(ValCpy, Scale_, Saturated_); + } + + llvm::APSInt getIntPart() const { return Val_ >> Scale_; } + + // If LHS > RHS, return 1. If LHS == RHS, return 0. If LHS < RHS, return -1. + int compare(const FixedPointNumber &Other) const; + bool operator==(const FixedPointNumber &Other) const { + return compare(Other) == 0; + } + bool operator!=(const FixedPointNumber &Other) const { + return compare(Other) != 0; + } + bool operator>(const FixedPointNumber &Other) const { + return compare(Other) > 0; + } + bool operator<(const FixedPointNumber &Other) const { + return compare(Other) < 0; + } + bool operator>=(const FixedPointNumber &Other) const { + return compare(Other) >= 0; + } + bool operator<=(const FixedPointNumber &Other) const { + return compare(Other) <= 0; + } + + private: + llvm::APSInt Val_; + unsigned Scale_; + bool Saturated_; +}; + +} // namespace clang + +#endif 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,8 @@ unsigned char getFixedPointScale(QualType Ty) const; unsigned char getFixedPointIBits(QualType Ty) const; + FixedPointNumber getFixedPointMax(QualType Ty) const; + FixedPointNumber getFixedPointMin(QualType Ty) const; DeclarationNameInfo getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits