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/WithColor.h"
20 
21 #include <cstdint>
22 #include <cassert>
23 
24 namespace llvm {
25 
26 template <typename T> struct DenseMapInfo;
27 
28 class ElementCount {
29 public:
30   unsigned Min;  // Minimum number of vector elements.
31   bool Scalable; // If true, NumElements is a multiple of 'Min' determined
32                  // at runtime rather than compile time.
33 
34   ElementCount() = default;
35 
36   ElementCount(unsigned Min, bool Scalable)
37   : Min(Min), Scalable(Scalable) {}
38 
39   ElementCount operator*(unsigned RHS) {
40     return { Min * RHS, Scalable };
41   }
42   ElementCount operator/(unsigned RHS) {
43     assert(Min % RHS == 0 && "Min is not a multiple of RHS.");
44     return { Min / RHS, Scalable };
45   }
46 
47   bool operator==(const ElementCount& RHS) const {
48     return Min == RHS.Min && Scalable == RHS.Scalable;
49   }
50   bool operator!=(const ElementCount& RHS) const {
51     return !(*this == RHS);
52   }
53   bool operator==(unsigned RHS) const { return Min == RHS && !Scalable; }
54   bool operator!=(unsigned RHS) const { return !(*this == RHS); }
55 
56   ElementCount NextPowerOf2() const {
57     return ElementCount(llvm::NextPowerOf2(Min), Scalable);
58   }
59 };
60 
61 // This class is used to represent the size of types. If the type is of fixed
62 // size, it will represent the exact size. If the type is a scalable vector,
63 // it will represent the known minimum size.
64 class TypeSize {
65   uint64_t MinSize;   // The known minimum size.
66   bool IsScalable;    // If true, then the runtime size is an integer multiple
67                       // of MinSize.
68 
69 public:
70   constexpr TypeSize(uint64_t MinSize, bool Scalable)
71     : MinSize(MinSize), IsScalable(Scalable) {}
72 
73   static constexpr TypeSize Fixed(uint64_t Size) {
74     return TypeSize(Size, /*IsScalable=*/false);
75   }
76 
77   static constexpr TypeSize Scalable(uint64_t MinSize) {
78     return TypeSize(MinSize, /*IsScalable=*/true);
79   }
80 
81   // Scalable vector types with the same minimum size as a fixed size type are
82   // not guaranteed to be the same size at runtime, so they are never
83   // considered to be equal.
84   friend bool operator==(const TypeSize &LHS, const TypeSize &RHS) {
85     return LHS.MinSize == RHS.MinSize && LHS.IsScalable == RHS.IsScalable;
86   }
87 
88   friend bool operator!=(const TypeSize &LHS, const TypeSize &RHS) {
89     return !(LHS == RHS);
90   }
91 
92   // For many cases, size ordering between scalable and fixed size types cannot
93   // be determined at compile time, so such comparisons aren't allowed.
94   //
95   // e.g. <vscale x 2 x i16> could be bigger than <4 x i32> with a runtime
96   // vscale >= 5, equal sized with a vscale of 4, and smaller with
97   // a vscale <= 3.
98   //
99   // If the scalable flags match, just perform the requested comparison
100   // between the minimum sizes.
101   friend bool operator<(const TypeSize &LHS, const TypeSize &RHS) {
102     assert(LHS.IsScalable == RHS.IsScalable &&
103            "Ordering comparison of scalable and fixed types");
104 
105     return LHS.MinSize < RHS.MinSize;
106   }
107 
108   friend bool operator>(const TypeSize &LHS, const TypeSize &RHS) {
109     return RHS < LHS;
110   }
111 
112   friend bool operator<=(const TypeSize &LHS, const TypeSize &RHS) {
113     return !(RHS < LHS);
114   }
115 
116   friend bool operator>=(const TypeSize &LHS, const TypeSize& RHS) {
117     return !(LHS < RHS);
118   }
119 
120   // Convenience operators to obtain relative sizes independently of
121   // the scalable flag.
122   TypeSize operator*(unsigned RHS) const {
123     return { MinSize * RHS, IsScalable };
124   }
125 
126   friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
127     return { LHS * RHS.MinSize, RHS.IsScalable };
128   }
129 
130   TypeSize operator/(unsigned RHS) const {
131     return { MinSize / RHS, IsScalable };
132   }
133 
134   // Return the minimum size with the assumption that the size is exact.
135   // Use in places where a scalable size doesn't make sense (e.g. non-vector
136   // types, or vectors in backends which don't support scalable vectors).
137   uint64_t getFixedSize() const {
138     assert(!IsScalable && "Request for a fixed size on a scalable object");
139     return MinSize;
140   }
141 
142   // Return the known minimum size. Use in places where the scalable property
143   // doesn't matter (e.g. determining alignment) or in conjunction with the
144   // isScalable method below.
145   uint64_t getKnownMinSize() const {
146     return MinSize;
147   }
148 
149   // Return whether or not the size is scalable.
150   bool isScalable() const {
151     return IsScalable;
152   }
153 
154   // Returns true if the number of bits is a multiple of an 8-bit byte.
155   bool isByteSized() const {
156     return (MinSize & 7) == 0;
157   }
158 
159   // Returns true if the type size is non-zero.
160   bool isNonZero() const { return MinSize != 0; }
161 
162   // Returns true if the type size is zero.
163   bool isZero() const { return MinSize == 0; }
164 
165   // Casts to a uint64_t if this is a fixed-width size.
166   //
167   // This interface is deprecated and will be removed in a future version
168   // of LLVM in favour of upgrading uses that rely on this implicit conversion
169   // to uint64_t. Calls to functions that return a TypeSize should use the
170   // proper interfaces to TypeSize.
171   // In practice this is mostly calls to MVT/EVT::getSizeInBits().
172   //
173   // To determine how to upgrade the code:
174   //
175   //   if (<algorithm works for both scalable and fixed-width vectors>)
176   //     use getKnownMinSize()
177   //   else if (<algorithm works only for fixed-width vectors>) {
178   //     if <algorithm can be adapted for both scalable and fixed-width vectors>
179   //       update the algorithm and use getKnownMinSize()
180   //     else
181   //       bail out early for scalable vectors and use getFixedSize()
182   //   }
183   operator uint64_t() const {
184 #ifdef STRICT_FIXED_SIZE_VECTORS
185     return getFixedSize();
186 #else
187     if (isScalable())
188       WithColor::warning() << "Compiler has made implicit assumption that "
189                               "TypeSize is not scalable. This may or may not "
190                               "lead to broken code.\n";
191     return getKnownMinSize();
192 #endif
193   }
194 
195   // Additional convenience operators needed to avoid ambiguous parses.
196   // TODO: Make uint64_t the default operator?
197   TypeSize operator*(uint64_t RHS) const {
198     return { MinSize * RHS, IsScalable };
199   }
200 
201   TypeSize operator*(int RHS) const {
202     return { MinSize * RHS, IsScalable };
203   }
204 
205   TypeSize operator*(int64_t RHS) const {
206     return { MinSize * RHS, IsScalable };
207   }
208 
209   friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
210     return { LHS * RHS.MinSize, RHS.IsScalable };
211   }
212 
213   friend TypeSize operator*(const int LHS, const TypeSize &RHS) {
214     return { LHS * RHS.MinSize, RHS.IsScalable };
215   }
216 
217   friend TypeSize operator*(const int64_t LHS, const TypeSize &RHS) {
218     return { LHS * RHS.MinSize, RHS.IsScalable };
219   }
220 
221   TypeSize operator/(uint64_t RHS) const {
222     return { MinSize / RHS, IsScalable };
223   }
224 
225   TypeSize operator/(int RHS) const {
226     return { MinSize / RHS, IsScalable };
227   }
228 
229   TypeSize operator/(int64_t RHS) const {
230     return { MinSize / RHS, IsScalable };
231   }
232 
233   TypeSize NextPowerOf2() const {
234     return TypeSize(llvm::NextPowerOf2(MinSize), IsScalable);
235   }
236 };
237 
238 /// Returns a TypeSize with a known minimum size that is the next integer
239 /// (mod 2**64) that is greater than or equal to \p Value and is a multiple
240 /// of \p Align. \p Align must be non-zero.
241 ///
242 /// Similar to the alignTo functions in MathExtras.h
243 inline TypeSize alignTo(TypeSize Size, uint64_t Align) {
244   assert(Align != 0u && "Align must be non-zero");
245   return {(Size.getKnownMinSize() + Align - 1) / Align * Align,
246           Size.isScalable()};
247 }
248 
249 template <> struct DenseMapInfo<ElementCount> {
250   static inline ElementCount getEmptyKey() { return {~0U, true}; }
251   static inline ElementCount getTombstoneKey() { return {~0U - 1, false}; }
252   static unsigned getHashValue(const ElementCount& EltCnt) {
253     if (EltCnt.Scalable)
254       return (EltCnt.Min * 37U) - 1U;
255 
256     return EltCnt.Min * 37U;
257   }
258 
259   static bool isEqual(const ElementCount& LHS, const ElementCount& RHS) {
260     return LHS == RHS;
261   }
262 };
263 
264 } // end namespace llvm
265 
266 #endif // LLVM_SUPPORT_TypeSize_H
267