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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits