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 <cassert>
19 #include <tuple>
20 
21 namespace llvm {
22 
23 class ElementCount {
24 public:
25   unsigned Min;  // Minimum number of vector elements.
26   bool Scalable; // If true, NumElements is a multiple of 'Min' determined
27                  // at runtime rather than compile time.
28 
29   ElementCount(unsigned Min, bool Scalable)
30   : Min(Min), Scalable(Scalable) {}
31 
32   ElementCount operator*(unsigned RHS) {
33     return { Min * RHS, Scalable };
34   }
35   ElementCount operator/(unsigned RHS) {
36     return { Min / RHS, Scalable };
37   }
38 
39   bool operator==(const ElementCount& RHS) const {
40     return Min == RHS.Min && Scalable == RHS.Scalable;
41   }
42   bool operator!=(const ElementCount& RHS) const {
43     return !(*this == RHS);
44   }
45 };
46 
47 // This class is used to represent the size of types. If the type is of fixed
48 // size, it will represent the exact size. If the type is a scalable vector,
49 // it will represent the known minimum size.
50 class TypeSize {
51   uint64_t MinSize;   // The known minimum size.
52   bool IsScalable;    // If true, then the runtime size is an integer multiple
53                       // of MinSize.
54 
55 public:
56   constexpr TypeSize(uint64_t MinSize, bool Scalable)
57     : MinSize(MinSize), IsScalable(Scalable) {}
58 
59   static constexpr TypeSize Fixed(uint64_t Size) {
60     return TypeSize(Size, /*IsScalable=*/false);
61   }
62 
63   static constexpr TypeSize Scalable(uint64_t MinSize) {
64     return TypeSize(MinSize, /*IsScalable=*/true);
65   }
66 
67   // Scalable vector types with the same minimum size as a fixed size type are
68   // not guaranteed to be the same size at runtime, so they are never
69   // considered to be equal.
70   friend bool operator==(const TypeSize &LHS, const TypeSize &RHS) {
71     return std::tie(LHS.MinSize, LHS.IsScalable) ==
72            std::tie(RHS.MinSize, RHS.IsScalable);
73   }
74 
75   friend bool operator!=(const TypeSize &LHS, const TypeSize &RHS) {
76     return !(LHS == RHS);
77   }
78 
79   // For many cases, size ordering between scalable and fixed size types cannot
80   // be determined at compile time, so such comparisons aren't allowed.
81   //
82   // e.g. <vscale x 2 x i16> could be bigger than <4 x i32> with a runtime
83   // vscale >= 5, equal sized with a vscale of 4, and smaller with
84   // a vscale <= 3.
85   //
86   // If the scalable flags match, just perform the requested comparison
87   // between the minimum sizes.
88   friend bool operator<(const TypeSize &LHS, const TypeSize &RHS) {
89     assert(LHS.IsScalable == RHS.IsScalable &&
90            "Ordering comparison of scalable and fixed types");
91 
92     return LHS.MinSize < RHS.MinSize;
93   }
94 
95   friend bool operator>(const TypeSize &LHS, const TypeSize &RHS) {
96     return RHS < LHS;
97   }
98 
99   friend bool operator<=(const TypeSize &LHS, const TypeSize &RHS) {
100     return !(RHS < LHS);
101   }
102 
103   friend bool operator>=(const TypeSize &LHS, const TypeSize& RHS) {
104     return !(LHS < RHS);
105   }
106 
107   // Convenience operators to obtain relative sizes independently of
108   // the scalable flag.
109   TypeSize operator*(unsigned RHS) const {
110     return { MinSize * RHS, IsScalable };
111   }
112 
113   friend TypeSize operator*(const unsigned LHS, const TypeSize &RHS) {
114     return { LHS * RHS.MinSize, RHS.IsScalable };
115   }
116 
117   TypeSize operator/(unsigned RHS) const {
118     return { MinSize / RHS, IsScalable };
119   }
120 
121   // Return the minimum size with the assumption that the size is exact.
122   // Use in places where a scalable size doesn't make sense (e.g. non-vector
123   // types, or vectors in backends which don't support scalable vectors).
124   uint64_t getFixedSize() const {
125     assert(!IsScalable && "Request for a fixed size on a scalable object");
126     return MinSize;
127   }
128 
129   // Return the known minimum size. Use in places where the scalable property
130   // doesn't matter (e.g. determining alignment) or in conjunction with the
131   // isScalable method below.
132   uint64_t getKnownMinSize() const {
133     return MinSize;
134   }
135 
136   // Return whether or not the size is scalable.
137   bool isScalable() const {
138     return IsScalable;
139   }
140 
141   // Returns true if the number of bits is a multiple of an 8-bit byte.
142   bool isByteSized() const {
143     return (MinSize & 7) == 0;
144   }
145 
146   // Casts to a uint64_t if this is a fixed-width size.
147   //
148   // NOTE: This interface is obsolete and will be removed in a future version
149   // of LLVM in favour of calling getFixedSize() directly.
150   operator uint64_t() const {
151     return getFixedSize();
152   }
153 
154   // Additional convenience operators needed to avoid ambiguous parses.
155   // TODO: Make uint64_t the default operator?
156   TypeSize operator*(uint64_t RHS) const {
157     return { MinSize * RHS, IsScalable };
158   }
159 
160   TypeSize operator*(int RHS) const {
161     return { MinSize * RHS, IsScalable };
162   }
163 
164   TypeSize operator*(int64_t RHS) const {
165     return { MinSize * RHS, IsScalable };
166   }
167 
168   friend TypeSize operator*(const uint64_t LHS, const TypeSize &RHS) {
169     return { LHS * RHS.MinSize, RHS.IsScalable };
170   }
171 
172   friend TypeSize operator*(const int LHS, const TypeSize &RHS) {
173     return { LHS * RHS.MinSize, RHS.IsScalable };
174   }
175 
176   friend TypeSize operator*(const int64_t LHS, const TypeSize &RHS) {
177     return { LHS * RHS.MinSize, RHS.IsScalable };
178   }
179 
180   TypeSize operator/(uint64_t RHS) const {
181     return { MinSize / RHS, IsScalable };
182   }
183 
184   TypeSize operator/(int RHS) const {
185     return { MinSize / RHS, IsScalable };
186   }
187 
188   TypeSize operator/(int64_t RHS) const {
189     return { MinSize / RHS, IsScalable };
190   }
191 };
192 
193 /// Returns a TypeSize with a known minimum size that is the next integer
194 /// (mod 2**64) that is greater than or equal to \p Value and is a multiple
195 /// of \p Align. \p Align must be non-zero.
196 ///
197 /// Similar to the alignTo functions in MathExtras.h
198 inline TypeSize alignTo(TypeSize Size, uint64_t Align) {
199   assert(Align != 0u && "Align must be non-zero");
200   return {(Size.getKnownMinSize() + Align - 1) / Align * Align,
201           Size.isScalable()};
202 }
203 
204 } // end namespace llvm
205 
206 #endif // LLVM_SUPPORT_TypeSize_H
207