leonardchan created this revision.
leonardchan added reviewers: phosek, mcgrathr, jakehehrlich.
leonardchan added a project: clang.

This patch includes the logic for subtraction on saturated _Fract types and a 
test for thm.

- Also fixed incorrect minimum value for each _Fract type
- Getters for each fixed point min and max value for a given type
- Correction when casting from a fixed point to a float. If the fixed point 
data and sign bits do not take up the whole width of the integer (ie. has 
padding), we will need to either zero out the padding or extend the sign into 
the padding if it is negative to ensure the correct floating point value is 
produced after casting.

This is a child of https://reviews.llvm.org/D46986


Repository:
  rC Clang

https://reviews.llvm.org/D46987

Files:
  include/clang/AST/Type.h
  include/clang/Basic/FixedPoint.h.in
  lib/AST/Type.cpp
  lib/CodeGen/CGExprScalar.cpp
  test/Frontend/fixed_point_all_builtin_operations.c

Index: test/Frontend/fixed_point_all_builtin_operations.c
===================================================================
--- test/Frontend/fixed_point_all_builtin_operations.c
+++ test/Frontend/fixed_point_all_builtin_operations.c
@@ -88,6 +88,15 @@
     TYPE a = 0.7 ## SUFFIX; \
     TYPE b = 0.9 ## SUFFIX; \
     ASSERT(add ## ID(a, b) == 1.0 ## SUFFIX); \
+    a = -0.7 ## SUFFIX; \
+    b = -0.9 ## SUFFIX; \
+    ASSERT(add ## ID(a, b) == -0.5 ## SUFFIX - 0.5 ## SUFFIX); \
+    a = 0.7 ## SUFFIX; \
+    b = -0.9 ## SUFFIX; \
+    ASSERT(sub ## ID(a, b) == 1.0 ## SUFFIX); \
+    a = -0.7 ## SUFFIX; \
+    b = 0.9 ## SUFFIX; \
+    ASSERT(sub ## ID(a, b) == -0.5 ## SUFFIX - 0.5 ## SUFFIX); \
   }
 
 int main(){
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -806,6 +806,11 @@
   }
   Value *VisitAsTypeExpr(AsTypeExpr *CE);
   Value *VisitAtomicExpr(AtomicExpr *AE);
+
+  // For all fixed point values, we usually do not care about the padding bits,
+  // but if we convert to another type dependent on the whole value of the
+  // underlying type, we will need to either zero extend or sign extend.
+  llvm::Value* FixedPointExtendSignToPadding(const QualType& Ty, llvm::Value* Val);
 };
 }  // end anonymous namespace.
 
@@ -1675,6 +1680,41 @@
   return true;
 }
 
+// For all fixed point values, we usually do not care about the padding bits,
+// but if we convert to another type dependent on the whole value of the
+// underlying type, we will need to either zero extend or sign extend.
+llvm::Value* ScalarExprEmitter::FixedPointExtendSignToPadding(const QualType& Ty,
+                                                              llvm::Value* Val) {
+  assert(Ty->isFixedPointType());
+
+  llvm::Type *opTy = CGF.CGM.getTypes().ConvertType(Ty);
+  unsigned BitWidth = opTy->getIntegerBitWidth();
+  unsigned fbits = getFixedPointFBits(Ty);
+  unsigned ibits = getFixedPointIBits(Ty);
+
+  if (Ty->isSignedFixedPointType()) {
+    assert((BitWidth >= fbits + ibits + 1) &&
+           "Cannot fit signed fixed point bits into integral type");
+
+    unsigned ShiftBits = BitWidth - (fbits + ibits + 1);
+    if (!ShiftBits) {
+      return Val;
+    }
+
+    return Builder.CreateAShr(Builder.CreateShl(Val, ShiftBits), ShiftBits);
+  } else {
+    assert((BitWidth >= fbits + ibits) &&
+           "Cannot fit unsigned fixed point bits into integral type");
+
+    unsigned ShiftBits = BitWidth - (fbits + ibits);
+    if (!ShiftBits) {
+      return Val;
+    }
+
+    return Builder.CreateLShr(Builder.CreateShl(Val, ShiftBits), ShiftBits);
+  }
+}
+
 // VisitCastExpr - Emit code for an explicit or implicit cast.  Implicit casts
 // have to handle a more broad range of conversions than explicit casts, as they
 // handle things like function to ptr-to-function decay etc.
@@ -1894,8 +1934,12 @@
     assert(DestTy->isFloatingType());
     assert(E->getType()->isFixedPointType());
     unsigned fbits = getFixedPointFBits(E->getType());
-    return Builder.CreateFDiv(EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()),
-                              llvm::ConstantFP::get(CGF.CGM.FloatTy, (1ULL << fbits) * 1.0));
+
+    llvm::Value *Val = FixedPointExtendSignToPadding(E->getType(), Visit(E));
+    Val = EmitScalarConversion(Val, E->getType(), DestTy, CE->getExprLoc());
+
+    return Builder.CreateFDiv(
+        Val, llvm::ConstantFP::get(CGF.CGM.FloatTy, (1ULL << fbits) * 1.0));
   }
 
   case CK_IntegralCast:
@@ -3104,61 +3148,10 @@
     assert(op.LHS->getType() == op.RHS->getType());
     assert(op.LHS->getType() == opTy);
 
-    llvm::Value *SatMaxVal;
-    llvm::Value *SatMinVal;
-
-    const auto &BT = op.Ty->getAs<BuiltinType>();
-    switch (BT->getKind()) {
-      default: llvm_unreachable("Unhandled saturated signed fixed point type");
-      case BuiltinType::SatShortAccum:
-         SatMaxVal = llvm::ConstantInt::get(opTy, SACCUM_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, SACCUM_MIN_AS_INT);
-         break;
-      case BuiltinType::SatAccum:
-         SatMaxVal = llvm::ConstantInt::get(opTy, ACCUM_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, ACCUM_MIN_AS_INT);
-         break;
-      case BuiltinType::SatLongAccum:
-         SatMaxVal = llvm::ConstantInt::get(opTy, LACCUM_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, LACCUM_MIN_AS_INT);
-         break;
-      case BuiltinType::SatUShortAccum:
-         SatMaxVal = llvm::ConstantInt::get(opTy, USACCUM_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, USACCUM_MIN_AS_INT);
-         break;
-      case BuiltinType::SatUAccum:
-         SatMaxVal = llvm::ConstantInt::get(opTy, UACCUM_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, UACCUM_MIN_AS_INT);
-         break;
-      case BuiltinType::SatULongAccum:
-         SatMaxVal = llvm::ConstantInt::get(opTy, ULACCUM_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, ULACCUM_MIN_AS_INT);
-         break;
-      case BuiltinType::SatShortFract:
-         SatMaxVal = llvm::ConstantInt::get(opTy, SFRACT_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, SFRACT_MIN_AS_INT);
-         break;
-      case BuiltinType::SatFract:
-         SatMaxVal = llvm::ConstantInt::get(opTy, FRACT_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, FRACT_MIN_AS_INT);
-         break;
-      case BuiltinType::SatLongFract:
-         SatMaxVal = llvm::ConstantInt::get(opTy, LFRACT_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, LFRACT_MIN_AS_INT);
-         break;
-      case BuiltinType::SatUShortFract:
-         SatMaxVal = llvm::ConstantInt::get(opTy, USFRACT_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, USFRACT_MIN_AS_INT);
-         break;
-      case BuiltinType::SatUFract:
-         SatMaxVal = llvm::ConstantInt::get(opTy, UFRACT_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, UFRACT_MIN_AS_INT);
-         break;
-      case BuiltinType::SatULongFract:
-         SatMaxVal = llvm::ConstantInt::get(opTy, ULFRACT_MAX_AS_INT);
-         SatMinVal = llvm::ConstantInt::get(opTy, ULFRACT_MIN_AS_INT);
-         break;
-    }
+    llvm::Value *SatMaxVal = llvm::ConstantInt::get(
+        opTy, getFixedPointMaxVal(op.Ty));
+    llvm::Value *SatMinVal = llvm::ConstantInt::get(
+        opTy, getFixedPointMinVal(op.Ty));
 
     unsigned MSBBitShift;
     if (op.Ty->isSignedFixedPointType()) {
@@ -3237,6 +3230,59 @@
       return propagateFMFlags(V, op);
     }
 
+    if (op.Ty->isSaturatedFixedPointType()) {
+      llvm::Type *opTy = CGF.CGM.getTypes().ConvertType(op.Ty);
+
+      assert(op.LHS->getType() == op.RHS->getType());
+      assert(op.LHS->getType() == opTy);
+
+      llvm::Value *SatMaxVal = llvm::ConstantInt::get(
+          opTy, getFixedPointMaxVal(op.Ty));
+      llvm::Value *SatMinVal = llvm::ConstantInt::get(
+          opTy, getFixedPointMinVal(op.Ty));
+
+      unsigned MSBBitShift;
+      if (op.Ty->isSignedFixedPointType()) {
+        MSBBitShift = getFixedPointIBits(op.Ty) + getFixedPointFBits(op.Ty);
+      } else {
+        MSBBitShift = getFixedPointIBits(op.Ty) + getFixedPointFBits(op.Ty) - 1;
+      }
+
+      llvm::Value *Diff = Builder.CreateSub(op.LHS, op.RHS);
+      llvm::Value *LHSMSB = Builder.CreateLShr(op.LHS, MSBBitShift);
+      llvm::Value *RHSMSB = Builder.CreateLShr(op.RHS, MSBBitShift);
+      llvm::Value *ResultMSB = Builder.CreateLShr(Diff, MSBBitShift);
+
+      if (op.Ty->isSignedFixedPointType()) {
+        // Cap at max if the LHS MSB is 0 and the RHS MSB is 1 and the result
+        // MSB is 1
+        llvm::Value *UseSatMax = Builder.CreateAnd(
+            Builder.CreateNot(LHSMSB),
+            Builder.CreateAnd(RHSMSB, ResultMSB));
+        UseSatMax = Builder.CreateIntCast(
+            UseSatMax,
+            llvm::Type::getInt1Ty(ResultMSB->getContext()), /*isSigned=*/true);
+
+        // Cap at min if the LHS MSB is 1 and the RHS MSB is 0 and the result
+        // MSB is 0
+        llvm::Value *UseSatMin = Builder.CreateAnd(
+            LHSMSB,
+            Builder.CreateNot(Builder.CreateOr(RHSMSB, ResultMSB)));
+        UseSatMin = Builder.CreateIntCast(
+            UseSatMin,
+            llvm::Type::getInt1Ty(ResultMSB->getContext()), /*isSigned=*/true);
+
+        return Builder.CreateSelect(
+            UseSatMax, SatMaxVal, Builder.CreateSelect(UseSatMin, SatMinVal, Diff));
+      } else {
+        // Cap at min if the LHS MSB is 0 and the resulting MSB is 1
+        llvm::Value *UseSatMin = Builder.CreateAnd(Builder.CreateNot(LHSMSB), ResultMSB);
+        UseSatMin = Builder.CreateIntCast(
+            UseSatMin,
+            llvm::Type::getInt1Ty(ResultMSB->getContext()), /*isSigned=*/true);
+        return Builder.CreateSelect(UseSatMin, SatMinVal, Diff);
+      }
+    }
     return Builder.CreateSub(op.LHS, op.RHS, "sub");
   }
 
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -4215,3 +4215,93 @@
       return Context.SatLongFractTy;
   }
 }
+
+uint64_t clang::getFixedPointMaxVal(const QualType& Ty) {
+  assert(Ty->isFixedPointType());
+
+  const auto &BT = Ty->getAs<BuiltinType>();
+  switch (BT->getKind()) {
+    default: llvm_unreachable("Unhandled fixed point type");
+    case BuiltinType::ShortAccum:
+    case BuiltinType::SatShortAccum:
+       return SACCUM_MAX_AS_INT;
+    case BuiltinType::Accum:
+    case BuiltinType::SatAccum:
+       return ACCUM_MAX_AS_INT;
+    case BuiltinType::LongAccum:
+    case BuiltinType::SatLongAccum:
+       return LACCUM_MAX_AS_INT;
+    case BuiltinType::UShortAccum:
+    case BuiltinType::SatUShortAccum:
+       return USACCUM_MAX_AS_INT;
+    case BuiltinType::UAccum:
+    case BuiltinType::SatUAccum:
+       return UACCUM_MAX_AS_INT;
+    case BuiltinType::ULongAccum:
+    case BuiltinType::SatULongAccum:
+       return ULACCUM_MAX_AS_INT;
+    case BuiltinType::ShortFract:
+    case BuiltinType::SatShortFract:
+       return SFRACT_MAX_AS_INT;
+    case BuiltinType::Fract:
+    case BuiltinType::SatFract:
+       return FRACT_MAX_AS_INT;
+    case BuiltinType::LongFract:
+    case BuiltinType::SatLongFract:
+       return LFRACT_MAX_AS_INT;
+    case BuiltinType::UShortFract:
+    case BuiltinType::SatUShortFract:
+       return USFRACT_MAX_AS_INT;
+    case BuiltinType::UFract:
+    case BuiltinType::SatUFract:
+       return UFRACT_MAX_AS_INT;
+    case BuiltinType::ULongFract:
+    case BuiltinType::SatULongFract:
+       return ULFRACT_MAX_AS_INT;
+  }
+}
+
+uint64_t clang::getFixedPointMinVal(const QualType& Ty) {
+  assert(Ty->isFixedPointType());
+
+  const auto &BT = Ty->getAs<BuiltinType>();
+  switch (BT->getKind()) {
+    default: llvm_unreachable("Unhandled fixed point type");
+    case BuiltinType::ShortAccum:
+    case BuiltinType::SatShortAccum:
+       return SACCUM_MIN_AS_INT;
+    case BuiltinType::Accum:
+    case BuiltinType::SatAccum:
+       return ACCUM_MIN_AS_INT;
+    case BuiltinType::LongAccum:
+    case BuiltinType::SatLongAccum:
+       return LACCUM_MIN_AS_INT;
+    case BuiltinType::UShortAccum:
+    case BuiltinType::SatUShortAccum:
+       return USACCUM_MIN_AS_INT;
+    case BuiltinType::UAccum:
+    case BuiltinType::SatUAccum:
+       return UACCUM_MIN_AS_INT;
+    case BuiltinType::ULongAccum:
+    case BuiltinType::SatULongAccum:
+       return ULACCUM_MIN_AS_INT;
+    case BuiltinType::ShortFract:
+    case BuiltinType::SatShortFract:
+       return SFRACT_MIN_AS_INT;
+    case BuiltinType::Fract:
+    case BuiltinType::SatFract:
+       return FRACT_MIN_AS_INT;
+    case BuiltinType::LongFract:
+    case BuiltinType::SatLongFract:
+       return LFRACT_MIN_AS_INT;
+    case BuiltinType::UShortFract:
+    case BuiltinType::SatUShortFract:
+       return USFRACT_MIN_AS_INT;
+    case BuiltinType::UFract:
+    case BuiltinType::SatUFract:
+       return UFRACT_MIN_AS_INT;
+    case BuiltinType::ULongFract:
+    case BuiltinType::SatULongFract:
+       return ULFRACT_MIN_AS_INT;
+  }
+}
Index: include/clang/Basic/FixedPoint.h.in
===================================================================
--- include/clang/Basic/FixedPoint.h.in
+++ include/clang/Basic/FixedPoint.h.in
@@ -56,9 +56,9 @@
 #define ULACCUM_MIN_AS_INT      0ULL
 
 // Min values of each _Fract type as integer bytes
-#define SFRACT_MIN_AS_INT       (1ULL << BUILTIN_SFRACT_FBIT)
-#define FRACT_MIN_AS_INT        (1ULL << BUILTIN_FRACT_FBIT)
-#define LFRACT_MIN_AS_INT       (1ULL << BUILTIN_LFRACT_FBIT)
+#define SFRACT_MIN_AS_INT       (-SFRACT_MAX_AS_INT - 1)
+#define FRACT_MIN_AS_INT        (-FRACT_MAX_AS_INT - 1)
+#define LFRACT_MIN_AS_INT       (-LFRACT_MAX_AS_INT - 1)
 #define USFRACT_MIN_AS_INT      0ULL
 #define UFRACT_MIN_AS_INT       0ULL
 #define ULFRACT_MIN_AS_INT      0ULL
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -6570,6 +6570,14 @@
   return getFixedPointIBits(*Ty);
 }
 
+// Return the highest possible value for this fixed point type represented as an
+// integer.
+uint64_t getFixedPointMaxVal(const QualType& Ty);
+
+// Return the smallest possible value for this fixed point type represented as an
+// integer.
+uint64_t getFixedPointMinVal(const QualType& Ty);
+
 // For a given fixed point type, if the type is unsaturated,
 // return the saturated equivalent of it. Otherwise if it is
 // saturated, return back the type itself.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to