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

Reply via email to