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