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/raw_ostream.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(unsigned Dim) const {
233     return Dim == UnivariateDim ? Value : 0;
234   }
235 
236   /// Add \p RHS to the value at the univariate dimension.
237   LeafTy getWithIncrement(ScalarTy RHS) const {
238     return static_cast<LeafTy>(
239         UnivariateLinearPolyBase(Value + RHS, UnivariateDim));
240   }
241 
242   /// Subtract \p RHS from the value at the univariate dimension.
243   LeafTy getWithDecrement(ScalarTy RHS) const {
244     return static_cast<LeafTy>(
245         UnivariateLinearPolyBase(Value - RHS, UnivariateDim));
246   }
247 };
248 
249 
250 //===----------------------------------------------------------------------===//
251 // LinearPolySize - base class for fixed- or scalable sizes.
252 //  ^  ^
253 //  |  |
254 //  |  +----- ElementCount - Leaf class to represent an element count
255 //  |                        (vscale x unsigned)
256 //  |
257 //  +-------- TypeSize - Leaf class to represent a type size
258 //                       (vscale x uint64_t)
259 //===----------------------------------------------------------------------===//
260 
261 /// LinearPolySize is a base class to represent sizes. It is either
262 /// fixed-sized or it is scalable-sized, but it cannot be both.
263 template <typename LeafTy>
264 class LinearPolySize : public UnivariateLinearPolyBase<LeafTy> {
265   // Make the parent class a friend, so that it can access the protected
266   // conversion/copy-constructor for UnivariatePolyBase<LeafTy> ->
267   // LinearPolySize<LeafTy>.
268   friend class UnivariateLinearPolyBase<LeafTy>;
269 
270 public:
271   using ScalarTy = typename UnivariateLinearPolyBase<LeafTy>::ScalarTy;
272   enum Dims : unsigned { FixedDim = 0, ScalableDim = 1 };
273 
274 protected:
275   LinearPolySize(ScalarTy MinVal, Dims D)
276       : UnivariateLinearPolyBase<LeafTy>(MinVal, D) {}
277 
278   LinearPolySize(const UnivariateLinearPolyBase<LeafTy> &V)
279       : UnivariateLinearPolyBase<LeafTy>(V) {}
280 
281 public:
282 
283   static LeafTy getFixed(ScalarTy MinVal) {
284     return static_cast<LeafTy>(LinearPolySize(MinVal, FixedDim));
285   }
286   static LeafTy getScalable(ScalarTy MinVal) {
287     return static_cast<LeafTy>(LinearPolySize(MinVal, ScalableDim));
288   }
289   static LeafTy get(ScalarTy MinVal, bool Scalable) {
290     return static_cast<LeafTy>(
291         LinearPolySize(MinVal, Scalable ? ScalableDim : FixedDim));
292   }
293   static LeafTy getNull() { return get(0, false); }
294 
295   /// Returns the minimum value this size can represent.
296   ScalarTy getKnownMinValue() const { return this->Value; }
297   /// Returns whether the size is scaled by a runtime quantity (vscale).
298   bool isScalable() const { return this->UnivariateDim == ScalableDim; }
299   /// A return value of true indicates we know at compile time that the number
300   /// of elements (vscale * Min) is definitely even. However, returning false
301   /// does not guarantee that the total number of elements is odd.
302   bool isKnownEven() const { return (getKnownMinValue() & 0x1) == 0; }
303   /// This function tells the caller whether the element count is known at
304   /// compile time to be a multiple of the scalar value RHS.
305   bool isKnownMultipleOf(ScalarTy RHS) const {
306     return getKnownMinValue() % RHS == 0;
307   }
308 
309   // Return the minimum value with the assumption that the count is exact.
310   // Use in places where a scalable count doesn't make sense (e.g. non-vector
311   // types, or vectors in backends which don't support scalable vectors).
312   ScalarTy getFixedValue() const {
313     assert(!isScalable() &&
314            "Request for a fixed element count on a scalable object");
315     return getKnownMinValue();
316   }
317 
318   // For some cases, size ordering between scalable and fixed size types cannot
319   // be determined at compile time, so such comparisons aren't allowed.
320   //
321   // e.g. <vscale x 2 x i16> could be bigger than <4 x i32> with a runtime
322   // vscale >= 5, equal sized with a vscale of 4, and smaller with
323   // a vscale <= 3.
324   //
325   // All the functions below make use of the fact vscale is always >= 1, which
326   // means that <vscale x 4 x i32> is guaranteed to be >= <4 x i32>, etc.
327 
328   static bool isKnownLT(const LinearPolySize &LHS, const LinearPolySize &RHS) {
329     if (!LHS.isScalable() || RHS.isScalable())
330       return LHS.getKnownMinValue() < RHS.getKnownMinValue();
331     return false;
332   }
333 
334   static bool isKnownGT(const LinearPolySize &LHS, const LinearPolySize &RHS) {
335     if (LHS.isScalable() || !RHS.isScalable())
336       return LHS.getKnownMinValue() > RHS.getKnownMinValue();
337     return false;
338   }
339 
340   static bool isKnownLE(const LinearPolySize &LHS, const LinearPolySize &RHS) {
341     if (!LHS.isScalable() || RHS.isScalable())
342       return LHS.getKnownMinValue() <= RHS.getKnownMinValue();
343     return false;
344   }
345 
346   static bool isKnownGE(const LinearPolySize &LHS, const LinearPolySize &RHS) {
347     if (LHS.isScalable() || !RHS.isScalable())
348       return LHS.getKnownMinValue() >= RHS.getKnownMinValue();
349     return false;
350   }
351 
352   /// We do not provide the '/' operator here because division for polynomial
353   /// types does not work in the same way as for normal integer types. We can
354   /// only divide the minimum value (or coefficient) by RHS, which is not the
355   /// same as
356   ///   (Min * Vscale) / RHS
357   /// The caller is recommended to use this function in combination with
358   /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to
359   /// perform a lossless divide by RHS.
360   LeafTy divideCoefficientBy(ScalarTy RHS) const {
361     return static_cast<LeafTy>(
362         LinearPolySize::get(getKnownMinValue() / RHS, isScalable()));
363   }
364 
365   LeafTy multiplyCoefficientBy(ScalarTy RHS) const {
366     return static_cast<LeafTy>(
367         LinearPolySize::get(getKnownMinValue() * RHS, isScalable()));
368   }
369 
370   LeafTy coefficientNextPowerOf2() const {
371     return static_cast<LeafTy>(LinearPolySize::get(
372         static_cast<ScalarTy>(llvm::NextPowerOf2(getKnownMinValue())),
373         isScalable()));
374   }
375 
376   /// Returns true if there exists a value X where RHS.multiplyCoefficientBy(X)
377   /// will result in a value whose size matches our own.
378   bool hasKnownScalarFactor(const LinearPolySize &RHS) const {
379     return isScalable() == RHS.isScalable() &&
380            getKnownMinValue() % RHS.getKnownMinValue() == 0;
381   }
382 
383   /// Returns a value X where RHS.multiplyCoefficientBy(X) will result in a
384   /// value whose size matches our own.
385   ScalarTy getKnownScalarFactor(const LinearPolySize &RHS) const {
386     assert(hasKnownScalarFactor(RHS) && "Expected RHS to be a known factor!");
387     return getKnownMinValue() / RHS.getKnownMinValue();
388   }
389 
390   /// Printing function.
391   void print(raw_ostream &OS) const {
392     if (isScalable())
393       OS << "vscale x ";
394     OS << getKnownMinValue();
395   }
396 };
397 
398 class ElementCount;
399 template <> struct LinearPolyBaseTypeTraits<ElementCount> {
400   using ScalarTy = unsigned;
401   static constexpr unsigned Dimensions = 2;
402 };
403 
404 class ElementCount : public LinearPolySize<ElementCount> {
405 public:
406   ElementCount() : LinearPolySize(LinearPolySize::getNull()) {}
407 
408   ElementCount(const LinearPolySize<ElementCount> &V) : LinearPolySize(V) {}
409 
410   /// Counting predicates.
411   ///
412   ///@{ Number of elements..
413   /// Exactly one element.
414   bool isScalar() const { return !isScalable() && getKnownMinValue() == 1; }
415   /// One or more elements.
416   bool isVector() const {
417     return (isScalable() && getKnownMinValue() != 0) || getKnownMinValue() > 1;
418   }
419   ///@}
420 };
421 
422 // This class is used to represent the size of types. If the type is of fixed
423 class TypeSize;
424 template <> struct LinearPolyBaseTypeTraits<TypeSize> {
425   using ScalarTy = uint64_t;
426   static constexpr unsigned Dimensions = 2;
427 };
428 
429 // TODO: Most functionality in this class will gradually be phased out
430 // so it will resemble LinearPolySize as much as possible.
431 //
432 // TypeSize is used to represent the size of types. If the type is of fixed
433 // size, it will represent the exact size. If the type is a scalable vector,
434 // it will represent the known minimum size.
435 class TypeSize : public LinearPolySize<TypeSize> {
436 public:
437   TypeSize(const LinearPolySize<TypeSize> &V) : LinearPolySize(V) {}
438   TypeSize(ScalarTy MinVal, bool IsScalable)
439       : LinearPolySize(LinearPolySize::get(MinVal, IsScalable)) {}
440 
441   static TypeSize Fixed(ScalarTy MinVal) { return TypeSize(MinVal, false); }
442   static TypeSize Scalable(ScalarTy MinVal) { return TypeSize(MinVal, true); }
443 
444   ScalarTy getFixedSize() const { return getFixedValue(); }
445   ScalarTy getKnownMinSize() const { return getKnownMinValue(); }
446 
447   // All code for this class below this point is needed because of the
448   // temporary implicit conversion to uint64_t. The operator overloads are
449   // needed because otherwise the conversion of the parent class
450   // UnivariateLinearPolyBase -> TypeSize is ambiguous.
451   // TODO: Remove the implicit conversion.
452 
453   // Casts to a uint64_t if this is a fixed-width size.
454   //
455   // This interface is deprecated and will be removed in a future version
456   // of LLVM in favour of upgrading uses that rely on this implicit conversion
457   // to uint64_t. Calls to functions that return a TypeSize should use the
458   // proper interfaces to TypeSize.
459   // In practice this is mostly calls to MVT/EVT::getSizeInBits().
460   //
461   // To determine how to upgrade the code:
462   //
463   //   if (<algorithm works for both scalable and fixed-width vectors>)
464   //     use getKnownMinValue()
465   //   else if (<algorithm works only for fixed-width vectors>) {
466   //     if <algorithm can be adapted for both scalable and fixed-width vectors>
467   //       update the algorithm and use getKnownMinValue()
468   //     else
469   //       bail out early for scalable vectors and use getFixedValue()
470   //   }
471   operator ScalarTy() const;
472 
473   // Additional operators needed to avoid ambiguous parses
474   // because of the implicit conversion hack.
475   friend TypeSize operator*(const TypeSize &LHS, const int RHS) {
476     return LHS * (ScalarTy)RHS;
477   }
478   friend TypeSize operator*(const TypeSize &LHS, const unsigned RHS) {
479     return LHS * (ScalarTy)RHS;
480   }
481   friend TypeSize operator*(const TypeSize &LHS, const int64_t RHS) {
482     return LHS * (ScalarTy)RHS;
483   }
484   friend TypeSize operator*(const int LHS, const TypeSize &RHS) {
485     return RHS * LHS;
486   }
487   friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
488     return RHS * LHS;
489   }
490   friend TypeSize operator*(const int64_t LHS, const TypeSize &RHS) {
491     return RHS * LHS;
492   }
493   friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
494     return RHS * LHS;
495   }
496 };
497 
498 //===----------------------------------------------------------------------===//
499 // Utilities
500 //===----------------------------------------------------------------------===//
501 
502 /// Returns a TypeSize with a known minimum size that is the next integer
503 /// (mod 2**64) that is greater than or equal to \p Value and is a multiple
504 /// of \p Align. \p Align must be non-zero.
505 ///
506 /// Similar to the alignTo functions in MathExtras.h
507 inline TypeSize alignTo(TypeSize Size, uint64_t Align) {
508   assert(Align != 0u && "Align must be non-zero");
509   return {(Size.getKnownMinValue() + Align - 1) / Align * Align,
510           Size.isScalable()};
511 }
512 
513 /// Stream operator function for `LinearPolySize`.
514 template <typename LeafTy>
515 inline raw_ostream &operator<<(raw_ostream &OS,
516                                const LinearPolySize<LeafTy> &PS) {
517   PS.print(OS);
518   return OS;
519 }
520 
521 template <> struct DenseMapInfo<ElementCount, void> {
522   static inline ElementCount getEmptyKey() {
523     return ElementCount::getScalable(~0U);
524   }
525   static inline ElementCount getTombstoneKey() {
526     return ElementCount::getFixed(~0U - 1);
527   }
528   static unsigned getHashValue(const ElementCount &EltCnt) {
529     unsigned HashVal = EltCnt.getKnownMinValue() * 37U;
530     if (EltCnt.isScalable())
531       return (HashVal - 1U);
532 
533     return HashVal;
534   }
535 
536   static bool isEqual(const ElementCount &LHS, const ElementCount &RHS) {
537     return LHS == RHS;
538   }
539 };
540 
541 } // end namespace llvm
542 
543 #endif // LLVM_SUPPORT_TYPESIZE_H
544