1 //===- TypeSize.h - Wrapper around type sizes -------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file provides a struct that can be used to query the size of IR types
10 // which may be scalable vectors. It provides convenience operators so that
11 // it can be used in much the same way as a single scalar value.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_SUPPORT_TYPESIZE_H
16 #define LLVM_SUPPORT_TYPESIZE_H
17 
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/Support/MathExtras.h"
20 #include "llvm/Support/WithColor.h"
21 
22 #include <algorithm>
23 #include <array>
24 #include <cassert>
25 #include <cstdint>
26 #include <type_traits>
27 
28 namespace llvm {
29 
30 /// Reports a diagnostic message to indicate an invalid size request has been
31 /// done on a scalable vector. This function may not return.
32 void reportInvalidSizeRequest(const char *Msg);
33 
34 template <typename LeafTy> struct LinearPolyBaseTypeTraits {};
35 
36 //===----------------------------------------------------------------------===//
37 // LinearPolyBase - a base class for linear polynomials with multiple
38 // dimensions. This can e.g. be used to describe offsets that are have both a
39 // fixed and scalable component.
40 //===----------------------------------------------------------------------===//
41 
42 /// LinearPolyBase describes a linear polynomial:
43 ///  c0 * scale0 + c1 * scale1 + ... + cK * scaleK
44 /// where the scale is implicit, so only the coefficients are encoded.
45 template <typename LeafTy>
46 class LinearPolyBase {
47 public:
48   using ScalarTy = typename LinearPolyBaseTypeTraits<LeafTy>::ScalarTy;
49   static constexpr auto Dimensions = LinearPolyBaseTypeTraits<LeafTy>::Dimensions;
50   static_assert(Dimensions != std::numeric_limits<unsigned>::max(),
51                 "Dimensions out of range");
52 
53 private:
54   std::array<ScalarTy, Dimensions> Coefficients;
55 
56 protected:
57   LinearPolyBase(ArrayRef<ScalarTy> Values) {
58     std::copy(Values.begin(), Values.end(), Coefficients.begin());
59   }
60 
61 public:
62   friend LeafTy &operator+=(LeafTy &LHS, const LeafTy &RHS) {
63     for (unsigned I=0; I<Dimensions; ++I)
64       LHS.Coefficients[I] += RHS.Coefficients[I];
65     return LHS;
66   }
67 
68   friend LeafTy &operator-=(LeafTy &LHS, const LeafTy &RHS) {
69     for (unsigned I=0; I<Dimensions; ++I)
70       LHS.Coefficients[I] -= RHS.Coefficients[I];
71     return LHS;
72   }
73 
74   friend LeafTy &operator*=(LeafTy &LHS, ScalarTy RHS) {
75     for (auto &C : LHS.Coefficients)
76       C *= RHS;
77     return LHS;
78   }
79 
80   friend LeafTy operator+(const LeafTy &LHS, const LeafTy &RHS) {
81     LeafTy Copy = LHS;
82     return Copy += RHS;
83   }
84 
85   friend LeafTy operator-(const LeafTy &LHS, const LeafTy &RHS) {
86     LeafTy Copy = LHS;
87     return Copy -= RHS;
88   }
89 
90   friend LeafTy operator*(const LeafTy &LHS, ScalarTy RHS) {
91     LeafTy Copy = LHS;
92     return Copy *= RHS;
93   }
94 
95   template <typename U = ScalarTy>
96   friend typename std::enable_if_t<std::is_signed<U>::value, LeafTy>
97   operator-(const LeafTy &LHS) {
98     LeafTy Copy = LHS;
99     return Copy *= -1;
100   }
101 
102   bool operator==(const LinearPolyBase &RHS) const {
103     return std::equal(Coefficients.begin(), Coefficients.end(),
104                       RHS.Coefficients.begin());
105   }
106 
107   bool operator!=(const LinearPolyBase &RHS) const {
108     return !(*this == RHS);
109   }
110 
111   bool isZero() const {
112     return all_of(Coefficients, [](const ScalarTy &C) { return C == 0; });
113   }
114   bool isNonZero() const { return !isZero(); }
115   explicit operator bool() const { return isNonZero(); }
116 
117   ScalarTy getValue(unsigned Dim) const { return Coefficients[Dim]; }
118 };
119 
120 //===----------------------------------------------------------------------===//
121 // StackOffset - Represent an offset with named fixed and scalable components.
122 //===----------------------------------------------------------------------===//
123 
124 class StackOffset;
125 template <> struct LinearPolyBaseTypeTraits<StackOffset> {
126   using ScalarTy = int64_t;
127   static constexpr unsigned Dimensions = 2;
128 };
129 
130 /// StackOffset is a class to represent an offset with 2 dimensions,
131 /// named fixed and scalable, respectively. This class allows a value for both
132 /// dimensions to depict e.g. "8 bytes and 16 scalable bytes", which is needed
133 /// to represent stack offsets.
134 class StackOffset : public LinearPolyBase<StackOffset> {
135 protected:
136   StackOffset(ScalarTy Fixed, ScalarTy Scalable)
137       : LinearPolyBase<StackOffset>({Fixed, Scalable}) {}
138 
139 public:
140   StackOffset() : StackOffset({0, 0}) {}
141   StackOffset(const LinearPolyBase<StackOffset> &Other)
142       : LinearPolyBase<StackOffset>(Other) {}
143   static StackOffset getFixed(ScalarTy Fixed) { return {Fixed, 0}; }
144   static StackOffset getScalable(ScalarTy Scalable) { return {0, Scalable}; }
145   static StackOffset get(ScalarTy Fixed, ScalarTy Scalable) {
146     return {Fixed, Scalable};
147   }
148 
149   ScalarTy getFixed() const { return this->getValue(0); }
150   ScalarTy getScalable() const { return this->getValue(1); }
151 };
152 
153 //===----------------------------------------------------------------------===//
154 // UnivariateLinearPolyBase - a base class for linear polynomials with multiple
155 // dimensions, but where only one dimension can be set at any time.
156 // This can e.g. be used to describe sizes that are either fixed or scalable.
157 //===----------------------------------------------------------------------===//
158 
159 /// UnivariateLinearPolyBase is a base class for ElementCount and TypeSize.
160 /// Like LinearPolyBase it tries to represent a linear polynomial
161 /// where only one dimension can be set at any time, e.g.
162 ///   0 * scale0 + 0 * scale1 + ... + cJ * scaleJ + ... + 0 * scaleK
163 /// The dimension that is set is the univariate dimension.
164 template <typename LeafTy>
165 class UnivariateLinearPolyBase {
166 public:
167   using ScalarTy = typename LinearPolyBaseTypeTraits<LeafTy>::ScalarTy;
168   static constexpr auto Dimensions = LinearPolyBaseTypeTraits<LeafTy>::Dimensions;
169   static_assert(Dimensions != std::numeric_limits<unsigned>::max(),
170                 "Dimensions out of range");
171 
172 protected:
173   ScalarTy Value;         // The value at the univeriate dimension.
174   unsigned UnivariateDim; // The univeriate dimension.
175 
176   UnivariateLinearPolyBase(ScalarTy Val, unsigned UnivariateDim)
177       : Value(Val), UnivariateDim(UnivariateDim) {
178     assert(UnivariateDim < Dimensions && "Dimension out of range");
179   }
180 
181   friend LeafTy &operator+=(LeafTy &LHS, const LeafTy &RHS) {
182     assert(LHS.UnivariateDim == RHS.UnivariateDim && "Invalid dimensions");
183     LHS.Value += RHS.Value;
184     return LHS;
185   }
186 
187   friend LeafTy &operator-=(LeafTy &LHS, const LeafTy &RHS) {
188     assert(LHS.UnivariateDim == RHS.UnivariateDim && "Invalid dimensions");
189     LHS.Value -= RHS.Value;
190     return LHS;
191   }
192 
193   friend LeafTy &operator*=(LeafTy &LHS, ScalarTy RHS) {
194     LHS.Value *= RHS;
195     return LHS;
196   }
197 
198   friend LeafTy operator+(const LeafTy &LHS, const LeafTy &RHS) {
199     LeafTy Copy = LHS;
200     return Copy += RHS;
201   }
202 
203   friend LeafTy operator-(const LeafTy &LHS, const LeafTy &RHS) {
204     LeafTy Copy = LHS;
205     return Copy -= RHS;
206   }
207 
208   friend LeafTy operator*(const LeafTy &LHS, ScalarTy RHS) {
209     LeafTy Copy = LHS;
210     return Copy *= RHS;
211   }
212 
213   template <typename U = ScalarTy>
214   friend typename std::enable_if<std::is_signed<U>::value, LeafTy>::type
215   operator-(const LeafTy &LHS) {
216     LeafTy Copy = LHS;
217     return Copy *= -1;
218   }
219 
220 public:
221   bool operator==(const UnivariateLinearPolyBase &RHS) const {
222     return Value == RHS.Value && UnivariateDim == RHS.UnivariateDim;
223   }
224 
225   bool operator!=(const UnivariateLinearPolyBase &RHS) const {
226     return !(*this == RHS);
227   }
228 
229   bool isZero() const { return !Value; }
230   bool isNonZero() const { return !isZero(); }
231   explicit operator bool() const { return isNonZero(); }
232   ScalarTy getValue() const { return Value; }
233   ScalarTy getValue(unsigned Dim) const {
234     return Dim == UnivariateDim ? Value : 0;
235   }
236 
237   /// Add \p RHS to the value at the univariate dimension.
238   LeafTy getWithIncrement(ScalarTy RHS) const {
239     return static_cast<LeafTy>(
240         UnivariateLinearPolyBase(Value + RHS, UnivariateDim));
241   }
242 
243   /// Subtract \p RHS from the value at the univariate dimension.
244   LeafTy getWithDecrement(ScalarTy RHS) const {
245     return static_cast<LeafTy>(
246         UnivariateLinearPolyBase(Value - RHS, UnivariateDim));
247   }
248 };
249 
250 
251 //===----------------------------------------------------------------------===//
252 // LinearPolySize - base class for fixed- or scalable sizes.
253 //  ^  ^
254 //  |  |
255 //  |  +----- ElementCount - Leaf class to represent an element count
256 //  |                        (vscale x unsigned)
257 //  |
258 //  +-------- TypeSize - Leaf class to represent a type size
259 //                       (vscale x uint64_t)
260 //===----------------------------------------------------------------------===//
261 
262 /// LinearPolySize is a base class to represent sizes. It is either
263 /// fixed-sized or it is scalable-sized, but it cannot be both.
264 template <typename LeafTy>
265 class LinearPolySize : public UnivariateLinearPolyBase<LeafTy> {
266   // Make the parent class a friend, so that it can access the protected
267   // conversion/copy-constructor for UnivariatePolyBase<LeafTy> ->
268   // LinearPolySize<LeafTy>.
269   friend class UnivariateLinearPolyBase<LeafTy>;
270 
271 public:
272   using ScalarTy = typename UnivariateLinearPolyBase<LeafTy>::ScalarTy;
273   enum Dims : unsigned { FixedDim = 0, ScalableDim = 1 };
274 
275 protected:
276   LinearPolySize(ScalarTy MinVal, Dims D)
277       : UnivariateLinearPolyBase<LeafTy>(MinVal, D) {}
278 
279   LinearPolySize(const UnivariateLinearPolyBase<LeafTy> &V)
280       : UnivariateLinearPolyBase<LeafTy>(V) {}
281 
282 public:
283 
284   static LeafTy getFixed(ScalarTy MinVal) {
285     return static_cast<LeafTy>(LinearPolySize(MinVal, FixedDim));
286   }
287   static LeafTy getScalable(ScalarTy MinVal) {
288     return static_cast<LeafTy>(LinearPolySize(MinVal, ScalableDim));
289   }
290   static LeafTy get(ScalarTy MinVal, bool Scalable) {
291     return static_cast<LeafTy>(
292         LinearPolySize(MinVal, Scalable ? ScalableDim : FixedDim));
293   }
294   static LeafTy getNull() { return get(0, false); }
295 
296   /// Returns the minimum value this size can represent.
297   ScalarTy getKnownMinValue() const { return this->getValue(); }
298   /// Returns whether the size is scaled by a runtime quantity (vscale).
299   bool isScalable() const { return this->UnivariateDim == ScalableDim; }
300   /// A return value of true indicates we know at compile time that the number
301   /// of elements (vscale * Min) is definitely even. However, returning false
302   /// does not guarantee that the total number of elements is odd.
303   bool isKnownEven() const { return (getKnownMinValue() & 0x1) == 0; }
304   /// This function tells the caller whether the element count is known at
305   /// compile time to be a multiple of the scalar value RHS.
306   bool isKnownMultipleOf(ScalarTy RHS) const {
307     return getKnownMinValue() % RHS == 0;
308   }
309 
310   // Return the minimum value with the assumption that the count is exact.
311   // Use in places where a scalable count doesn't make sense (e.g. non-vector
312   // types, or vectors in backends which don't support scalable vectors).
313   ScalarTy getFixedValue() const {
314     assert(!isScalable() &&
315            "Request for a fixed element count on a scalable object");
316     return getKnownMinValue();
317   }
318 
319   // For some cases, size ordering between scalable and fixed size types cannot
320   // be determined at compile time, so such comparisons aren't allowed.
321   //
322   // e.g. <vscale x 2 x i16> could be bigger than <4 x i32> with a runtime
323   // vscale >= 5, equal sized with a vscale of 4, and smaller with
324   // a vscale <= 3.
325   //
326   // All the functions below make use of the fact vscale is always >= 1, which
327   // means that <vscale x 4 x i32> is guaranteed to be >= <4 x i32>, etc.
328 
329   static bool isKnownLT(const LinearPolySize &LHS, const LinearPolySize &RHS) {
330     if (!LHS.isScalable() || RHS.isScalable())
331       return LHS.getKnownMinValue() < RHS.getKnownMinValue();
332     return false;
333   }
334 
335   static bool isKnownGT(const LinearPolySize &LHS, const LinearPolySize &RHS) {
336     if (LHS.isScalable() || !RHS.isScalable())
337       return LHS.getKnownMinValue() > RHS.getKnownMinValue();
338     return false;
339   }
340 
341   static bool isKnownLE(const LinearPolySize &LHS, const LinearPolySize &RHS) {
342     if (!LHS.isScalable() || RHS.isScalable())
343       return LHS.getKnownMinValue() <= RHS.getKnownMinValue();
344     return false;
345   }
346 
347   static bool isKnownGE(const LinearPolySize &LHS, const LinearPolySize &RHS) {
348     if (LHS.isScalable() || !RHS.isScalable())
349       return LHS.getKnownMinValue() >= RHS.getKnownMinValue();
350     return false;
351   }
352 
353   /// We do not provide the '/' operator here because division for polynomial
354   /// types does not work in the same way as for normal integer types. We can
355   /// only divide the minimum value (or coefficient) by RHS, which is not the
356   /// same as
357   ///   (Min * Vscale) / RHS
358   /// The caller is recommended to use this function in combination with
359   /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to
360   /// perform a lossless divide by RHS.
361   LeafTy divideCoefficientBy(ScalarTy RHS) const {
362     return static_cast<LeafTy>(
363         LinearPolySize::get(getKnownMinValue() / RHS, isScalable()));
364   }
365 
366   LeafTy coefficientNextPowerOf2() const {
367     return static_cast<LeafTy>(LinearPolySize::get(
368         static_cast<ScalarTy>(llvm::NextPowerOf2(getKnownMinValue())),
369         isScalable()));
370   }
371 
372   /// Printing function.
373   void print(raw_ostream &OS) const {
374     if (isScalable())
375       OS << "vscale x ";
376     OS << getKnownMinValue();
377   }
378 };
379 
380 class ElementCount;
381 template <> struct LinearPolyBaseTypeTraits<ElementCount> {
382   using ScalarTy = unsigned;
383   static constexpr unsigned Dimensions = 2;
384 };
385 
386 class ElementCount : public LinearPolySize<ElementCount> {
387 public:
388   ElementCount() : LinearPolySize(LinearPolySize::getNull()) {}
389 
390   ElementCount(const LinearPolySize<ElementCount> &V) : LinearPolySize(V) {}
391 
392   /// Counting predicates.
393   ///
394   ///@{ Number of elements..
395   /// Exactly one element.
396   bool isScalar() const { return !isScalable() && getKnownMinValue() == 1; }
397   /// One or more elements.
398   bool isVector() const {
399     return (isScalable() && getKnownMinValue() != 0) || getKnownMinValue() > 1;
400   }
401   ///@}
402 };
403 
404 // This class is used to represent the size of types. If the type is of fixed
405 class TypeSize;
406 template <> struct LinearPolyBaseTypeTraits<TypeSize> {
407   using ScalarTy = uint64_t;
408   static constexpr unsigned Dimensions = 2;
409 };
410 
411 // TODO: Most functionality in this class will gradually be phased out
412 // so it will resemble LinearPolySize as much as possible.
413 //
414 // TypeSize is used to represent the size of types. If the type is of fixed
415 // size, it will represent the exact size. If the type is a scalable vector,
416 // it will represent the known minimum size.
417 class TypeSize : public LinearPolySize<TypeSize> {
418 public:
419   TypeSize(const LinearPolySize<TypeSize> &V) : LinearPolySize(V) {}
420   TypeSize(ScalarTy MinVal, bool IsScalable)
421       : LinearPolySize(LinearPolySize::get(MinVal, IsScalable)) {}
422 
423   static TypeSize Fixed(ScalarTy MinVal) { return TypeSize(MinVal, false); }
424   static TypeSize Scalable(ScalarTy MinVal) { return TypeSize(MinVal, true); }
425 
426   ScalarTy getFixedSize() const { return getFixedValue(); }
427   ScalarTy getKnownMinSize() const { return getKnownMinValue(); }
428 
429   // All code for this class below this point is needed because of the
430   // temporary implicit conversion to uint64_t. The operator overloads are
431   // needed because otherwise the conversion of the parent class
432   // UnivariateLinearPolyBase -> TypeSize is ambiguous.
433   // TODO: Remove the implicit conversion.
434 
435   // Casts to a uint64_t if this is a fixed-width size.
436   //
437   // This interface is deprecated and will be removed in a future version
438   // of LLVM in favour of upgrading uses that rely on this implicit conversion
439   // to uint64_t. Calls to functions that return a TypeSize should use the
440   // proper interfaces to TypeSize.
441   // In practice this is mostly calls to MVT/EVT::getSizeInBits().
442   //
443   // To determine how to upgrade the code:
444   //
445   //   if (<algorithm works for both scalable and fixed-width vectors>)
446   //     use getKnownMinValue()
447   //   else if (<algorithm works only for fixed-width vectors>) {
448   //     if <algorithm can be adapted for both scalable and fixed-width vectors>
449   //       update the algorithm and use getKnownMinValue()
450   //     else
451   //       bail out early for scalable vectors and use getFixedValue()
452   //   }
453   operator ScalarTy() const;
454 
455   // Additional operators needed to avoid ambiguous parses
456   // because of the implicit conversion hack.
457   friend TypeSize operator*(const TypeSize &LHS, const int RHS) {
458     return LHS * (ScalarTy)RHS;
459   }
460   friend TypeSize operator*(const TypeSize &LHS, const unsigned RHS) {
461     return LHS * (ScalarTy)RHS;
462   }
463   friend TypeSize operator*(const TypeSize &LHS, const int64_t RHS) {
464     return LHS * (ScalarTy)RHS;
465   }
466   friend TypeSize operator*(const int LHS, const TypeSize &RHS) {
467     return RHS * LHS;
468   }
469   friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
470     return RHS * LHS;
471   }
472   friend TypeSize operator*(const int64_t LHS, const TypeSize &RHS) {
473     return RHS * LHS;
474   }
475   friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
476     return RHS * LHS;
477   }
478 };
479 
480 //===----------------------------------------------------------------------===//
481 // Utilities
482 //===----------------------------------------------------------------------===//
483 
484 /// Returns a TypeSize with a known minimum size that is the next integer
485 /// (mod 2**64) that is greater than or equal to \p Value and is a multiple
486 /// of \p Align. \p Align must be non-zero.
487 ///
488 /// Similar to the alignTo functions in MathExtras.h
489 inline TypeSize alignTo(TypeSize Size, uint64_t Align) {
490   assert(Align != 0u && "Align must be non-zero");
491   return {(Size.getKnownMinValue() + Align - 1) / Align * Align,
492           Size.isScalable()};
493 }
494 
495 /// Stream operator function for `LinearPolySize`.
496 template <typename LeafTy>
497 inline raw_ostream &operator<<(raw_ostream &OS,
498                                const LinearPolySize<LeafTy> &PS) {
499   PS.print(OS);
500   return OS;
501 }
502 
503 template <typename T> struct DenseMapInfo;
504 template <> struct DenseMapInfo<ElementCount> {
505   static inline ElementCount getEmptyKey() {
506     return ElementCount::getScalable(~0U);
507   }
508   static inline ElementCount getTombstoneKey() {
509     return ElementCount::getFixed(~0U - 1);
510   }
511   static unsigned getHashValue(const ElementCount &EltCnt) {
512     unsigned HashVal = EltCnt.getKnownMinValue() * 37U;
513     if (EltCnt.isScalable())
514       return (HashVal - 1U);
515 
516     return HashVal;
517   }
518 
519   static bool isEqual(const ElementCount &LHS, const ElementCount &RHS) {
520     return LHS == RHS;
521   }
522 };
523 
524 } // end namespace llvm
525 
526 #endif // LLVM_SUPPORT_TYPESIZE_H
527