xref: /llvm-project/mlir/include/mlir/Interfaces/DataLayoutInterfaces.h (revision 5c1752e368585e55c0335a7d7651fe43d42af282)
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