[Mlir-commits] [mlir] [MLIR][SideEffects][MemoryEffects] Modified LICM to be more aggressive when checking movability of ops with MemWrite effects (PR #155344)
Mo Bagherbeik
llvmlistbot at llvm.org
Tue Aug 26 13:58:53 PDT 2025
https://github.com/mbagherbeikTT updated https://github.com/llvm/llvm-project/pull/155344
>From 34913fe68d21cccbe91862f9a9145acaeb895d55 Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Tue, 12 Aug 2025 20:28:54 +0000
Subject: [PATCH 1/8] Added 'Init' Memory Effect which defines an Idempotent
MemWrite effect and modified LICM pass. Allows speculatable ops with 'Init'
Memory Effects to be moved out of loops if op does not have other, non-Init,
Memory Effects and no other operations within it's nested region(s) have
Memory Effects that apply to the same resources as the original op.
---
.../mlir/Interfaces/SideEffectInterfaces.h | 43 ++++++
.../mlir/Interfaces/SideEffectInterfaces.td | 12 ++
mlir/lib/Interfaces/SideEffectInterfaces.cpp | 124 +++++++++++++++++-
.../Utils/LoopInvariantCodeMotionUtils.cpp | 2 +-
4 files changed, 174 insertions(+), 7 deletions(-)
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
index aef7ec622fe4f..f534a35d4bd8b 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
@@ -377,6 +377,13 @@ struct Read : public Effect::Base<Read> {};
/// 'write' effect implies only mutating a resource, and not any visible
/// dereference or read.
struct Write : public Effect::Base<Write> {};
+
+// The following effect indicates that the operation initializes some
+// memory resource to a known value i.e., an idempotent MemWrite.
+// An 'init' effect implies only mutating a resource in a way that's
+// identical across calls if inputs are the same, and not any visible
+// dereference or read.
+struct Init : public Effect::Base<Init> {};
} // namespace MemoryEffects
//===----------------------------------------------------------------------===//
@@ -421,6 +428,15 @@ bool isOpTriviallyDead(Operation *op);
/// Note: Terminators and symbols are never considered to be trivially dead.
bool wouldOpBeTriviallyDead(Operation *op);
+/// Returns true if the given operation is movable under memory effects.
+///
+/// An operation is movable if any of the following are true:
+/// (1) isMemoryEffectFree(op) --> true
+/// (2) isMemoryInitMovable(op) --> true
+///
+/// If the operation meets either criteria, then it is movable
+bool isMemoryEffectMovable(Operation *op);
+
/// Returns true if the given operation is free of memory effects.
///
/// An operation is free of memory effects if its implementation of
@@ -433,6 +449,33 @@ bool wouldOpBeTriviallyDead(Operation *op);
/// conditions are satisfied.
bool isMemoryEffectFree(Operation *op);
+/// Returns true if the given operation has a collision-free 'Init' memory
+/// effect.
+///
+/// An operation is movable if:
+/// (1) it has memory effects AND all of its memory effects are of type 'Init'
+/// (2) there are no other ops with memory effects on any ofthose same resources
+/// within the operation's region(s)
+///
+/// If the operation meets both criteria, then it is movable
+bool isMemoryInitMovable(Operation *op);
+
+/// Returns true if op and all operations within its nested regions
+/// have >1 Memory Effects on ANY of the input resources.
+///
+/// The first call to this function is by an op with >=1 MemInit effect on
+/// >=1 unique resources. To check that none of these resources are in conflict
+/// with other Memory Effects, we scan the entire parent region and maintain
+/// a count of Memory Effects that apply to the resources of the original op.
+/// If any resource has more than 1 Memory Effect in that region, the resource
+/// is in conflict and the op can't be moved by LICM.
+///
+/// Function mutates resources map
+///
+/// If no resources are in conflict, the op is movable.
+bool hasMemoryEffectInitConflict(
+ Operation *op, std::unordered_map<std::string, int> &resources);
+
/// Returns the side effects of an operation. If the operation has
/// RecursiveMemoryEffects, include all side effects of child operations.
///
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
index b292174fccb36..37083690bae52 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
@@ -87,6 +87,18 @@ def MemWrite : MemWrite<DefaultResource, 0, PartialEffect>;
class MemWriteAt<int stage, EffectRange range = PartialEffect>
: MemWrite<DefaultResource, stage, range>;
+// The following effect indicates that the operation initializes some
+// memory resource to a known value i.e., an idempotent MemWrite.
+// An 'init' effect implies only mutating a resource in a way that's
+// identical across calls if inputs are the same, and not any visible
+// dereference or read.
+class MemInit<Resource resource, int stage = 0,
+ EffectRange range = PartialEffect>
+ : MemoryEffect<"::mlir::MemoryEffects::Init", resource, stage, range>;
+def MemInit : MemInit<DefaultResource, 0, PartialEffect>;
+class MemInitAt<int stage, EffectRange range = PartialEffect>
+ : MemInit<DefaultResource, stage, range>;
+
//===----------------------------------------------------------------------===//
// Effect Traits
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Interfaces/SideEffectInterfaces.cpp b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
index 59fd19310cea5..de0eb58c8fedb 100644
--- a/mlir/lib/Interfaces/SideEffectInterfaces.cpp
+++ b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
@@ -10,6 +10,7 @@
#include "mlir/IR/SymbolTable.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include <unordered_set>
#include <utility>
using namespace mlir;
@@ -26,7 +27,7 @@ using namespace mlir;
//===----------------------------------------------------------------------===//
bool MemoryEffects::Effect::classof(const SideEffects::Effect *effect) {
- return isa<Allocate, Free, Read, Write>(effect);
+ return isa<Allocate, Free, Read, Write, Init>(effect);
}
//===----------------------------------------------------------------------===//
@@ -131,6 +132,7 @@ template bool mlir::hasSingleEffect<MemoryEffects::Allocate>(Operation *);
template bool mlir::hasSingleEffect<MemoryEffects::Free>(Operation *);
template bool mlir::hasSingleEffect<MemoryEffects::Read>(Operation *);
template bool mlir::hasSingleEffect<MemoryEffects::Write>(Operation *);
+template bool mlir::hasSingleEffect<MemoryEffects::Init>(Operation *);
template <typename EffectTy>
bool mlir::hasSingleEffect(Operation *op, Value value) {
@@ -160,6 +162,8 @@ template bool mlir::hasSingleEffect<MemoryEffects::Read>(Operation *,
Value value);
template bool mlir::hasSingleEffect<MemoryEffects::Write>(Operation *,
Value value);
+template bool mlir::hasSingleEffect<MemoryEffects::Init>(Operation *,
+ Value value);
template <typename ValueTy, typename EffectTy>
bool mlir::hasSingleEffect(Operation *op, ValueTy value) {
@@ -194,6 +198,9 @@ template bool
mlir::hasSingleEffect<OpOperand *, MemoryEffects::Write>(Operation *,
OpOperand *);
template bool
+mlir::hasSingleEffect<OpOperand *, MemoryEffects::Init>(Operation *,
+ OpOperand *);
+template bool
mlir::hasSingleEffect<OpResult, MemoryEffects::Allocate>(Operation *, OpResult);
template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Free>(Operation *,
OpResult);
@@ -201,6 +208,8 @@ template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Read>(Operation *,
OpResult);
template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Write>(Operation *,
OpResult);
+template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Init>(Operation *,
+ OpResult);
template bool
mlir::hasSingleEffect<BlockArgument, MemoryEffects::Allocate>(Operation *,
BlockArgument);
@@ -213,6 +222,9 @@ mlir::hasSingleEffect<BlockArgument, MemoryEffects::Read>(Operation *,
template bool
mlir::hasSingleEffect<BlockArgument, MemoryEffects::Write>(Operation *,
BlockArgument);
+template bool
+mlir::hasSingleEffect<BlockArgument, MemoryEffects::Init>(Operation *,
+ BlockArgument);
template <typename... EffectTys>
bool mlir::hasEffect(Operation *op) {
@@ -229,6 +241,7 @@ template bool mlir::hasEffect<MemoryEffects::Allocate>(Operation *);
template bool mlir::hasEffect<MemoryEffects::Free>(Operation *);
template bool mlir::hasEffect<MemoryEffects::Read>(Operation *);
template bool mlir::hasEffect<MemoryEffects::Write>(Operation *);
+template bool mlir::hasEffect<MemoryEffects::Init>(Operation *);
template bool
mlir::hasEffect<MemoryEffects::Write, MemoryEffects::Free>(Operation *);
@@ -250,6 +263,7 @@ template bool mlir::hasEffect<MemoryEffects::Allocate>(Operation *,
template bool mlir::hasEffect<MemoryEffects::Free>(Operation *, Value value);
template bool mlir::hasEffect<MemoryEffects::Read>(Operation *, Value value);
template bool mlir::hasEffect<MemoryEffects::Write>(Operation *, Value value);
+template bool mlir::hasEffect<MemoryEffects::Init>(Operation *, Value value);
template bool
mlir::hasEffect<MemoryEffects::Write, MemoryEffects::Free>(Operation *,
Value value);
@@ -275,6 +289,8 @@ template bool mlir::hasEffect<OpOperand *, MemoryEffects::Read>(Operation *,
OpOperand *);
template bool mlir::hasEffect<OpOperand *, MemoryEffects::Write>(Operation *,
OpOperand *);
+template bool mlir::hasEffect<OpOperand *, MemoryEffects::Init>(Operation *,
+ OpOperand *);
template bool
mlir::hasEffect<OpOperand *, MemoryEffects::Write, MemoryEffects::Free>(
Operation *, OpOperand *);
@@ -287,6 +303,8 @@ template bool mlir::hasEffect<OpResult, MemoryEffects::Read>(Operation *,
OpResult);
template bool mlir::hasEffect<OpResult, MemoryEffects::Write>(Operation *,
OpResult);
+template bool mlir::hasEffect<OpResult, MemoryEffects::Init>(Operation *,
+ OpResult);
template bool
mlir::hasEffect<OpResult, MemoryEffects::Write, MemoryEffects::Free>(
Operation *, OpResult);
@@ -302,6 +320,8 @@ template bool
mlir::hasEffect<BlockArgument, MemoryEffects::Write>(Operation *,
BlockArgument);
template bool
+mlir::hasEffect<BlockArgument, MemoryEffects::Init>(Operation *, BlockArgument);
+template bool
mlir::hasEffect<BlockArgument, MemoryEffects::Write, MemoryEffects::Free>(
Operation *, BlockArgument);
@@ -313,14 +333,20 @@ bool mlir::wouldOpBeTriviallyDead(Operation *op) {
return wouldOpBeTriviallyDeadImpl(op);
}
+bool mlir::isMemoryEffectMovable(Operation *op) {
+ return (isMemoryEffectFree(op) || isMemoryInitMovable(op));
+}
+
bool mlir::isMemoryEffectFree(Operation *op) {
if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
- if (!memInterface.hasNoEffect())
+ if (!memInterface.hasNoEffect()) {
return false;
+ }
// If the op does not have recursive side effects, then it is memory effect
// free.
- if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
+ if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
return true;
+ }
} else if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
// Otherwise, if the op does not implement the memory effect interface and
// it does not have recursive side effects, then it cannot be known that the
@@ -330,13 +356,99 @@ bool mlir::isMemoryEffectFree(Operation *op) {
// Recurse into the regions and ensure that all nested ops are memory effect
// free.
- for (Region ®ion : op->getRegions())
- for (Operation &op : region.getOps())
- if (!isMemoryEffectFree(&op))
+ for (Region ®ion : op->getRegions()) {
+ for (Operation &op : region.getOps()) {
+ if (!isMemoryEffectFree(&op)) {
return false;
+ }
+ }
+ }
return true;
}
+bool mlir::isMemoryInitMovable(Operation *op) {
+ if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
+ // gather all effects on op
+ llvm::SmallVector<MemoryEffects::EffectInstance> effects;
+ memInterface.getEffects(effects);
+
+ // op has interface but no effects, be conservative
+ if (effects.empty()) {
+ return false;
+ }
+
+ std::unordered_map<std::string, int> resources;
+
+ // ensure op only has Init effects and gather unique
+ // resource names
+ for (const MemoryEffects::EffectInstance &effect : effects) {
+ if (!isa<MemoryEffects::Init>(effect.getEffect())) {
+ return false;
+ }
+
+ std::string name = effect.getResource()->getName().str();
+ resources.try_emplace(name, 0);
+ }
+
+ // op itself is good, need to check rest of its parent region
+ Operation *parent = op->getParentOp();
+
+ for (Region ®ion : parent->getRegions()) {
+ for (Operation &op_i : region.getOps()) {
+ if (hasMemoryEffectInitConflict(&op_i, resources)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ // op does not implement the memory effect op interface
+ // meaning it doesn't have any memory init effects and
+ // shouldn't be flagged as movable to be conservative
+ return false;
+}
+
+bool mlir::hasMemoryEffectInitConflict(
+ Operation *op, std::unordered_map<std::string, int> &resources) {
+ if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
+ if (!memInterface.hasNoEffect()) {
+ llvm::SmallVector<MemoryEffects::EffectInstance> effects;
+ memInterface.getEffects(effects);
+
+ // ensure op only has Init effects and gather unique
+ // resource names
+ for (const MemoryEffects::EffectInstance &effect : effects) {
+ if (!isa<MemoryEffects::Init>(effect.getEffect())) {
+ return true;
+ }
+
+ // only care about resources of the op that called
+ // this recursive function for the first time
+ std::string name = effect.getResource()->getName().str();
+
+ if (resources.find(name) != resources.end()) {
+ if (++resources[name] > 1) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ // Recurse into the regions and ensure that nested ops don't
+ // conflict with each others MemInits
+ for (Region ®ion : op->getRegions()) {
+ for (Operation &op : region.getOps()) {
+ if (hasMemoryEffectInitConflict(&op, resources)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
// the returned vector may contain duplicate effects
std::optional<llvm::SmallVector<MemoryEffects::EffectInstance>>
mlir::getEffectsRecursively(Operation *rootOp) {
diff --git a/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp b/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp
index cb3f2c52e2116..06fabcf8a2ad5 100644
--- a/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp
+++ b/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp
@@ -110,7 +110,7 @@ size_t mlir::moveLoopInvariantCode(LoopLikeOpInterface loopLike) {
return loopLike.isDefinedOutsideOfLoop(value);
},
[&](Operation *op, Region *) {
- return isMemoryEffectFree(op) && isSpeculatable(op);
+ return isMemoryEffectMovable(op) && isSpeculatable(op);
},
[&](Operation *op, Region *) { loopLike.moveOutOfLoop(op); });
}
>From 0438627801b7250333eb3254faba8c97b6dbe90a Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Wed, 13 Aug 2025 19:35:18 +0000
Subject: [PATCH 2/8] fixed braces and early returns
---
mlir/lib/Interfaces/SideEffectInterfaces.cpp | 97 +++++++++-----------
1 file changed, 43 insertions(+), 54 deletions(-)
diff --git a/mlir/lib/Interfaces/SideEffectInterfaces.cpp b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
index de0eb58c8fedb..eea8b418ef5d8 100644
--- a/mlir/lib/Interfaces/SideEffectInterfaces.cpp
+++ b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
@@ -339,14 +339,14 @@ bool mlir::isMemoryEffectMovable(Operation *op) {
bool mlir::isMemoryEffectFree(Operation *op) {
if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
- if (!memInterface.hasNoEffect()) {
+ if (!memInterface.hasNoEffect())
return false;
- }
+
// If the op does not have recursive side effects, then it is memory effect
// free.
- if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
+ if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>())
return true;
- }
+
} else if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
// Otherwise, if the op does not implement the memory effect interface and
// it does not have recursive side effects, then it cannot be known that the
@@ -356,61 +356,55 @@ bool mlir::isMemoryEffectFree(Operation *op) {
// Recurse into the regions and ensure that all nested ops are memory effect
// free.
- for (Region ®ion : op->getRegions()) {
- for (Operation &op : region.getOps()) {
- if (!isMemoryEffectFree(&op)) {
+ for (Region ®ion : op->getRegions())
+ for (Operation &op : region.getOps())
+ if (!isMemoryEffectFree(&op))
return false;
- }
- }
- }
+
return true;
}
bool mlir::isMemoryInitMovable(Operation *op) {
- if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
- // gather all effects on op
- llvm::SmallVector<MemoryEffects::EffectInstance> effects;
- memInterface.getEffects(effects);
+ auto memInterface = dyn_cast<MemoryEffectOpInterface>(op);
+ // op does not implement the memory effect op interface
+ // meaning it doesn't have any memory init effects and
+ // shouldn't be flagged as movable to be conservative
+ if (!memInterface) return false;
- // op has interface but no effects, be conservative
- if (effects.empty()) {
- return false;
- }
+ // gather all effects on op
+ llvm::SmallVector<MemoryEffects::EffectInstance> effects;
+ memInterface.getEffects(effects);
- std::unordered_map<std::string, int> resources;
+ // op has interface but no effects, be conservative
+ if (effects.empty()) return false;
- // ensure op only has Init effects and gather unique
- // resource names
- for (const MemoryEffects::EffectInstance &effect : effects) {
- if (!isa<MemoryEffects::Init>(effect.getEffect())) {
- return false;
- }
- std::string name = effect.getResource()->getName().str();
- resources.try_emplace(name, 0);
- }
+ std::unordered_map<std::string, int> resources;
- // op itself is good, need to check rest of its parent region
- Operation *parent = op->getParentOp();
+ // ensure op only has Init effects and gather unique
+ // resource names
+ for (const MemoryEffects::EffectInstance &effect : effects) {
+ if (!isa<MemoryEffects::Init>(effect.getEffect()))
+ return false;
- for (Region ®ion : parent->getRegions()) {
- for (Operation &op_i : region.getOps()) {
- if (hasMemoryEffectInitConflict(&op_i, resources)) {
- return false;
- }
- }
- }
- return true;
+ std::string name = effect.getResource()->getName().str();
+ resources.try_emplace(name, 0);
}
- // op does not implement the memory effect op interface
- // meaning it doesn't have any memory init effects and
- // shouldn't be flagged as movable to be conservative
- return false;
+ // op itself is good, need to check rest of its parent region
+ Operation *parent = op->getParentOp();
+
+ for (Region ®ion : parent->getRegions())
+ for (Operation &op_i : region.getOps())
+ if (hasMemoryEffectInitConflict(&op_i, resources))
+ return false;
+
+ return true;
}
bool mlir::hasMemoryEffectInitConflict(
Operation *op, std::unordered_map<std::string, int> &resources) {
+
if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
if (!memInterface.hasNoEffect()) {
llvm::SmallVector<MemoryEffects::EffectInstance> effects;
@@ -419,19 +413,16 @@ bool mlir::hasMemoryEffectInitConflict(
// ensure op only has Init effects and gather unique
// resource names
for (const MemoryEffects::EffectInstance &effect : effects) {
- if (!isa<MemoryEffects::Init>(effect.getEffect())) {
+ if (!isa<MemoryEffects::Init>(effect.getEffect()))
return true;
- }
// only care about resources of the op that called
// this recursive function for the first time
std::string name = effect.getResource()->getName().str();
- if (resources.find(name) != resources.end()) {
- if (++resources[name] > 1) {
+ if (resources.find(name) != resources.end())
+ if (++resources[name] > 1)
return true;
- }
- }
}
return false;
}
@@ -439,13 +430,11 @@ bool mlir::hasMemoryEffectInitConflict(
// Recurse into the regions and ensure that nested ops don't
// conflict with each others MemInits
- for (Region ®ion : op->getRegions()) {
- for (Operation &op : region.getOps()) {
- if (hasMemoryEffectInitConflict(&op, resources)) {
+ for (Region ®ion : op->getRegions())
+ for (Operation &op : region.getOps())
+ if (hasMemoryEffectInitConflict(&op, resources))
return true;
- }
- }
- }
+
return false;
}
>From 872d60e786dc35615cd5e0182bde37fa85e4e7fa Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Wed, 13 Aug 2025 19:38:18 +0000
Subject: [PATCH 3/8] switched to DenseMap
---
.../mlir/Interfaces/SideEffectInterfaces.h | 2 +-
mlir/lib/Interfaces/SideEffectInterfaces.cpp | 17 ++++++++---------
2 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
index f534a35d4bd8b..8f49812016ac6 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
@@ -474,7 +474,7 @@ bool isMemoryInitMovable(Operation *op);
///
/// If no resources are in conflict, the op is movable.
bool hasMemoryEffectInitConflict(
- Operation *op, std::unordered_map<std::string, int> &resources);
+ Operation *op, DenseMap<TypeID, int> &resourceCounts);
/// Returns the side effects of an operation. If the operation has
/// RecursiveMemoryEffects, include all side effects of child operations.
diff --git a/mlir/lib/Interfaces/SideEffectInterfaces.cpp b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
index eea8b418ef5d8..2b88d286fcce2 100644
--- a/mlir/lib/Interfaces/SideEffectInterfaces.cpp
+++ b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
@@ -379,7 +379,7 @@ bool mlir::isMemoryInitMovable(Operation *op) {
if (effects.empty()) return false;
- std::unordered_map<std::string, int> resources;
+ DenseMap<TypeID, int> resourceCounts;
// ensure op only has Init effects and gather unique
// resource names
@@ -387,8 +387,7 @@ bool mlir::isMemoryInitMovable(Operation *op) {
if (!isa<MemoryEffects::Init>(effect.getEffect()))
return false;
- std::string name = effect.getResource()->getName().str();
- resources.try_emplace(name, 0);
+ resourceCounts.try_emplace(effect.getResource()->getResourceID(), 0);
}
// op itself is good, need to check rest of its parent region
@@ -396,14 +395,14 @@ bool mlir::isMemoryInitMovable(Operation *op) {
for (Region ®ion : parent->getRegions())
for (Operation &op_i : region.getOps())
- if (hasMemoryEffectInitConflict(&op_i, resources))
+ if (hasMemoryEffectInitConflict(&op_i, resourceCounts))
return false;
return true;
}
bool mlir::hasMemoryEffectInitConflict(
- Operation *op, std::unordered_map<std::string, int> &resources) {
+ Operation *op, DenseMap<TypeID, int> &resourceCounts) {
if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
if (!memInterface.hasNoEffect()) {
@@ -418,10 +417,10 @@ bool mlir::hasMemoryEffectInitConflict(
// only care about resources of the op that called
// this recursive function for the first time
- std::string name = effect.getResource()->getName().str();
+ auto resourceID = effect.getResource()->getResourceID();
- if (resources.find(name) != resources.end())
- if (++resources[name] > 1)
+ if (resourceCounts.contains(resourceID))
+ if (++resourceCounts[resourceID] > 1)
return true;
}
return false;
@@ -432,7 +431,7 @@ bool mlir::hasMemoryEffectInitConflict(
// conflict with each others MemInits
for (Region ®ion : op->getRegions())
for (Operation &op : region.getOps())
- if (hasMemoryEffectInitConflict(&op, resources))
+ if (hasMemoryEffectInitConflict(&op, resourceCounts))
return true;
return false;
>From 0c23f0ccb2e4af55db74d12559c837afb86a767a Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Wed, 13 Aug 2025 20:10:49 +0000
Subject: [PATCH 4/8] reordered shouldMoveOutofRegion condition checks for LICM
---
mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp b/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp
index 06fabcf8a2ad5..dc3baba865bf1 100644
--- a/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp
+++ b/mlir/lib/Transforms/Utils/LoopInvariantCodeMotionUtils.cpp
@@ -110,7 +110,7 @@ size_t mlir::moveLoopInvariantCode(LoopLikeOpInterface loopLike) {
return loopLike.isDefinedOutsideOfLoop(value);
},
[&](Operation *op, Region *) {
- return isMemoryEffectMovable(op) && isSpeculatable(op);
+ return isSpeculatable(op) && isMemoryEffectMovable(op);
},
[&](Operation *op, Region *) { loopLike.moveOutOfLoop(op); });
}
>From 8fb4b974fbac5b1176ded32ee8968c36f445d354 Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Tue, 26 Aug 2025 00:47:51 +0000
Subject: [PATCH 5/8] removed memInit and refactored LICM
---
.../mlir/Interfaces/SideEffectInterfaces.h | 57 +++--
.../mlir/Interfaces/SideEffectInterfaces.td | 11 -
mlir/lib/Interfaces/SideEffectInterfaces.cpp | 95 ++++----
.../loop-invariant-code-motion.mlir | 204 ++++++++++++++++++
mlir/test/lib/Dialect/Test/TestOps.h | 23 ++
mlir/test/lib/Dialect/Test/TestOps.td | 54 +++++
6 files changed, 353 insertions(+), 91 deletions(-)
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
index 8f49812016ac6..ca48d23574feb 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
@@ -15,6 +15,7 @@
#define MLIR_INTERFACES_SIDEEFFECTINTERFACES_H
#include "mlir/IR/OpDefinition.h"
+#include "mlir/IR/Dominance.h"
namespace mlir {
namespace SideEffects {
@@ -378,12 +379,6 @@ struct Read : public Effect::Base<Read> {};
/// dereference or read.
struct Write : public Effect::Base<Write> {};
-// The following effect indicates that the operation initializes some
-// memory resource to a known value i.e., an idempotent MemWrite.
-// An 'init' effect implies only mutating a resource in a way that's
-// identical across calls if inputs are the same, and not any visible
-// dereference or read.
-struct Init : public Effect::Base<Init> {};
} // namespace MemoryEffects
//===----------------------------------------------------------------------===//
@@ -428,13 +423,15 @@ bool isOpTriviallyDead(Operation *op);
/// Note: Terminators and symbols are never considered to be trivially dead.
bool wouldOpBeTriviallyDead(Operation *op);
-/// Returns true if the given operation is movable under memory effects.
+/// Returns true if the given operation is allowed to be moved under the
+/// memory effects interface.
///
-/// An operation is movable if any of the following are true:
-/// (1) isMemoryEffectFree(op) --> true
-/// (2) isMemoryInitMovable(op) --> true
+/// An operation is movable if either case is true:
+/// (a) free of memory effects as defined in isMemoryEffectFree()
+/// (b) if the operation does have memory effects, it must be conflict-free
+/// as defined in isMemoryEffectConflictFree()
///
-/// If the operation meets either criteria, then it is movable
+/// If the operation meets either criteria, then it is movable under memory effects
bool isMemoryEffectMovable(Operation *op);
/// Returns true if the given operation is free of memory effects.
@@ -449,32 +446,32 @@ bool isMemoryEffectMovable(Operation *op);
/// conditions are satisfied.
bool isMemoryEffectFree(Operation *op);
-/// Returns true if the given operation has a collision-free 'Init' memory
-/// effect.
+/// Returns true if the given operation has conflict-free write effects
///
-/// An operation is movable if:
-/// (1) it has memory effects AND all of its memory effects are of type 'Init'
-/// (2) there are no other ops with memory effects on any ofthose same resources
-/// within the operation's region(s)
+/// An operation is conflict free:
+/// (1) all of its memory effects are of type Write
+/// (2) there are no other ops with Alloc/Free/Write effects on the same
+/// resources within the ops parent region
+/// (3) all ops in the parent region with Read effects on the same resources
+/// are dominated by the operation
///
-/// If the operation meets both criteria, then it is movable
-bool isMemoryInitMovable(Operation *op);
+/// If the operation meets all 3 criteria, then it is conflict free
+bool isMemoryEffectConflictFree(Operation *op);
-/// Returns true if op and all operations within its nested regions
-/// have >1 Memory Effects on ANY of the input resources.
+/// Returns true if op and/or any operations within its nested regions
+/// have a memory effect conflict with mainOp as defined below:
///
-/// The first call to this function is by an op with >=1 MemInit effect on
-/// >=1 unique resources. To check that none of these resources are in conflict
-/// with other Memory Effects, we scan the entire parent region and maintain
-/// a count of Memory Effects that apply to the resources of the original op.
-/// If any resource has more than 1 Memory Effect in that region, the resource
-/// is in conflict and the op can't be moved by LICM.
+/// op has a memory effect conflict with mainOp if op and/or any of
+/// the operations in its nested regions meet any of these criteria:
+/// (a) they have any Alloc/Free/Write effects on the resources used by mainOp
+/// (b) they dominate mainOp and have any read effect on the resources used by mainOp
///
/// Function mutates resources map
///
-/// If no resources are in conflict, the op is movable.
-bool hasMemoryEffectInitConflict(
- Operation *op, DenseMap<TypeID, int> &resourceCounts);
+/// If none of the critera above are met, mainOp and op are conflict free
+bool hasMemoryEffectConflict(
+ Operation *mainOp, Operation *op,
+ mlir::DominanceInfo &dom, DenseMap<TypeID, int> &resourceCounts);
/// Returns the side effects of an operation. If the operation has
/// RecursiveMemoryEffects, include all side effects of child operations.
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
index 37083690bae52..841da64a509bd 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
@@ -87,17 +87,6 @@ def MemWrite : MemWrite<DefaultResource, 0, PartialEffect>;
class MemWriteAt<int stage, EffectRange range = PartialEffect>
: MemWrite<DefaultResource, stage, range>;
-// The following effect indicates that the operation initializes some
-// memory resource to a known value i.e., an idempotent MemWrite.
-// An 'init' effect implies only mutating a resource in a way that's
-// identical across calls if inputs are the same, and not any visible
-// dereference or read.
-class MemInit<Resource resource, int stage = 0,
- EffectRange range = PartialEffect>
- : MemoryEffect<"::mlir::MemoryEffects::Init", resource, stage, range>;
-def MemInit : MemInit<DefaultResource, 0, PartialEffect>;
-class MemInitAt<int stage, EffectRange range = PartialEffect>
- : MemInit<DefaultResource, stage, range>;
//===----------------------------------------------------------------------===//
// Effect Traits
diff --git a/mlir/lib/Interfaces/SideEffectInterfaces.cpp b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
index 2b88d286fcce2..245850014f507 100644
--- a/mlir/lib/Interfaces/SideEffectInterfaces.cpp
+++ b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
@@ -8,6 +8,7 @@
#include "mlir/Interfaces/SideEffectInterfaces.h"
+#include "mlir/IR/Dominance.h"
#include "mlir/IR/SymbolTable.h"
#include "llvm/ADT/SmallPtrSet.h"
#include <unordered_set>
@@ -27,7 +28,7 @@ using namespace mlir;
//===----------------------------------------------------------------------===//
bool MemoryEffects::Effect::classof(const SideEffects::Effect *effect) {
- return isa<Allocate, Free, Read, Write, Init>(effect);
+ return isa<Allocate, Free, Read, Write>(effect);
}
//===----------------------------------------------------------------------===//
@@ -132,7 +133,6 @@ template bool mlir::hasSingleEffect<MemoryEffects::Allocate>(Operation *);
template bool mlir::hasSingleEffect<MemoryEffects::Free>(Operation *);
template bool mlir::hasSingleEffect<MemoryEffects::Read>(Operation *);
template bool mlir::hasSingleEffect<MemoryEffects::Write>(Operation *);
-template bool mlir::hasSingleEffect<MemoryEffects::Init>(Operation *);
template <typename EffectTy>
bool mlir::hasSingleEffect(Operation *op, Value value) {
@@ -162,8 +162,6 @@ template bool mlir::hasSingleEffect<MemoryEffects::Read>(Operation *,
Value value);
template bool mlir::hasSingleEffect<MemoryEffects::Write>(Operation *,
Value value);
-template bool mlir::hasSingleEffect<MemoryEffects::Init>(Operation *,
- Value value);
template <typename ValueTy, typename EffectTy>
bool mlir::hasSingleEffect(Operation *op, ValueTy value) {
@@ -198,9 +196,6 @@ template bool
mlir::hasSingleEffect<OpOperand *, MemoryEffects::Write>(Operation *,
OpOperand *);
template bool
-mlir::hasSingleEffect<OpOperand *, MemoryEffects::Init>(Operation *,
- OpOperand *);
-template bool
mlir::hasSingleEffect<OpResult, MemoryEffects::Allocate>(Operation *, OpResult);
template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Free>(Operation *,
OpResult);
@@ -208,8 +203,6 @@ template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Read>(Operation *,
OpResult);
template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Write>(Operation *,
OpResult);
-template bool mlir::hasSingleEffect<OpResult, MemoryEffects::Init>(Operation *,
- OpResult);
template bool
mlir::hasSingleEffect<BlockArgument, MemoryEffects::Allocate>(Operation *,
BlockArgument);
@@ -222,9 +215,6 @@ mlir::hasSingleEffect<BlockArgument, MemoryEffects::Read>(Operation *,
template bool
mlir::hasSingleEffect<BlockArgument, MemoryEffects::Write>(Operation *,
BlockArgument);
-template bool
-mlir::hasSingleEffect<BlockArgument, MemoryEffects::Init>(Operation *,
- BlockArgument);
template <typename... EffectTys>
bool mlir::hasEffect(Operation *op) {
@@ -241,7 +231,6 @@ template bool mlir::hasEffect<MemoryEffects::Allocate>(Operation *);
template bool mlir::hasEffect<MemoryEffects::Free>(Operation *);
template bool mlir::hasEffect<MemoryEffects::Read>(Operation *);
template bool mlir::hasEffect<MemoryEffects::Write>(Operation *);
-template bool mlir::hasEffect<MemoryEffects::Init>(Operation *);
template bool
mlir::hasEffect<MemoryEffects::Write, MemoryEffects::Free>(Operation *);
@@ -263,7 +252,6 @@ template bool mlir::hasEffect<MemoryEffects::Allocate>(Operation *,
template bool mlir::hasEffect<MemoryEffects::Free>(Operation *, Value value);
template bool mlir::hasEffect<MemoryEffects::Read>(Operation *, Value value);
template bool mlir::hasEffect<MemoryEffects::Write>(Operation *, Value value);
-template bool mlir::hasEffect<MemoryEffects::Init>(Operation *, Value value);
template bool
mlir::hasEffect<MemoryEffects::Write, MemoryEffects::Free>(Operation *,
Value value);
@@ -289,8 +277,6 @@ template bool mlir::hasEffect<OpOperand *, MemoryEffects::Read>(Operation *,
OpOperand *);
template bool mlir::hasEffect<OpOperand *, MemoryEffects::Write>(Operation *,
OpOperand *);
-template bool mlir::hasEffect<OpOperand *, MemoryEffects::Init>(Operation *,
- OpOperand *);
template bool
mlir::hasEffect<OpOperand *, MemoryEffects::Write, MemoryEffects::Free>(
Operation *, OpOperand *);
@@ -303,8 +289,6 @@ template bool mlir::hasEffect<OpResult, MemoryEffects::Read>(Operation *,
OpResult);
template bool mlir::hasEffect<OpResult, MemoryEffects::Write>(Operation *,
OpResult);
-template bool mlir::hasEffect<OpResult, MemoryEffects::Init>(Operation *,
- OpResult);
template bool
mlir::hasEffect<OpResult, MemoryEffects::Write, MemoryEffects::Free>(
Operation *, OpResult);
@@ -320,8 +304,6 @@ template bool
mlir::hasEffect<BlockArgument, MemoryEffects::Write>(Operation *,
BlockArgument);
template bool
-mlir::hasEffect<BlockArgument, MemoryEffects::Init>(Operation *, BlockArgument);
-template bool
mlir::hasEffect<BlockArgument, MemoryEffects::Write, MemoryEffects::Free>(
Operation *, BlockArgument);
@@ -334,7 +316,13 @@ bool mlir::wouldOpBeTriviallyDead(Operation *op) {
}
bool mlir::isMemoryEffectMovable(Operation *op) {
- return (isMemoryEffectFree(op) || isMemoryInitMovable(op));
+ if (isMemoryEffectFree(op))
+ return true;
+
+ if (isMemoryEffectConflictFree(op))
+ return true;
+
+ return false;
}
bool mlir::isMemoryEffectFree(Operation *op) {
@@ -358,16 +346,15 @@ bool mlir::isMemoryEffectFree(Operation *op) {
// free.
for (Region ®ion : op->getRegions())
for (Operation &op : region.getOps())
- if (!isMemoryEffectFree(&op))
+ if (!isMemoryEffectMovable(&op))
return false;
return true;
}
-bool mlir::isMemoryInitMovable(Operation *op) {
+bool mlir::isMemoryEffectConflictFree(Operation *op) {
auto memInterface = dyn_cast<MemoryEffectOpInterface>(op);
// op does not implement the memory effect op interface
- // meaning it doesn't have any memory init effects and
// shouldn't be flagged as movable to be conservative
if (!memInterface) return false;
@@ -378,13 +365,12 @@ bool mlir::isMemoryInitMovable(Operation *op) {
// op has interface but no effects, be conservative
if (effects.empty()) return false;
-
DenseMap<TypeID, int> resourceCounts;
- // ensure op only has Init effects and gather unique
+ // ensure op only has Write effects and gather unique
// resource names
for (const MemoryEffects::EffectInstance &effect : effects) {
- if (!isa<MemoryEffects::Init>(effect.getEffect()))
+ if (!isa<MemoryEffects::Write>(effect.getEffect()))
return false;
resourceCounts.try_emplace(effect.getResource()->getResourceID(), 0);
@@ -392,46 +378,55 @@ bool mlir::isMemoryInitMovable(Operation *op) {
// op itself is good, need to check rest of its parent region
Operation *parent = op->getParentOp();
+ mlir::DominanceInfo dom(parent);
for (Region ®ion : parent->getRegions())
- for (Operation &op_i : region.getOps())
- if (hasMemoryEffectInitConflict(&op_i, resourceCounts))
+ for (Operation &opI : region.getOps())
+ if (hasMemoryEffectConflict(op, &opI, dom, resourceCounts))
return false;
return true;
}
-bool mlir::hasMemoryEffectInitConflict(
- Operation *op, DenseMap<TypeID, int> &resourceCounts) {
+bool mlir::hasMemoryEffectConflict(
+ Operation *mainOp, Operation *op,
+ mlir::DominanceInfo &dom, DenseMap<TypeID, int> &resourceCounts) {
if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
- if (!memInterface.hasNoEffect()) {
- llvm::SmallVector<MemoryEffects::EffectInstance> effects;
- memInterface.getEffects(effects);
-
- // ensure op only has Init effects and gather unique
- // resource names
- for (const MemoryEffects::EffectInstance &effect : effects) {
- if (!isa<MemoryEffects::Init>(effect.getEffect()))
- return true;
+
+ llvm::SmallVector<MemoryEffects::EffectInstance> effects;
+ memInterface.getEffects(effects);
+
+ auto isDominated = dom.properlyDominates(mainOp, op);
- // only care about resources of the op that called
- // this recursive function for the first time
- auto resourceID = effect.getResource()->getResourceID();
+ // ensure op only has Write or dominated Read effects
+ // check used resources
+ for (const MemoryEffects::EffectInstance &effect : effects) {
+ auto resourceID = effect.getResource()->getResourceID();
- if (resourceCounts.contains(resourceID))
- if (++resourceCounts[resourceID] > 1)
- return true;
+ if (resourceCounts.contains(resourceID)) {
+ if (isa<MemoryEffects::Read>(effect.getEffect())) {
+ if (isDominated) {
+ continue; // skip dominated reads
+ }
+ }
+ else if (!isa<MemoryEffects::Write>(effect.getEffect())) {
+ return true; // count alloc/free in same region as conflict, be conservative
+ }
+
+ // update write counts, should always be <=1 per resource in region
+ if (++resourceCounts[resourceID] > 1) {
+ return true;
+ }
}
- return false;
}
}
// Recurse into the regions and ensure that nested ops don't
- // conflict with each others MemInits
+ // conflict with each other's MemWrites
for (Region ®ion : op->getRegions())
- for (Operation &op : region.getOps())
- if (hasMemoryEffectInitConflict(&op, resourceCounts))
+ for (Operation &opI : region.getOps())
+ if (hasMemoryEffectConflict(mainOp, &opI, dom, resourceCounts))
return true;
return false;
diff --git a/mlir/test/Transforms/loop-invariant-code-motion.mlir b/mlir/test/Transforms/loop-invariant-code-motion.mlir
index c1604e226a334..50882f91ea86e 100644
--- a/mlir/test/Transforms/loop-invariant-code-motion.mlir
+++ b/mlir/test/Transforms/loop-invariant-code-motion.mlir
@@ -1437,3 +1437,207 @@ func.func @do_not_hoist_vector_transfer_ops_memref(
}
func.return %final : vector<4x4xf32>
}
+
+// -----
+
+// CHECK-LABEL func.func @move_single_resource_basic
+func.func @move_single_resource_basic() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c0_i32_0 = arith.constant 0 : i32
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg1 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg2 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_A"() : () -> ()
+ }
+ }
+ }
+ return
+}
+
+// -----
+
+// CHECK-LABEL func.func @move_single_resource_write_dominant
+func.func @move_single_resource_write_dominant() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c0_i32_0 = arith.constant 0 : i32
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg1 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg2 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_A"() : () -> ()
+ // CHECK: "test.test_effects_read_A"() : () -> ()
+ "test.test_effects_read_A"() : () -> ()
+ }
+ }
+ }
+ return
+}
+
+// -----
+
+// CHECK-LABEL func.func @move_single_resource_read_dominant_negative
+func.func @move_single_resource_read_dominant_negative() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c0_i32_0 = arith.constant 0 : i32
+
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg1 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg2 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ // CHECK: "test.test_effects_read_A"() : () -> ()
+ "test.test_effects_read_A"() : () -> ()
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ "test.test_effects_write_A"() : () -> ()
+ }
+ }
+ }
+ return
+}
+
+// -----
+
+// CHECK-LABEL func.func @move_single_resource_basic_conflict
+func.func @move_single_resource_basic_conflict() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c0_i32_0 = arith.constant 0 : i32
+ %c0_i32_1 = arith.constant 0 : i32
+ %c0_i32_2 = arith.constant 0 : i32
+
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg1 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg2 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ "test.test_effects_write_A"() : () -> ()
+ // CHECK: "test.test_effects_read_A"() : () -> ()
+ "test.test_effects_read_A"() : () -> ()
+ // CHECK: "test.test_effects_write_AC"() : () -> ()
+ "test.test_effects_write_AC"() : () -> ()
+ // CHECK: "test.test_effects_read_AC"() : () -> ()
+ "test.test_effects_read_AC"() : () -> ()
+ }
+ }
+ }
+ return
+}
+
+// -----
+
+// CHECK-LABEL func.func @move_single_resource_if_region_negative
+func.func @move_single_resource_if_region_negative() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c3_i32 = arith.constant 5 : i32
+
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg1 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg2 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ %1 = arith.cmpi slt, %arg0, %c3_i32 : i32
+
+ scf.if %1 {
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ "test.test_effects_write_A"() : () -> ()
+ // CHECK: "test.test_effects_read_A"() : () -> ()
+ "test.test_effects_read_A"() : () -> ()
+ }
+ }
+ }
+ }
+ return
+}
+
+// -----
+
+// CHECK-LABEL func.func @move_single_resource_for_inside_if_region
+func.func @move_single_resource_for_inside_if_region() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c3_i32 = arith.constant 5 : i32
+
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ %1 = arith.cmpi slt, %arg0, %c3_i32 : i32
+
+ scf.if %1 {
+ %c0_i32_0 = arith.constant 0 : i32
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ scf.for %arg1 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg2 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_A"() : () -> ()
+ }
+ }
+ }
+ }
+ return
+}
+
+// -----
+
+// CHECK-LABEL func.func @move_multi_resource_comprehensive
+func.func @move_multi_resource_comprehensive() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c0_i32_2 = arith.constant 0 : i32
+ %c3_i32 = arith.constant 5 : i32
+
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ // CHECK: "test.test_effects_write_CD"() : () -> ()
+ // CHECK: "test.test_effects_write_EF"() : () -> ()
+ scf.for %arg1 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ scf.for %arg2 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_CD"() : () -> ()
+ // CHECK: "test.test_effects_read_CD"() : () -> ()
+ "test.test_effects_read_CD"() : () -> ()
+
+ "test.test_effects_write_EF"() : () -> ()
+ // CHECK: "test.test_effects_read_EF"() : () -> ()
+ "test.test_effects_read_EF"() : () -> ()
+ }
+ }
+
+ %1 = arith.cmpi slt, %arg0, %c3_i32 : i32
+ scf.if %1 {
+ %c0_i32_0 = arith.constant 0 : i32
+ %c0_i32_1 = arith.constant 0 : i32
+
+ // CHECK: "test.test_effects_write_B"() : () -> ()
+ scf.for %arg3 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ scf.for %arg4 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_A"() : () -> ()
+ // CHECK: "test.test_effects_read_A"() : () -> ()
+ "test.test_effects_read_A"() : () -> ()
+ }
+
+ scf.for %arg5 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_B"() : () -> ()
+ // CHECK: "test.test_effects_read_B"() : () -> ()
+ "test.test_effects_read_B"() : () -> ()
+ }
+
+ // CHECK: "test.test_effects_write_AC"() : () -> ()
+ scf.for %arg6 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_AC"() : () -> ()
+ // CHECK: "test.test_effects_read_AC"() : () -> ()
+ "test.test_effects_read_AC"() : () -> ()
+ }
+ }
+ }
+ else {
+ // CHECK: "test.test_effects_write_F"() : () -> ()
+ "test.test_effects_write_F"() : () -> ()
+ // CHECK: "test.test_effects_read_F"() : () -> ()
+ "test.test_effects_read_F"() : () -> ()
+ }
+ }
+ return
+}
diff --git a/mlir/test/lib/Dialect/Test/TestOps.h b/mlir/test/lib/Dialect/Test/TestOps.h
index b414b47c87425..ab803663d7847 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.h
+++ b/mlir/test/lib/Dialect/Test/TestOps.h
@@ -56,6 +56,29 @@ struct TestResource : public mlir::SideEffects::Resource::Base<TestResource> {
llvm::StringRef getName() final { return "<Test>"; }
};
+struct TestResourceA : public mlir::SideEffects::Resource::Base<TestResourceA> {
+ llvm::StringRef getName() final { return "<TestA>"; }
+};
+
+struct TestResourceB : public mlir::SideEffects::Resource::Base<TestResourceB> {
+ llvm::StringRef getName() final { return "<TestB>"; }
+};
+
+struct TestResourceC : public mlir::SideEffects::Resource::Base<TestResourceC> {
+ llvm::StringRef getName() final { return "<TestC>"; }
+};
+
+struct TestResourceD : public mlir::SideEffects::Resource::Base<TestResourceD> {
+ llvm::StringRef getName() final { return "<TestD>"; }
+};
+
+struct TestResourceE : public mlir::SideEffects::Resource::Base<TestResourceE> {
+ llvm::StringRef getName() final { return "<TestE>"; }
+};
+
+struct TestResourceF : public mlir::SideEffects::Resource::Base<TestResourceF> {
+ llvm::StringRef getName() final { return "<TestF>"; }
+};
//===----------------------------------------------------------------------===//
// PropertiesWithCustomPrint
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td
index 231400ec9cd29..550678e9e91d1 100644
--- a/mlir/test/lib/Dialect/Test/TestOps.td
+++ b/mlir/test/lib/Dialect/Test/TestOps.td
@@ -2977,6 +2977,60 @@ def TestEffectsResult : TEST_Op<"test_effects_result"> {
let results = (outs Res<I32, "", [MemAlloc, MemWrite]>);
}
+//===----------------------------------------------------------------------===//
+// Test Ops with multiple effects for Loop Invariant Code Motion
+//===----------------------------------------------------------------------===//
+
+def TestResourceA : Resource<"TestResourceA">;
+def TestResourceB : Resource<"TestResourceB">;
+def TestResourceC : Resource<"TestResourceC">;
+def TestResourceD : Resource<"TestResourceD">;
+def TestResourceE : Resource<"TestResourceE">;
+def TestResourceF : Resource<"TestResourceF">;
+
+def TestEffectsWriteA : TEST_Op<"test_effects_write_A",
+ [MemoryEffects<[MemWrite<TestResourceA>]>,
+ AlwaysSpeculatable]>;
+
+def TestEffectsReadA : TEST_Op<"test_effects_read_A",
+ [MemoryEffects<[MemRead<TestResourceA>]>]>;
+
+def TestEffectsWriteB : TEST_Op<"test_effects_write_B",
+ [MemoryEffects<[MemWrite<TestResourceB>]>,
+ AlwaysSpeculatable]>;
+
+def TestEffectsReadB : TEST_Op<"test_effects_read_B",
+ [MemoryEffects<[MemRead<TestResourceB>]>]>;
+
+def TestEffectsWriteF : TEST_Op<"test_effects_write_F",
+ [MemoryEffects<[MemWrite<TestResourceF>]>,
+ AlwaysSpeculatable]>;
+
+def TestEffectsReadF : TEST_Op<"test_effects_read_F",
+ [MemoryEffects<[MemRead<TestResourceF>]>]>;
+
+def TestEffectsWriteAC : TEST_Op<"test_effects_write_AC",
+ [MemoryEffects<[MemWrite<TestResourceA>, MemWrite<TestResourceC>]>,
+ AlwaysSpeculatable]>;
+
+def TestEffectsReadAC : TEST_Op<"test_effects_read_AC",
+ [MemoryEffects<[MemRead<TestResourceA>, MemRead<TestResourceC>]>]>;
+
+def TestEffectsWriteCD : TEST_Op<"test_effects_write_CD",
+ [MemoryEffects<[MemWrite<TestResourceC>, MemWrite<TestResourceD>]>,
+ AlwaysSpeculatable]>;
+
+def TestEffectsReadCD : TEST_Op<"test_effects_read_CD",
+ [MemoryEffects<[MemRead<TestResourceC>, MemRead<TestResourceD>]>]>;
+
+def TestEffectsWriteEF : TEST_Op<"test_effects_write_EF",
+ [MemoryEffects<[MemWrite<TestResourceE>, MemWrite<TestResourceF>]>,
+ AlwaysSpeculatable]>;
+
+def TestEffectsReadEF : TEST_Op<"test_effects_read_EF",
+ [MemoryEffects<[MemRead<TestResourceE>, MemRead<TestResourceF>]>]>;
+
+
//===----------------------------------------------------------------------===//
// Test Ops with verifiers
//===----------------------------------------------------------------------===//
>From 8bad9d4a6bee2d61b06d0810da4d2b9bfb1454f6 Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Tue, 26 Aug 2025 01:44:03 +0000
Subject: [PATCH 6/8] typo fix
---
mlir/lib/Interfaces/SideEffectInterfaces.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/lib/Interfaces/SideEffectInterfaces.cpp b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
index 245850014f507..e66c6480872dd 100644
--- a/mlir/lib/Interfaces/SideEffectInterfaces.cpp
+++ b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
@@ -346,7 +346,7 @@ bool mlir::isMemoryEffectFree(Operation *op) {
// free.
for (Region ®ion : op->getRegions())
for (Operation &op : region.getOps())
- if (!isMemoryEffectMovable(&op))
+ if (!isMemoryEffectFree(&op))
return false;
return true;
>From ccb7f415a5b83bc4b74111e5e3d483bb462e3e3e Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Tue, 26 Aug 2025 20:39:38 +0000
Subject: [PATCH 7/8] LICM now checks if parent loop has constant bounds/steps
and isn't zero trip
---
.../mlir/Interfaces/SideEffectInterfaces.h | 26 +++++++---
mlir/lib/Interfaces/CMakeLists.txt | 16 +++++-
mlir/lib/Interfaces/SideEffectInterfaces.cpp | 52 +++++++++++++++++--
.../loop-invariant-code-motion.mlir | 41 +++++++++++++--
4 files changed, 119 insertions(+), 16 deletions(-)
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
index ca48d23574feb..7c6ce6c8a51ee 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
@@ -14,8 +14,9 @@
#ifndef MLIR_INTERFACES_SIDEEFFECTINTERFACES_H
#define MLIR_INTERFACES_SIDEEFFECTINTERFACES_H
-#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/Dominance.h"
+#include "mlir/IR/OpDefinition.h"
+#include "mlir/Interfaces/LoopLikeInterface.h"
namespace mlir {
namespace SideEffects {
@@ -423,6 +424,15 @@ bool isOpTriviallyDead(Operation *op);
/// Note: Terminators and symbols are never considered to be trivially dead.
bool wouldOpBeTriviallyDead(Operation *op);
+/// Returns TRUE if the loop is dead/zero-trip,
+/// FALSE if loop has constant bounds/steps and has at least 1 iteration
+/// on every dimension, returns nullopt otherwise
+///
+/// Can only infer if loop is dead if it has constant loop bounds/steps.
+/// Otherwise we assume that it's dead to be conservative.
+///
+std::optional<bool> isZeroTrip(mlir::LoopLikeOpInterface loop);
+
/// Returns true if the given operation is allowed to be moved under the
/// memory effects interface.
///
@@ -449,13 +459,15 @@ bool isMemoryEffectFree(Operation *op);
/// Returns true if the given operation has conflict-free write effects
///
/// An operation is conflict free:
-/// (1) all of its memory effects are of type Write
-/// (2) there are no other ops with Alloc/Free/Write effects on the same
-/// resources within the ops parent region
-/// (3) all ops in the parent region with Read effects on the same resources
-/// are dominated by the operation
+/// (1) Parent is a loop with the LoopLikeOpInterface
+/// (2) Parent loop is not a zero trip loop and has constant bounds/steps
+/// (3) all of the op's memory effects are of type Write
+/// (4) there are no other ops with Alloc/Free/Write effects on the same
+/// resources within the op's parent loop region
+/// (5) all ops in the parent loop region with Read effects on the same
+/// resources are dominated by the operation
///
-/// If the operation meets all 3 criteria, then it is conflict free
+/// If the operation meets all criteria, then it is conflict free
bool isMemoryEffectConflictFree(Operation *op);
/// Returns true if op and/or any operations within its nested regions
diff --git a/mlir/lib/Interfaces/CMakeLists.txt b/mlir/lib/Interfaces/CMakeLists.txt
index af923d98c76ff..ea699922dc9ec 100644
--- a/mlir/lib/Interfaces/CMakeLists.txt
+++ b/mlir/lib/Interfaces/CMakeLists.txt
@@ -85,7 +85,21 @@ add_mlir_interface_library(MemorySlotInterfaces)
add_mlir_interface_library(ParallelCombiningOpInterface)
add_mlir_interface_library(RuntimeVerifiableOpInterface)
add_mlir_interface_library(ShapedOpInterfaces)
-add_mlir_interface_library(SideEffectInterfaces)
+
+add_mlir_library(MLIRSideEffectInterfaces
+ SideEffectInterfaces.cpp
+
+ ADDITIONAL_HEADER_DIRS
+ ${MLIR_MAIN_INCLUDE_DIR}/mlir/Interfaces
+
+ DEPENDS
+ MLIRSideEffectInterfacesIncGen
+
+ LINK_LIBS PUBLIC
+ MLIRIR
+ MLIRDialectUtils
+ MLIRLoopLikeInterface
+)
add_mlir_library(MLIRSubsetOpInterface
SubsetOpInterface.cpp
diff --git a/mlir/lib/Interfaces/SideEffectInterfaces.cpp b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
index e66c6480872dd..da926b66adf5f 100644
--- a/mlir/lib/Interfaces/SideEffectInterfaces.cpp
+++ b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
@@ -8,10 +8,9 @@
#include "mlir/Interfaces/SideEffectInterfaces.h"
+#include "mlir/Dialect/Utils/StaticValueUtils.h"
#include "mlir/IR/Dominance.h"
#include "mlir/IR/SymbolTable.h"
-#include "llvm/ADT/SmallPtrSet.h"
-#include <unordered_set>
#include <utility>
using namespace mlir;
@@ -23,6 +22,13 @@ using namespace mlir;
/// Include the definitions of the side effect interfaces.
#include "mlir/Interfaces/SideEffectInterfaces.cpp.inc"
+// //===----------------------------------------------------------------------===//
+// // LoopLike Interfaces
+// //===----------------------------------------------------------------------===//
+
+// /// Include the definitions of the loop like interfaces.
+// #include "mlir/Interfaces/LoopLikeInterface.cpp.inc"
+
//===----------------------------------------------------------------------===//
// MemoryEffects
//===----------------------------------------------------------------------===//
@@ -315,6 +321,33 @@ bool mlir::wouldOpBeTriviallyDead(Operation *op) {
return wouldOpBeTriviallyDeadImpl(op);
}
+std::optional<bool> mlir::isZeroTrip(mlir::LoopLikeOpInterface loop) {
+ auto lbs = loop.getLoopLowerBounds();
+ auto ubs = loop.getLoopUpperBounds();
+ auto steps = loop.getLoopSteps();
+
+ if (!lbs || !ubs || !steps)
+ return std::nullopt;
+
+ if (lbs->size() != ubs->size() || ubs->size() != steps->size())
+ return std::nullopt;
+
+ for (size_t i = 0; i < steps->size(); ++i) {
+ auto lb = getConstantIntValue((*lbs)[i]);
+ auto ub = getConstantIntValue((*ubs)[i]);
+ auto st = getConstantIntValue((*steps)[i]);
+
+ if (!lb || !ub || !st)
+ return std::nullopt; // non-constant -> unknown
+
+ if (*st >= 0 && *lb >= *ub)
+ return true;
+ if (*st < 0 && *lb <= *ub)
+ return true;
+ }
+ return false;
+}
+
bool mlir::isMemoryEffectMovable(Operation *op) {
if (isMemoryEffectFree(op))
return true;
@@ -358,6 +391,19 @@ bool mlir::isMemoryEffectConflictFree(Operation *op) {
// shouldn't be flagged as movable to be conservative
if (!memInterface) return false;
+ // check parent loop to make sure it's not dead
+ Operation *parent = op->getParentOp();
+ if (!parent)
+ return false;
+
+ auto loopInterface = dyn_cast<LoopLikeOpInterface>(parent);
+ if (!loopInterface)
+ return false;
+
+ auto isDead = isZeroTrip(loopInterface);
+ if (!isDead.has_value() || isDead.value())
+ return false;
+
// gather all effects on op
llvm::SmallVector<MemoryEffects::EffectInstance> effects;
memInterface.getEffects(effects);
@@ -376,8 +422,6 @@ bool mlir::isMemoryEffectConflictFree(Operation *op) {
resourceCounts.try_emplace(effect.getResource()->getResourceID(), 0);
}
- // op itself is good, need to check rest of its parent region
- Operation *parent = op->getParentOp();
mlir::DominanceInfo dom(parent);
for (Region ®ion : parent->getRegions())
diff --git a/mlir/test/Transforms/loop-invariant-code-motion.mlir b/mlir/test/Transforms/loop-invariant-code-motion.mlir
index 50882f91ea86e..769e8c3a1b9ec 100644
--- a/mlir/test/Transforms/loop-invariant-code-motion.mlir
+++ b/mlir/test/Transforms/loop-invariant-code-motion.mlir
@@ -1480,8 +1480,8 @@ func.func @move_single_resource_write_dominant() attributes {} {
// -----
-// CHECK-LABEL func.func @move_single_resource_read_dominant_negative
-func.func @move_single_resource_read_dominant_negative() attributes {} {
+// CHECK-LABEL func.func @move_single_resource_read_dominant
+func.func @move_single_resource_read_dominant() attributes {} {
%c0_i32 = arith.constant 0 : i32
%c1_i32 = arith.constant 10 : i32
%c2_i32 = arith.constant 1 : i32
@@ -1530,8 +1530,8 @@ func.func @move_single_resource_basic_conflict() attributes {} {
// -----
-// CHECK-LABEL func.func @move_single_resource_if_region_negative
-func.func @move_single_resource_if_region_negative() attributes {} {
+// CHECK-LABEL func.func @move_single_resource_if_region
+func.func @move_single_resource_if_region() attributes {} {
%c0_i32 = arith.constant 0 : i32
%c1_i32 = arith.constant 10 : i32
%c2_i32 = arith.constant 1 : i32
@@ -1641,3 +1641,36 @@ func.func @move_multi_resource_comprehensive() attributes {} {
}
return
}
+
+// -----
+
+// CHECK-LABEL func.func @move_single_resource_dead_loops
+func.func @move_single_resource_dead_loops() attributes {} {
+ %c0_i32 = arith.constant 0 : i32
+ %c1_i32 = arith.constant 10 : i32
+ %c2_i32 = arith.constant 1 : i32
+ %c3_i32 = arith.constant -1 : i32
+
+ scf.for %arg0 = %c0_i32 to %c1_i32 step %c3_i32 : i32 {
+ // CHECK: "test.test_effects_write_C"() : () -> ()
+ "test.test_effects_write_C"() : () -> ()
+
+ scf.for %arg1 = %c1_i32 to %c0_i32 step %c2_i32 : i32 {
+ // CHECK: "test.test_effects_write_B"() : () -> ()
+ "test.test_effects_write_B"() : () -> ()
+
+ scf.for %arg2 = %c0_i32 to %c0_i32 step %c0_i32 : i32 {
+ // CHECK: "test.test_effects_write_A"() : () -> ()
+ "test.test_effects_write_A"() : () -> ()
+
+ // CHECK: "test.test_effects_write_EF"() : () -> ()
+ scf.for %arg3 = %c0_i32 to %c1_i32 step %c2_i32 : i32 {
+ "test.test_effects_write_EF"() : () -> ()
+ // CHECK: "test.test_effects_read_EF"() : () -> ()
+ "test.test_effects_read_EF"() : () -> ()
+ }
+ }
+ }
+ }
+ return
+}
>From f74d2f02c8e2218d9e7a610f2343563552a4c6ce Mon Sep 17 00:00:00 2001
From: Mo Bagherbeik <mbagherbeik at tenstorrent.com>
Date: Tue, 26 Aug 2025 20:58:35 +0000
Subject: [PATCH 8/8] some comments/blanks cleanup
---
mlir/include/mlir/Interfaces/SideEffectInterfaces.h | 1 -
mlir/include/mlir/Interfaces/SideEffectInterfaces.td | 1 -
mlir/lib/Interfaces/SideEffectInterfaces.cpp | 7 -------
3 files changed, 9 deletions(-)
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
index 5f8d1d3e5ac05..5363185f13d38 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.h
@@ -379,7 +379,6 @@ struct Read : public Effect::Base<Read> {};
/// 'write' effect implies only mutating a resource, and not any visible
/// dereference or read.
struct Write : public Effect::Base<Write> {};
-
} // namespace MemoryEffects
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
index 841da64a509bd..b292174fccb36 100644
--- a/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
+++ b/mlir/include/mlir/Interfaces/SideEffectInterfaces.td
@@ -87,7 +87,6 @@ def MemWrite : MemWrite<DefaultResource, 0, PartialEffect>;
class MemWriteAt<int stage, EffectRange range = PartialEffect>
: MemWrite<DefaultResource, stage, range>;
-
//===----------------------------------------------------------------------===//
// Effect Traits
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Interfaces/SideEffectInterfaces.cpp b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
index 0e5f5e87883ad..d9c2fd44fe50f 100644
--- a/mlir/lib/Interfaces/SideEffectInterfaces.cpp
+++ b/mlir/lib/Interfaces/SideEffectInterfaces.cpp
@@ -22,13 +22,6 @@ using namespace mlir;
/// Include the definitions of the side effect interfaces.
#include "mlir/Interfaces/SideEffectInterfaces.cpp.inc"
-// //===----------------------------------------------------------------------===//
-// // LoopLike Interfaces
-// //===----------------------------------------------------------------------===//
-
-// /// Include the definitions of the loop like interfaces.
-// #include "mlir/Interfaces/LoopLikeInterface.cpp.inc"
-
//===----------------------------------------------------------------------===//
// MemoryEffects
//===----------------------------------------------------------------------===//
More information about the Mlir-commits
mailing list