[Fixed Point Arithmetic] FixedPointCast

This patch is a part of https://reviews.llvm.org/D48456 in an attempt to
split them up. This contains the code for casting between fixed point types
and other fixed point types.

The method for converting between fixed point types is based off the convert()
method in APFixedPoint.

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

llvm-svn: 344530
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 028aa96..55fa455 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4153,6 +4153,7 @@
   case CK_CopyAndAutoreleaseBlockObject:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     return EmitUnsupportedLValue(E, "unexpected cast lvalue");
 
   case CK_Dependent:
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 6264110..9e80987 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -851,6 +851,7 @@
   case CK_ZeroToOCLQueue:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     llvm_unreachable("cast kind invalid for aggregate types");
   }
 }
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp
index fb176093..1d54386 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -509,6 +509,7 @@
   case CK_ZeroToOCLQueue:
   case CK_AddressSpaceConversion:
   case CK_IntToOCLSampler:
+  case CK_FixedPointCast:
     llvm_unreachable("invalid cast kind for complex value");
 
   case CK_FloatingRealToComplex:
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 651b05a..3dbb20d 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -871,6 +871,7 @@
     case CK_FloatingCast:
     case CK_ZeroToOCLEvent:
     case CK_ZeroToOCLQueue:
+    case CK_FixedPointCast:
       return nullptr;
     }
     llvm_unreachable("Invalid CastKind");
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 0bbd788..3abb269 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -11,11 +11,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "CodeGenFunction.h"
-#include "CGCleanup.h"
 #include "CGCXXABI.h"
+#include "CGCleanup.h"
 #include "CGDebugInfo.h"
 #include "CGObjCRuntime.h"
+#include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "TargetInfo.h"
 #include "clang/AST/ASTContext.h"
@@ -23,6 +23,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/FixedPoint.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Frontend/CodeGenOptions.h"
 #include "llvm/ADT/Optional.h"
@@ -327,6 +328,9 @@
                        SourceLocation Loc,
                        ScalarConversionOpts Opts = ScalarConversionOpts());
 
+  Value *EmitFixedPointConversion(Value *Src, QualType SrcTy, QualType DstTy,
+                                  SourceLocation Loc);
+
   /// Emit a conversion from the specified complex type to the specified
   /// destination type, where the destination type is an LLVM scalar type.
   Value *EmitComplexToScalarConversion(CodeGenFunction::ComplexPairTy Src,
@@ -1011,6 +1015,10 @@
                                                QualType DstType,
                                                SourceLocation Loc,
                                                ScalarConversionOpts Opts) {
+  assert(!SrcType->isFixedPointType() && !DstType->isFixedPointType() &&
+         "Use the ScalarExprEmitter::EmitFixedPoint family functions for "
+         "handling conversions involving fixed point types.");
+
   QualType NoncanonicalSrcType = SrcType;
   QualType NoncanonicalDstType = DstType;
 
@@ -1204,6 +1212,101 @@
   return Res;
 }
 
+Value *ScalarExprEmitter::EmitFixedPointConversion(Value *Src, QualType SrcTy,
+                                                   QualType DstTy,
+                                                   SourceLocation Loc) {
+  using llvm::APInt;
+  using llvm::ConstantInt;
+  using llvm::Value;
+
+  assert(SrcTy->isFixedPointType());
+  assert(DstTy->isFixedPointType());
+
+  FixedPointSemantics SrcFPSema =
+      CGF.getContext().getFixedPointSemantics(SrcTy);
+  FixedPointSemantics DstFPSema =
+      CGF.getContext().getFixedPointSemantics(DstTy);
+  unsigned SrcWidth = SrcFPSema.getWidth();
+  unsigned DstWidth = DstFPSema.getWidth();
+  unsigned SrcScale = SrcFPSema.getScale();
+  unsigned DstScale = DstFPSema.getScale();
+  bool IsSigned = SrcFPSema.isSigned();
+
+  Value *Result = Src;
+  unsigned ResultWidth = SrcWidth;
+
+  if (!DstFPSema.isSaturated()) {
+    // Downscale
+    if (DstScale < SrcScale) {
+      if (IsSigned)
+        Result = Builder.CreateAShr(Result, SrcScale - DstScale);
+      else
+        Result = Builder.CreateLShr(Result, SrcScale - DstScale);
+    }
+
+    // Resize
+    llvm::Type *DstIntTy = Builder.getIntNTy(DstWidth);
+    if (IsSigned)
+      Result = Builder.CreateSExtOrTrunc(Result, DstIntTy);
+    else
+      Result = Builder.CreateZExtOrTrunc(Result, DstIntTy);
+
+    // Upscale
+    if (DstScale > SrcScale)
+      Result = Builder.CreateShl(Result, DstScale - SrcScale);
+  } else {
+    if (DstScale > SrcScale) {
+      // Need to extend first before scaling up
+      ResultWidth = SrcWidth + DstScale - SrcScale;
+      llvm::Type *UpscaledTy = Builder.getIntNTy(ResultWidth);
+
+      if (IsSigned)
+        Result = Builder.CreateSExt(Result, UpscaledTy);
+      else
+        Result = Builder.CreateZExt(Result, UpscaledTy);
+
+      Result = Builder.CreateShl(Result, DstScale - SrcScale);
+    } else if (DstScale < SrcScale) {
+      if (IsSigned)
+        Result = Builder.CreateAShr(Result, SrcScale - DstScale);
+      else
+        Result = Builder.CreateLShr(Result, SrcScale - DstScale);
+    }
+
+    if (DstFPSema.getIntegralBits() < SrcFPSema.getIntegralBits()) {
+      auto Max = ConstantInt::get(
+          CGF.getLLVMContext(),
+          APFixedPoint::getMax(DstFPSema).getValue().extOrTrunc(ResultWidth));
+      Value *TooHigh = IsSigned ? Builder.CreateICmpSGT(Result, Max)
+                                : Builder.CreateICmpUGT(Result, Max);
+      Result = Builder.CreateSelect(TooHigh, Max, Result);
+
+      if (IsSigned) {
+        // Cannot overflow min to dest type is src is unsigned since all fixed
+        // point types can cover the unsigned min of 0.
+        auto Min = ConstantInt::get(
+            CGF.getLLVMContext(),
+            APFixedPoint::getMin(DstFPSema).getValue().extOrTrunc(ResultWidth));
+        Value *TooLow = Builder.CreateICmpSLT(Result, Min);
+        Result = Builder.CreateSelect(TooLow, Min, Result);
+      }
+    } else if (IsSigned && !DstFPSema.isSigned()) {
+      llvm::Type *ResultTy = Builder.getIntNTy(ResultWidth);
+      Value *Zero = ConstantInt::getNullValue(ResultTy);
+      Value *LTZero = Builder.CreateICmpSLT(Result, Zero);
+      Result = Builder.CreateSelect(LTZero, Zero, Result);
+    }
+
+    // Final resizing to dst width
+    llvm::Type *DstIntTy = Builder.getIntNTy(DstWidth);
+    if (IsSigned)
+      Result = Builder.CreateSExtOrTrunc(Result, DstIntTy);
+    else
+      Result = Builder.CreateZExtOrTrunc(Result, DstIntTy);
+  }
+  return Result;
+}
+
 /// Emit a conversion from the specified complex type to the specified
 /// destination type, where the destination type is an LLVM scalar type.
 Value *ScalarExprEmitter::EmitComplexToScalarConversion(
@@ -1894,6 +1997,10 @@
     return Builder.CreateVectorSplat(NumElements, Elt, "splat");
   }
 
+  case CK_FixedPointCast:
+    return EmitFixedPointConversion(Visit(E), E->getType(), DestTy,
+                                    CE->getExprLoc());
+
   case CK_IntegralCast: {
     ScalarConversionOpts Opts;
     if (CGF.SanOpts.hasOneOf(SanitizerKind::ImplicitIntegerTruncation)) {