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

This patch implements the remaining arithmetic and logical operations on the 
primary fixed point types.

The operations are `+`, `-`, `*`, `/`, `<<`, and `>>`.

  // 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);

`%` is not a valod operation on fixed point types.

This is the parent of https://reviews.llvm.org/D46917


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
@@ -1,5 +1,5 @@
-// RUN: %clang -S -emit-llvm %s -o - | FileCheck %s
 // RUN: %clang_cc1 -S -emit-llvm -o - %s | lli
+// RUN: %clang -S -emit-llvm %s -o - | FileCheck %s
 
 // The first test checks the emitted llvm IR.
 // The second test checks the output.
@@ -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
@@ -322,6 +322,7 @@
 
     switch (CastE->getCastKind()) {
       case CK_IntegralToFixedPoint: llvm_unreachable("ExprEngine::VisitCast CK_IntegralToFixedPoint"); // TODO
+      case CK_FixedPointCast: llvm_unreachable("CK_FixedPointCast"); // TODO
       case CK_LValueToRValue:
         llvm_unreachable("LValueToRValue casts handled earlier.");
       case CK_ToVoid:
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -1244,15 +1244,37 @@
 
   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.");
   }
 }
 
@@ -3500,9 +3522,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
@@ -3515,7 +3537,7 @@
       }
     }
 
-    uint64_t fract_part_as_int = static_cast<uint64_t>(fabs(fract_part) * (1 << fbits));
+    uint64_t fract_part_as_int = static_cast<uint64_t>(fract_part * (1ULL << fbits));
     uint64_t final_fixed_point_as_int = (int_part_as_int << fbits) + fract_part_as_int;
 
     llvm::APInt ResultVal(bit_width, final_fixed_point_as_int, isSigned);
@@ -9506,8 +9528,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
@@ -1003,6 +1003,7 @@
   if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Arg)) {
     switch (ICE->getCastKind()) {
     case CK_IntegralToFixedPoint: llvm_unreachable("rewriteToNumericBoxedExpression CK_IntegralToFixedPoint"); // TODO
+    case CK_FixedPointCast: llvm_unreachable("rewriteToNumericBoxedExpression CK_FixedPointCast"); // TODO
     case CK_LValueToRValue:
     case CK_NoOp:
     case CK_UserDefinedConversion:
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -655,6 +655,38 @@
       }
     }
 
+    // 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 +1116,8 @@
   }
 
   if (isa<llvm::IntegerType>(SrcTy)) {
-    bool InputSigned = SrcType->isSignedIntegerOrEnumerationType();
+    bool InputSigned = (SrcType->isSignedIntegerOrEnumerationType() ||
+                        SrcType->isFixedPointType());
     if (SrcType->isBooleanType() && TreatBooleanAsSigned) {
       InputSigned = true;
     }
@@ -1777,30 +1810,45 @@
     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:
@@ -2123,7 +2171,7 @@
       case BuiltinType::UFract:       fbits = BUILTIN_UFRACT_FBIT; break;
       case BuiltinType::ULongFract:   fbits = BUILTIN_ULFRACT_FBIT; break;
     }
-    llvm::Value *amt = llvm::ConstantInt::get(value->getType(), 1 << fbits,
+    llvm::Value *amt = llvm::ConstantInt::get(value->getType(), 1ULL << fbits,
                                               /*isSigned=*/type->isSignedFixedPointType());
     if (isInc) {
       value = Builder.CreateAdd(value, amt, "fixed_point_post_inc");
@@ -2598,6 +2646,38 @@
     }
   }
 
+  // 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,7 @@
     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: {
       // GCC cast to union extension
Index: lib/CodeGen/CGExprComplex.cpp
===================================================================
--- lib/CodeGen/CGExprComplex.cpp
+++ lib/CodeGen/CGExprComplex.cpp
@@ -447,6 +447,7 @@
                                            QualType DestTy) {
   switch (CK) {
   case CK_IntegralToFixedPoint: llvm_unreachable("ComplexExprEmitter::EmitCast CK_IntegralToFixedPoint"); // TODO
+  case CK_FixedPointCast: llvm_unreachable("CK_FixedPointCast"); // TODO
   case CK_Dependent: llvm_unreachable("dependent cast kind in IR gen!");
 
   // Atomic to non-atomic casts may be more than a no-op for some platforms and
Index: lib/CodeGen/CGExprAgg.cpp
===================================================================
--- lib/CodeGen/CGExprAgg.cpp
+++ lib/CodeGen/CGExprAgg.cpp
@@ -671,6 +671,7 @@
   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
   case CK_Dynamic: {
     // FIXME: Can this actually happen? We have no test coverage for it.
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -4045,6 +4045,7 @@
 LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
   switch (E->getCastKind()) {
   case CK_IntegralToFixedPoint: llvm_unreachable("CodeGenFunction::EmitCastLValue CK_IntegralToFixedPoint"); // TODO
+  case CK_FixedPointCast: llvm_unreachable("CK_FixedPointCast"); // TODO
   case CK_ToVoid:
   case CK_BitCast:
   case CK_ArrayToPointerDecay:
Index: lib/AST/Type.cpp
===================================================================
--- lib/AST/Type.cpp
+++ lib/AST/Type.cpp
@@ -33,6 +33,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/Linkage.h"
+#include "clang/Basic/FixedPoint.h"
 #include "clang/Basic/Specifiers.h"
 #include "clang/Basic/TargetCXXABI.h"
 #include "clang/Basic/TargetInfo.h"
@@ -3992,3 +3993,105 @@
 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
@@ -7278,6 +7278,7 @@
   }
 
   bool VisitUnaryOperator(const UnaryOperator *E);
+  bool VisitCastExpr(const CastExpr* E);
 };
 } // end anonymous namespace
 
@@ -8955,6 +8956,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");
@@ -9130,6 +9163,7 @@
   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
   case CK_BaseToDerived:
   case CK_DerivedToBase:
@@ -9342,6 +9376,71 @@
   }
 }
 
+/// 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
 //===----------------------------------------------------------------------===//
@@ -9660,6 +9759,7 @@
 
   switch (E->getCastKind()) {
   case CK_IntegralToFixedPoint: llvm_unreachable("ComplexExprEvaluator::VisitCastExpr CK_IntegralToFixedPoint"); // TODO
+  case CK_FixedPointCast: llvm_unreachable("CK_FixedPointCast"); // TODO
   case CK_BitCast:
   case CK_BaseToDerived:
   case CK_DerivedToBase:
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -1652,6 +1652,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
@@ -112,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();
@@ -5350,6 +5359,51 @@
   }
 }
 
+/// 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.
@@ -5395,6 +5449,21 @@
   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
@@ -2116,12 +2116,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.
@@ -6336,6 +6345,22 @@
   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 &&
@@ -6531,6 +6556,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
@@ -2419,6 +2419,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
  • [PATCH] D46925: Remaining Bin... Leonard Chan via Phabricator via cfe-commits

Reply via email to