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