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
21namespace llvm {
22
23class ElementCount {
24public:
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.
50class 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
55public:
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
198inline 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