[llvm] 1667d23 - [NFCI] Add StackOffset class and base classes for ElementCount, TypeSize.

Sander de Smalen via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 3 01:44:18 PST 2020


Author: Sander de Smalen
Date: 2020-11-03T09:41:39Z
New Revision: 1667d23e585c13835c66b8d61c2d5ef93f196dd5

URL: https://github.com/llvm/llvm-project/commit/1667d23e585c13835c66b8d61c2d5ef93f196dd5
DIFF: https://github.com/llvm/llvm-project/commit/1667d23e585c13835c66b8d61c2d5ef93f196dd5.diff

LOG: [NFCI] Add StackOffset class and base classes for ElementCount, TypeSize.

This patch adds a linear polynomial base class, called LinearPolyBase, which
serves as a base class for StackOffset. It tries to represent a linear
polynomial like:

  c0 * scale0 + c1 * scale1 + ... + cK * scaleK

where the scale is implicit, meaning that only the coefficients are
encoded.

This patch also adds a univariate linear polynomial, which serves as
a base class for ElementCount and TypeSize. This tries to represent a
linear polynomial where only one dimension can be set at any one time,
i.e. a TypeSize is either fixed-sized, or scalable-sized, but cannot be
a combination of the two.

  class LinearPolyBase
     ^
     |
     +---- class StackOffset  (dimensions = 2 (fixed/scalable), type = int64_t)

  class UnivariateLinearPolyBase
     |
     |
     +---- class LinearPolySize (dimensions = 2 (fixed/scalable))
                  ^
                  |
                  +-------- class ElementCount  (type = unsigned)
                  |
                  |
                  +-------- class TypeSize      (type = uint64_t)

Reviewed By: ctetreau, david-arm

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

Added: 
    llvm/unittests/Support/LinearPolyBaseTest.cpp

Modified: 
    llvm/include/llvm/Support/TypeSize.h
    llvm/unittests/Support/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Support/TypeSize.h b/llvm/include/llvm/Support/TypeSize.h
index 392dd1a33605..e78e23df8f28 100644
--- a/llvm/include/llvm/Support/TypeSize.h
+++ b/llvm/include/llvm/Support/TypeSize.h
@@ -15,69 +15,290 @@
 #ifndef LLVM_SUPPORT_TYPESIZE_H
 #define LLVM_SUPPORT_TYPESIZE_H
 
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/MathExtras.h"
 #include "llvm/Support/WithColor.h"
 
-#include <cstdint>
+#include <algorithm>
+#include <array>
 #include <cassert>
+#include <cstdint>
+#include <type_traits>
 
 namespace llvm {
 
-template <typename T> struct DenseMapInfo;
+template <typename LeafTy> struct LinearPolyBaseTypeTraits {};
+
+//===----------------------------------------------------------------------===//
+// LinearPolyBase - a base class for linear polynomials with multiple
+// dimensions. This can e.g. be used to describe offsets that are have both a
+// fixed and scalable component.
+//===----------------------------------------------------------------------===//
+
+/// LinearPolyBase describes a linear polynomial:
+///  c0 * scale0 + c1 * scale1 + ... + cK * scaleK
+/// where the scale is implicit, so only the coefficients are encoded.
+template <typename LeafTy>
+class LinearPolyBase {
+public:
+  using ScalarTy = typename LinearPolyBaseTypeTraits<LeafTy>::ScalarTy;
+  static constexpr auto Dimensions = LinearPolyBaseTypeTraits<LeafTy>::Dimensions;
+  static_assert(Dimensions != std::numeric_limits<unsigned>::max(),
+                "Dimensions out of range");
+
+private:
+  std::array<ScalarTy, Dimensions> Coefficients;
+
+protected:
+  LinearPolyBase(ArrayRef<ScalarTy> Values) {
+    std::copy(Values.begin(), Values.end(), Coefficients.begin());
+  }
+
+public:
+  friend LeafTy &operator+=(LeafTy &LHS, const LeafTy &RHS) {
+    for (unsigned I=0; I<Dimensions; ++I)
+      LHS.Coefficients[I] += RHS.Coefficients[I];
+    return LHS;
+  }
+
+  friend LeafTy &operator-=(LeafTy &LHS, const LeafTy &RHS) {
+    for (unsigned I=0; I<Dimensions; ++I)
+      LHS.Coefficients[I] -= RHS.Coefficients[I];
+    return LHS;
+  }
+
+  friend LeafTy &operator*=(LeafTy &LHS, ScalarTy RHS) {
+    for (auto &C : LHS.Coefficients)
+      C *= RHS;
+    return LHS;
+  }
+
+  friend LeafTy operator+(const LeafTy &LHS, const LeafTy &RHS) {
+    LeafTy Copy = LHS;
+    return Copy += RHS;
+  }
+
+  friend LeafTy operator-(const LeafTy &LHS, const LeafTy &RHS) {
+    LeafTy Copy = LHS;
+    return Copy -= RHS;
+  }
+
+  friend LeafTy operator*(const LeafTy &LHS, ScalarTy RHS) {
+    LeafTy Copy = LHS;
+    return Copy *= RHS;
+  }
+
+  template <typename U = ScalarTy>
+  friend typename std::enable_if_t<std::is_signed<U>::value, LeafTy>
+  operator-(const LeafTy &LHS) {
+    LeafTy Copy = LHS;
+    return Copy *= -1;
+  }
+
+  bool operator==(const LinearPolyBase &RHS) const {
+    return std::equal(Coefficients.begin(), Coefficients.end(),
+                      RHS.Coefficients.begin());
+  }
+
+  bool operator!=(const LinearPolyBase &RHS) const {
+    return !(*this == RHS);
+  }
+
+  bool isZero() const {
+    return all_of(Coefficients, [](const ScalarTy &C) { return C == 0; });
+  }
+  bool isNonZero() const { return !isZero(); }
+  explicit operator bool() const { return isNonZero(); }
+
+  ScalarTy getValue(unsigned Dim) const { return Coefficients[Dim]; }
+};
+
+//===----------------------------------------------------------------------===//
+// StackOffset - Represent an offset with named fixed and scalable components.
+//===----------------------------------------------------------------------===//
+
+namespace NewStackOffset {
+class StackOffset;
+} // end namespace NewStackOffset
+
+template <> struct LinearPolyBaseTypeTraits<NewStackOffset::StackOffset> {
+  using ScalarTy = int64_t;
+  static constexpr unsigned Dimensions = 2;
+};
+
+namespace NewStackOffset {
 
-// TODO: This class will be redesigned in a later patch that introduces full
-// polynomial behaviour, i.e. the ability to have composites made up of both
-// fixed and scalable sizes.
-template <typename T> class PolySize {
+/// StackOffset is a class to represent an offset with 2 dimensions,
+/// named fixed and scalable, respectively. This class allows a value for both
+/// dimensions to depict e.g. "8 bytes and 16 scalable bytes", which is needed
+/// to represent stack offsets.
+class StackOffset : public LinearPolyBase<StackOffset> {
 protected:
-  T MinVal;        // The minimum value that it could be.
-  bool IsScalable; // If true, the total value is determined by multiplying
-                   // 'MinVal' by a runtime determinded quantity, 'vscale'.
+  StackOffset(ScalarTy Fixed, ScalarTy Scalable)
+      : LinearPolyBase<StackOffset>({Fixed, Scalable}) {}
+
+public:
+  StackOffset() : StackOffset({0, 0}) {}
+  StackOffset(const LinearPolyBase<StackOffset> &Other)
+      : LinearPolyBase<StackOffset>(Other) {}
+  static StackOffset getFixed(ScalarTy Fixed) { return {Fixed, 0}; }
+  static StackOffset getScalable(ScalarTy Scalable) { return {0, Scalable}; }
+  static StackOffset get(ScalarTy Fixed, ScalarTy Scalable) {
+    return {Fixed, Scalable};
+  }
+
+  ScalarTy getFixed() const { return this->getValue(0); }
+  ScalarTy getScalable() const { return this->getValue(1); }
+};
 
-  constexpr PolySize(T MinVal, bool IsScalable)
-      : MinVal(MinVal), IsScalable(IsScalable) {}
+} // end namespace NewStackOffset
 
+
+//===----------------------------------------------------------------------===//
+// UnivariateLinearPolyBase - a base class for linear polynomials with multiple
+// dimensions, but where only one dimension can be set at any time.
+// This can e.g. be used to describe sizes that are either fixed or scalable.
+//===----------------------------------------------------------------------===//
+
+/// UnivariateLinearPolyBase is a base class for ElementCount and TypeSize.
+/// Like LinearPolyBase it tries to represent a linear polynomial
+/// where only one dimension can be set at any time, e.g.
+///   0 * scale0 + 0 * scale1 + ... + cJ * scaleJ + ... + 0 * scaleK
+/// The dimension that is set is the univariate dimension.
+template <typename LeafTy>
+class UnivariateLinearPolyBase {
 public:
+  using ScalarTy = typename LinearPolyBaseTypeTraits<LeafTy>::ScalarTy;
+  static constexpr auto Dimensions = LinearPolyBaseTypeTraits<LeafTy>::Dimensions;
+  static_assert(Dimensions != std::numeric_limits<unsigned>::max(),
+                "Dimensions out of range");
 
-  static constexpr PolySize getFixed(T MinVal) { return {MinVal, false}; }
-  static constexpr PolySize getScalable(T MinVal) { return {MinVal, true}; }
-  static constexpr PolySize get(T MinVal, bool IsScalable) {
-    return {MinVal, IsScalable};
+protected:
+  ScalarTy Value;         // The value at the univeriate dimension.
+  unsigned UnivariateDim; // The univeriate dimension.
+
+  UnivariateLinearPolyBase(ScalarTy &Val, unsigned UnivariateDim)
+      : Value(Val), UnivariateDim(UnivariateDim) {
+    assert(UnivariateDim < Dimensions && "Dimension out of range");
   }
 
-  static constexpr PolySize getNull() { return {0, false}; }
+  friend LeafTy &operator+=(LeafTy &LHS, const LeafTy &RHS) {
+    assert(LHS.UnivariateDim == RHS.UnivariateDim && "Invalid dimensions");
+    LHS.Value += RHS.Value;
+    return LHS;
+  }
 
-  /// Counting predicates.
-  ///
-  ///@{ No elements..
-  bool isZero() const { return MinVal == 0; }
-  /// At least one element.
+  friend LeafTy &operator-=(LeafTy &LHS, const LeafTy &RHS) {
+    assert(LHS.UnivariateDim == RHS.UnivariateDim && "Invalid dimensions");
+    LHS.Value -= RHS.Value;
+    return LHS;
+  }
+
+  friend LeafTy &operator*=(LeafTy &LHS, ScalarTy RHS) {
+    LHS.Value *= RHS;
+    return LHS;
+  }
+
+  friend LeafTy operator+(const LeafTy &LHS, const LeafTy &RHS) {
+    LeafTy Copy = LHS;
+    return Copy += RHS;
+  }
+
+  friend LeafTy operator-(const LeafTy &LHS, const LeafTy &RHS) {
+    LeafTy Copy = LHS;
+    return Copy -= RHS;
+  }
+
+  friend LeafTy operator*(const LeafTy &LHS, ScalarTy RHS) {
+    LeafTy Copy = LHS;
+    return Copy *= RHS;
+  }
+
+  template <typename U = ScalarTy>
+  friend typename std::enable_if<std::is_signed<U>::value, LeafTy>::type
+  operator-(const LeafTy &LHS) {
+    LeafTy Copy = LHS;
+    return Copy *= -1;
+  }
+
+public:
+  bool operator==(const UnivariateLinearPolyBase &RHS) const {
+    return Value == RHS.Value && UnivariateDim == RHS.UnivariateDim;
+  }
+
+  bool operator!=(const UnivariateLinearPolyBase &RHS) const {
+    return !(*this == RHS);
+  }
+
+  bool isZero() const { return !Value; }
   bool isNonZero() const { return !isZero(); }
+  explicit operator bool() const { return isNonZero(); }
+  ScalarTy getValue() const { return Value; }
+  ScalarTy getValue(unsigned Dim) const {
+    return Dim == UnivariateDim ? Value : 0;
+  }
+};
+
+
+//===----------------------------------------------------------------------===//
+// LinearPolySize - base class for fixed- or scalable sizes.
+//  ^  ^ 
+//  |  |
+//  |  +----- ElementCount - Leaf class to represent an element count
+//  |                        (vscale x unsigned)
+//  |
+//  +-------- TypeSize - Leaf class to represent a type size
+//                       (vscale x uint64_t)
+//===----------------------------------------------------------------------===//
+
+/// LinearPolySize is a base class to represent sizes. It is either
+/// fixed-sized or it is scalable-sized, but it cannot be both.
+template <typename LeafTy>
+class LinearPolySize : public UnivariateLinearPolyBase<LeafTy> {
+public:
+  using ScalarTy = typename UnivariateLinearPolyBase<LeafTy>::ScalarTy;
+  enum Dims : unsigned { FixedDim = 0, ScalableDim = 1 };
+
+protected:
+  LinearPolySize(ScalarTy MinVal, Dims D)
+      : UnivariateLinearPolyBase<LeafTy>(MinVal, D) {}
+
+public:
+  static LeafTy getFixed(ScalarTy MinVal) {
+    return static_cast<LeafTy>(LinearPolySize(MinVal, FixedDim));
+  }
+  static LeafTy getScalable(ScalarTy MinVal) {
+    return static_cast<LeafTy>(LinearPolySize(MinVal, ScalableDim));
+  }
+  static LeafTy get(ScalarTy MinVal, bool Scalable) {
+    return static_cast<LeafTy>(
+        LinearPolySize(MinVal, Scalable ? ScalableDim : FixedDim));
+  }
+  static LeafTy getNull() { return get(0, false); }
+
+  /// Returns the minimum value this size can represent.
+  ScalarTy getKnownMinValue() const { return this->getValue(); }
+  /// Returns whether the size is scaled by a runtime quantity (vscale).
+  bool isScalable() const { return this->UnivariateDim == ScalableDim; }
   /// A return value of true indicates we know at compile time that the number
   /// of elements (vscale * Min) is definitely even. However, returning false
   /// does not guarantee that the total number of elements is odd.
-  bool isKnownEven() const { return (MinVal & 0x1) == 0; }
-  ///@}
-
-  T getKnownMinValue() const { return MinVal; }
+  bool isKnownEven() const { return (getKnownMinValue() & 0x1) == 0; }
+  /// This function tells the caller whether the element count is known at
+  /// compile time to be a multiple of the scalar value RHS.
+  bool isKnownMultipleOf(ScalarTy RHS) const {
+    return getKnownMinValue() % RHS == 0;
+  }
 
   // Return the minimum value with the assumption that the count is exact.
   // Use in places where a scalable count doesn't make sense (e.g. non-vector
   // types, or vectors in backends which don't support scalable vectors).
-  T getFixedValue() const {
-    assert(!IsScalable &&
+  ScalarTy getFixedValue() const {
+    assert(!isScalable() &&
            "Request for a fixed element count on a scalable object");
-    return MinVal;
-  }
-
-  bool isScalable() const { return IsScalable; }
-
-  bool operator==(const PolySize &RHS) const {
-    return MinVal == RHS.MinVal && IsScalable == RHS.IsScalable;
+    return getKnownMinValue();
   }
 
-  bool operator!=(const PolySize &RHS) const { return !(*this == RHS); }
-
   // For some cases, size ordering between scalable and fixed size types cannot
   // be determined at compile time, so such comparisons aren't allowed.
   //
@@ -88,55 +309,30 @@ template <typename T> class PolySize {
   // All the functions below make use of the fact vscale is always >= 1, which
   // means that <vscale x 4 x i32> is guaranteed to be >= <4 x i32>, etc.
 
-  static bool isKnownLT(const PolySize &LHS, const PolySize &RHS) {
-    if (!LHS.IsScalable || RHS.IsScalable)
-      return LHS.MinVal < RHS.MinVal;
-
-    // LHS.IsScalable = true, RHS.IsScalable = false
+  static bool isKnownLT(const LinearPolySize &LHS, const LinearPolySize &RHS) {
+    if (!LHS.isScalable() || RHS.isScalable())
+      return LHS.getKnownMinValue() < RHS.getKnownMinValue();
     return false;
   }
 
-  static bool isKnownGT(const PolySize &LHS, const PolySize &RHS) {
-    if (LHS.IsScalable || !RHS.IsScalable)
-      return LHS.MinVal > RHS.MinVal;
-
-    // LHS.IsScalable = false, RHS.IsScalable = true
+  static bool isKnownGT(const LinearPolySize &LHS, const LinearPolySize &RHS) {
+    if (LHS.isScalable() || !RHS.isScalable())
+      return LHS.getKnownMinValue() > RHS.getKnownMinValue();
     return false;
   }
 
-  static bool isKnownLE(const PolySize &LHS, const PolySize &RHS) {
-    if (!LHS.IsScalable || RHS.IsScalable)
-      return LHS.MinVal <= RHS.MinVal;
-
-    // LHS.IsScalable = true, RHS.IsScalable = false
+  static bool isKnownLE(const LinearPolySize &LHS, const LinearPolySize &RHS) {
+    if (!LHS.isScalable() || RHS.isScalable())
+      return LHS.getKnownMinValue() <= RHS.getKnownMinValue();
     return false;
   }
 
-  static bool isKnownGE(const PolySize &LHS, const PolySize &RHS) {
-    if (LHS.IsScalable || !RHS.IsScalable)
-      return LHS.MinVal >= RHS.MinVal;
-
-    // LHS.IsScalable = false, RHS.IsScalable = true
+  static bool isKnownGE(const LinearPolySize &LHS, const LinearPolySize &RHS) {
+    if (LHS.isScalable() || !RHS.isScalable())
+      return LHS.getKnownMinValue() >= RHS.getKnownMinValue();
     return false;
   }
 
-  PolySize operator*(T RHS) { return {MinVal * RHS, IsScalable}; }
-
-  PolySize &operator*=(T RHS) {
-    MinVal *= RHS;
-    return *this;
-  }
-
-  friend PolySize operator-(const PolySize &LHS, const PolySize &RHS) {
-    assert(LHS.IsScalable == RHS.IsScalable &&
-           "Arithmetic using mixed scalable and fixed types");
-    return {LHS.MinVal - RHS.MinVal, LHS.IsScalable};
-  }
-
-  /// This function tells the caller whether the element count is known at
-  /// compile time to be a multiple of the scalar value RHS.
-  bool isKnownMultipleOf(T RHS) const { return MinVal % RHS == 0; }
-
   /// We do not provide the '/' operator here because division for polynomial
   /// types does not work in the same way as for normal integer types. We can
   /// only divide the minimum value (or coefficient) by RHS, which is not the
@@ -145,86 +341,78 @@ template <typename T> class PolySize {
   /// The caller is recommended to use this function in combination with
   /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to
   /// perform a lossless divide by RHS.
-  PolySize divideCoefficientBy(T RHS) const {
-    return PolySize(MinVal / RHS, IsScalable);
+  LeafTy divideCoefficientBy(ScalarTy RHS) const {
+    return static_cast<LeafTy>(
+        LinearPolySize::get(getKnownMinValue() / RHS, isScalable()));
   }
 
-  PolySize coefficientNextPowerOf2() const {
-    return PolySize(static_cast<T>(llvm::NextPowerOf2(MinVal)), IsScalable);
+  LeafTy coefficientNextPowerOf2() const {
+    return static_cast<LeafTy>(LinearPolySize::get(
+        static_cast<ScalarTy>(llvm::NextPowerOf2(getKnownMinValue())),
+        isScalable()));
   }
 
   /// Printing function.
   void print(raw_ostream &OS) const {
-    if (IsScalable)
+    if (isScalable())
       OS << "vscale x ";
-    OS << MinVal;
+    OS << getKnownMinValue();
   }
 };
 
-/// Stream operator function for `PolySize`.
-template <typename T>
-inline raw_ostream &operator<<(raw_ostream &OS, const PolySize<T> &PS) {
-  PS.print(OS);
-  return OS;
-}
+class ElementCount;
+template <> struct LinearPolyBaseTypeTraits<ElementCount> {
+  using ScalarTy = unsigned;
+  static constexpr unsigned Dimensions = 2;
+};
 
-class ElementCount : public PolySize<unsigned> {
+class ElementCount : public LinearPolySize<ElementCount> {
 public:
 
-  constexpr ElementCount(PolySize<unsigned> V) : PolySize(V) {}
+  ElementCount(const LinearPolySize<ElementCount> &V) : LinearPolySize(V) {}
 
   /// Counting predicates.
   ///
-  /// Notice that MinVal = 1 and IsScalable = true is considered more than
-  /// one element.
-  ///
-  ///@{ No elements..
+  ///@{ Number of elements..
   /// Exactly one element.
-  bool isScalar() const { return !IsScalable && MinVal == 1; }
+  bool isScalar() const { return !isScalable() && getKnownMinValue() == 1; }
   /// One or more elements.
-  bool isVector() const { return (IsScalable && MinVal != 0) || MinVal > 1; }
+  bool isVector() const {
+    return (isScalable() && getKnownMinValue() != 0) || getKnownMinValue() > 1;
+  }
   ///@}
 };
 
 // This class is used to represent the size of types. If the type is of fixed
+class TypeSize;
+template <> struct LinearPolyBaseTypeTraits<TypeSize> {
+  using ScalarTy = uint64_t;
+  static constexpr unsigned Dimensions = 2;
+};
+
+// TODO: Most functionality in this class will gradually be phased out
+// so it will resemble LinearPolySize as much as possible.
+//
+// TypeSize is used to represent the size of types. If the type is of fixed
 // size, it will represent the exact size. If the type is a scalable vector,
 // it will represent the known minimum size.
-class TypeSize : public PolySize<uint64_t> {
+class TypeSize : public LinearPolySize<TypeSize> {
 public:
-  constexpr TypeSize(PolySize<uint64_t> V) : PolySize(V) {}
+  TypeSize(const LinearPolySize<TypeSize> &V) : LinearPolySize(V) {}
+  TypeSize(ScalarTy MinVal, bool IsScalable)
+      : LinearPolySize(LinearPolySize::get(MinVal, IsScalable)) {}
 
-  constexpr TypeSize(uint64_t MinVal, bool IsScalable)
-      : PolySize(MinVal, IsScalable) {}
+  static TypeSize Fixed(ScalarTy MinVal) { return TypeSize(MinVal, false); }
+  static TypeSize Scalable(ScalarTy MinVal) { return TypeSize(MinVal, true); }
 
-  static constexpr TypeSize Fixed(uint64_t MinVal) {
-    return TypeSize(MinVal, false);
-  }
-  static constexpr TypeSize Scalable(uint64_t MinVal) {
-    return TypeSize(MinVal, true);
-  }
+  ScalarTy getFixedSize() const { return getFixedValue(); }
+  ScalarTy getKnownMinSize() const { return getKnownMinValue(); }
 
-  uint64_t getFixedSize() const { return getFixedValue(); }
-  uint64_t getKnownMinSize() const { return getKnownMinValue(); }
-
-  TypeSize &operator-=(TypeSize RHS) {
-    assert(IsScalable == RHS.IsScalable &&
-           "Subtraction using mixed scalable and fixed types");
-    MinVal -= RHS.MinVal;
-    return *this;
-  }
-
-  TypeSize &operator+=(TypeSize RHS) {
-    assert(IsScalable == RHS.IsScalable &&
-           "Addition using mixed scalable and fixed types");
-    MinVal += RHS.MinVal;
-    return *this;
-  }
-
-  friend TypeSize operator-(const TypeSize &LHS, const TypeSize &RHS) {
-    assert(LHS.IsScalable == RHS.IsScalable &&
-           "Arithmetic using mixed scalable and fixed types");
-    return {LHS.MinVal - RHS.MinVal, LHS.IsScalable};
-  }
+  // All code for this class below this point is needed because of the
+  // temporary implicit conversion to uint64_t. The operator overloads are
+  // needed because otherwise the conversion of the parent class
+  // UnivariateLinearPolyBase -> TypeSize is ambiguous.
+  // TODO: Remove the implicit conversion.
 
   // Casts to a uint64_t if this is a fixed-width size.
   //
@@ -244,7 +432,7 @@ class TypeSize : public PolySize<uint64_t> {
   //     else
   //       bail out early for scalable vectors and use getFixedValue()
   //   }
-  operator uint64_t() const {
+  operator ScalarTy() const {
 #ifdef STRICT_FIXED_SIZE_VECTORS
     return getFixedValue();
 #else
@@ -256,35 +444,35 @@ class TypeSize : public PolySize<uint64_t> {
 #endif
   }
 
-  // Convenience operators to obtain relative sizes independently of
-  // the scalable flag.
-  TypeSize operator*(unsigned RHS) const { return {MinVal * RHS, IsScalable}; }
-
-  friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
-    return {LHS * RHS.MinVal, RHS.IsScalable};
+  // Additional operators needed to avoid ambiguous parses
+  // because of the implicit conversion hack.
+  friend TypeSize operator*(const TypeSize &LHS, const int RHS) {
+    return LHS * (ScalarTy)RHS;
   }
-
-  // Additional convenience operators needed to avoid ambiguous parses.
-  // TODO: Make uint64_t the default operator?
-  TypeSize operator*(uint64_t RHS) const { return {MinVal * RHS, IsScalable}; }
-
-  TypeSize operator*(int RHS) const { return {MinVal * RHS, IsScalable}; }
-
-  TypeSize operator*(int64_t RHS) const { return {MinVal * RHS, IsScalable}; }
-
-  friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
-    return {LHS * RHS.MinVal, RHS.IsScalable};
+  friend TypeSize operator*(const TypeSize &LHS, const unsigned RHS) {
+    return LHS * (ScalarTy)RHS;
+  }
+  friend TypeSize operator*(const TypeSize &LHS, const int64_t RHS) {
+    return LHS * (ScalarTy)RHS;
   }
-
   friend TypeSize operator*(const int LHS, const TypeSize &RHS) {
-    return {LHS * RHS.MinVal, RHS.IsScalable};
+    return RHS * LHS;
+  }
+  friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
+    return RHS * LHS;
   }
-
   friend TypeSize operator*(const int64_t LHS, const TypeSize &RHS) {
-    return {LHS * RHS.MinVal, RHS.IsScalable};
+    return RHS * LHS;
+  }
+  friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
+    return RHS * LHS;
   }
 };
 
+//===----------------------------------------------------------------------===//
+// Utilities
+//===----------------------------------------------------------------------===//
+
 /// Returns a TypeSize with a known minimum size that is the next integer
 /// (mod 2**64) that is greater than or equal to \p Value and is a multiple
 /// of \p Align. \p Align must be non-zero.
@@ -296,6 +484,15 @@ inline TypeSize alignTo(TypeSize Size, uint64_t Align) {
           Size.isScalable()};
 }
 
+/// Stream operator function for `LinearPolySize`.
+template <typename LeafTy>
+inline raw_ostream &operator<<(raw_ostream &OS,
+                               const LinearPolySize<LeafTy> &PS) {
+  PS.print(OS);
+  return OS;
+}
+
+template <typename T> struct DenseMapInfo;
 template <> struct DenseMapInfo<ElementCount> {
   static inline ElementCount getEmptyKey() {
     return ElementCount::getScalable(~0U);
@@ -303,7 +500,7 @@ template <> struct DenseMapInfo<ElementCount> {
   static inline ElementCount getTombstoneKey() {
     return ElementCount::getFixed(~0U - 1);
   }
-  static unsigned getHashValue(const ElementCount& EltCnt) {
+  static unsigned getHashValue(const ElementCount &EltCnt) {
     unsigned HashVal = EltCnt.getKnownMinValue() * 37U;
     if (EltCnt.isScalable())
       return (HashVal - 1U);
@@ -311,7 +508,7 @@ template <> struct DenseMapInfo<ElementCount> {
     return HashVal;
   }
 
-  static bool isEqual(const ElementCount& LHS, const ElementCount& RHS) {
+  static bool isEqual(const ElementCount &LHS, const ElementCount &RHS) {
     return LHS == RHS;
   }
 };

diff  --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 0fb636a11a8f..86a25faa7d78 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -45,6 +45,7 @@ add_llvm_unittest(SupportTests
   JSONTest.cpp
   KnownBitsTest.cpp
   LEB128Test.cpp
+  LinearPolyBaseTest.cpp
   LineIteratorTest.cpp
   LockFileManagerTest.cpp
   MatchersTest.cpp

diff  --git a/llvm/unittests/Support/LinearPolyBaseTest.cpp b/llvm/unittests/Support/LinearPolyBaseTest.cpp
new file mode 100644
index 000000000000..85507012003d
--- /dev/null
+++ b/llvm/unittests/Support/LinearPolyBaseTest.cpp
@@ -0,0 +1,164 @@
+//===- TestPoly3D.cpp - Poly3D unit tests------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/TypeSize.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+class Poly3D;
+template <> struct llvm::LinearPolyBaseTypeTraits<Poly3D> {
+  using ScalarTy = int64_t;
+  static const unsigned Dimensions = 3;
+};
+
+using Poly3DBase = LinearPolyBase<Poly3D>;
+class Poly3D : public Poly3DBase {
+public:
+  using ScalarTy = Poly3DBase::ScalarTy;
+  Poly3D(ScalarTy x, ScalarTy y, ScalarTy z) : Poly3DBase({x, y, z}) {}
+  Poly3D(const Poly3DBase &Convert) : Poly3DBase(Convert) {}
+};
+
+TEST(LinearPolyBase, Poly3D_isZero) {
+  EXPECT_TRUE(Poly3D(0, 0, 0).isZero());
+  EXPECT_TRUE(Poly3D(0, 0, 1).isNonZero());
+  EXPECT_TRUE(Poly3D(0, 0, 1));
+}
+
+TEST(LinearPolyBase, Poly3D_Equality) {
+  EXPECT_EQ(Poly3D(1, 2, 3), Poly3D(1, 2, 3));
+  EXPECT_NE(Poly3D(1, 2, 3), Poly3D(1, 2, 4));
+}
+
+TEST(LinearPolyBase, Poly3D_GetValue) {
+  EXPECT_EQ(Poly3D(1, 2, 3).getValue(0), 1);
+  EXPECT_EQ(Poly3D(1, 2, 3).getValue(1), 2);
+  EXPECT_EQ(Poly3D(1, 2, 3).getValue(2), 3);
+}
+
+TEST(LinearPolyBase, Poly3D_Add) {
+  // Test operator+
+  EXPECT_EQ(Poly3D(42, 0, 0) + Poly3D(0, 42, 0) + Poly3D(0, 0, 42),
+            Poly3D(42, 42, 42));
+
+  // Test operator+=
+  Poly3D X(42, 0, 0);
+  X += Poly3D(0, 42, 0);
+  X += Poly3D(0, 0, 42);
+  EXPECT_EQ(X, Poly3D(42, 42, 42));
+}
+
+TEST(LinearPolyBase, Poly3D_Sub) {
+  // Test operator-
+  EXPECT_EQ(Poly3D(42, 42, 42) - Poly3D(42, 0, 0) - Poly3D(0, 42, 0) -
+                Poly3D(0, 0, 42),
+            Poly3D(0, 0, 0));
+
+  // Test operator-=
+  Poly3D X(42, 42, 42);
+  X -= Poly3D(42, 0, 0);
+  X -= Poly3D(0, 42, 0);
+  X -= Poly3D(0, 0, 42);
+  EXPECT_EQ(X, Poly3D(0, 0, 0));
+}
+
+TEST(LinearPolyBase, Poly3D_Scale) {
+  // Test operator*
+  EXPECT_EQ(Poly3D(1, 2, 4) * 2, Poly3D(2, 4, 8));
+  EXPECT_EQ(Poly3D(1, 2, 4) * -2, Poly3D(-2, -4, -8));
+}
+
+TEST(LinearPolyBase, Poly3D_Invert) {
+  // Test operator-
+  EXPECT_EQ(-Poly3D(2, 4, 8), Poly3D(-2, -4, -8));
+}
+
+class Univariate3D;
+template <> struct llvm::LinearPolyBaseTypeTraits<Univariate3D> {
+  using ScalarTy = int64_t;
+  static const unsigned Dimensions = 3;
+};
+
+using Univariate3DBase = UnivariateLinearPolyBase<Univariate3D>;
+class Univariate3D : public Univariate3DBase {
+public:
+  using ScalarTy = Univariate3DBase::ScalarTy;
+  Univariate3D(ScalarTy x, unsigned Dim) : Univariate3DBase(x, Dim) {}
+  Univariate3D(const Univariate3DBase &Convert) : Univariate3DBase(Convert) {}
+};
+
+TEST(UnivariateLinearPolyBase, Univariate3D_isZero) {
+  EXPECT_TRUE(Univariate3D(0, 0).isZero());
+  EXPECT_TRUE(Univariate3D(0, 1).isZero());
+  EXPECT_TRUE(Univariate3D(0, 2).isZero());
+  EXPECT_TRUE(Univariate3D(1, 0).isNonZero());
+  EXPECT_TRUE(Univariate3D(1, 1).isNonZero());
+  EXPECT_TRUE(Univariate3D(1, 2).isNonZero());
+  EXPECT_TRUE(Univariate3D(1, 0));
+}
+
+TEST(UnivariateLinearPolyBase, Univariate3D_Equality) {
+  EXPECT_EQ(Univariate3D(1, 0), Univariate3D(1, 0));
+  EXPECT_NE(Univariate3D(1, 0), Univariate3D(1, 2));
+  EXPECT_NE(Univariate3D(1, 0), Univariate3D(1, 1));
+  EXPECT_NE(Univariate3D(1, 0), Univariate3D(2, 0));
+  EXPECT_NE(Univariate3D(1, 0), Univariate3D(0, 0));
+}
+
+TEST(UnivariateLinearPolyBase, Univariate3D_GetValue) {
+  EXPECT_EQ(Univariate3D(42, 0).getValue(0), 42);
+  EXPECT_EQ(Univariate3D(42, 0).getValue(1), 0);
+  EXPECT_EQ(Univariate3D(42, 0).getValue(2), 0);
+
+  EXPECT_EQ(Univariate3D(42, 1).getValue(0), 0);
+  EXPECT_EQ(Univariate3D(42, 1).getValue(1), 42);
+  EXPECT_EQ(Univariate3D(42, 1).getValue(2), 0);
+
+  EXPECT_EQ(Univariate3D(42, 0).getValue(), 42);
+  EXPECT_EQ(Univariate3D(42, 1).getValue(), 42);
+}
+
+TEST(UnivariateLinearPolyBase, Univariate3D_Add) {
+  // Test operator+
+  EXPECT_EQ(Univariate3D(42, 0) + Univariate3D(42, 0), Univariate3D(84, 0));
+  EXPECT_EQ(Univariate3D(42, 1) + Univariate3D(42, 1), Univariate3D(84, 1));
+  EXPECT_DEBUG_DEATH(Univariate3D(42, 0) + Univariate3D(42, 1),
+                     "Invalid dimensions");
+
+  // Test operator+=
+  Univariate3D X(42, 0);
+  X += Univariate3D(42, 0);
+  EXPECT_EQ(X, Univariate3D(84, 0));
+}
+
+TEST(UnivariateLinearPolyBase, Univariate3D_Sub) {
+  // Test operator+
+  EXPECT_EQ(Univariate3D(84, 0) - Univariate3D(42, 0), Univariate3D(42, 0));
+  EXPECT_EQ(Univariate3D(84, 1) - Univariate3D(42, 1), Univariate3D(42, 1));
+  EXPECT_DEBUG_DEATH(Univariate3D(84, 0) - Univariate3D(42, 1),
+                     "Invalid dimensions");
+
+  // Test operator+=
+  Univariate3D X(84, 0);
+  X -= Univariate3D(42, 0);
+  EXPECT_EQ(X, Univariate3D(42, 0));
+}
+
+TEST(UnivariateLinearPolyBase, Univariate3D_Scale) {
+  // Test operator*
+  EXPECT_EQ(Univariate3D(4, 0) * 2, Univariate3D(8, 0));
+  EXPECT_EQ(Univariate3D(4, 1) * -2, Univariate3D(-8, 1));
+}
+
+TEST(UnivariateLinearPolyBase, Univariate3D_Invert) {
+  // Test operator-
+  EXPECT_EQ(-Univariate3D(4, 0), Univariate3D(-4, 0));
+  EXPECT_EQ(-Univariate3D(4, 1), Univariate3D(-4, 1));
+}
+


        


More information about the llvm-commits mailing list