[Fixed Point Arithmetic] Fixed Point Constant

This patch proposes an abstract type that represents fixed point numbers, similar to APInt or APSInt that was discussed in https://reviews.llvm.org/D48456#inline-425585. This type holds a value, scale, and saturation and is meant to perform intermediate calculations on constant fixed point values.

Currently this class is used as a way for handling the conversions between fixed point numbers with different sizes and radixes. For example, if I'm casting from a signed _Accum to a saturated unsigned short _Accum, I will need to check the value of the signed _Accum to see if it fits into the short _Accum which involves getting and comparing against the max/min values of the short _Accum. The FixedPointNumber class currently handles the radix shifting and extension when converting to a signed _Accum.

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

llvm-svn: 339028
diff --git a/clang/lib/Basic/FixedPoint.cpp b/clang/lib/Basic/FixedPoint.cpp
new file mode 100644
index 0000000..8473f67
--- /dev/null
+++ b/clang/lib/Basic/FixedPoint.cpp
@@ -0,0 +1,115 @@
+//===- FixedPoint.cpp - Fixed point constant handling -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Defines the implementation for the fixed point number interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/FixedPoint.h"
+
+namespace clang {
+
+APFixedPoint APFixedPoint::convert(const FixedPointSemantics &DstSema) const {
+  llvm::APSInt NewVal = Val;
+  unsigned DstWidth = DstSema.getWidth();
+  unsigned DstScale = DstSema.getScale();
+  bool Upscaling = DstScale > getScale();
+
+  if (Upscaling) {
+    NewVal = NewVal.extend(NewVal.getBitWidth() + DstScale - getScale());
+    NewVal <<= (DstScale - getScale());
+  } else {
+    NewVal >>= (getScale() - DstScale);
+  }
+
+  if (DstSema.isSaturated()) {
+    auto Mask = llvm::APInt::getBitsSetFrom(
+        NewVal.getBitWidth(),
+        std::min(DstScale + DstSema.getIntegralBits(), NewVal.getBitWidth()));
+    llvm::APInt Masked(NewVal & Mask);
+
+    // Change in the bits above the sign
+    if (!(Masked == Mask || Masked == 0))
+      NewVal = NewVal.isNegative() ? Mask : ~Mask;
+
+    if (!DstSema.isSigned() && NewVal.isNegative())
+      NewVal = 0;
+  }
+
+  NewVal = NewVal.extOrTrunc(DstWidth);
+  NewVal.setIsSigned(DstSema.isSigned());
+  return APFixedPoint(NewVal, DstSema);
+}
+
+int APFixedPoint::compare(const APFixedPoint &Other) const {
+  llvm::APSInt ThisVal = getValue();
+  llvm::APSInt OtherVal = Other.getValue();
+  bool ThisSigned = Val.isSigned();
+  bool OtherSigned = OtherVal.isSigned();
+  unsigned OtherScale = Other.getScale();
+  unsigned OtherWidth = OtherVal.getBitWidth();
+
+  unsigned CommonWidth = std::max(Val.getBitWidth(), OtherWidth);
+
+  // Prevent overflow in the event the widths are the same but the scales differ
+  CommonWidth += std::abs(static_cast<int>(getScale() - OtherScale));
+
+  ThisVal = ThisVal.extOrTrunc(CommonWidth);
+  OtherVal = OtherVal.extOrTrunc(CommonWidth);
+
+  unsigned CommonScale = std::max(getScale(), OtherScale);
+  ThisVal = ThisVal.shl(CommonScale - getScale());
+  OtherVal = OtherVal.shl(CommonScale - OtherScale);
+
+  if (ThisSigned && OtherSigned) {
+    if (ThisVal.sgt(OtherVal))
+      return 1;
+    else if (ThisVal.slt(OtherVal))
+      return -1;
+  } else if (!ThisSigned && !OtherSigned) {
+    if (ThisVal.ugt(OtherVal))
+      return 1;
+    else if (ThisVal.ult(OtherVal))
+      return -1;
+  } else if (ThisSigned && !OtherSigned) {
+    if (ThisVal.isSignBitSet())
+      return -1;
+    else if (ThisVal.ugt(OtherVal))
+      return 1;
+    else if (ThisVal.ult(OtherVal))
+      return -1;
+  } else {
+    // !ThisSigned && OtherSigned
+    if (OtherVal.isSignBitSet())
+      return 1;
+    else if (ThisVal.ugt(OtherVal))
+      return 1;
+    else if (ThisVal.ult(OtherVal))
+      return -1;
+  }
+
+  return 0;
+}
+
+APFixedPoint APFixedPoint::getMax(const FixedPointSemantics &Sema) {
+  bool IsUnsigned = !Sema.isSigned();
+  auto Val = llvm::APSInt::getMaxValue(Sema.getWidth(), IsUnsigned);
+  if (IsUnsigned && Sema.hasUnsignedPadding())
+    Val = Val.lshr(1);
+  return APFixedPoint(Val, Sema);
+}
+
+APFixedPoint APFixedPoint::getMin(const FixedPointSemantics &Sema) {
+  auto Val = llvm::APSInt::getMinValue(Sema.getWidth(), !Sema.isSigned());
+  return APFixedPoint(Val, Sema);
+}
+
+}  // namespace clang