-
Notifications
You must be signed in to change notification settings - Fork 0
MLIR SROA #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MLIR SROA #15
Changes from all commits
0ab5c83
beb7682
b03a2e6
4e5475f
a4c4021
5e49c8f
e88b395
805a4f4
5536c49
7dfdfe8
06d4bdd
40ad9c7
2bd8bbc
cd62893
14f666e
a4fce9a
fc227c0
07cc660
207e278
a28abf2
8e6c3ec
3843f6e
a843ed5
6011b2b
5abecde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
|
||
#include "mlir/IR/Types.h" | ||
#include "mlir/Interfaces/DataLayoutInterfaces.h" | ||
#include "mlir/Interfaces/MemorySlotInterfaces.h" | ||
#include <optional> | ||
|
||
namespace llvm { | ||
|
@@ -103,6 +104,7 @@ DEFINE_TRIVIAL_LLVM_TYPE(LLVMMetadataType); | |
class LLVMStructType | ||
: public Type::TypeBase<LLVMStructType, Type, detail::LLVMStructTypeStorage, | ||
DataLayoutTypeInterface::Trait, | ||
DestructurableTypeInterface::Trait, | ||
TypeTrait::IsMutable> { | ||
public: | ||
/// Inherit base constructors. | ||
|
@@ -198,6 +200,12 @@ class LLVMStructType | |
|
||
LogicalResult verifyEntries(DataLayoutEntryListRef entries, | ||
Location loc) const; | ||
|
||
/// Destructs the struct into its indexed field types. | ||
Optional<DenseMap<Attribute, Type>> getSubelementIndexMap(); | ||
|
||
/// Returns which type is stored at a given integer index within the struct. | ||
Type getTypeAtIndex(Attribute index); | ||
Moxinilian marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At a first glance, it seems odd to use an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the reason for this is that sometimes indices are not integers but fancy things. For example, if you wanted to use SROA on for example a hashmap dialect, your indices could be anything. |
||
}; | ||
|
||
//===----------------------------------------------------------------------===// | ||
|
Moxinilian marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would explicitly mention which interfaces are SROA-related in the source code There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So it's not super clear where the boundary is. There is "PromotableOpInterface" which is used by both, "TypeSafeOpInterface" which is used by SROA but has nothing SROA-specific to it. Generally I think those interfaces are meant to be used in any setting and happen to be used by mem2reg and SROA. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
//===-- Mem2RegInterfaces.td - Mem2Reg interfaces ----------*- tablegen -*-===// | ||
//===-- 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_MEM2REGINTERFACES | ||
#define MLIR_INTERFACES_MEM2REGINTERFACES | ||
#ifndef MLIR_INTERFACES_MEMORYSLOTINTERFACES | ||
#define MLIR_INTERFACES_MEMORYSLOTINTERFACES | ||
|
||
include "mlir/IR/OpBase.td" | ||
|
||
|
@@ -76,6 +76,9 @@ def PromotableMemOpInterface : OpInterface<"PromotableMemOpInterface"> { | |
to memory slots. Loads and stores must be of whole values of the same | ||
type as the slot itself. | ||
|
||
For a memory operation on a slot to be valid, it must operate on the slot | ||
pointer *only as a pointer to an element of the type of the slot*. | ||
|
||
If the same operation does both loads and stores on the same slot, the | ||
load must semantically happen first. | ||
}]; | ||
|
@@ -152,21 +155,21 @@ def PromotableOpInterface : OpInterface<"PromotableOpInterface"> { | |
let methods = [ | ||
InterfaceMethod<[{ | ||
Checks that this operation can be promoted to no longer use the provided | ||
blocking uses, in the context of promoting `slot`. | ||
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. | ||
}], "bool", "canUsesBeRemoved", | ||
(ins "const ::mlir::MemorySlot &":$slot, | ||
"const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &":$blockingUses, | ||
(ins "const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &":$blockingUses, | ||
"::llvm::SmallVectorImpl<::mlir::OpOperand *> &":$newBlockingUses) | ||
>, | ||
InterfaceMethod<[{ | ||
Transforms IR to ensure that the current operation does not use the | ||
provided memory slot anymore. In contrast to `PromotableMemOpInterface`, | ||
operations implementing this interface must not need access to the | ||
reaching definition of the content of the slot. | ||
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 | ||
|
@@ -186,11 +189,148 @@ def PromotableOpInterface : OpInterface<"PromotableOpInterface"> { | |
}], | ||
"::mlir::DeletionKind", | ||
"removeBlockingUses", | ||
(ins "const ::mlir::MemorySlot &":$slot, | ||
"const ::llvm::SmallPtrSetImpl<mlir::OpOperand *> &":$blockingUses, | ||
(ins "const ::llvm::SmallPtrSetImpl<mlir::OpOperand *> &":$blockingUses, | ||
"::mlir::OpBuilder &":$builder) | ||
>, | ||
]; | ||
} | ||
|
||
#endif // MLIR_INTERFACES_MEM2REGINTERFACES | ||
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 | ||
pointers of each subslots to be generated. The type of the memory slot | ||
must implement `DestructurableTypeInterface`. | ||
}], | ||
"::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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Does this mean that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
The builder is located at the beginning of the block where the slot | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems a bit odd that the insertion point of the builder is part of the precondition. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree. This comes from suggestions upstream, though. I don't know if I should try to fight it too much. |
||
pointer is defined. | ||
}], | ||
"::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot>", | ||
"destructure", | ||
(ins "const ::mlir::DestructurableMemorySlot &":$slot, | ||
"::llvm::SmallPtrSetImpl<::mlir::Attribute> &":$usedIndices, | ||
"::mlir::OpBuilder &":$builder) | ||
>, | ||
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. | ||
}], | ||
"void", "handleDestructuringComplete", | ||
(ins "const ::mlir::DestructurableMemorySlot &":$slot) | ||
>, | ||
]; | ||
} | ||
|
||
def TypeSafeOpInterface : OpInterface<"TypeSafeOpInterface"> { | ||
let description = [{ | ||
Describes operations using memory slots in a type-safe manner. | ||
}]; | ||
let cppNamespace = "::mlir"; | ||
|
||
let methods = [ | ||
InterfaceMethod<[{ | ||
Returns whether all accesses in this operation to the provided slot are | ||
done in a type-safe manner. To be type-safe, the access must only load | ||
the value in this type as the type of the slot, and without assuming any | ||
context around the slot. For example, a type-safe load must not load | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TypeSafeInBoundsOpInterface ... it is a bit too long though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's wrong with TypeSafeOpInterface? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it misses the inbounds aspect but we can keep it if you prefer the name. |
||
outside the bounds of the slot. | ||
|
||
If the type-safety of the accesses depends on the type-safety of the | ||
accesses to further memory slots, the result of this method will be | ||
conditioned to the type-safety of the accesses to the slots added by | ||
this method to `mustBeSafelyUsed`. | ||
}], | ||
"::mlir::LogicalResult", | ||
"ensureOnlyTypeSafeAccesses", | ||
(ins "const ::mlir::MemorySlot &":$slot, | ||
"::mlir::SmallVectorImpl<::mlir::MemorySlot> &":$mustBeSafelyUsed) | ||
> | ||
]; | ||
} | ||
|
||
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, and usually | ||
amounts to checking if the pointer types match. | ||
|
||
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 locally type-safe manner. | ||
}], | ||
"bool", | ||
"canRewire", | ||
(ins "const ::mlir::DestructurableMemorySlot &":$slot, | ||
"::llvm::SmallPtrSetImpl<::mlir::Attribute> &":$usedIndices, | ||
"::mlir::SmallVectorImpl<::mlir::MemorySlot> &":$mustBeSafelyUsed) | ||
>, | ||
InterfaceMethod<[{ | ||
Rewires the use of a slot to the generated subslots, without deleting | ||
any operation. Returns whether the accessor should be deleted. | ||
}], | ||
"::mlir::DeletionKind", | ||
"rewire", | ||
(ins "const ::mlir::DestructurableMemorySlot &":$slot, | ||
"::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot> &":$subslots) | ||
> | ||
]; | ||
} | ||
|
||
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. | ||
}], | ||
"::llvm::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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does "destruct" mean here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's go for destructure.