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 | |
37 | StackOffset(int64_t Fixed, int64_t Scalable) |
38 | : Fixed(Fixed), Scalable(Scalable) {} |
39 | |
40 | public: |
41 | StackOffset() = default; |
42 | static StackOffset getFixed(int64_t Fixed) { return {Fixed, 0}; } |
43 | static StackOffset getScalable(int64_t Scalable) { return {0, 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. |
49 | int64_t getFixed() const { return Fixed; } |
50 | |
51 | /// Returns the scalable component of the stack. |
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; |
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 | |
156 | constexpr bool isZero() const { return Quantity == 0; } |
157 | |
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. |
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. |
168 | constexpr ScalarTy getKnownMinValue() const { return Quantity; } |
169 | |
170 | /// Returns whether the quantity is scaled by a runtime quantity (vscale). |
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. |
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. |
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). |
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 | |
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 | |
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 | |
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 | |
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. |
239 | constexpr LeafTy divideCoefficientBy(ScalarTy RHS) const { |
240 | return LeafTy::get(getKnownMinValue() / RHS, isScalable()); |
241 | } |
242 | |
243 | constexpr LeafTy multiplyCoefficientBy(ScalarTy RHS) const { |
244 | return LeafTy::get(getKnownMinValue() * RHS, isScalable()); |
245 | } |
246 | |
247 | constexpr LeafTy coefficientNextPowerOf2() const { |
248 | return LeafTy::get( |
249 | static_cast<ScalarTy>(llvm::NextPowerOf2(A: 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 |
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 |
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. |
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> { |
286 | constexpr ElementCount(ScalarTy MinVal, bool Scalable) |
287 | : FixedOrScalableQuantity(MinVal, Scalable) {} |
288 | |
289 | constexpr ElementCount( |
290 | const FixedOrScalableQuantity<ElementCount, unsigned> &V) |
291 | : FixedOrScalableQuantity(V) {} |
292 | |
293 | public: |
294 | constexpr ElementCount() : FixedOrScalableQuantity() {} |
295 | |
296 | static constexpr ElementCount getFixed(ScalarTy MinVal) { |
297 | return ElementCount(MinVal, false); |
298 | } |
299 | static constexpr ElementCount getScalable(ScalarTy MinVal) { |
300 | return ElementCount(MinVal, true); |
301 | } |
302 | static constexpr ElementCount get(ScalarTy MinVal, bool Scalable) { |
303 | return ElementCount(MinVal, Scalable); |
304 | } |
305 | |
306 | /// Exactly one element. |
307 | constexpr bool isScalar() const { |
308 | return !isScalable() && getKnownMinValue() == 1; |
309 | } |
310 | /// One or more elements. |
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> { |
320 | TypeSize(const FixedOrScalableQuantity<TypeSize, uint64_t> &V) |
321 | : FixedOrScalableQuantity(V) {} |
322 | |
323 | public: |
324 | constexpr TypeSize() : FixedOrScalableQuantity(0, false) {} |
325 | |
326 | constexpr TypeSize(ScalarTy Quantity, bool Scalable) |
327 | : FixedOrScalableQuantity(Quantity, Scalable) {} |
328 | |
329 | static constexpr TypeSize get(ScalarTy Quantity, bool Scalable) { |
330 | return TypeSize(Quantity, Scalable); |
331 | } |
332 | static constexpr TypeSize getFixed(ScalarTy ExactSize) { |
333 | return TypeSize(ExactSize, false); |
334 | } |
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 |
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(MinVal: ~0U); |
417 | } |
418 | static inline ElementCount getTombstoneKey() { |
419 | return ElementCount::getFixed(MinVal: ~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 | |