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/Support/MathExtras.h"
19 #include "llvm/Support/raw_ostream.h"
20 
21 #include <algorithm>
22 #include <cassert>
23 #include <cstdint>
24 #include <type_traits>
25 
26 namespace llvm {
27 
28 /// Reports a diagnostic message to indicate an invalid size request has been
29 /// done on a scalable vector. This function may not return.
30 void reportInvalidSizeRequest(const char *Msg);
31 
32 /// StackOffset holds a fixed and a scalable offset in bytes.
33 class StackOffset {
34   int64_t Fixed = 0;
35   int64_t Scalable = 0;
36 
StackOffset(int64_t Fixed,int64_t Scalable)37   StackOffset(int64_t Fixed, int64_t Scalable)
38       : Fixed(Fixed), Scalable(Scalable) {}
39 
40 public:
41   StackOffset() = default;
getFixed(int64_t Fixed)42   static StackOffset getFixed(int64_t Fixed) { return {Fixed, 0}; }
getScalable(int64_t Scalable)43   static StackOffset getScalable(int64_t Scalable) { return {0, Scalable}; }
get(int64_t Fixed,int64_t Scalable)44   static StackOffset get(int64_t Fixed, int64_t Scalable) {
45     return {Fixed, Scalable};
46   }
47 
48   /// Returns the fixed component of the stack.
getFixed()49   int64_t getFixed() const { return Fixed; }
50 
51   /// Returns the scalable component of the stack.
getScalable()52   int64_t getScalable() const { return Scalable; }
53 
54   // Arithmetic operations.
55   StackOffset operator+(const StackOffset &RHS) const {
56     return {Fixed + RHS.Fixed, Scalable + RHS.Scalable};
57   }
58   StackOffset operator-(const StackOffset &RHS) const {
59     return {Fixed - RHS.Fixed, Scalable - RHS.Scalable};
60   }
61   StackOffset &operator+=(const StackOffset &RHS) {
62     Fixed += RHS.Fixed;
63     Scalable += RHS.Scalable;
64     return *this;
65   }
66   StackOffset &operator-=(const StackOffset &RHS) {
67     Fixed -= RHS.Fixed;
68     Scalable -= RHS.Scalable;
69     return *this;
70   }
71   StackOffset operator-() const { return {-Fixed, -Scalable}; }
72 
73   // Equality comparisons.
74   bool operator==(const StackOffset &RHS) const {
75     return Fixed == RHS.Fixed && Scalable == RHS.Scalable;
76   }
77   bool operator!=(const StackOffset &RHS) const {
78     return Fixed != RHS.Fixed || Scalable != RHS.Scalable;
79   }
80 
81   // The bool operator returns true iff any of the components is non zero.
82   explicit operator bool() const { return Fixed != 0 || Scalable != 0; }
83 };
84 
85 namespace details {
86 
87 // Base class for ElementCount and TypeSize below.
88 template <typename LeafTy, typename ValueTy> class FixedOrScalableQuantity {
89 public:
90   using ScalarTy = ValueTy;
91 
92 protected:
93   ScalarTy Quantity = 0;
94   bool Scalable = false;
95 
96   constexpr FixedOrScalableQuantity() = default;
FixedOrScalableQuantity(ScalarTy Quantity,bool Scalable)97   constexpr FixedOrScalableQuantity(ScalarTy Quantity, bool Scalable)
98       : Quantity(Quantity), Scalable(Scalable) {}
99 
100   friend constexpr LeafTy &operator+=(LeafTy &LHS, const LeafTy &RHS) {
101     assert((LHS.Quantity == 0 || RHS.Quantity == 0 ||
102             LHS.Scalable == RHS.Scalable) &&
103            "Incompatible types");
104     LHS.Quantity += RHS.Quantity;
105     if (!RHS.isZero())
106       LHS.Scalable = RHS.Scalable;
107     return LHS;
108   }
109 
110   friend constexpr LeafTy &operator-=(LeafTy &LHS, const LeafTy &RHS) {
111     assert((LHS.Quantity == 0 || RHS.Quantity == 0 ||
112             LHS.Scalable == RHS.Scalable) &&
113            "Incompatible types");
114     LHS.Quantity -= RHS.Quantity;
115     if (!RHS.isZero())
116       LHS.Scalable = RHS.Scalable;
117     return LHS;
118   }
119 
120   friend constexpr LeafTy &operator*=(LeafTy &LHS, ScalarTy RHS) {
121     LHS.Quantity *= RHS;
122     return LHS;
123   }
124 
125   friend constexpr LeafTy operator+(const LeafTy &LHS, const LeafTy &RHS) {
126     LeafTy Copy = LHS;
127     return Copy += RHS;
128   }
129 
130   friend constexpr LeafTy operator-(const LeafTy &LHS, const LeafTy &RHS) {
131     LeafTy Copy = LHS;
132     return Copy -= RHS;
133   }
134 
135   friend constexpr LeafTy operator*(const LeafTy &LHS, ScalarTy RHS) {
136     LeafTy Copy = LHS;
137     return Copy *= RHS;
138   }
139 
140   template <typename U = ScalarTy>
141   friend constexpr std::enable_if_t<std::is_signed_v<U>, LeafTy>
142   operator-(const LeafTy &LHS) {
143     LeafTy Copy = LHS;
144     return Copy *= -1;
145   }
146 
147 public:
148   constexpr bool operator==(const FixedOrScalableQuantity &RHS) const {
149     return Quantity == RHS.Quantity && Scalable == RHS.Scalable;
150   }
151 
152   constexpr bool operator!=(const FixedOrScalableQuantity &RHS) const {
153     return Quantity != RHS.Quantity || Scalable != RHS.Scalable;
154   }
155 
isZero()156   constexpr bool isZero() const { return Quantity == 0; }
157 
isNonZero()158   constexpr bool isNonZero() const { return Quantity != 0; }
159 
160   explicit operator bool() const { return isNonZero(); }
161 
162   /// Add \p RHS to the underlying quantity.
getWithIncrement(ScalarTy RHS)163   constexpr LeafTy getWithIncrement(ScalarTy RHS) const {
164     return LeafTy::get(Quantity + RHS, Scalable);
165   }
166 
167   /// Returns the minimum value this quantity can represent.
getKnownMinValue()168   constexpr ScalarTy getKnownMinValue() const { return Quantity; }
169 
170   /// Returns whether the quantity is scaled by a runtime quantity (vscale).
isScalable()171   constexpr bool isScalable() const { return Scalable; }
172 
173   /// A return value of true indicates we know at compile time that the number
174   /// of elements (vscale * Min) is definitely even. However, returning false
175   /// does not guarantee that the total number of elements is odd.
isKnownEven()176   constexpr bool isKnownEven() const { return (getKnownMinValue() & 0x1) == 0; }
177 
178   /// This function tells the caller whether the element count is known at
179   /// compile time to be a multiple of the scalar value RHS.
isKnownMultipleOf(ScalarTy RHS)180   constexpr bool isKnownMultipleOf(ScalarTy RHS) const {
181     return getKnownMinValue() % RHS == 0;
182   }
183 
184   // Return the minimum value with the assumption that the count is exact.
185   // Use in places where a scalable count doesn't make sense (e.g. non-vector
186   // types, or vectors in backends which don't support scalable vectors).
getFixedValue()187   constexpr ScalarTy getFixedValue() const {
188     assert((!isScalable() || isZero()) &&
189            "Request for a fixed element count on a scalable object");
190     return getKnownMinValue();
191   }
192 
193   // For some cases, quantity ordering between scalable and fixed quantity types
194   // cannot be determined at compile time, so such comparisons aren't allowed.
195   //
196   // e.g. <vscale x 2 x i16> could be bigger than <4 x i32> with a runtime
197   // vscale >= 5, equal sized with a vscale of 4, and smaller with
198   // a vscale <= 3.
199   //
200   // All the functions below make use of the fact vscale is always >= 1, which
201   // means that <vscale x 4 x i32> is guaranteed to be >= <4 x i32>, etc.
202 
isKnownLT(const FixedOrScalableQuantity & LHS,const FixedOrScalableQuantity & RHS)203   static constexpr bool isKnownLT(const FixedOrScalableQuantity &LHS,
204                                   const FixedOrScalableQuantity &RHS) {
205     if (!LHS.isScalable() || RHS.isScalable())
206       return LHS.getKnownMinValue() < RHS.getKnownMinValue();
207     return false;
208   }
209 
isKnownGT(const FixedOrScalableQuantity & LHS,const FixedOrScalableQuantity & RHS)210   static constexpr bool isKnownGT(const FixedOrScalableQuantity &LHS,
211                                   const FixedOrScalableQuantity &RHS) {
212     if (LHS.isScalable() || !RHS.isScalable())
213       return LHS.getKnownMinValue() > RHS.getKnownMinValue();
214     return false;
215   }
216 
isKnownLE(const FixedOrScalableQuantity & LHS,const FixedOrScalableQuantity & RHS)217   static constexpr bool isKnownLE(const FixedOrScalableQuantity &LHS,
218                                   const FixedOrScalableQuantity &RHS) {
219     if (!LHS.isScalable() || RHS.isScalable())
220       return LHS.getKnownMinValue() <= RHS.getKnownMinValue();
221     return false;
222   }
223 
isKnownGE(const FixedOrScalableQuantity & LHS,const FixedOrScalableQuantity & RHS)224   static constexpr bool isKnownGE(const FixedOrScalableQuantity &LHS,
225                                   const FixedOrScalableQuantity &RHS) {
226     if (LHS.isScalable() || !RHS.isScalable())
227       return LHS.getKnownMinValue() >= RHS.getKnownMinValue();
228     return false;
229   }
230 
231   /// We do not provide the '/' operator here because division for polynomial
232   /// types does not work in the same way as for normal integer types. We can
233   /// only divide the minimum value (or coefficient) by RHS, which is not the
234   /// same as
235   ///   (Min * Vscale) / RHS
236   /// The caller is recommended to use this function in combination with
237   /// isKnownMultipleOf(RHS), which lets the caller know if it's possible to
238   /// perform a lossless divide by RHS.
divideCoefficientBy(ScalarTy RHS)239   constexpr LeafTy divideCoefficientBy(ScalarTy RHS) const {
240     return LeafTy::get(getKnownMinValue() / RHS, isScalable());
241   }
242 
multiplyCoefficientBy(ScalarTy RHS)243   constexpr LeafTy multiplyCoefficientBy(ScalarTy RHS) const {
244     return LeafTy::get(getKnownMinValue() * RHS, isScalable());
245   }
246 
coefficientNextPowerOf2()247   constexpr LeafTy coefficientNextPowerOf2() const {
248     return LeafTy::get(
249         static_cast<ScalarTy>(llvm::NextPowerOf2(getKnownMinValue())),
250         isScalable());
251   }
252 
253   /// Returns true if there exists a value X where RHS.multiplyCoefficientBy(X)
254   /// will result in a value whose quantity matches our own.
255   constexpr bool
hasKnownScalarFactor(const FixedOrScalableQuantity & RHS)256   hasKnownScalarFactor(const FixedOrScalableQuantity &RHS) const {
257     return isScalable() == RHS.isScalable() &&
258            getKnownMinValue() % RHS.getKnownMinValue() == 0;
259   }
260 
261   /// Returns a value X where RHS.multiplyCoefficientBy(X) will result in a
262   /// value whose quantity matches our own.
263   constexpr ScalarTy
getKnownScalarFactor(const FixedOrScalableQuantity & RHS)264   getKnownScalarFactor(const FixedOrScalableQuantity &RHS) const {
265     assert(hasKnownScalarFactor(RHS) && "Expected RHS to be a known factor!");
266     return getKnownMinValue() / RHS.getKnownMinValue();
267   }
268 
269   /// Printing function.
print(raw_ostream & OS)270   void print(raw_ostream &OS) const {
271     if (isScalable())
272       OS << "vscale x ";
273     OS << getKnownMinValue();
274   }
275 };
276 
277 } // namespace details
278 
279 // Stores the number of elements for a type and whether this type is fixed
280 // (N-Elements) or scalable (e.g., SVE).
281 //  - ElementCount::getFixed(1) : A scalar value.
282 //  - ElementCount::getFixed(2) : A vector type holding 2 values.
283 //  - ElementCount::getScalable(4) : A scalable vector type holding 4 values.
284 class ElementCount
285     : public details::FixedOrScalableQuantity<ElementCount, unsigned> {
ElementCount(ScalarTy MinVal,bool Scalable)286   constexpr ElementCount(ScalarTy MinVal, bool Scalable)
287       : FixedOrScalableQuantity(MinVal, Scalable) {}
288 
ElementCount(const FixedOrScalableQuantity<ElementCount,unsigned> & V)289   constexpr ElementCount(
290       const FixedOrScalableQuantity<ElementCount, unsigned> &V)
291       : FixedOrScalableQuantity(V) {}
292 
293 public:
ElementCount()294   constexpr ElementCount() : FixedOrScalableQuantity() {}
295 
getFixed(ScalarTy MinVal)296   static constexpr ElementCount getFixed(ScalarTy MinVal) {
297     return ElementCount(MinVal, false);
298   }
getScalable(ScalarTy MinVal)299   static constexpr ElementCount getScalable(ScalarTy MinVal) {
300     return ElementCount(MinVal, true);
301   }
get(ScalarTy MinVal,bool Scalable)302   static constexpr ElementCount get(ScalarTy MinVal, bool Scalable) {
303     return ElementCount(MinVal, Scalable);
304   }
305 
306   /// Exactly one element.
isScalar()307   constexpr bool isScalar() const {
308     return !isScalable() && getKnownMinValue() == 1;
309   }
310   /// One or more elements.
isVector()311   constexpr bool isVector() const {
312     return (isScalable() && getKnownMinValue() != 0) || getKnownMinValue() > 1;
313   }
314 };
315 
316 // Stores the size of a type. If the type is of fixed size, it will represent
317 // the exact size. If the type is a scalable vector, it will represent the known
318 // minimum size.
319 class TypeSize : public details::FixedOrScalableQuantity<TypeSize, uint64_t> {
TypeSize(const FixedOrScalableQuantity<TypeSize,uint64_t> & V)320   TypeSize(const FixedOrScalableQuantity<TypeSize, uint64_t> &V)
321       : FixedOrScalableQuantity(V) {}
322 
323 public:
TypeSize()324   constexpr TypeSize() : FixedOrScalableQuantity(0, false) {}
325 
TypeSize(ScalarTy Quantity,bool Scalable)326   constexpr TypeSize(ScalarTy Quantity, bool Scalable)
327       : FixedOrScalableQuantity(Quantity, Scalable) {}
328 
get(ScalarTy Quantity,bool Scalable)329   static constexpr TypeSize get(ScalarTy Quantity, bool Scalable) {
330     return TypeSize(Quantity, Scalable);
331   }
getFixed(ScalarTy ExactSize)332   static constexpr TypeSize getFixed(ScalarTy ExactSize) {
333     return TypeSize(ExactSize, false);
334   }
getScalable(ScalarTy MinimumSize)335   static constexpr TypeSize getScalable(ScalarTy MinimumSize) {
336     return TypeSize(MinimumSize, true);
337   }
338 
339   // All code for this class below this point is needed because of the
340   // temporary implicit conversion to uint64_t. The operator overloads are
341   // needed because otherwise the conversion of the parent class
342   // UnivariateLinearPolyBase -> TypeSize is ambiguous.
343   // TODO: Remove the implicit conversion.
344 
345   // Casts to a uint64_t if this is a fixed-width size.
346   //
347   // This interface is deprecated and will be removed in a future version
348   // of LLVM in favour of upgrading uses that rely on this implicit conversion
349   // to uint64_t. Calls to functions that return a TypeSize should use the
350   // proper interfaces to TypeSize.
351   // In practice this is mostly calls to MVT/EVT::getSizeInBits().
352   //
353   // To determine how to upgrade the code:
354   //
355   //   if (<algorithm works for both scalable and fixed-width vectors>)
356   //     use getKnownMinValue()
357   //   else if (<algorithm works only for fixed-width vectors>) {
358   //     if <algorithm can be adapted for both scalable and fixed-width vectors>
359   //       update the algorithm and use getKnownMinValue()
360   //     else
361   //       bail out early for scalable vectors and use getFixedValue()
362   //   }
363   operator ScalarTy() const;
364 
365   // Additional operators needed to avoid ambiguous parses
366   // because of the implicit conversion hack.
367   friend constexpr TypeSize operator*(const TypeSize &LHS, const int RHS) {
368     return LHS * (ScalarTy)RHS;
369   }
370   friend constexpr TypeSize operator*(const TypeSize &LHS, const unsigned RHS) {
371     return LHS * (ScalarTy)RHS;
372   }
373   friend constexpr TypeSize operator*(const TypeSize &LHS, const int64_t RHS) {
374     return LHS * (ScalarTy)RHS;
375   }
376   friend constexpr TypeSize operator*(const int LHS, const TypeSize &RHS) {
377     return RHS * LHS;
378   }
379   friend constexpr TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
380     return RHS * LHS;
381   }
382   friend constexpr TypeSize operator*(const int64_t LHS, const TypeSize &RHS) {
383     return RHS * LHS;
384   }
385   friend constexpr TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
386     return RHS * LHS;
387   }
388 };
389 
390 //===----------------------------------------------------------------------===//
391 // Utilities
392 //===----------------------------------------------------------------------===//
393 
394 /// Returns a TypeSize with a known minimum size that is the next integer
395 /// (mod 2**64) that is greater than or equal to \p Quantity and is a multiple
396 /// of \p Align. \p Align must be non-zero.
397 ///
398 /// Similar to the alignTo functions in MathExtras.h
alignTo(TypeSize Size,uint64_t Align)399 inline constexpr TypeSize alignTo(TypeSize Size, uint64_t Align) {
400   assert(Align != 0u && "Align must be non-zero");
401   return {(Size.getKnownMinValue() + Align - 1) / Align * Align,
402           Size.isScalable()};
403 }
404 
405 /// Stream operator function for `FixedOrScalableQuantity`.
406 template <typename LeafTy, typename ScalarTy>
407 inline raw_ostream &
408 operator<<(raw_ostream &OS,
409            const details::FixedOrScalableQuantity<LeafTy, ScalarTy> &PS) {
410   PS.print(OS);
411   return OS;
412 }
413 
414 template <> struct DenseMapInfo<ElementCount, void> {
415   static inline ElementCount getEmptyKey() {
416     return ElementCount::getScalable(~0U);
417   }
418   static inline ElementCount getTombstoneKey() {
419     return ElementCount::getFixed(~0U - 1);
420   }
421   static unsigned getHashValue(const ElementCount &EltCnt) {
422     unsigned HashVal = EltCnt.getKnownMinValue() * 37U;
423     if (EltCnt.isScalable())
424       return (HashVal - 1U);
425 
426     return HashVal;
427   }
428   static bool isEqual(const ElementCount &LHS, const ElementCount &RHS) {
429     return LHS == RHS;
430   }
431 };
432 
433 } // end namespace llvm
434 
435 #endif // LLVM_SUPPORT_TYPESIZE_H
436