//===-- MemorySlotInterfaces.td - MemorySlot interfaces ----*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_INTERFACES_MEMORYSLOTINTERFACES
#define MLIR_INTERFACES_MEMORYSLOTINTERFACES
include "mlir/IR/OpBase.td"
def PromotableAllocationOpInterface
: OpInterface<"PromotableAllocationOpInterface"> {
let description = [{
Describes an operation allocating a memory slot that can be promoted into
SSA values.
}];
let cppNamespace = "::mlir";
let methods = [
InterfaceMethod<[{
Returns a list of memory slots for which promotion should be attempted.
This only considers the local semantics of the allocator, ignoring
whether the slot pointer is properly used or not. This allocator is the
"owner" of the returned slots, meaning no two allocators should return
the same slot. The content of the memory slot must only be reachable
using loads and stores to the provided slot pointer, no aliasing is
allowed.
Promotion of the slot will lead to the slot pointer no longer being
used, leaving the content of the memory slot unreachable.
No IR mutation is allowed in this method.
}], "::llvm::SmallVector<::mlir::MemorySlot>", "getPromotableSlots",
(ins)
>,
InterfaceMethod<[{
Provides the default Value of this memory slot. The provided Value
will be used as the reaching definition of loads done before any store.
This Value must outlive the promotion and dominate all the uses of this
slot's pointer. The provided builder can be used to create the default
value on the fly.
The builder is located at the beginning of the block where the slot
pointer is defined.
}], "::mlir::Value", "getDefaultValue",
(ins
"const ::mlir::MemorySlot &":$slot,
"::mlir::OpBuilder &":$builder)
>,
InterfaceMethod<[{
Hook triggered for every new block argument added to a block.
This will only be called for slots declared by this operation.
The builder is located at the beginning of the block on call. All IR
mutations must happen through the builder.
}],
"void", "handleBlockArgument",
(ins
"const ::mlir::MemorySlot &":$slot,
"::mlir::BlockArgument":$argument,
"::mlir::OpBuilder &":$builder
)
>,
InterfaceMethod<[{
Hook triggered once the promotion of a slot is complete. This can
also clean up the created default value if necessary.
This will only be called for slots declared by this operation.
Must return a new promotable allocation op if this operation produced
multiple promotable slots, nullopt otherwise.
}],
"::std::optional<::mlir::PromotableAllocationOpInterface>",
"handlePromotionComplete",
(ins
"const ::mlir::MemorySlot &":$slot,
"::mlir::Value":$defaultValue,
"::mlir::OpBuilder &":$builder)
>,
];
}
def PromotableMemOpInterface : OpInterface<"PromotableMemOpInterface"> {
let description = [{
Describes an operation that can load from memory slots and/or store
to memory slots.
For a memory operation on a slot to be valid, it must strictly operate
within the bounds of the slot.
If the same operation does both loads and stores on the same slot, the
load must semantically happen first.
}];
let cppNamespace = "::mlir";
let methods = [
InterfaceMethod<[{
Gets whether this operation loads from the specified slot.
No IR mutation is allowed in this method.
}],
"bool", "loadsFrom",
(ins "const ::mlir::MemorySlot &":$slot)
>,
InterfaceMethod<[{
Gets whether this operation stores to the specified slot.
No IR mutation is allowed in this method.
}],
"bool", "storesTo",
(ins "const ::mlir::MemorySlot &":$slot)
>,
InterfaceMethod<[{
Gets the value stored to the provided memory slot, or returns a null
value if this operation does not store to this slot. An operation
storing a value to a slot must always be able to provide the value it
stores. This method is only called once per slot promotion, and only
on operations that store to the slot according to the `storesTo` method.
The returned value must dominate all operations dominated by the storing
operation.
The builder is located immediately after the memory operation on call.
No IR deletion is allowed in this method. IR mutations must not
introduce new uses of the memory slot. Existing control flow must not
be modified.
}],
"::mlir::Value", "getStored",
(ins "const ::mlir::MemorySlot &":$slot,
"::mlir::OpBuilder &":$builder,
"::mlir::Value":$reachingDef,
"const ::mlir::DataLayout &":$dataLayout)
>,
InterfaceMethod<[{
Checks that this operation can be promoted to no longer use the provided
blocking uses, in the context of promoting `slot`.
If the removal procedure of the use will require that other uses get
removed, that dependency should be added to the `newBlockingUses`
argument. Dependent uses must only be uses of results of this operation.
No IR mutation is allowed in this method.
}], "bool", "canUsesBeRemoved",
(ins "const ::mlir::MemorySlot &":$slot,
"const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &":$blockingUses,
"::llvm::SmallVectorImpl<::mlir::OpOperand *> &":$newBlockingUses,
"const ::mlir::DataLayout &":$datalayout)
>,
InterfaceMethod<[{
Transforms IR to ensure that the current operation does not use the
provided memory slot anymore. `reachingDefinition` contains the value
currently stored in the provided memory slot, immediately before the
current operation.
During the transformation, *no operation should be deleted*.
The operation can only schedule its own deletion by returning the
appropriate `DeletionKind`. The deletion must be legal assuming the
blocking uses passed through the `newBlockingUses` list in
`canUseBeRemoved` have been removed.
After calling this method, the blocking uses should have disappeared
or this operation should have scheduled its own deletion.
This method will only be called after ensuring promotion is allowed via
`canUseBeRemoved`. The requested blocking use removal may or may not
have been done at the point of calling this method, but it will be done
eventually.
The builder is located after the promotable operation on call.
}],
"::mlir::DeletionKind",
"removeBlockingUses",
(ins "const ::mlir::MemorySlot &":$slot,
"const ::llvm::SmallPtrSetImpl<mlir::OpOperand *> &":$blockingUses,
"::mlir::OpBuilder &":$builder,
"::mlir::Value":$reachingDefinition,
"const ::mlir::DataLayout &":$dataLayout)
>,
];
}
def PromotableOpInterface : OpInterface<"PromotableOpInterface"> {
let description = [{
Describes an operation that can be transformed or deleted so it no longer
uses a provided value (blocking use), in case this would allow the promotion
of a memory slot.
}];
let cppNamespace = "::mlir";
let methods = [
InterfaceMethod<[{
Checks that this operation can be promoted to no longer use the provided
blocking uses, in order to allow optimization.
If the removal procedure of the use will require that other uses get
removed, that dependency should be added to the `newBlockingUses`
argument. Dependent uses must only be uses of results of this operation.
No IR mutation is allowed in this method.
}], "bool", "canUsesBeRemoved",
(ins "const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &":$blockingUses,
"::llvm::SmallVectorImpl<::mlir::OpOperand *> &":$newBlockingUses,
"const ::mlir::DataLayout &":$datalayout)
>,
InterfaceMethod<[{
Transforms IR to ensure that the current operation does not use the
provided blocking uses anymore. In contrast to
`PromotableMemOpInterface`, operations implementing this interface
must not need access to the reaching definition of the content of the
slot.
During the transformation, *no operation should be deleted*.
The operation can only schedule its own deletion by returning the
appropriate `DeletionKind`. The deletion must be legal assuming the
blocking uses passed through the `newBlockingUses` list in
`canUseBeRemoved` have been removed.
After calling this method, the blocking uses should have disappeared
or this operation should have scheduled its own deletion.
This method will only be called after ensuring promotion is allowed via
`canUseBeRemoved`. The requested blocking use removal may or may not
have been done at the point of calling this method, but it will be done
eventually.
The builder is located after the promotable operation on call.
}],
"::mlir::DeletionKind",
"removeBlockingUses",
(ins "const ::llvm::SmallPtrSetImpl<mlir::OpOperand *> &":$blockingUses,
"::mlir::OpBuilder &":$builder)
>,
InterfaceMethod<[{
This method allows the promoted operation to visit the SSA values used
in place of the memory slot once the promotion process of the memory
slot is complete.
If this method returns true, the `visitReplacedValues` method on this
operation will be called after the main mutation stage finishes
(i.e., after all ops have been processed with `removeBlockingUses`).
Operations should only the replaced values if the intended
transformation applies to all the replaced values. Furthermore, replaced
values must not be deleted.
}], "bool", "requiresReplacedValues", (ins), [{}],
[{ return false; }]
>,
InterfaceMethod<[{
Transforms the IR using the SSA values that replaced the memory slot.
This method will only be called after all blocking uses have been
scheduled for removal and if `requiresReplacedValues` returned
true.
The builder is located after the promotable operation on call. During
the transformation, *no operation should be deleted*.
}],
"void", "visitReplacedValues",
(ins "::llvm::ArrayRef<std::pair<::mlir::Operation*, ::mlir::Value>>":$mutatedDefs,
"::mlir::OpBuilder &":$builder), [{}], [{ return; }]
>,
];
}
def DestructurableAllocationOpInterface
: OpInterface<"DestructurableAllocationOpInterface"> {
let description = [{
Describes operations allocating memory slots of aggregates that can be
destructured into multiple smaller allocations.
}];
let cppNamespace = "::mlir";
let methods = [
InterfaceMethod<[{
Returns the list of slots for which destructuring should be attempted,
specifying in which way the slot should be destructured into subslots.
The subslots are indexed by attributes. This computes the type of the
pointer for each subslot to be generated. The type of the memory slot
must implement `DestructurableTypeInterface`.
No IR mutation is allowed in this method.
}],
"::llvm::SmallVector<::mlir::DestructurableMemorySlot>",
"getDestructurableSlots",
(ins)
>,
InterfaceMethod<[{
Destructures this slot into multiple subslots. The newly generated slots
may belong to a different allocator. The original slot must still exist
at the end of this call. Only generates subslots for the indices found in
`usedIndices` since all other subslots are unused.
The builder is located at the beginning of the block where the slot
pointer is defined.
}],
"::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot>",
"destructure",
(ins "const ::mlir::DestructurableMemorySlot &":$slot,
"const ::llvm::SmallPtrSetImpl<::mlir::Attribute> &":$usedIndices,
"::mlir::OpBuilder &":$builder,
"::mlir::SmallVectorImpl<::mlir::DestructurableAllocationOpInterface> &":
$newAllocators)
>,
InterfaceMethod<[{
Hook triggered once the destructuring of a slot is complete, meaning the
original slot is no longer being refered to and could be deleted.
This will only be called for slots declared by this operation.
Must return a new destructurable allocation op if this hook creates
a new destructurable op, nullopt otherwise.
}],
"::std::optional<::mlir::DestructurableAllocationOpInterface>",
"handleDestructuringComplete",
(ins "const ::mlir::DestructurableMemorySlot &":$slot,
"::mlir::OpBuilder &":$builder)
>,
];
}
def SafeMemorySlotAccessOpInterface
: OpInterface<"SafeMemorySlotAccessOpInterface"> {
let description = [{
Describes operations using memory slots in a safe manner.
}];
let cppNamespace = "::mlir";
let methods = [
InterfaceMethod<[{
Returns whether all accesses in this operation to the provided slot are
done in a safe manner. To be safe, the access most only access the slot
inside the bounds that its type implies.
If the safety of the accesses depends on the safety of the accesses to
further memory slots, the result of this method will be conditioned to
the safety of the accesses to the slots added by this method to
`mustBeSafelyUsed`.
No IR mutation is allowed in this method.
}],
"::llvm::LogicalResult",
"ensureOnlySafeAccesses",
(ins "const ::mlir::MemorySlot &":$slot,
"::mlir::SmallVectorImpl<::mlir::MemorySlot> &":$mustBeSafelyUsed,
"const ::mlir::DataLayout &":$dataLayout)
>
];
}
def DestructurableAccessorOpInterface
: OpInterface<"DestructurableAccessorOpInterface"> {
let description = [{
Describes operations that can access a sub-element of a destructurable slot.
}];
let cppNamespace = "::mlir";
let methods = [
InterfaceMethod<[{
For a given destructurable memory slot, returns whether this operation can
rewire its uses of the slot to use the slots generated after
destructuring. This may involve creating new operations.
This method must also register the indices it will access within the
`usedIndices` set. If the accessor generates new slots mapping to
subelements, they must be registered in `mustBeSafelyUsed` to ensure
they are used in a safe manner.
No IR mutation is allowed in this method.
}],
"bool",
"canRewire",
(ins "const ::mlir::DestructurableMemorySlot &":$slot,
"::llvm::SmallPtrSetImpl<::mlir::Attribute> &":$usedIndices,
"::mlir::SmallVectorImpl<::mlir::MemorySlot> &":$mustBeSafelyUsed,
"const ::mlir::DataLayout &":$dataLayout)
>,
InterfaceMethod<[{
Rewires the use of a slot to the generated subslots, without deleting
any operation. Returns whether the accessor should be deleted.
Deletion of operations is not allowed, only the accessor can be
scheduled for deletion by returning the appropriate value.
}],
"::mlir::DeletionKind",
"rewire",
(ins "const ::mlir::DestructurableMemorySlot &":$slot,
"::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot> &":$subslots,
"::mlir::OpBuilder &":$builder,
"const ::mlir::DataLayout &":$dataLayout)
>
];
}
def DestructurableTypeInterface
: TypeInterface<"DestructurableTypeInterface"> {
let description = [{
Describes a type that can be broken down into indexable sub-element types.
}];
let cppNamespace = "::mlir";
let methods = [
InterfaceMethod<[{
Destructures the type into subelements into a map of index attributes to
types of subelements. Returns nothing if the type cannot be destructured.
}],
"::std::optional<::llvm::DenseMap<::mlir::Attribute, ::mlir::Type>>",
"getSubelementIndexMap",
(ins)
>,
InterfaceMethod<[{
Indicates which type is held at the provided index, returning a null
Type if no type could be computed. While this can return information
even when the type cannot be completely destructured, it must be coherent
with the types returned by `getSubelementIndexMap` when they exist.
}],
"::mlir::Type",
"getTypeAtIndex",
(ins "::mlir::Attribute":$index)
>
];
}
#endif // MLIR_INTERFACES_MEMORYSLOTINTERFACES