leonardchan updated this revision to Diff 147880.
leonardchan added a comment.

formatting


Repository:
  rC Clang

https://reviews.llvm.org/D46925

Files:
  include/clang/AST/ASTContext.h
  include/clang/AST/OperationKinds.def
  include/clang/AST/Type.h
  lib/AST/ASTContext.cpp
  lib/AST/Expr.cpp
  lib/AST/ExprConstant.cpp
  lib/AST/Type.cpp
  lib/CodeGen/CGExpr.cpp
  lib/CodeGen/CGExprAgg.cpp
  lib/CodeGen/CGExprComplex.cpp
  lib/CodeGen/CGExprConstant.cpp
  lib/CodeGen/CGExprScalar.cpp
  lib/Edit/RewriteObjCFoundationAPI.cpp
  lib/Sema/SemaExpr.cpp
  lib/StaticAnalyzer/Core/ExprEngineC.cpp
  test/Frontend/fixed_point_validation.c

Index: test/Frontend/fixed_point_validation.c
===================================================================
--- test/Frontend/fixed_point_validation.c
+++ test/Frontend/fixed_point_validation.c
@@ -74,6 +74,9 @@
   assert(2 == s_accum);
   // CHECK:      {{.*}} = icmp eq i16 256, {{.*}}
 
+  assert(2 == 2.0hk);
+  assert(2 != 2.01hk);
+
   int x = 2;
   assert(s_accum == x);
   // CHECK:      {{.*}} = load i32, i32* %x, align 4
@@ -128,6 +131,46 @@
   // numbers.
   assert(2 == 2.001hk);  // This is valid if SACCUM_FBITS == 7
 
+  // Comparisons between fixed-point types
+  // Signed _Accum to signed _Accum types.
+  assert(2.5hk == 2.5k);
+  assert(2.5k == 2.5lk);
+  assert(-2.5hk == -2.5k);
+  assert(-2.5k == -2.5lk);
+
+  // Unsigned _Accum to unigned _Accum
+  assert(2.5uhk == 2.5uk);
+  assert(2.5uk == 2.5ulk);
+
+  // Signed _Fract to signed _Fract types.
+  assert(0.333hr != 0.333r);  // Loss of precision since different fractional widths
+  assert(0.333r != 0.333lr);
+  assert(-0.333hr != -0.333r);
+  assert(-0.333r != -0.333lr);
+
+  // Unsigned _Fract to unsigned _Fract types.
+  assert(0.333uhr != 0.333ur);
+  assert(0.333ur != 0.333ulr);
+
+  // Signed _Accum to signed _Fract
+  assert(0.333hk == 0.333hr);
+  assert(0.333k == 0.333r);
+  assert(0.333lk == 0.333lr);
+  assert(0.333hk == 0.333r);  // Although _Fract has higher precision, it gets casted up to
+                              // short _Accum which (using default precisions)
+                              // has fewer fractional bits.
+
+  // Signed _Accum to unsigned _Fract
+  assert(0.333hk == 0.333uhr);
+  assert(0.333k == 0.333ur);
+  assert(0.333lk == 0.333ulr);
+
+  // Signed _Accum to unsigned _Accum
+  assert(2.5hk == 2.5uhk);
+  assert(2.5k == 2.5uk);
+  assert(2.5lk == 2.5ulk);
+
+
   /**************** Unary operations ***************/
 
   s_accum = 0.0hk;
@@ -167,4 +210,58 @@
   assert(+s_fract == s_fract);
   assert(+s_fract2 == s_fract2);  // s_fract2 is negative
   assert(-s_fract == s_fract2);
+
+  /**************** Binary operations ***************/
+
+  // Addition
+  s_accum = 3.0hk;
+  short _Accum s_accum_sum = s_accum + s_accum2;
+  assert(s_accum_sum == 5);
+  assert(s_fract + s_fract2 == 0);
+
+  // Subtraction
+  short _Accum s_accum_diff = s_accum - s_accum2;
+  assert(s_accum_diff == 1);
+  assert(s_accum2 - s_accum == -1);
+
+  // Multiplication
+  short _Accum s_accum_mul = s_accum * s_accum2;
+  assert(s_accum_mul == 6);
+  assert(2.0hk * 3.0hk == 6);
+  assert(2.0hk * 3 == 6);
+  assert(2.5hk * 3 == 7.5k);
+  assert(-2.5hk * 3 == -7.5lk);
+  assert(3 * -2.5hk == -7.5hk);
+  assert(-2.5hk * 0 == 0);
+
+  // Division
+  const short _Accum s_accum3 = 2.5hk;
+  short _Accum s_accum_div = s_accum3 / s_accum2;
+  assert(s_accum_div == 1.25hk);
+  assert(5.0hk / s_accum3 == 2);
+  assert(-5.0hk / s_accum3 == -2);
+  assert(9.9k / 3.3k == 3);
+  assert(9.9hk / 3.3k != 3);  // We lose precision when converting between types of different
+                              // fractional width.
+  assert(6.75hk / 2.25k == 3);  // Unless the fractional part can be evenly represented with
+                                // sums of powers of 2.
+  assert(0 / 2.0hk == 0);
+
+  // Left shift
+  short _Accum s_accum_shl = s_accum2 << 3;
+  assert(s_accum_shl == 16);
+  assert(1.0hk << 3 == 8);
+  assert(-1.0hk << 3 == -8);
+  assert(1.5k << 1 == 3);  // LShift is equivalent to multiplying by 2
+  assert(-1.25hk << 2 == -5);
+
+  // Right shift
+  const signed short _Accum s_accum4 = 16.0hk;
+  short _Accum s_accum_shr = s_accum4 >> 3;
+  assert(s_accum_shr == 2);
+  assert(s_accum_shr >> 1 == 1);
+  assert(s_accum_shr >> 2 == 0.5hr);  // RShift is equivalent to dividing by 2
+  assert(5.0hk >> 2 == 1.25hk);
+  assert(-5.0hk >> 2 == -1.25k);
+  assert(0.0hr >> 2 == 0);
 }
Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -321,6 +321,8 @@
     const LocationContext *LCtx = Pred->getLocationContext();
 
     switch (CastE->getCastKind()) {
+      case CK_FixedPointCast:
+        llvm_unreachable("CK_FixedPointCast");  // TODO
       case CK_IntegralToFixedPoint:
         llvm_unreachable(
             "ExprEngine::VisitCast CK_IntegralToFixedPoint");  // TODO
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -1218,7 +1218,6 @@
                               CK_IntegralRealToComplex);
   return ComplexType;
 }
-
 /// \brief Handle arithmetic conversion from integer to fixed point.  Helper
 /// function of UsualArithmeticConversions()
 static QualType handleIntToFixedPointConversion(Sema &S,
@@ -1248,15 +1247,38 @@
 
   if (LHSFixed && RHSFixed) {
     // Cast up the smaller operand to the bigger
-    llvm_unreachable("Unhandled conversion between fixed point types");  // TODO
+    int order = S.Context.getFixedPointTypeOrder(LHSType, RHSType);
+    if (order > 0) {
+      RHS = S.ImpCastExprToType(RHS.get(), LHSType, CK_FixedPointCast);
+      return LHSType;
+    } else if (!order) {
+      bool LHSSigned = LHSType->isSignedFixedPointType();
+      bool RHSSigned = RHSType->isSignedFixedPointType();
+      if (LHSSigned && !RHSSigned) {
+        RHS = S.ImpCastExprToType(RHS.get(), LHSType, CK_FixedPointCast);
+        return LHSType;
+      } else if (!LHSSigned && RHSSigned) {
+        if (!IsCompAssign)
+          LHS = S.ImpCastExprToType(LHS.get(), RHSType, CK_FixedPointCast);
+        return RHSType;
+      } else {
+        assert(LHSType == RHSType);
+        return LHSType;
+      }
+    } else {
+      if (!IsCompAssign)
+        LHS = S.ImpCastExprToType(LHS.get(), RHSType, CK_FixedPointCast);
+      return RHSType;
+    }
   } else if (LHSFixed) {
     assert(RHSType->isIntegerType());
     return handleIntToFixedPointConversion(S, LHS, RHS, LHSType, RHSType);
   } else if (RHSFixed) {
     assert(LHSType->isIntegerType());
     return handleIntToFixedPointConversion(S, RHS, LHS, RHSType, LHSType);
   } else {
-    llvm_unreachable("Expected LHS and RHS to both be fixed point types.");
+    llvm_unreachable(
+        "Expected LHS and RHS to both be fixed point or integral types.");
   }
 }
 
@@ -3509,9 +3531,9 @@
       // Make sure the integral part fits into the integral bits we have.
       uint64_t max_int_val = 0;
       if (isSigned) {
-        max_int_val = 1 << (ibits - 1);  // min signed is -2^(n-1)
+        max_int_val = 1ULL << (ibits - 1);  // min signed is -2^(n-1)
       } else {
-        max_int_val = (1 << ibits) - 1;
+        max_int_val = (1ULL << ibits) - 1;
       }
 
       // TODO: What should be done for literals with no unsigned suffix whose
@@ -3526,7 +3548,7 @@
     }
 
     uint64_t fract_part_as_int =
-        static_cast<uint64_t>(fabs(fract_part) * (1 << fbits));
+        static_cast<uint64_t>(fract_part * (1ULL << fbits));
     uint64_t final_fixed_point_as_int =
         (int_part_as_int << fbits) + fract_part_as_int;
 
@@ -9531,8 +9553,11 @@
 
   // C99 6.5.7p2: Each of the operands shall have integer type.
   if (!LHSType->hasIntegerRepresentation() ||
-      !RHSType->hasIntegerRepresentation())
-    return InvalidOperands(Loc, LHS, RHS);
+      !RHSType->hasIntegerRepresentation()) {
+    if (!LHSType->isFixedPointType()) {
+      return InvalidOperands(Loc, LHS, RHS);
+    }
+  }
 
   // C++0x: Don't allow scoped enums. FIXME: Use something better than
   // hasIntegerRepresentation() above instead of this.
Index: lib/Edit/RewriteObjCFoundationAPI.cpp
===================================================================
--- lib/Edit/RewriteObjCFoundationAPI.cpp
+++ lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -1002,6 +1002,9 @@
 
   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
     switch (ICE->getCastKind()) {
+    case CK_FixedPointCast:
+      llvm_unreachable(
+          "rewriteToNumericBoxedExpression CK_FixedPointCast");  // TODO
     case CK_IntegralToFixedPoint:
       llvm_unreachable(
           "rewriteToNumericBoxedExpression CK_IntegralToFixedPoint");  // TODO
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -655,6 +655,42 @@
       }
     }
 
+    // If either is a fixed point, we will need to cast up before multiplying.
+    if (Ops.Ty->isFixedPointType()) {
+      const auto *BinExpr = dyn_cast<BinaryOperator>(Ops.E);
+      const Expr *LHS = BinExpr->getLHS();
+      const Expr *RHS = BinExpr->getRHS();
+      Value *LHSVal = Ops.LHS;
+      Value *RHSVal = Ops.RHS;
+      QualType LHSTy = LHS->getType();
+      QualType RHSTy = RHS->getType();
+
+      // At least one side should be implicitely casted up to fixed point
+      assert(LHSTy->isFixedPointType() && RHSTy->isFixedPointType());
+      assert(LHSTy == RHSTy);
+
+      bool isSignedResult =
+          LHSTy->isSignedFixedPointType() || RHSTy->isSignedFixedPointType();
+
+      // Round up the bit widths to allocate enough space for calculating the
+      // result.
+      unsigned LHSWidth = CGF.getContext().getIntWidth(LHSTy);
+      unsigned bufferWidth = LHSWidth * 2;
+      if (bufferWidth > 128) {
+        bufferWidth = 128;
+      }
+
+      LHSVal = Builder.CreateIntCast(LHSVal, Builder.getIntNTy(bufferWidth),
+                                     isSignedResult);
+      RHSVal = Builder.CreateIntCast(RHSVal, Builder.getIntNTy(bufferWidth),
+                                     isSignedResult);
+
+      llvm::Value *MulResult = Builder.CreateMul(LHSVal, RHSVal);
+      MulResult = Builder.CreateAShr(MulResult, getFixedPointFBits(Ops.Ty));
+      return Builder.CreateIntCast(MulResult, Builder.getIntNTy(LHSWidth),
+                                   isSignedResult);
+    }
+
     if (Ops.Ty->isUnsignedIntegerType() &&
         CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow) &&
         !CanElideOverflowCheck(CGF.getContext(), Ops))
@@ -1084,7 +1120,8 @@
   }
 
   if (isa<llvm::IntegerType>(SrcTy)) {
-    bool InputSigned = SrcType->isSignedIntegerOrEnumerationType();
+    bool InputSigned = (SrcType->isSignedIntegerOrEnumerationType() ||
+                        SrcType->isFixedPointType());
     if (SrcType->isBooleanType() && TreatBooleanAsSigned) {
       InputSigned = true;
     }
@@ -1777,56 +1814,48 @@
     return Builder.CreateVectorSplat(NumElements, Elt, "splat");
   }
 
-  case CK_IntegralToFixedPoint: {
+  case CK_FixedPointCast: {
+    // Casting between fixed point types involves separating the integral and
+    // fractional bits, potentially shifting them, then joining back together.
     assert(DestTy->isFixedPointType());
-    assert(E->getType()->isIntegerType());
+    assert(E->getType()->isFixedPointType());
 
-    unsigned fbits;
-    const auto *BT = DestTy->getAs<BuiltinType>();
-    switch (BT->getKind()) {
-      default:
-        llvm_unreachable("Not a fixed point type!");
-      case BuiltinType::ShortAccum:
-        fbits = BUILTIN_SACCUM_FBIT;
-        break;
-      case BuiltinType::Accum:
-        fbits = BUILTIN_ACCUM_FBIT;
-        break;
-      case BuiltinType::LongAccum:
-        fbits = BUILTIN_LACCUM_FBIT;
-        break;
-      case BuiltinType::UShortAccum:
-        fbits = BUILTIN_USACCUM_FBIT;
-        break;
-      case BuiltinType::UAccum:
-        fbits = BUILTIN_UACCUM_FBIT;
-        break;
-      case BuiltinType::ULongAccum:
-        fbits = BUILTIN_ULACCUM_FBIT;
-        break;
-      case BuiltinType::ShortFract:
-        fbits = BUILTIN_SFRACT_FBIT;
-        break;
-      case BuiltinType::Fract:
-        fbits = BUILTIN_FRACT_FBIT;
-        break;
-      case BuiltinType::LongFract:
-        fbits = BUILTIN_LFRACT_FBIT;
-        break;
-      case BuiltinType::UShortFract:
-        fbits = BUILTIN_USFRACT_FBIT;
-        break;
-      case BuiltinType::UFract:
-        fbits = BUILTIN_UFRACT_FBIT;
-        break;
-      case BuiltinType::ULongFract:
-        fbits = BUILTIN_ULFRACT_FBIT;
-        break;
+    unsigned dest_fbits = getFixedPointFBits(DestTy);
+    unsigned src_fbits = getFixedPointFBits(E->getType());
+    unsigned dest_ibits = getFixedPointIBits(DestTy);
+    unsigned src_ibits = getFixedPointIBits(E->getType());
+
+    llvm::Value *result =
+        EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc());
+
+    // If the number of integral bits is decreasing, trim off any extra bits
+    // while retaining the sign.
+    if (dest_ibits < src_ibits) {
+      result = Builder.CreateShl(result, src_ibits - dest_ibits);
+      result = Builder.CreateAShr(result, src_ibits - dest_ibits);
+    }
+
+    // Move the radix. For irrational numbers, there will be loss of precision
+    // using this method when the number of fbits increases since we will be
+    // right padding zeros. Precision can still be retained if we temporarily
+    // convert to a float and perform some floating point arithmetic, though
+    // this may cost more. Enable that if #pragma FX_FULL_PRECISION is provided.
+    if (dest_fbits > src_fbits) {
+      result =
+          EmitScalarConversion(result, E->getType(), DestTy, CE->getExprLoc());
+      result = Builder.CreateShl(result, dest_fbits - src_fbits);
+    } else if (dest_fbits < src_fbits) {
+      result = Builder.CreateAShr(result, src_fbits - dest_fbits);
     }
+    return result;
+  }
 
+  case CK_IntegralToFixedPoint: {
+    assert(DestTy->isFixedPointType());
+    assert(E->getType()->isIntegerType());
     return Builder.CreateShl(
         EmitScalarConversion(Visit(E), E->getType(), DestTy, CE->getExprLoc()),
-        fbits, "fixed_point_shl");
+        getFixedPointFBits(DestTy), "int_to_fixed");
   }
 
   case CK_IntegralCast:
@@ -2175,7 +2204,7 @@
         break;
     }
     llvm::Value *amt =
-        llvm::ConstantInt::get(value->getType(), 1 << fbits,
+        llvm::ConstantInt::get(value->getType(), 1ULL << fbits,
                                /*isSigned=*/type->isSignedFixedPointType());
     if (isInc) {
       value = Builder.CreateAdd(value, amt, "fixed_point_post_inc");
@@ -2650,6 +2679,42 @@
     }
   }
 
+  // If either is a fixed point, we will need to cast up before dividing.
+  if (Ops.Ty->isFixedPointType()) {
+    const auto *BinExpr = dyn_cast<BinaryOperator>(Ops.E);
+    const Expr *LHS = BinExpr->getLHS();
+    const Expr *RHS = BinExpr->getRHS();
+    Value *LHSVal = Ops.LHS;
+    Value *RHSVal = Ops.RHS;
+    QualType LHSTy = LHS->getType();
+    QualType RHSTy = RHS->getType();
+
+    // At least one side should be implicitely casted up to fixed point
+    assert(LHSTy->isFixedPointType() && RHSTy->isFixedPointType());
+    assert(LHSTy == RHSTy);
+
+    bool isSignedResult =
+        LHSTy->isSignedFixedPointType() || RHSTy->isSignedFixedPointType();
+
+    // Round up the bit widths to allocate enough space for calculating the
+    // result.
+    unsigned LHSWidth = CGF.getContext().getIntWidth(LHSTy);
+    unsigned bufferWidth = LHSWidth * 2;
+    if (bufferWidth > 128) {
+      bufferWidth = 128;
+    }
+
+    LHSVal = Builder.CreateIntCast(LHSVal, Builder.getIntNTy(bufferWidth),
+                                   isSignedResult);
+    RHSVal = Builder.CreateIntCast(RHSVal, Builder.getIntNTy(bufferWidth),
+                                   isSignedResult);
+    LHSVal = Builder.CreateShl(LHSVal, getFixedPointFBits(LHSTy));
+
+    llvm::Value *DivResult = Builder.CreateSDiv(LHSVal, RHSVal);
+    return Builder.CreateIntCast(DivResult, Builder.getIntNTy(LHSWidth),
+                                 isSignedResult);
+  }
+
   if (Ops.LHS->getType()->isFPOrFPVectorTy()) {
     llvm::Value *Val = Builder.CreateFDiv(Ops.LHS, Ops.RHS, "div");
     if (CGF.getLangOpts().OpenCL &&
Index: lib/CodeGen/CGExprConstant.cpp
===================================================================
--- lib/CodeGen/CGExprConstant.cpp
+++ lib/CodeGen/CGExprConstant.cpp
@@ -686,6 +686,8 @@
     Expr *subExpr = E->getSubExpr();
 
     switch (E->getCastKind()) {
+    case CK_FixedPointCast:
+      llvm_unreachable("CK_FixedPointCast");  // TODO
     case CK_IntegralToFixedPoint:
       llvm_unreachable("VisitCastExpr CK_IntegralToFixedPoint");  // TODO
     case CK_ToUnion: {
Index: lib/CodeGen/CGExprComplex.cpp
===================================================================
--- lib/CodeGen/CGExprComplex.cpp
+++ lib/CodeGen/CGExprComplex.cpp
@@ -446,6 +446,8 @@
 ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
                                            QualType DestTy) {
   switch (CK) {
+  case CK_FixedPointCast:
+    llvm_unreachable("CK_FixedPointCast");  // TODO
   case CK_IntegralToFixedPoint:
     llvm_unreachable(
         "ComplexExprEmitter::EmitCast CK_IntegralToFixedPoint");  // TODO
Index: lib/CodeGen/CGExprAgg.cpp
===================================================================
--- lib/CodeGen/CGExprAgg.cpp
+++ lib/CodeGen/CGExprAgg.cpp
@@ -671,6 +671,8 @@
   if (const auto *ECE = dyn_cast<ExplicitCastExpr>(E))
     CGF.CGM.EmitExplicitCastExprType(ECE, &CGF);
   switch (E->getCastKind()) {
+  case CK_FixedPointCast:
+    llvm_unreachable("CK_FixedPointCast");  // TODO
   case CK_IntegralToFixedPoint:
     llvm_unreachable(
         "AggExprEmitter::VisitCastExpr CK_IntegralToFixedPoint");  // TODO
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -4044,6 +4044,8 @@
 /// cast from scalar to union.
 LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
   switch (E->getCastKind()) {
+  case CK_FixedPointCast:
+    llvm_unreachable("CK_FixedPointCast");  // TODO
   case CK_IntegralToFixedPoint:
     llvm_unreachable(
         "CodeGenFunction::EmitCastLValue CK_IntegralToFixedPoint");  // TODO
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -29,6 +29,7 @@
 #include "clang/AST/TypeVisitor.h"
 #include "clang/Basic/AddressSpaces.h"
 #include "clang/Basic/ExceptionSpecificationType.h"
+#include "clang/Basic/FixedPoint.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/LangOptions.h"
@@ -3992,3 +3993,107 @@
 CXXRecordDecl *MemberPointerType::getMostRecentCXXRecordDecl() const {
   return getClass()->getAsCXXRecordDecl()->getMostRecentDecl();
 }
+
+unsigned clang::getFixedPointFBits(const QualType &Ty) {
+  assert(Ty->isFixedPointType());
+
+  const auto *BT = Ty->getAs<BuiltinType>();
+  switch (BT->getKind()) {
+    default:
+      llvm_unreachable("Not a fixed point type!");
+    case BuiltinType::ShortAccum:
+    case BuiltinType::SatShortAccum:
+      return BUILTIN_SACCUM_FBIT;
+
+    case BuiltinType::Accum:
+    case BuiltinType::SatAccum:
+      return BUILTIN_ACCUM_FBIT;
+
+    case BuiltinType::LongAccum:
+    case BuiltinType::SatLongAccum:
+      return BUILTIN_LACCUM_FBIT;
+
+    case BuiltinType::UShortAccum:
+    case BuiltinType::SatUShortAccum:
+      return BUILTIN_USACCUM_FBIT;
+
+    case BuiltinType::UAccum:
+    case BuiltinType::SatUAccum:
+      return BUILTIN_UACCUM_FBIT;
+
+    case BuiltinType::ULongAccum:
+    case BuiltinType::SatULongAccum:
+      return BUILTIN_ULACCUM_FBIT;
+
+    case BuiltinType::ShortFract:
+    case BuiltinType::SatShortFract:
+      return BUILTIN_SFRACT_FBIT;
+
+    case BuiltinType::Fract:
+    case BuiltinType::SatFract:
+      return BUILTIN_FRACT_FBIT;
+
+    case BuiltinType::LongFract:
+    case BuiltinType::SatLongFract:
+      return BUILTIN_LFRACT_FBIT;
+
+    case BuiltinType::UShortFract:
+    case BuiltinType::SatUShortFract:
+      return BUILTIN_USFRACT_FBIT;
+
+    case BuiltinType::UFract:
+    case BuiltinType::SatUFract:
+      return BUILTIN_UFRACT_FBIT;
+
+    case BuiltinType::ULongFract:
+    case BuiltinType::SatULongFract:
+      return BUILTIN_ULFRACT_FBIT;
+  }
+}
+
+unsigned clang::getFixedPointIBits(const QualType &Ty) {
+  assert(Ty->isFixedPointType());
+
+  const auto *BT = Ty->getAs<BuiltinType>();
+  switch (BT->getKind()) {
+    default:
+      llvm_unreachable("Not a fixed point type!");
+    case BuiltinType::ShortAccum:
+    case BuiltinType::SatShortAccum:
+      return BUILTIN_SACCUM_IBIT;
+
+    case BuiltinType::Accum:
+    case BuiltinType::SatAccum:
+      return BUILTIN_ACCUM_IBIT;
+
+    case BuiltinType::LongAccum:
+    case BuiltinType::SatLongAccum:
+      return BUILTIN_LACCUM_IBIT;
+
+    case BuiltinType::UShortAccum:
+    case BuiltinType::SatUShortAccum:
+      return BUILTIN_USACCUM_IBIT;
+
+    case BuiltinType::UAccum:
+    case BuiltinType::SatUAccum:
+      return BUILTIN_UACCUM_IBIT;
+
+    case BuiltinType::ULongAccum:
+    case BuiltinType::SatULongAccum:
+      return BUILTIN_ULACCUM_IBIT;
+
+    case BuiltinType::ShortFract:
+    case BuiltinType::Fract:
+    case BuiltinType::LongFract:
+    case BuiltinType::UShortFract:
+    case BuiltinType::UFract:
+    case BuiltinType::ULongFract:
+    case BuiltinType::SatShortFract:
+    case BuiltinType::SatFract:
+    case BuiltinType::SatLongFract:
+    case BuiltinType::SatUShortFract:
+    case BuiltinType::SatUFract:
+    case BuiltinType::SatULongFract:
+      return 0;
+  }
+}
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -7275,6 +7275,7 @@
   }
 
   bool VisitUnaryOperator(const UnaryOperator *E);
+  bool VisitCastExpr(const CastExpr *E);
 };
 } // end anonymous namespace
 
@@ -8952,6 +8953,38 @@
     return Success(Opcode == BO_EQ || Opcode == BO_LE || Opcode == BO_GE, E);
   }
 
+  if (LHSTy->isFixedPointType() && RHSTy->isFixedPointType()) {
+    APValue LHSVal, RHSVal;
+
+    if (!FixedPointExprEvaluator(Info, LHSVal).Visit(E->getLHS())) {
+      return false;
+    }
+
+    if (!FixedPointExprEvaluator(Info, RHSVal).Visit(E->getRHS())) {
+      return false;
+    }
+
+    const APSInt& LHSInt = LHSVal.getInt();
+    const APSInt& RHSInt = RHSVal.getInt();
+
+    switch (E->getOpcode()) {
+      default:
+        llvm_unreachable("Invalid binary operator!");
+      case BO_LT:
+        return Success(LHSInt < RHSInt, E);
+      case BO_GT:
+        return Success(LHSInt > RHSInt, E);
+      case BO_LE:
+        return Success(LHSInt <= RHSInt, E);
+      case BO_GE:
+        return Success(LHSInt >= RHSInt, E);
+      case BO_EQ:
+        return Success(LHSInt == RHSInt, E);
+      case BO_NE:
+        return Success(LHSInt != RHSInt, E);
+    }
+  }
+
   assert((!LHSTy->isIntegralOrEnumerationType() ||
           !RHSTy->isIntegralOrEnumerationType()) &&
          "DataRecursiveIntBinOpEvaluator should have handled integral types");
@@ -9127,6 +9160,8 @@
   QualType SrcType = SubExpr->getType();
 
   switch (E->getCastKind()) {
+  case CK_FixedPointCast:
+    llvm_unreachable("CK_FixedPointCast");  // TODO
   case CK_IntegralToFixedPoint:
     llvm_unreachable(
         "IntExprEvaluator::VisitCastExpr CK_IntegralToFixedPoint");  // TODO
@@ -9339,6 +9374,70 @@
   }
 }
 
+/// HandleCast - This is used to evaluate implicit or explicit casts where the
+/// result type is integer.
+bool FixedPointExprEvaluator::VisitCastExpr(const CastExpr *E) {
+  const Expr *SubExpr = E->getSubExpr();
+  QualType DestType = E->getType();
+  QualType SrcType = SubExpr->getType();
+
+  switch (E->getCastKind()) {
+    default:
+      llvm_unreachable("unknown cast resulting in fixed point value");
+    case CK_FixedPointCast: {
+      assert(DestType->isFixedPointType());
+      assert(SrcType->isFixedPointType());
+
+      if (!Visit(SubExpr)) return false;
+
+      assert(Result.isInt());
+
+      unsigned dest_fbits = getFixedPointFBits(DestType);
+      unsigned src_fbits = getFixedPointFBits(SrcType);
+      APSInt Val = Result.getInt();
+
+      Val.setIsSigned(DestType->isSignedFixedPointType());
+      unsigned DestWidth = Info.Ctx.getIntWidth(DestType);
+      Val = Val.extOrTrunc(DestWidth);
+
+      if (dest_fbits > src_fbits) {
+        Val <<= (dest_fbits - src_fbits);
+      } else if (dest_fbits < src_fbits) {
+        Val >>= (src_fbits - dest_fbits);
+      }
+
+      return Success(Val, E);
+    }
+
+    case CK_IntegralToFixedPoint: {
+      assert(DestType->isFixedPointType());
+      assert(SrcType->isIntegralOrEnumerationType());
+
+      if (!Visit(SubExpr)) return false;
+
+      assert(Result.isInt());
+
+      unsigned dest_fbits = getFixedPointFBits(DestType);
+
+      APSInt Val = Result.getInt();
+
+      Val.setIsSigned(DestType->isSignedFixedPointType());
+      Val <<= dest_fbits;
+
+      unsigned DestWidth = Info.Ctx.getIntWidth(DestType);
+      Val = Val.extOrTrunc(DestWidth);
+
+      return Success(Val, E);
+    }
+
+    case CK_UserDefinedConversion:
+    case CK_LValueToRValue:
+    case CK_AtomicToNonAtomic:
+    case CK_NoOp:
+      return ExprEvaluatorBaseTy::VisitCastExpr(E);
+  }
+}
+
 //===----------------------------------------------------------------------===//
 // Float Evaluation
 //===----------------------------------------------------------------------===//
@@ -9656,6 +9755,8 @@
 bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
 
   switch (E->getCastKind()) {
+  case CK_FixedPointCast:
+    llvm_unreachable("CK_FixedPointCast");  // TODO
   case CK_IntegralToFixedPoint:
     llvm_unreachable(
         "ComplexExprEvaluator::VisitCastExpr CK_IntegralToFixedPoint");  // TODO
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -1654,6 +1654,7 @@
   case CK_ZeroToOCLQueue:
   case CK_IntToOCLSampler:
   case CK_IntegralToFixedPoint:
+  case CK_FixedPointCast:
     assert(!getType()->isBooleanType() && "unheralded conversion to bool");
     goto CheckNoBasePath;
 
Index: lib/AST/ASTContext.cpp
===================================================================
--- lib/AST/ASTContext.cpp
+++ lib/AST/ASTContext.cpp
@@ -60,6 +60,7 @@
 #include "clang/Basic/TargetCXXABI.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Basic/XRayLists.h"
+#include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -111,6 +112,15 @@
   Float16Rank, HalfRank, FloatRank, DoubleRank, LongDoubleRank, Float128Rank
 };
 
+enum FixedPointRank {
+  ShortFractRank,
+  FractRank,
+  LongFractRank,
+  ShortAccumRank,
+  AccumRank,
+  LongAccumRank
+};
+
 RawComment *ASTContext::getRawCommentForDeclNoCache(const Decl *D) const {
   if (!CommentsLoaded && ExternalSource) {
     ExternalSource->ReadComments();
@@ -5349,6 +5359,52 @@
   }
 }
 
+/// getFixedPointRank - Return a relative rank for fixed point types.
+/// This routine will assert if passed a built-in type that isn't a fixed point.
+static FixedPointRank getFixedPointRank(QualType T) {
+  assert(T->isFixedPointType() && T->getAs<BuiltinType>() &&
+         "getFixedPointRank(): not a fixed point type");
+  switch (T->getAs<BuiltinType>()->getKind()) {
+    default:
+      llvm_unreachable("getFixedPointRank(): not a fixed point type");
+    case BuiltinType::ShortAccum:
+    case BuiltinType::UShortAccum:
+    case BuiltinType::SatShortAccum:
+    case BuiltinType::SatUShortAccum:
+      return ShortAccumRank;
+
+    case BuiltinType::Accum:
+    case BuiltinType::UAccum:
+    case BuiltinType::SatAccum:
+    case BuiltinType::SatUAccum:
+      return AccumRank;
+
+    case BuiltinType::LongAccum:
+    case BuiltinType::ULongAccum:
+    case BuiltinType::SatLongAccum:
+    case BuiltinType::SatULongAccum:
+      return LongAccumRank;
+
+    case BuiltinType::ShortFract:
+    case BuiltinType::UShortFract:
+    case BuiltinType::SatShortFract:
+    case BuiltinType::SatUShortFract:
+      return ShortFractRank;
+
+    case BuiltinType::Fract:
+    case BuiltinType::UFract:
+    case BuiltinType::SatFract:
+    case BuiltinType::SatUFract:
+      return FractRank;
+
+    case BuiltinType::LongFract:
+    case BuiltinType::ULongFract:
+    case BuiltinType::SatLongFract:
+    case BuiltinType::SatULongFract:
+      return LongFractRank;
+  }
+}
+
 /// getFloatingTypeOfSizeWithinDomain - Returns a real floating
 /// point or a complex type (based on typeDomain/typeSize).
 /// 'typeDomain' is a real floating point or complex type.
@@ -5394,6 +5450,19 @@
   return -1;
 }
 
+/// getFixedPointTypeOrder - Compare the rank of the two specified fixed
+/// point types.
+/// If LHS > RHS, return 1.  If LHS == RHS, return 0. If
+/// LHS < RHS, return -1.
+int ASTContext::getFixedPointTypeOrder(QualType LHS, QualType RHS) const {
+  FixedPointRank LHSR = getFixedPointRank(LHS);
+  FixedPointRank RHSR = getFixedPointRank(RHS);
+
+  if (LHSR == RHSR) return 0;
+  if (LHSR > RHSR) return 1;
+  return -1;
+}
+
 /// getIntegerRank - Return an integer conversion rank (C99 6.3.1.1p1). This
 /// routine will assert if passed a built-in type that isn't an integer or enum,
 /// or if it is not canonicalized.
Index: include/clang/AST/Type.h
===================================================================
--- include/clang/AST/Type.h
+++ include/clang/AST/Type.h
@@ -2117,12 +2117,21 @@
 
   // Return true if this is a fixed point type that is signed according
   // to ISO/IEC JTC1 SC22 WG14 N1169. [short _Fract, _Accum, long _Fract...]
+  // This type can also be saturated.
   bool isSignedFixedPointType() const;
 
   // Return true if this is a fixed point type that is unsigned according
-  // to ISO/IEC JTC1 SC22 WG14 N1169.
+  // to ISO/IEC JTC1 SC22 WG14 N1169. This type can also be saturated.
   bool isUnsignedFixedPointType() const;
 
+  // Return true if this is a fixed point type that is also an _Accum type.
+  // This type can also be saturated.
+  bool isAccumFixedPointType() const;
+
+  // Return true if this is a fixed point type that is also an _Fract type.
+  // This type can also be saturated.
+  bool isFractFixedPointType() const;
+
   /// Return true if this is not a variable sized type,
   /// according to the rules of C99 6.7.5p3.  It is not legal to call this on
   /// incomplete types.
@@ -6347,6 +6356,26 @@
   return false;
 }
 
+inline bool Type::isAccumFixedPointType() const {
+  if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) {
+    return ((BT->getKind() >= BuiltinType::ShortAccum &&
+             BT->getKind() <= BuiltinType::ULongAccum) ||
+            (BT->getKind() >= BuiltinType::SatShortAccum &&
+             BT->getKind() <= BuiltinType::SatULongAccum));
+  }
+  return false;
+}
+
+inline bool Type::isFractFixedPointType() const {
+  if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType)) {
+    return ((BT->getKind() >= BuiltinType::ShortFract &&
+             BT->getKind() <= BuiltinType::ULongFract) ||
+            (BT->getKind() >= BuiltinType::SatShortFract &&
+             BT->getKind() <= BuiltinType::SatULongFract));
+  }
+  return false;
+}
+
 inline bool Type::isScalarType() const {
   if (const auto *BT = dyn_cast<BuiltinType>(CanonicalType))
     return BT->getKind() > BuiltinType::Void &&
@@ -6542,6 +6571,12 @@
   return cast<PointerType>(Decayed)->getPointeeType();
 }
 
+// Return the number of fractional bits in a fixed point type.
+unsigned getFixedPointFBits(const QualType &Ty);
+
+// Return the number of integral bits in a fixed point type.
+unsigned getFixedPointIBits(const QualType &Ty);
+
 } // namespace clang
 
 #endif // LLVM_CLANG_AST_TYPE_H
Index: include/clang/AST/OperationKinds.def
===================================================================
--- include/clang/AST/OperationKinds.def
+++ include/clang/AST/OperationKinds.def
@@ -197,8 +197,13 @@
 ///    float f = i;
 CAST_OPERATION(IntegralToFloating)
 
+/// CK_FixedPointCast - Cast between fixed point types.
+///    short _Accum a = 1.2k;
+///    (short _Fract) a
+CAST_OPERATION(FixedPointCast)
+
 /// CK_IntegralToFixedPoint - Integral to fixed point.
-///    (short _Accum) i;
+///    (short _Accum) i
 CAST_OPERATION(IntegralToFixedPoint)
 
 /// CK_FloatingToIntegral - Floating point to integral.  Rounds
Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -2422,6 +2422,13 @@
   /// \p LHS < \p RHS, return -1.
   int getFloatingTypeOrder(QualType LHS, QualType RHS) const;
 
+  /// \brief Compare the rank of the two specified fixed point types according
+  /// to 4.1.1 of ISO/IEC JTC1 SC22 WG14 N1169.
+  ///
+  /// If \p LHS > \p RHS, returns 1.  If \p LHS == \p RHS, returns 0.  If
+  /// \p LHS < \p RHS, return -1.
+  int getFixedPointTypeOrder(QualType LHS, QualType RHS) const;
+
   /// \brief Return a real floating point or a complex type (based on
   /// \p typeDomain/\p typeSize).
   ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to