1# Data Layout Modeling
2
3Data layout information allows the compiler to answer questions related to how a
4value of a particular type is stored in memory. For example, the size of a value
5or its address alignment requirements. It enables, among others, the generation
6of various linear memory addressing schemes for containers of abstract types and
7deeper reasoning about vectors.
8
9The data layout subsystem is designed to scale to MLIR's open type and operation
10system. At the top level, it consists of:
11
12*   attribute interfaces that can be implemented by concrete data layout
13    specifications;
14*   type interfaces that should be implemented by types subject to data layout;
15*   operation interfaces that must be implemented by operations that can serve
16    as data layout scopes (e.g., modules);
17*   and dialect interfaces for data layout properties unrelated to specific
18    types.
19
20Built-in types are handled specially to decrease the overall query cost.
21
22## Usage
23
24### Scoping
25
26Following MLIR's nested structure, data layout properties are _scoped_ to
27regions belonging to specific operations that implement the
28`DataLayoutOpInterface`. Such scoping operations partially control the data
29layout properties and may have attributes that affect them, typically organized
30in a data layout specification.
31
32Types may have a different data layout in different scopes, including scopes
33that are nested in other scopes such as modules contained in other modules. At
34the same time, within the given scope excluding any nested scope, a given type
35has fixed data layout properties. Types are also expected to have a default,
36"natural" data layout in case they are used outside of any operation that
37provides data layout scope for them. This ensure data layout queries always have
38a valid result.
39
40### Compatibility and Transformations
41
42The information necessary to compute layout properties can be combined from
43nested scopes. For example, an outer scope can define layout properties for a
44subset of types while inner scopes define them for a disjoint subset, or scopes
45can progressively relax alignment requirements on a type. This mechanism is
46supported by the notion of data layout _compatibility_: the layout defined in a
47nested scope is expected to be compatible with that of the outer scope. MLIR
48does not prescribe what compatibility means for particular ops and types but
49provides hooks for them to provide target- and type-specific checks. For
50example, one may want to only allow relaxation of alignment constraints (i.e.,
51smaller alignment) in nested modules or, alternatively, one may require nested
52modules to fully redefine all constraints of the outer scope.
53
54Data layout compatibility is also relevant during IR transformation. Any
55transformation that affects the data layout scoping operation is expected to
56maintain data layout compatibility. It is under responsibility of the
57transformation to ensure it is indeed the case.
58
59### Queries
60
61Data layout property queries can be performed on the special object --
62`DataLayout` -- which can be created for the given scoping operation. These
63objects allow one to interface with the data layout infrastructure and query
64properties of given types in the scope of the object. The signature of
65`DataLayout` class is as follows.
66
67```c++
68class DataLayout {
69public:
70  explicit DataLayout(DataLayoutOpInterface scope);
71
72  unsigned getTypeSize(Type type) const;
73  unsigned getTypeABIAlignment(Type type) const;
74  unsigned getTypePreferredAlignment(Type type) const;
75};
76```
77
78The user can construct the `DataLayout` object for the scope of interest. Since
79the data layout properties are fixed in the scope, they will be computed only
80once upon first request and cached for further use. Therefore,
81`DataLayout(op.getParentOfType<DataLayoutOpInterface>()).getTypeSize(type)` is
82considered an anti-pattern since it discards the cache after use. Because of
83caching, a `DataLayout` object returns valid results as long as the data layout
84properties of enclosing scopes remain the same, that is, as long as none of the
85ancestor operations are modified in a way that affects data layout. After such a
86modification, the user is expected to create a fresh `DataLayout` object. To aid
87with this, `DataLayout` asserts that the scope remains identical if MLIR is
88compiled with assertions enabled.
89
90## Custom Implementations
91
92Extensibility of the data layout modeling is provided through a set of MLIR
93[Interfaces](Interfaces.md).
94
95### Data Layout Specifications
96
97Data layout specification is an [attribute](LangRef.md#attributes) that is
98conceptually a collection of key-value pairs called data layout specification
99_entries_. Data layout specification attributes implement the
100`DataLayoutSpecInterface`, described below. Each entry is itself an attribute
101that implements the `DataLayoutEntryInterface`. Entries have a key, either a
102`Type` or an `Identifier`, and a value. Keys are used to associate entries with
103specific types or dialects: when handling a data layout properties request, a
104type or a dialect can only see the specification entries relevant to them and
105must go through the supplied `DataLayout` object for any recursive query. This
106supports and enforces better composability because types cannot (and should not)
107understand layout details of other types. Entry values are arbitrary attributes,
108specific to the type.
109
110For example, a data layout specification may be an actual list of pairs with
111simple custom syntax resembling the following:
112
113```
114#my_dialect.layout_spec<
115  #my_dialect.layout_entry<!my_dialect.type, size=42>,
116  #my_dialect.layout_entry<"my_dialect.endianness", "little">,
117  #my_dialect.layout_entry<!my_dialect.vector, prefer_large_alignment>>
118```
119
120The exact details of the specification and entry attributes, as well as their
121syntax, are up to implementations.
122
123We use the notion of _type class_ throughout the data layout subsystem. It
124corresponds to the C++ class of the given type, e.g., `IntegerType` for built-in
125integers. MLIR does not have a mechanism to represent type classes in the IR.
126Instead, data layout entries contain specific _instances_ of a type class, for
127example, `IntegerType{signedness=signless, bitwidth=8}` (or `i8` in the IR) or
128`IntegerType{signedness=unsigned, bitwidth=32}` (or `ui32` in the IR). When
129handling a data layout property query, a type class will be supplied with _all_
130entries with keys belonging to this type class. For example, `IntegerType` will
131see the entries for `i8`, `si16` and `ui32`, but will _not_ see those for `f32`
132or `memref<?xi32>` (neither will `MemRefType` see the entry for `i32`). This
133allows for type-specific "interpolation" behavior where a type class can compute
134data layout properties of _any_ specific type instance given properties of other
135instances. Using integers as an example again, their alignment could be computed
136by taking that of the closest from above integer type with power-of-two
137bitwidth.
138
139[include "Interfaces/DataLayoutAttrInterface.md"]
140
141### Data Layout Scoping Operations
142
143Operations that define a scope for data layout queries, and that can be used to
144create a `DataLayout` object, are expected to implement the
145`DataLayoutOpInterface`. Such ops must provide at least a way of obtaining the
146data layout specification. The specification need not be necessarily attached to
147the operation as an attribute and may be constructed on-the-fly; it is only
148fetched once per `DataLayout` object and cached. Such ops may also provide
149custom handlers for data layout queries that provide results without forwarding
150the queries down to specific types or post-processing the results returned by
151types in target- or scope-specific ways. These custom handlers make it possible
152for scoping operations to (re)define data layout properties for types without
153having to modify the types themselves, e.g., when types are defined in another
154dialect.
155
156[include "Interfaces/DataLayoutOpInterface.md"]
157
158### Types with Data Layout
159
160Type classes that intend to handle data layout queries themselves are expected
161to implement the `DataLayoutTypeInterface`. This interface provides overridable
162hooks for each data layout query. Each of these hooks is supplied with the type
163instance, a `DataLayout` object suitable for recursive queries, and a list of
164data layout queries relevant for the type class. It is expected to provide a
165valid result even if the list of entries is empty. These hooks do not have
166access to the operation in the scope of which the query is handled and should
167use the supplied entries instead.
168
169[include "Interfaces/DataLayoutTypeInterface.md"]
170
171### Dialects with Data Layout Identifiers
172
173For data layout entries that are not related to a particular type class, the key
174of the entry is an Identifier that belongs to some dialect. In this case, the
175dialect is expected to implement the `DataLayoutDialectInterface`. This dialect
176provides hooks for verifying the validity of the entry value attributes and for
177and the compatibility of nested entries.
178
179### Query Dispatch
180
181The overall flow of a data layout property query is as follows.
182
183-   The user constructs a `DataLayout` at the given scope. The constructor
184    fetches the data layout specification and combines it with those of
185    enclosing scopes (layouts are expected to be compatible).
186-   The user calls `DataLayout::query(Type ty)`.
187-   If `DataLayout` has a cached response, this response is returned
188    immediately.
189-   Otherwise, the query is handed down by `DataLayout` to
190    `DataLayoutOpInterface::query(ty, *this, relevantEntries)` where the
191    relevant entries are computed as described above.
192-   Unless the `query` hook is reimplemented by the op interface, the query is
193    handled further down to `DataLayoutTypeInterface::query(dataLayout,
194    relevantEntries)` after casting `ty` to the type interface. If the type does
195    not implement the interface, an unrecoverable fatal error is produced.
196-   The type is expected to always provide the response, which is returned up
197    the call stack and cached by the `DataLayout.`
198
199## Default Implementation
200
201The default implementation of the data layout interfaces directly handles
202queries for a subset of built-in types.
203
204### Built-in Types
205
206The following describes the default properties of built-in types.
207
208The size of built-in integers and floats in bytes is computed as
209`ceildiv(bitwidth, 8)`. The ABI alignment of integer types with bitwidth below
21064 and of the float types is the closest from above power-of-two number of
211bytes. The ABI alignment of integer types with bitwidth 64 and above is 4 bytes
212(32 bits).
213
214The size of built-in vectors is computed by first rounding their number of
215elements in the _innermost_ dimension to the closest power-of-two from above,
216then getting the total number of elements, and finally multiplying it with the
217element size. For example, `vector<3xi32>` and `vector<4xi32>` have the same
218size. So do `vector<2x3xf32>` and `vector<2x4xf32>`, but `vector<3x4xf32>` and
219`vector<4x4xf32>` have different sizes. The ABI and preferred alignment of
220vector types is computed by taking the innermost dimension of the vector,
221rounding it up to the closest power-of-two, taking a product of that with
222element size in bytes, and rounding the result up again to the closest
223power-of-two.
224
225Note: these values are selected for consistency with the
226[default data layout in LLVM](https://llvm.org/docs/LangRef.html#data-layout),
227which MLIR assumed until the introduction of proper data layout modeling, and
228with the
229[modeling of n-D vectors](https://mlir.llvm.org/docs/Dialects/Vector/#deeperdive).
230They **may change** in the future.
231
232### DLTI Dialect
233
234The [DLTI](Dialects/DLTI.md) dialect provides the attributes implementing
235`DataLayoutSpecInterface` and `DataLayoutEntryInterface`, as well as a dialect
236attribute that can be used to attach the specification to a given operation. The
237verifier of this attribute triggers those of the specification and checks the
238compatiblity of nested specifications.
239