xref: /llvm-project/mlir/include/mlir/Interfaces/MemorySlotInterfaces.td (revision db791b278a414fb6df1acc1799adcf11d8fb9169)
1//===-- MemorySlotInterfaces.td - MemorySlot interfaces ----*- tablegen -*-===//
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#ifndef MLIR_INTERFACES_MEMORYSLOTINTERFACES
10#define MLIR_INTERFACES_MEMORYSLOTINTERFACES
11
12include "mlir/IR/OpBase.td"
13
14def PromotableAllocationOpInterface
15    : OpInterface<"PromotableAllocationOpInterface"> {
16  let description = [{
17    Describes an operation allocating a memory slot that can be promoted into
18    SSA values.
19  }];
20  let cppNamespace = "::mlir";
21
22  let methods = [
23    InterfaceMethod<[{
24        Returns a list of memory slots for which promotion should be attempted.
25        This only considers the local semantics of the allocator, ignoring
26        whether the slot pointer is properly used or not. This allocator is the
27        "owner" of the returned slots, meaning no two allocators should return
28        the same slot. The content of the memory slot must only be reachable
29        using loads and stores to the provided slot pointer, no aliasing is
30        allowed.
31
32        Promotion of the slot will lead to the slot pointer no longer being
33        used, leaving the content of the memory slot unreachable.
34
35        No IR mutation is allowed in this method.
36      }], "::llvm::SmallVector<::mlir::MemorySlot>", "getPromotableSlots",
37      (ins)
38    >,
39    InterfaceMethod<[{
40        Provides the default Value of this memory slot. The provided Value
41        will be used as the reaching definition of loads done before any store.
42        This Value must outlive the promotion and dominate all the uses of this
43        slot's pointer. The provided builder can be used to create the default
44        value on the fly.
45
46        The builder is located at the beginning of the block where the slot
47        pointer is defined.
48      }], "::mlir::Value", "getDefaultValue",
49      (ins
50        "const ::mlir::MemorySlot &":$slot,
51        "::mlir::OpBuilder &":$builder)
52    >,
53    InterfaceMethod<[{
54        Hook triggered for every new block argument added to a block.
55        This will only be called for slots declared by this operation.
56
57        The builder is located at the beginning of the block on call. All IR
58        mutations must happen through the builder.
59      }],
60      "void", "handleBlockArgument",
61      (ins
62        "const ::mlir::MemorySlot &":$slot,
63        "::mlir::BlockArgument":$argument,
64        "::mlir::OpBuilder &":$builder
65      )
66    >,
67    InterfaceMethod<[{
68        Hook triggered once the promotion of a slot is complete. This can
69        also clean up the created default value if necessary.
70        This will only be called for slots declared by this operation.
71
72        Must return a new promotable allocation op if this operation produced
73        multiple promotable slots, nullopt otherwise.
74      }],
75      "::std::optional<::mlir::PromotableAllocationOpInterface>",
76        "handlePromotionComplete",
77      (ins
78        "const ::mlir::MemorySlot &":$slot,
79        "::mlir::Value":$defaultValue,
80        "::mlir::OpBuilder &":$builder)
81    >,
82  ];
83}
84
85def PromotableMemOpInterface : OpInterface<"PromotableMemOpInterface"> {
86  let description = [{
87    Describes an operation that can load from memory slots and/or store
88    to memory slots.
89
90    For a memory operation on a slot to be valid, it must strictly operate
91    within the bounds of the slot.
92
93    If the same operation does both loads and stores on the same slot, the
94    load must semantically happen first.
95  }];
96  let cppNamespace = "::mlir";
97
98  let methods = [
99    InterfaceMethod<[{
100        Gets whether this operation loads from the specified slot.
101
102        No IR mutation is allowed in this method.
103      }],
104      "bool", "loadsFrom",
105      (ins "const ::mlir::MemorySlot &":$slot)
106    >,
107    InterfaceMethod<[{
108        Gets whether this operation stores to the specified slot.
109
110        No IR mutation is allowed in this method.
111      }],
112      "bool", "storesTo",
113      (ins "const ::mlir::MemorySlot &":$slot)
114    >,
115    InterfaceMethod<[{
116        Gets the value stored to the provided memory slot, or returns a null
117        value if this operation does not store to this slot. An operation
118        storing a value to a slot must always be able to provide the value it
119        stores. This method is only called once per slot promotion, and only
120        on operations that store to the slot according to the `storesTo` method.
121        The returned value must dominate all operations dominated by the storing
122        operation.
123
124        The builder is located immediately after the memory operation on call.
125        No IR deletion is allowed in this method. IR mutations must not
126        introduce new uses of the memory slot. Existing control flow must not
127        be modified.
128      }],
129      "::mlir::Value", "getStored",
130      (ins "const ::mlir::MemorySlot &":$slot,
131           "::mlir::OpBuilder &":$builder,
132           "::mlir::Value":$reachingDef,
133           "const ::mlir::DataLayout &":$dataLayout)
134    >,
135    InterfaceMethod<[{
136        Checks that this operation can be promoted to no longer use the provided
137        blocking uses, in the context of promoting `slot`.
138
139        If the removal procedure of the use will require that other uses get
140        removed, that dependency should be added to the `newBlockingUses`
141        argument. Dependent uses must only be uses of results of this operation.
142
143        No IR mutation is allowed in this method.
144      }], "bool", "canUsesBeRemoved",
145      (ins "const ::mlir::MemorySlot &":$slot,
146           "const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &":$blockingUses,
147           "::llvm::SmallVectorImpl<::mlir::OpOperand *> &":$newBlockingUses,
148           "const ::mlir::DataLayout &":$datalayout)
149    >,
150    InterfaceMethod<[{
151        Transforms IR to ensure that the current operation does not use the
152        provided memory slot anymore. `reachingDefinition` contains the value
153        currently stored in the provided memory slot, immediately before the
154        current operation.
155
156        During the transformation, *no operation should be deleted*.
157        The operation can only schedule its own deletion by returning the
158        appropriate `DeletionKind`. The deletion must be legal assuming the
159        blocking uses passed through the `newBlockingUses` list in
160        `canUseBeRemoved` have been removed.
161
162        After calling this method, the blocking uses should have disappeared
163        or this operation should have scheduled its own deletion.
164
165        This method will only be called after ensuring promotion is allowed via
166        `canUseBeRemoved`. The requested blocking use removal may or may not
167        have been done at the point of calling this method, but it will be done
168        eventually.
169
170        The builder is located after the promotable operation on call.
171      }],
172      "::mlir::DeletionKind",
173      "removeBlockingUses",
174      (ins "const ::mlir::MemorySlot &":$slot,
175           "const ::llvm::SmallPtrSetImpl<mlir::OpOperand *> &":$blockingUses,
176           "::mlir::OpBuilder &":$builder,
177           "::mlir::Value":$reachingDefinition,
178           "const ::mlir::DataLayout &":$dataLayout)
179    >,
180  ];
181}
182
183def PromotableOpInterface : OpInterface<"PromotableOpInterface"> {
184  let description = [{
185    Describes an operation that can be transformed or deleted so it no longer
186    uses a provided value (blocking use), in case this would allow the promotion
187    of a memory slot.
188  }];
189  let cppNamespace = "::mlir";
190
191  let methods = [
192    InterfaceMethod<[{
193        Checks that this operation can be promoted to no longer use the provided
194        blocking uses, in order to allow optimization.
195
196        If the removal procedure of the use will require that other uses get
197        removed, that dependency should be added to the `newBlockingUses`
198        argument. Dependent uses must only be uses of results of this operation.
199
200        No IR mutation is allowed in this method.
201      }], "bool", "canUsesBeRemoved",
202      (ins "const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &":$blockingUses,
203           "::llvm::SmallVectorImpl<::mlir::OpOperand *> &":$newBlockingUses,
204           "const ::mlir::DataLayout &":$datalayout)
205    >,
206    InterfaceMethod<[{
207        Transforms IR to ensure that the current operation does not use the
208        provided blocking uses anymore. In contrast to
209        `PromotableMemOpInterface`, operations implementing this interface
210        must not need access to the reaching definition of the content of the
211        slot.
212
213        During the transformation, *no operation should be deleted*.
214        The operation can only schedule its own deletion by returning the
215        appropriate `DeletionKind`. The deletion must be legal assuming the
216        blocking uses passed through the `newBlockingUses` list in
217        `canUseBeRemoved` have been removed.
218
219        After calling this method, the blocking uses should have disappeared
220        or this operation should have scheduled its own deletion.
221
222        This method will only be called after ensuring promotion is allowed via
223        `canUseBeRemoved`. The requested blocking use removal may or may not
224        have been done at the point of calling this method, but it will be done
225        eventually.
226
227        The builder is located after the promotable operation on call.
228      }],
229      "::mlir::DeletionKind",
230      "removeBlockingUses",
231      (ins "const ::llvm::SmallPtrSetImpl<mlir::OpOperand *> &":$blockingUses,
232           "::mlir::OpBuilder &":$builder)
233    >,
234    InterfaceMethod<[{
235        This method allows the promoted operation to visit the SSA values used
236        in place of the memory slot once the promotion process of the memory
237        slot is complete.
238
239        If this method returns true, the `visitReplacedValues` method on this
240        operation will be called after the main mutation stage finishes
241        (i.e., after all ops have been processed with `removeBlockingUses`).
242
243        Operations should only the replaced values if the intended
244        transformation applies to all the replaced values. Furthermore, replaced
245        values must not be deleted.
246      }], "bool", "requiresReplacedValues", (ins), [{}],
247      [{ return false; }]
248    >,
249    InterfaceMethod<[{
250        Transforms the IR using the SSA values that replaced the memory slot.
251
252        This method will only be called after all blocking uses have been
253        scheduled for removal and if `requiresReplacedValues` returned
254        true.
255
256        The builder is located after the promotable operation on call. During
257        the transformation, *no operation should be deleted*.
258      }],
259      "void", "visitReplacedValues",
260      (ins "::llvm::ArrayRef<std::pair<::mlir::Operation*, ::mlir::Value>>":$mutatedDefs,
261           "::mlir::OpBuilder &":$builder), [{}], [{ return; }]
262    >,
263  ];
264}
265
266def DestructurableAllocationOpInterface
267  : OpInterface<"DestructurableAllocationOpInterface"> {
268  let description = [{
269    Describes operations allocating memory slots of aggregates that can be
270    destructured into multiple smaller allocations.
271  }];
272  let cppNamespace = "::mlir";
273
274  let methods = [
275    InterfaceMethod<[{
276        Returns the list of slots for which destructuring should be attempted,
277        specifying in which way the slot should be destructured into subslots.
278        The subslots are indexed by attributes. This computes the type of the
279        pointer for each subslot to be generated. The type of the memory slot
280        must implement `DestructurableTypeInterface`.
281
282        No IR mutation is allowed in this method.
283      }],
284      "::llvm::SmallVector<::mlir::DestructurableMemorySlot>",
285      "getDestructurableSlots",
286      (ins)
287    >,
288    InterfaceMethod<[{
289        Destructures this slot into multiple subslots. The newly generated slots
290        may belong to a different allocator. The original slot must still exist
291        at the end of this call. Only generates subslots for the indices found in
292        `usedIndices` since all other subslots are unused.
293
294        The builder is located at the beginning of the block where the slot
295        pointer is defined.
296      }],
297      "::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot>",
298      "destructure",
299      (ins "const ::mlir::DestructurableMemorySlot &":$slot,
300           "const ::llvm::SmallPtrSetImpl<::mlir::Attribute> &":$usedIndices,
301           "::mlir::OpBuilder &":$builder,
302           "::mlir::SmallVectorImpl<::mlir::DestructurableAllocationOpInterface> &":
303             $newAllocators)
304    >,
305    InterfaceMethod<[{
306        Hook triggered once the destructuring of a slot is complete, meaning the
307        original slot is no longer being refered to and could be deleted.
308        This will only be called for slots declared by this operation.
309
310        Must return a new destructurable allocation op if this hook creates
311        a new destructurable op, nullopt otherwise.
312      }],
313      "::std::optional<::mlir::DestructurableAllocationOpInterface>",
314      "handleDestructuringComplete",
315      (ins "const ::mlir::DestructurableMemorySlot &":$slot,
316           "::mlir::OpBuilder &":$builder)
317    >,
318  ];
319}
320
321def SafeMemorySlotAccessOpInterface
322  : OpInterface<"SafeMemorySlotAccessOpInterface"> {
323  let description = [{
324    Describes operations using memory slots in a safe manner.
325  }];
326  let cppNamespace = "::mlir";
327
328  let methods = [
329    InterfaceMethod<[{
330        Returns whether all accesses in this operation to the provided slot are
331        done in a safe manner. To be safe, the access most only access the slot
332        inside the bounds that its type implies.
333
334        If the safety of the accesses depends on the safety of the accesses to
335        further memory slots, the result of this method will be conditioned to
336        the safety of the accesses to the slots added by this method to
337        `mustBeSafelyUsed`.
338
339        No IR mutation is allowed in this method.
340      }],
341      "::llvm::LogicalResult",
342      "ensureOnlySafeAccesses",
343      (ins "const ::mlir::MemorySlot &":$slot,
344           "::mlir::SmallVectorImpl<::mlir::MemorySlot> &":$mustBeSafelyUsed,
345           "const ::mlir::DataLayout &":$dataLayout)
346    >
347  ];
348}
349
350def DestructurableAccessorOpInterface
351  : OpInterface<"DestructurableAccessorOpInterface"> {
352  let description = [{
353    Describes operations that can access a sub-element of a destructurable slot.
354  }];
355  let cppNamespace = "::mlir";
356
357  let methods = [
358    InterfaceMethod<[{
359        For a given destructurable memory slot, returns whether this operation can
360        rewire its uses of the slot to use the slots generated after
361        destructuring. This may involve creating new operations.
362
363        This method must also register the indices it will access within the
364        `usedIndices` set. If the accessor generates new slots mapping to
365        subelements, they must be registered in `mustBeSafelyUsed` to ensure
366        they are used in a safe manner.
367
368        No IR mutation is allowed in this method.
369      }],
370      "bool",
371      "canRewire",
372      (ins "const ::mlir::DestructurableMemorySlot &":$slot,
373           "::llvm::SmallPtrSetImpl<::mlir::Attribute> &":$usedIndices,
374           "::mlir::SmallVectorImpl<::mlir::MemorySlot> &":$mustBeSafelyUsed,
375           "const ::mlir::DataLayout &":$dataLayout)
376    >,
377    InterfaceMethod<[{
378        Rewires the use of a slot to the generated subslots, without deleting
379        any operation. Returns whether the accessor should be deleted.
380
381        Deletion of operations is not allowed, only the accessor can be
382        scheduled for deletion by returning the appropriate value.
383      }],
384      "::mlir::DeletionKind",
385      "rewire",
386      (ins "const ::mlir::DestructurableMemorySlot &":$slot,
387           "::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot> &":$subslots,
388           "::mlir::OpBuilder &":$builder,
389           "const ::mlir::DataLayout &":$dataLayout)
390    >
391  ];
392}
393
394def DestructurableTypeInterface
395  : TypeInterface<"DestructurableTypeInterface"> {
396  let description = [{
397    Describes a type that can be broken down into indexable sub-element types.
398  }];
399  let cppNamespace = "::mlir";
400
401  let methods = [
402    InterfaceMethod<[{
403        Destructures the type into subelements into a map of index attributes to
404        types of subelements. Returns nothing if the type cannot be destructured.
405      }],
406      "::std::optional<::llvm::DenseMap<::mlir::Attribute, ::mlir::Type>>",
407      "getSubelementIndexMap",
408      (ins)
409    >,
410    InterfaceMethod<[{
411        Indicates which type is held at the provided index, returning a null
412        Type if no type could be computed. While this can return information
413        even when the type cannot be completely destructured, it must be coherent
414        with the types returned by `getSubelementIndexMap` when they exist.
415      }],
416      "::mlir::Type",
417      "getTypeAtIndex",
418      (ins "::mlir::Attribute":$index)
419    >
420  ];
421}
422
423#endif // MLIR_INTERFACES_MEMORYSLOTINTERFACES
424