-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir] Add requiresReplacedValues
and visitReplacedValues
to PromotableOpInterface
#86792
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
Conversation
…romotableOpInterface` Add `requiresVisitingMutatedDefs` and `visitMutatedDefs` methods to `PromotableOpInterface`. These methods allow `PromotableOpInterface` ops to transforms definitions mutated by a `store`. This change is necessary to correctly handle the promotion of `LLVM_DbgDeclareOp`.
@llvm/pr-subscribers-mlir-llvm @llvm/pr-subscribers-mlir Author: Fabian Mora (fabianmcg) ChangesAdd This change is necessary to correctly handle the promotion of Full diff: https://github.com/llvm/llvm-project/pull/86792.diff 4 Files Affected:
diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
index f4bac9376f2ea0..d3857fbe06e355 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td
@@ -562,8 +562,10 @@ class LLVM_DbgIntrOp<string name, string argName, list<Trait> traits = []>
}];
}
-def LLVM_DbgDeclareOp : LLVM_DbgIntrOp<"dbg.declare", "addr",
- [DeclareOpInterfaceMethods<PromotableOpInterface>]> {
+def LLVM_DbgDeclareOp : LLVM_DbgIntrOp<"dbg.declare", "addr", [
+ DeclareOpInterfaceMethods<PromotableOpInterface, [
+ "requiresVisitingMutatedDefs", "visitMutatedDefs"
+ ]>]> {
let summary = "Describes how the address relates to a source language variable.";
let arguments = (ins
LLVM_AnyPointer:$addr,
diff --git a/mlir/include/mlir/Interfaces/MemorySlotInterfaces.td b/mlir/include/mlir/Interfaces/MemorySlotInterfaces.td
index e10e2d4e104c3e..af120eb059aebf 100644
--- a/mlir/include/mlir/Interfaces/MemorySlotInterfaces.td
+++ b/mlir/include/mlir/Interfaces/MemorySlotInterfaces.td
@@ -229,6 +229,27 @@ def PromotableOpInterface : OpInterface<"PromotableOpInterface"> {
(ins "const ::llvm::SmallPtrSetImpl<mlir::OpOperand *> &":$blockingUses,
"::mlir::RewriterBase &":$rewriter)
>,
+ InterfaceMethod<[{
+ Checks whether the operation requires visiting the mutated
+ definitions by a store operation.
+ }], "bool", "requiresVisitingMutatedDefs", (ins), [{}],
+ [{ return false; }]
+ >,
+ InterfaceMethod<[{
+ Visits all the mutated definitions by a store operation.
+
+ This method will only be called after all blocking issues haven been
+ scheduled for removal and if `requiresVisitingMutatedDefs` returned
+ true.
+
+ The rewriter is located after the promotable operation on call. All IR
+ mutations must happen through the rewriter. During the transformation,
+ *no operation should be deleted*.
+ }],
+ "void", "visitMutatedDefs",
+ (ins "::llvm::ArrayRef<std::pair<::mlir::Operation*, ::mlir::Value>>":$mutatedDefs,
+ "::mlir::RewriterBase &":$rewriter), [{}], [{ return; }]
+ >,
];
}
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
index f171bf7cc4bec3..b5c5f4454dede9 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
@@ -166,13 +166,6 @@ bool LLVM::StoreOp::canUsesBeRemoved(
DeletionKind LLVM::StoreOp::removeBlockingUses(
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
RewriterBase &rewriter, Value reachingDefinition) {
- // `canUsesBeRemoved` checked this blocking use must be the stored slot
- // pointer.
- for (Operation *user : slot.ptr.getUsers())
- if (auto declareOp = dyn_cast<LLVM::DbgDeclareOp>(user))
- rewriter.create<LLVM::DbgValueOp>(declareOp->getLoc(), getValue(),
- declareOp.getVarInfo(),
- declareOp.getLocationExpr());
return DeletionKind::Delete;
}
@@ -405,6 +398,18 @@ DeletionKind LLVM::DbgValueOp::removeBlockingUses(
return DeletionKind::Keep;
}
+bool LLVM::DbgDeclareOp::requiresVisitingMutatedDefs() { return true; }
+
+void LLVM::DbgDeclareOp::visitMutatedDefs(
+ ArrayRef<std::pair<Operation *, Value>> definitions,
+ RewriterBase &rewriter) {
+ for (auto [op, value] : definitions) {
+ rewriter.setInsertionPointAfter(op);
+ rewriter.create<LLVM::DbgValueOp>(getLoc(), value, getVarInfo(),
+ getLocationExpr());
+ }
+}
+
//===----------------------------------------------------------------------===//
// Interfaces for GEPOp
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Transforms/Mem2Reg.cpp b/mlir/lib/Transforms/Mem2Reg.cpp
index 80e3b790163297..3aca5eb19ff5af 100644
--- a/mlir/lib/Transforms/Mem2Reg.cpp
+++ b/mlir/lib/Transforms/Mem2Reg.cpp
@@ -202,6 +202,7 @@ class MemorySlotPromoter {
/// Contains the reaching definition at this operation. Reaching definitions
/// are only computed for promotable memory operations with blocking uses.
DenseMap<PromotableMemOpInterface, Value> reachingDefs;
+ DenseMap<PromotableMemOpInterface, Value> mutatedValues;
DominanceInfo &dominance;
MemorySlotPromotionInfo info;
const Mem2RegStatistics &statistics;
@@ -438,6 +439,7 @@ Value MemorySlotPromoter::computeReachingDefInBlock(Block *block,
assert(stored && "a memory operation storing to a slot must provide a "
"new definition of the slot");
reachingDef = stored;
+ mutatedValues[memOp] = stored;
}
}
}
@@ -552,6 +554,8 @@ void MemorySlotPromoter::removeBlockingUses() {
dominanceSort(usersToRemoveUses, *slot.ptr.getParentBlock()->getParent());
llvm::SmallVector<Operation *> toErase;
+ llvm::SmallVector<std::pair<Operation *, Value>> mutatedDefinitions;
+ llvm::SmallVector<PromotableOpInterface> visitMutatedDefinitions;
for (Operation *toPromote : llvm::reverse(usersToRemoveUses)) {
if (auto toPromoteMemOp = dyn_cast<PromotableMemOpInterface>(toPromote)) {
Value reachingDef = reachingDefs.lookup(toPromoteMemOp);
@@ -565,7 +569,9 @@ void MemorySlotPromoter::removeBlockingUses() {
slot, info.userToBlockingUses[toPromote], rewriter,
reachingDef) == DeletionKind::Delete)
toErase.push_back(toPromote);
-
+ if (toPromoteMemOp.storesTo(slot))
+ if (Value mutatedValue = mutatedValues[toPromoteMemOp])
+ mutatedDefinitions.push_back({toPromoteMemOp, mutatedValue});
continue;
}
@@ -574,6 +580,12 @@ void MemorySlotPromoter::removeBlockingUses() {
if (toPromoteBasic.removeBlockingUses(info.userToBlockingUses[toPromote],
rewriter) == DeletionKind::Delete)
toErase.push_back(toPromote);
+ if (toPromoteBasic.requiresVisitingMutatedDefs())
+ visitMutatedDefinitions.push_back(toPromoteBasic);
+ }
+ for (PromotableOpInterface op : visitMutatedDefinitions) {
+ rewriter.setInsertionPointAfter(op);
+ op.visitMutatedDefs(mutatedDefinitions, rewriter);
}
for (Operation *toEraseOp : toErase)
|
Checks whether the operation requires visiting the mutated | ||
definitions by a store operation. |
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.
Could you explain what this means more in the documentation?
Co-authored-by: Théo Degioanni <[email protected]>
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.
I do not see how this is equivalent to the existing implementation.
If I understand correctly, this change will pass all N changed definitions to each visitor-function, instead of running the visitor for each DbgValueOp
separately.
Maybe add a test with two dbg value ops on the same store to determine that this works as intended.
Are you referring to a test with two dbg declare ops? Because this change should only affect |
Sorry for the confusion, yes, I meant two dbg declares. I'm currently working on extensions for both SROA and Mem2Reg, and I can tell you that the test coverage is unfortunately not sufficient for such cases. |
@Dinistro because I'm not 100% sure I understand what you are looking for in the test, are you referring to something like: module {
llvm.func @basic_store_load(%arg0: i64) -> i64 {
%0 = llvm.mlir.constant(1 : i32) : i32
%1 = llvm.alloca %0 x i64 {alignment = 8 : i64} : (i32) -> !llvm.ptr
llvm.store %arg0, %1 {alignment = 4 : i64} : i64, !llvm.ptr
llvm.intr.dbg.declare #di_local_variable = %1 : !llvm.ptr
llvm.intr.dbg.declare #di_local_variable = %1 : !llvm.ptr
%2 = llvm.load %1 {alignment = 4 : i64} : !llvm.ptr -> i64
llvm.return %2 : i64
}
}
WIth result: module {
llvm.func @basic_store_load(%arg0: i64) -> i64 {
%0 = llvm.mlir.constant(1 : i32) : i32
llvm.intr.dbg.value #di_local_variable = %arg0 : i64
llvm.intr.dbg.value #di_local_variable = %arg0 : i64
llvm.return %arg0 : i64
}
} |
@fabianmcg yes, this is what I'm referring to. Looking at the code indicates to me that this might go wrong, due to running |
@Dinistro I added a test with two |
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.
Oh, I was indeed confused. Thanks for adding a test for this case.
LGTM, but maybe consider to wait for @Moxinilian to confirm that this is fine for him as well.
I'd just like the doc to be a bit clearer about what this is about but otherwise looks good! |
requiresVisitingMutatedDefs
and visitMutatedDefs
to PromotableOpInterface
requiresAmendingMutatedDefs
and amendMutatedDefs
to PromotableOpInterface
@Moxinilian I improved the docs. I also changed the names of the methods to |
I'm sorry but I still don't understand what those methods are about 😅 |
What about:
? |
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.
I think I see what you mean now. Here are some rewording suggestions, I think using simple English helps documentation clarity a lot :)
This method returns whether the promoted operation requires amending the | ||
mutated definitions by a store operation. |
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.
This method returns whether the promoted operation requires amending the | |
mutated definitions by a store operation. | |
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 operation will be visited using the | ||
`amendMutatedDefs` method after the main mutation stage finishes | ||
(i.e., after all ops have been processed with `removeBlockingUses`). |
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.
If this method returns true, the operation will be visited using the | |
`amendMutatedDefs` method after the main mutation stage finishes | |
(i.e., after all ops have been processed with `removeBlockingUses`). | |
If this method returns true, the `amendMutatedDefs` method on this | |
operation will be called after the main mutation stage finishes | |
(i.e., after all ops have been processed with `removeBlockingUses`). |
Transforms the IR to amend all the mutated definitions to the slot by a | ||
store operation. |
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.
Transforms the IR to amend all the mutated definitions to the slot by a | |
store operation. | |
Transforms IR using the SSA values that replaced the memory slot. |
(ins "::llvm::ArrayRef<std::pair<::mlir::Operation*, ::mlir::Value>>":$mutatedDefs, | ||
"::mlir::RewriterBase &":$rewriter), [{}], [{ return; }] |
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.
I feel like to be absolutely complete this method should also provide the memory slot, as one may want to know which memory slot specifically those definitions correspond to. But at the same time I am not really sure that this would be useful in practice, so change it only if you feel like it.
Operations should only request amending the mutated definitions if the | ||
intended transformation applies to all mutated values. Furthermore, | ||
mutated values must not be deleted. | ||
}], "bool", "requiresAmendingMutatedDefs", (ins), [{}], |
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.
In a perfect world I'd prefer those methods to be called something like visitReplacingValues
, because "amending mutated definitions" feels very vague in context.
mutations must happen through the rewriter. During the transformation, | ||
*no operation should be deleted*. | ||
}], | ||
"void", "amendMutatedDefs", |
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.
Name suggestion: visitReplacedValues
*no operation should be deleted*. | ||
}], | ||
"void", "amendMutatedDefs", | ||
(ins "::llvm::ArrayRef<std::pair<::mlir::Operation*, ::mlir::Value>>":$mutatedDefs, |
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.
Name suggestion: replacingValues
mlir/lib/Transforms/Mem2Reg.cpp
Outdated
@@ -202,6 +202,7 @@ class MemorySlotPromoter { | |||
/// Contains the reaching definition at this operation. Reaching definitions | |||
/// are only computed for promotable memory operations with blocking uses. | |||
DenseMap<PromotableMemOpInterface, Value> reachingDefs; | |||
DenseMap<PromotableMemOpInterface, Value> mutatedValues; |
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.
I really dislike the "mutatedValues" name because it is unclear to me in what way those values are mutated. This name suggests you put something in those values when what happens conceptually is the opposite.
mlir/lib/Transforms/Mem2Reg.cpp
Outdated
@@ -552,6 +554,8 @@ void MemorySlotPromoter::removeBlockingUses() { | |||
dominanceSort(usersToRemoveUses, *slot.ptr.getParentBlock()->getParent()); | |||
|
|||
llvm::SmallVector<Operation *> toErase; | |||
llvm::SmallVector<std::pair<Operation *, Value>> mutatedDefinitions; |
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.
Same as above for this name.
@Moxinilian I followed all your suggestions but one. I changed |
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.
Thank you very much for putting up with my requests ahah
requiresAmendingMutatedDefs
and amendMutatedDefs
to PromotableOpInterface
requiresReplacedValues
and visitReplacedValues
to PromotableOpInterface
Can we land this to avoid unnecessary conflicts with potentially upcoming PRs? |
@Moxinilian, @Dinistro are you both in favor and with high confidence on the change? |
I think this is fine, feel free to land |
Same |
Add
requiresReplacedValues
andvisitReplacedValues
methods toPromotableOpInterface
. These methods allowPromotableOpInterface
ops to transforms definitions mutated by astore
.This change is necessary to correctly handle the promotion of
LLVM_DbgDeclareOp
. It is also necessary for enabling #73057