[clang] aa0d19a - [Fixed Point] Add fixed-point shift operations and consteval.

Bevin Hansson via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 7 06:10:46 PDT 2020


Author: Bevin Hansson
Date: 2020-08-07T15:09:24+02:00
New Revision: aa0d19a0c8f5ebccb5768411dfc4feddff7bed08

URL: https://github.com/llvm/llvm-project/commit/aa0d19a0c8f5ebccb5768411dfc4feddff7bed08
DIFF: https://github.com/llvm/llvm-project/commit/aa0d19a0c8f5ebccb5768411dfc4feddff7bed08.diff

LOG: [Fixed Point] Add fixed-point shift operations and consteval.

Reviewers: rjmccall, leonardchan, bjope

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D83212

Added: 
    clang/test/Frontend/fixed_point_shift.c

Modified: 
    clang/include/clang/Basic/FixedPoint.h
    clang/lib/AST/ExprConstant.cpp
    clang/lib/Basic/FixedPoint.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/test/Frontend/fixed_point_errors.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/FixedPoint.h b/clang/include/clang/Basic/FixedPoint.h
index 0d181f30907f..ee52a2b0a615 100644
--- a/clang/include/clang/Basic/FixedPoint.h
+++ b/clang/include/clang/Basic/FixedPoint.h
@@ -132,17 +132,20 @@ class APFixedPoint {
   APFixedPoint mul(const APFixedPoint &Other, bool *Overflow = nullptr) const;
   APFixedPoint div(const APFixedPoint &Other, bool *Overflow = nullptr) const;
 
-  /// Perform a unary negation (-X) on this fixed point type, taking into
-  /// account saturation if applicable.
-  APFixedPoint negate(bool *Overflow = nullptr) const;
-
-  APFixedPoint shr(unsigned Amt) const {
+  // Perform shift operations on a fixed point type. Unlike the other binary
+  // operations, the resulting fixed point value will be in the original
+  // semantic.
+  APFixedPoint shl(unsigned Amt, bool *Overflow = nullptr) const;
+  APFixedPoint shr(unsigned Amt, bool *Overflow = nullptr) const {
+    // Right shift cannot overflow.
+    if (Overflow)
+      *Overflow = false;
     return APFixedPoint(Val >> Amt, Sema);
   }
 
-  APFixedPoint shl(unsigned Amt) const {
-    return APFixedPoint(Val << Amt, Sema);
-  }
+  /// Perform a unary negation (-X) on this fixed point type, taking into
+  /// account saturation if applicable.
+  APFixedPoint negate(bool *Overflow = nullptr) const;
 
   /// Return the integral part of this fixed point number, rounded towards
   /// zero. (-2.5k -> -2)

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d0587cb723bc..11ba8db24355 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13003,6 +13003,29 @@ bool FixedPointExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
                   .convert(ResultFXSema, &ConversionOverflow);
     break;
   }
+  case BO_Shl:
+  case BO_Shr: {
+    FixedPointSemantics LHSSema = LHSFX.getSemantics();
+    llvm::APSInt RHSVal = RHSFX.getValue();
+
+    unsigned ShiftBW =
+        LHSSema.getWidth() - (unsigned)LHSSema.hasUnsignedPadding();
+    unsigned Amt = RHSVal.getLimitedValue(ShiftBW - 1);
+    // Embedded-C 4.1.6.2.2:
+    //   The right operand must be nonnegative and less than the total number
+    //   of (nonpadding) bits of the fixed-point operand ...
+    if (RHSVal.isNegative())
+      Info.CCEDiag(E, diag::note_constexpr_negative_shift) << RHSVal;
+    else if (Amt != RHSVal)
+      Info.CCEDiag(E, diag::note_constexpr_large_shift)
+          << RHSVal << E->getType() << ShiftBW;
+
+    if (E->getOpcode() == BO_Shl)
+      Result = LHSFX.shl(Amt, &OpOverflow);
+    else
+      Result = LHSFX.shr(Amt, &OpOverflow);
+    break;
+  }
   default:
     return false;
   }

diff  --git a/clang/lib/Basic/FixedPoint.cpp b/clang/lib/Basic/FixedPoint.cpp
index ed8b92c98fdb..892b8c19fd79 100644
--- a/clang/lib/Basic/FixedPoint.cpp
+++ b/clang/lib/Basic/FixedPoint.cpp
@@ -309,6 +309,40 @@ APFixedPoint APFixedPoint::div(const APFixedPoint &Other,
                       CommonFXSema);
 }
 
+APFixedPoint APFixedPoint::shl(unsigned Amt, bool *Overflow) const {
+  llvm::APSInt ThisVal = Val;
+  bool Overflowed = false;
+
+  // Widen the LHS.
+  unsigned Wide = Sema.getWidth() * 2;
+  if (Sema.isSigned())
+    ThisVal = ThisVal.sextOrSelf(Wide);
+  else
+    ThisVal = ThisVal.zextOrSelf(Wide);
+
+  // Clamp the shift amount at the original width, and perform the shift.
+  Amt = std::min(Amt, ThisVal.getBitWidth());
+  llvm::APSInt Result = ThisVal << Amt;
+  Result.setIsSigned(Sema.isSigned());
+
+  // If our result lies outside of the representative range of the
+  // semantic, we either have overflow or saturation.
+  llvm::APSInt Max = APFixedPoint::getMax(Sema).getValue().extOrTrunc(Wide);
+  llvm::APSInt Min = APFixedPoint::getMin(Sema).getValue().extOrTrunc(Wide);
+  if (Sema.isSaturated()) {
+    if (Result < Min)
+      Result = Min;
+    else if (Result > Max)
+      Result = Max;
+  } else
+    Overflowed = Result < Min || Result > Max;
+
+  if (Overflow)
+    *Overflow = Overflowed;
+
+  return APFixedPoint(Result.sextOrTrunc(Sema.getWidth()), Sema);
+}
+
 void APFixedPoint::toString(llvm::SmallVectorImpl<char> &Str) const {
   llvm::APSInt Val = getValue();
   unsigned Scale = getScale();

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b681c930b2a7..28da268e637f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10601,9 +10601,13 @@ static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS,
   }
 
   QualType LHSExprType = LHS.get()->getType();
-  uint64_t LeftSize = LHSExprType->isExtIntType()
-                          ? S.Context.getIntWidth(LHSExprType)
-                          : S.Context.getTypeSize(LHSExprType);
+  uint64_t LeftSize = S.Context.getTypeSize(LHSExprType);
+  if (LHSExprType->isExtIntType())
+    LeftSize = S.Context.getIntWidth(LHSExprType);
+  else if (LHSExprType->isFixedPointType()) {
+    FixedPointSemantics FXSema = S.Context.getFixedPointSemantics(LHSExprType);
+    LeftSize = FXSema.getWidth() - (unsigned)FXSema.hasUnsignedPadding();
+  }
   llvm::APInt LeftBits(Right.getBitWidth(), LeftSize);
   if (Right.uge(LeftBits)) {
     S.DiagRuntimeBehavior(Loc, RHS.get(),
@@ -10612,7 +10616,8 @@ static void DiagnoseBadShiftValues(Sema& S, ExprResult &LHS, ExprResult &RHS,
     return;
   }
 
-  if (Opc != BO_Shl)
+  // FIXME: We probably need to handle fixed point types specially here.
+  if (Opc != BO_Shl || LHSExprType->isFixedPointType())
     return;
 
   // When left shifting an ICE which is signed, we can check for overflow which
@@ -10796,7 +10801,9 @@ QualType Sema::CheckShiftOperands(ExprResult &LHS, ExprResult &RHS,
   QualType RHSType = RHS.get()->getType();
 
   // C99 6.5.7p2: Each of the operands shall have integer type.
-  if (!LHSType->hasIntegerRepresentation() ||
+  // Embedded-C 4.1.6.2.2: The LHS may also be fixed-point.
+  if ((!LHSType->isFixedPointOrIntegerType() &&
+       !LHSType->hasIntegerRepresentation()) ||
       !RHSType->hasIntegerRepresentation())
     return InvalidOperands(Loc, LHS, RHS);
 

diff  --git a/clang/test/Frontend/fixed_point_errors.c b/clang/test/Frontend/fixed_point_errors.c
index 2fd8cfd1fdf7..6c41bf6df163 100644
--- a/clang/test/Frontend/fixed_point_errors.c
+++ b/clang/test/Frontend/fixed_point_errors.c
@@ -259,11 +259,30 @@ short _Accum mul_ovf1 = 200.0uhk * 10.0uhk;                   // expected-warnin
 short _Accum mul_ovf2 = (-0.5hr - 0.5hr) * (-0.5hr - 0.5hr);  // expected-warning {{overflow in expression; result is -1.0 with type 'short _Fract'}}
 short _Accum div_ovf1 = 255.0hk / 0.5hk;                      // expected-warning {{overflow in expression; result is -2.0 with type 'short _Accum'}}
 
+short _Accum shl_ovf1 = 255.0hk << 8;           // expected-warning {{overflow in expression; result is -256.0 with type 'short _Accum'}}
+short _Fract shl_ovf2 = -0.25hr << 3;           // expected-warning {{overflow in expression; result is 0.0 with type 'short _Fract'}}
+unsigned short _Accum shl_ovf3 = 100.5uhk << 3; // expected-warning {{overflow in expression; result is 36.0 with type 'unsigned short _Accum'}}
+short _Fract shl_ovf4 = 0.25hr << 2;            // expected-warning {{overflow in expression; result is -1.0 with type 'short _Fract'}}
+
+_Accum shl_bw1 = 0.000091552734375k << 32;                   // expected-warning {{shift count >= width of type}} \
+                                                                 expected-warning {{overflow in expression; result is -65536.0 with type '_Accum'}}
+unsigned _Fract shl_bw2 = 0.65ur << 16;                      // expected-warning {{shift count >= width of type}} \
+                                                                 expected-warning {{overflow in expression; result is 0.0 with type 'unsigned _Fract'}}
+_Sat short _Accum shl_bw3 = (_Sat short _Accum)80.0hk << 17; // expected-warning {{shift count >= width of type}}
+short _Accum shr_bw1 = 1.0hk >> 17;                          // expected-warning {{shift count >= width of type}}
+
+_Accum shl_neg1 = 25.5k << -5;  // expected-warning {{shift count is negative}} \
+                                                              // expected-warning {{overflow in expression; result is 0.0 with type '_Accum'}}
+_Accum shr_neg1 = 8.75k >> -9;  // expected-warning {{shift count is negative}}
+_Fract shl_neg2 = 0.25r << -17; // expected-warning {{shift count is negative}} \
+                                                              // expected-warning {{overflow in expression; result is 0.0 with type '_Fract'}}
+
 // No warnings for saturation
 short _Fract add_sat  = (_Sat short _Fract)0.5hr + 0.5hr;
 short _Accum sub_sat  = (_Sat short _Accum)-200.0hk - 80.0hk;
 short _Accum mul_sat  = (_Sat short _Accum)80.0hk * 10.0hk;
 short _Fract div_sat  = (_Sat short _Fract)0.9hr / 0.1hr;
+short _Accum shl_sat = (_Sat short _Accum)200.0hk << 5;
 
 // Division by zero
 short _Accum div_zero = 4.5k / 0.0lr;  // expected-error {{initializer element is not a compile-time constant}}

diff  --git a/clang/test/Frontend/fixed_point_shift.c b/clang/test/Frontend/fixed_point_shift.c
new file mode 100644
index 000000000000..cbd4f38ab8b0
--- /dev/null
+++ b/clang/test/Frontend/fixed_point_shift.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -ffixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED
+// RUN: %clang_cc1 -ffixed-point -fpadding-on-unsigned-fixed-point -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,UNSIGNED
+
+short _Accum sa_const1 = 1.0hk << 2;       // CHECK-DAG: @sa_const1 = {{.*}}global i16 512
+short _Accum sa_const2 = 0.5hk << 2;       // CHECK-DAG: @sa_const2 = {{.*}}global i16 256
+short _Accum sa_const3 = 10.0hk >> 3;      // CHECK-DAG: @sa_const3 = {{.*}}global i16 160
+short _Accum sa_const4 = 0.0546875hk << 8; // CHECK-DAG: @sa_const4 = {{.*}}global i16 1792
+short _Accum sa_const5 = -1.0hk << 2;      // CHECK-DAG: @sa_const5 = {{.*}}global i16 -512
+short _Accum sa_const6 = -255.0hk >> 8;    // CHECK-DAG: @sa_const6 = {{.*}}global i16 -128
+
+_Fract f_const1 = -1.0r >> 5;              // CHECK-DAG: @f_const1 = {{.*}}global i16 -1024
+_Fract f_const2 = 0.0052490234375r >> 3;   // CHECK-DAG: @f_const2 = {{.*}}global i16 21
+_Fract f_const3 = -0.0001r << 5;           // CHECK-DAG: @f_const3 = {{.*}}global i16 -96
+_Fract f_const4 = -0.75r >> 15;            // CHECK-DAG: @f_const4 = {{.*}}global i16 -1
+_Fract f_const5 = 0.078216552734375r << 3; // CHECK-DAG: @f_const5 = {{.*}}global i16 20504
+
+unsigned _Fract uf_const1 = 0.375ur >> 13;
+// SIGNED-DAG:   @uf_const1 = {{.*}}global i16 3
+// UNSIGNED-DAG: @uf_const1 = {{.*}}global i16 1
+unsigned _Fract uf_const2 = 0.0546875ur << 3;
+// SIGNED-DAG:   @uf_const2 = {{.*}}global i16 28672
+// UNSIGNED-DAG: @uf_const2 = {{.*}}global i16 14336
+
+_Sat short _Accum ssa_const1 = (_Sat short _Accum)31.875hk << 4; // CHECK-DAG: @ssa_const1 = {{.*}}global i16 32767
+_Sat short _Accum ssa_const2 = (_Sat short _Accum) - 1.0hk << 8; // CHECK-DAG: @ssa_const2 = {{.*}}global i16 -32768
+_Sat short _Accum ssa_const3 = (_Sat short _Accum)128.0hk << 8;  // CHECK-DAG: @ssa_const3 = {{.*}}global i16 32767
+_Sat short _Fract ssf_const1 = (_Sat short _Fract) - 0.5hr << 3; // CHECK-DAG: @ssf_const1 = {{.*}}global i8 -128
+
+_Sat unsigned _Fract suf_const1 = (_Sat unsigned _Fract)0.5r << 1;
+// SIGNED-DAG:   @suf_const1 = {{.*}}global i16 -1
+// UNSIGNED-DAG: @suf_const1 = {{.*}}global i16 32767
+_Sat unsigned _Fract suf_const2 = (_Sat unsigned _Fract)0.25r << 1;
+// SIGNED-DAG:   @suf_const2 = {{.*}}global i16 -32768
+// UNSIGNED-DAG: @suf_const2 = {{.*}}global i16 16384
+_Sat unsigned _Accum sua_const2 = (_Sat unsigned _Accum)128.0uk << 10;
+// SIGNED-DAG:   @sua_const2 = {{.*}}global i32 -1
+// UNSIGNED-DAG: @sua_const2 = {{.*}}global i32 2147483647


        


More information about the cfe-commits mailing list