1 //===- DataLayoutInterfaces.h - Data Layout Interface Decls -----*- 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 // Defines the interfaces for the data layout specification, operations to which 10 // they can be attached, types subject to data layout and dialects containing 11 // data layout entries. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #ifndef MLIR_INTERFACES_DATALAYOUTINTERFACES_H 16 #define MLIR_INTERFACES_DATALAYOUTINTERFACES_H 17 18 #include "mlir/IR/Attributes.h" 19 #include "mlir/IR/DialectInterface.h" 20 #include "mlir/IR/OpDefinition.h" 21 #include "llvm/ADT/DenseMap.h" 22 #include "llvm/Support/TypeSize.h" 23 24 namespace mlir { 25 class DataLayout; 26 class DataLayoutEntryInterface; 27 class DLTIQueryInterface; 28 class TargetDeviceSpecInterface; 29 class TargetSystemSpecInterface; 30 using DataLayoutEntryKey = llvm::PointerUnion<Type, StringAttr>; 31 // Using explicit SmallVector size because we cannot infer the size from the 32 // forward declaration, and we need the typedef in the actual declaration. 33 using DataLayoutEntryList = llvm::SmallVector<DataLayoutEntryInterface, 4>; 34 using DataLayoutEntryListRef = llvm::ArrayRef<DataLayoutEntryInterface>; 35 using TargetDeviceSpecListRef = llvm::ArrayRef<TargetDeviceSpecInterface>; 36 using TargetDeviceSpecEntry = std::pair<StringAttr, TargetDeviceSpecInterface>; 37 class DataLayoutOpInterface; 38 class DataLayoutSpecInterface; 39 class ModuleOp; 40 41 namespace detail { 42 /// Default handler for the type size request. Computes results for built-in 43 /// types and dispatches to the DataLayoutTypeInterface for other types. 44 llvm::TypeSize getDefaultTypeSize(Type type, const DataLayout &dataLayout, 45 DataLayoutEntryListRef params); 46 47 /// Default handler for the type size in bits request. Computes results for 48 /// built-in types and dispatches to the DataLayoutTypeInterface for other 49 /// types. 50 llvm::TypeSize getDefaultTypeSizeInBits(Type type, const DataLayout &dataLayout, 51 DataLayoutEntryListRef params); 52 53 /// Default handler for the required alignment request. Computes results for 54 /// built-in types and dispatches to the DataLayoutTypeInterface for other 55 /// types. 56 uint64_t getDefaultABIAlignment(Type type, const DataLayout &dataLayout, 57 ArrayRef<DataLayoutEntryInterface> params); 58 59 /// Default handler for the preferred alignment request. Computes results for 60 /// built-in types and dispatches to the DataLayoutTypeInterface for other 61 /// types. 62 uint64_t 63 getDefaultPreferredAlignment(Type type, const DataLayout &dataLayout, 64 ArrayRef<DataLayoutEntryInterface> params); 65 66 /// Default handler for the index bitwidth request. Computes the result for 67 /// the built-in index type and dispatches to the DataLayoutTypeInterface for 68 /// other types. 69 std::optional<uint64_t> 70 getDefaultIndexBitwidth(Type type, const DataLayout &dataLayout, 71 ArrayRef<DataLayoutEntryInterface> params); 72 73 /// Default handler for endianness request. Dispatches to the 74 /// DataLayoutInterface if specified, otherwise returns the default. 75 Attribute getDefaultEndianness(DataLayoutEntryInterface entry); 76 77 /// Default handler for alloca memory space request. Dispatches to the 78 /// DataLayoutInterface if specified, otherwise returns the default. 79 Attribute getDefaultAllocaMemorySpace(DataLayoutEntryInterface entry); 80 81 /// Default handler for program memory space request. Dispatches to the 82 /// DataLayoutInterface if specified, otherwise returns the default. 83 Attribute getDefaultProgramMemorySpace(DataLayoutEntryInterface entry); 84 85 /// Default handler for global memory space request. Dispatches to the 86 /// DataLayoutInterface if specified, otherwise returns the default. 87 Attribute getDefaultGlobalMemorySpace(DataLayoutEntryInterface entry); 88 89 /// Default handler for the stack alignment request. Dispatches to the 90 /// DataLayoutInterface if specified, otherwise returns the default. 91 uint64_t getDefaultStackAlignment(DataLayoutEntryInterface entry); 92 93 /// Returns the value of the property from the specified DataLayoutEntry. If the 94 /// property is missing from the entry, returns std::nullopt. 95 std::optional<Attribute> getDevicePropertyValue(DataLayoutEntryInterface entry); 96 97 /// Given a list of data layout entries, returns a new list containing the 98 /// entries with keys having the given type ID, i.e. belonging to the same type 99 /// class. 100 DataLayoutEntryList filterEntriesForType(DataLayoutEntryListRef entries, 101 TypeID typeID); 102 103 /// Given a list of data layout entries, returns the entry that has the given 104 /// identifier as key, if such an entry exists in the list. 105 DataLayoutEntryInterface 106 filterEntryForIdentifier(DataLayoutEntryListRef entries, StringAttr id); 107 108 /// Given a list of target device entries, returns the entry that has the given 109 /// identifier as key, if such an entry exists in the list. 110 TargetDeviceSpecInterface 111 filterEntryForIdentifier(TargetDeviceSpecListRef entries, StringAttr id); 112 113 /// Verifies that the operation implementing the data layout interface, or a 114 /// module operation, is valid. This calls the verifier of the spec attribute 115 /// and checks if the layout is compatible with specs attached to the enclosing 116 /// operations. 117 LogicalResult verifyDataLayoutOp(Operation *op); 118 119 /// Verifies that a data layout spec is valid. This dispatches to individual 120 /// entry verifiers, and then to the verifiers implemented by the relevant type 121 /// and dialect interfaces for type and identifier keys respectively. 122 LogicalResult verifyDataLayoutSpec(DataLayoutSpecInterface spec, Location loc); 123 124 /// Verifies that a target system desc spec is valid. This dispatches to 125 /// individual entry verifiers, and then to the verifiers implemented by the 126 /// relevant dialect interfaces for identifier keys. 127 LogicalResult verifyTargetSystemSpec(TargetSystemSpecInterface spec, 128 Location loc); 129 130 /// Divides the known min value of the numerator by the denominator and rounds 131 /// the result up to the next integer. Preserves the scalable flag. 132 llvm::TypeSize divideCeil(llvm::TypeSize numerator, uint64_t denominator); 133 } // namespace detail 134 } // namespace mlir 135 136 #include "mlir/Interfaces/DataLayoutAttrInterface.h.inc" 137 #include "mlir/Interfaces/DataLayoutOpInterface.h.inc" 138 #include "mlir/Interfaces/DataLayoutTypeInterface.h.inc" 139 140 namespace mlir { 141 142 //===----------------------------------------------------------------------===// 143 // DataLayoutDialectInterface 144 //===----------------------------------------------------------------------===// 145 146 /// An interface to be implemented by dialects that can have identifiers in the 147 /// data layout specification entries. Provides hooks for verifying the entry 148 /// validity and combining two entries. 149 class DataLayoutDialectInterface 150 : public DialectInterface::Base<DataLayoutDialectInterface> { 151 public: 152 DataLayoutDialectInterface(Dialect *dialect) : Base(dialect) {} 153 154 /// Checks whether the given data layout entry is valid and reports any errors 155 /// at the provided location. Derived classes should override this. 156 virtual LogicalResult verifyEntry(DataLayoutEntryInterface entry, 157 Location loc) const { 158 return success(); 159 } 160 161 /// Checks whether the given data layout entry is valid and reports any errors 162 /// at the provided location. Derived classes should override this. 163 virtual LogicalResult verifyEntry(TargetDeviceSpecInterface entry, 164 Location loc) const { 165 return success(); 166 } 167 168 /// Default implementation of entry combination that combines identical 169 /// entries and returns null otherwise. 170 static DataLayoutEntryInterface 171 defaultCombine(DataLayoutEntryInterface outer, 172 DataLayoutEntryInterface inner) { 173 if (!outer || outer == inner) 174 return inner; 175 return {}; 176 } 177 178 /// Combines two entries with identifiers that belong to this dialect. Returns 179 /// the combined entry or null if the entries are not compatible. Derived 180 /// classes likely need to reimplement this. 181 virtual DataLayoutEntryInterface 182 combine(DataLayoutEntryInterface outer, 183 DataLayoutEntryInterface inner) const { 184 return defaultCombine(outer, inner); 185 } 186 }; 187 188 //===----------------------------------------------------------------------===// 189 // DataLayout 190 //===----------------------------------------------------------------------===// 191 192 /// The main mechanism for performing data layout queries. Instances of this 193 /// class can be created for an operation implementing DataLayoutOpInterface. 194 /// Upon construction, a layout spec combining that of the given operation with 195 /// all its ancestors will be computed and used to handle further requests. For 196 /// efficiency, results to all requests will be cached in this object. 197 /// Therefore, if the data layout spec for the scoping operation, or any of the 198 /// enclosing operations, changes, the cache is no longer valid. The user is 199 /// responsible creating a new DataLayout object after any spec change. In debug 200 /// mode, the cache validity is being checked in every request. 201 class DataLayout { 202 public: 203 explicit DataLayout(); 204 explicit DataLayout(DataLayoutOpInterface op); 205 explicit DataLayout(ModuleOp op); 206 207 /// Returns the layout of the closest parent operation carrying layout info. 208 static DataLayout closest(Operation *op); 209 210 /// Returns the size of the given type in the current scope. 211 llvm::TypeSize getTypeSize(Type t) const; 212 213 /// Returns the size in bits of the given type in the current scope. 214 llvm::TypeSize getTypeSizeInBits(Type t) const; 215 216 /// Returns the required alignment of the given type in the current scope. 217 uint64_t getTypeABIAlignment(Type t) const; 218 219 /// Returns the preferred of the given type in the current scope. 220 uint64_t getTypePreferredAlignment(Type t) const; 221 222 /// Returns the bitwidth that should be used when performing index 223 /// computations for the given pointer-like type in the current scope. If the 224 /// type is not a pointer-like type, it returns std::nullopt. 225 std::optional<uint64_t> getTypeIndexBitwidth(Type t) const; 226 227 /// Returns the specified endianness. 228 Attribute getEndianness() const; 229 230 /// Returns the memory space used for AllocaOps. 231 Attribute getAllocaMemorySpace() const; 232 233 /// Returns the memory space used for program memory operations. 234 Attribute getProgramMemorySpace() const; 235 236 /// Returns the memory space used for global operations. 237 Attribute getGlobalMemorySpace() const; 238 239 /// Returns the natural alignment of the stack in bits. Alignment promotion of 240 /// stack variables should be limited to the natural stack alignment to 241 /// prevent dynamic stack alignment. Returns zero if the stack alignment is 242 /// unspecified. 243 uint64_t getStackAlignment() const; 244 245 /// Returns the value of the specified property if the property is defined for 246 /// the given device ID, otherwise returns std::nullopt. 247 std::optional<Attribute> 248 getDevicePropertyValue(TargetSystemSpecInterface::DeviceID, 249 StringAttr propertyName) const; 250 251 private: 252 /// Combined layout spec at the given scope. 253 const DataLayoutSpecInterface originalLayout; 254 255 /// Combined target system desc spec at the given scope. 256 const TargetSystemSpecInterface originalTargetSystemDesc; 257 258 #if LLVM_ENABLE_ABI_BREAKING_CHECKS 259 /// List of enclosing layout specs. 260 SmallVector<DataLayoutSpecInterface, 2> layoutStack; 261 #endif 262 263 /// Asserts that the cache is still valid. Expensive in debug mode. No-op in 264 /// release mode. 265 void checkValid() const; 266 267 /// Operation defining the scope of requests. 268 Operation *scope; 269 270 /// Caches for individual requests. 271 mutable DenseMap<Type, llvm::TypeSize> sizes; 272 mutable DenseMap<Type, llvm::TypeSize> bitsizes; 273 mutable DenseMap<Type, uint64_t> abiAlignments; 274 mutable DenseMap<Type, uint64_t> preferredAlignments; 275 mutable DenseMap<Type, std::optional<uint64_t>> indexBitwidths; 276 277 /// Cache for the endianness. 278 mutable std::optional<Attribute> endianness; 279 /// Cache for alloca, global, and program memory spaces. 280 mutable std::optional<Attribute> allocaMemorySpace; 281 mutable std::optional<Attribute> programMemorySpace; 282 mutable std::optional<Attribute> globalMemorySpace; 283 284 /// Cache for stack alignment. 285 mutable std::optional<uint64_t> stackAlignment; 286 }; 287 288 } // namespace mlir 289 290 #endif // MLIR_INTERFACES_DATALAYOUTINTERFACES_H 291