Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
34913fe
Added 'Init' Memory Effect which defines an Idempotent MemWrite effec…
mbagherbeikTT Aug 12, 2025
8097e75
Merge branch 'main' into mbagherbeikTT/mem_init
mbagherbeikTT Aug 12, 2025
78a55f1
Merge branch 'main' into mbagherbeikTT/mem_init
mbagherbeikTT Aug 12, 2025
0438627
fixed braces and early returns
mbagherbeikTT Aug 13, 2025
872d60e
switched to DenseMap
mbagherbeikTT Aug 13, 2025
0c23f0c
reordered shouldMoveOutofRegion condition checks for LICM
mbagherbeikTT Aug 13, 2025
8fb4b97
removed memInit and refactored LICM
mbagherbeikTT Aug 26, 2025
8bad9d4
typo fix
mbagherbeikTT Aug 26, 2025
531c4b3
Merge branch 'main' into mbagherbeik/mlir/LICM_improvements
mbagherbeikTT Aug 26, 2025
ccb7f41
LICM now checks if parent loop has constant bounds/steps and isn't ze…
mbagherbeikTT Aug 26, 2025
6fd7e6e
Merge branch 'mbagherbeik/mlir/LICM_improvements' of github.com:mbagh…
mbagherbeikTT Aug 26, 2025
f74d2f0
some comments/blanks cleanup
mbagherbeikTT Aug 26, 2025
2f3c151
made isZeroDrop pass-by-reference
mbagherbeikTT Aug 26, 2025
efa9550
applying minor fixes from code review
mbagherbeikTT Aug 27, 2025
9463b0c
wip
mbagherbeikTT Sep 2, 2025
a87023b
working version without isZeroTrip
mbagherbeikTT Sep 4, 2025
85d37f3
docstrings
mbagherbeikTT Sep 5, 2025
871ecf4
PR cleanup
mbagherbeikTT Sep 12, 2025
5c0c100
removed LDBGs
mbagherbeikTT Sep 12, 2025
30307b6
fixed formatting
mbagherbeikTT Sep 12, 2025
1dbe5db
Apply suggestions from code review
mbagherbeikTT Sep 13, 2025
6333c00
cleanup + LDBG
mbagherbeikTT Sep 14, 2025
aaa7cc5
clarifying test cases
mbagherbeikTT Sep 14, 2025
d56ccc3
addin isZeroTrip back
mbagherbeikTT Sep 15, 2025
a396617
isZeroTrip() check will only skip loops that are verifiably dead
mbagherbeikTT Sep 16, 2025
4618db9
added outer loop to LICM pass
mbagherbeikTT Sep 16, 2025
2b7803b
Merge branch 'main' into mbagherbeik/mlir/LICM_improvements
mbagherbeikTT Sep 22, 2025
0fcf496
removed AlwaysSpeculatable from memory effect test ops
mbagherbeikTT Sep 22, 2025
e61febe
added map type alias
mbagherbeikTT Sep 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions mlir/include/mlir/Interfaces/SideEffectInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#ifndef MLIR_INTERFACES_SIDEEFFECTINTERFACES_H
#define MLIR_INTERFACES_SIDEEFFECTINTERFACES_H

#include "mlir/IR/Dominance.h"
#include "mlir/IR/OpDefinition.h"

namespace mlir {
Expand Down Expand Up @@ -346,6 +347,20 @@ struct AlwaysSpeculatableImplTrait
//===----------------------------------------------------------------------===//

namespace MemoryEffects {
/// Defines the priority of the different memory effects.
///
/// Sorting/ordering memory effects of an operation is done based on
/// their defined stage and priority, in that order. If stage values for two
/// effect instances are equal, they are then sorted by priority. Lower priority
/// values indicate higher precedence.
enum Priority {
DefaultPriority = 0,
AllocPriority = 1,
FreePriority = 2,
ReadPriority = 3,
WritePriority = 4
};

/// This class represents the base class used for memory effects.
struct Effect : public SideEffects::Effect {
using SideEffects::Effect::Effect;
Expand All @@ -355,28 +370,50 @@ struct Effect : public SideEffects::Effect {
using Base = SideEffects::Effect::Base<DerivedEffect, Effect>;

static bool classof(const SideEffects::Effect *effect);

/// Return the priority associated with this memory effect.
Priority getPriority() const { return priority; }

protected:
/// Priority value for this effect. Lower numbers indicate higher precedence.
Priority priority = Priority::DefaultPriority;
};
using EffectInstance = SideEffects::EffectInstance<Effect>;

/// Returns vector of the op's memory effects sorted in increasing stage order
/// and in increasing priority order within each stage.
llvm::SmallVector<MemoryEffects::EffectInstance>
getMemoryEffectsSorted(Operation *op);

/// The following effect indicates that the operation allocates from some
/// resource. An 'allocate' effect implies only allocation of the resource, and
/// not any visible mutation or dereference.
struct Allocate : public Effect::Base<Allocate> {};
struct Allocate : public Effect::Base<Allocate> {
Allocate() : Effect::Base<Allocate>() {
this->priority = Priority::AllocPriority;
}
};

/// The following effect indicates that the operation frees some resource that
/// has been allocated. An 'allocate' effect implies only de-allocation of the
/// resource, and not any visible allocation, mutation or dereference.
struct Free : public Effect::Base<Free> {};
struct Free : public Effect::Base<Free> {
Free() : Effect::Base<Free>() { this->priority = Priority::FreePriority; }
};

/// The following effect indicates that the operation reads from some resource.
/// A 'read' effect implies only dereferencing of the resource, and not any
/// visible mutation.
struct Read : public Effect::Base<Read> {};
struct Read : public Effect::Base<Read> {
Read() : Effect::Base<Read>() { this->priority = Priority::ReadPriority; }
};

/// The following effect indicates that the operation writes to some resource. A
/// 'write' effect implies only mutating a resource, and not any visible
/// dereference or read.
struct Write : public Effect::Base<Write> {};
struct Write : public Effect::Base<Write> {
Write() : Effect::Base<Write>() { this->priority = Priority::WritePriority; }
};
} // namespace MemoryEffects

//===----------------------------------------------------------------------===//
Expand Down
40 changes: 38 additions & 2 deletions mlir/include/mlir/Transforms/LoopInvariantCodeMotionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
#ifndef MLIR_TRANSFORMS_LOOPINVARIANTCODEMOTIONUTILS_H
#define MLIR_TRANSFORMS_LOOPINVARIANTCODEMOTIONUTILS_H

#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Support/TypeID.h"

#include "llvm/ADT/SmallVector.h"
#include <utility>

namespace mlir {

Expand All @@ -21,6 +23,40 @@ class Region;
class RewriterBase;
class Value;

/// Gathers potential conflicts on all memory resources used within loop.
///
/// Given a target loop and an op within it (or the loop op itself),
/// gathers op's memory effects and flags potential resource conflicts
/// in a map and then recurses into the op's regions to gather nested
/// resource conflicts.
///
/// Typical usage:
/// \code
/// LoopLikeOpInterface myLoop = ...;
/// DenseMap<TypeID, std::pair<bool, MemoryEffects::EffectInstance>>
/// myConflicts; gatherResourceConflicts(myLoop, myLoop.getOperation(),
/// resourceConflicts);
/// \endcode
///
/// \param loop The loop to gather resource conflicts for.
/// \param op The operation to gather resource conflicts for,
/// typically the loop op itself via loop.getOperation().
/// \param resourceConflicts Map to store potential resource conflicts.
/// Key is the resource ID that effects are applied to. Value is a pair of
/// a boolean, indicating if the resource has a conflict, and the last effect
/// that was applied to the resource (if no conflicts exist) or the effect
/// that caused the conflict (if conflicts exist). This was done so we
/// check the effect that causes the conflict for debugging purposes.
/// First call should use loop = someLoop and op = someLoop.getOperation()
///
/// resourceConflicts is modified by the function and will be non-empty
/// as long as there are memory effects within the loop, even if there are
/// no conflicts.
void gatherResourceConflicts(
LoopLikeOpInterface loop, Operation *op,
DenseMap<TypeID, std::pair<bool, MemoryEffects::EffectInstance>>
&resourceConflicts);

/// Given a list of regions, perform loop-invariant code motion. An operation is
/// loop-invariant if it depends only of values defined outside of the loop.
/// LICM moves these operations out of the loop body so that they are not
Expand Down Expand Up @@ -63,7 +99,7 @@ class Value;
///
/// Returns the number of operations moved.
size_t moveLoopInvariantCode(
ArrayRef<Region *> regions,
LoopLikeOpInterface loopLike,
function_ref<bool(Value, Region *)> isDefinedOutsideRegion,
function_ref<bool(Operation *, Region *)> shouldMoveOutOfRegion,
function_ref<void(Operation *, Region *)> moveOutOfRegion);
Expand Down
34 changes: 34 additions & 0 deletions mlir/lib/Interfaces/SideEffectInterfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

#include "mlir/Interfaces/SideEffectInterfaces.h"

#include "mlir/IR/Dominance.h"
#include "mlir/IR/SymbolTable.h"
#include <optional>
#include <utility>

using namespace mlir;
Expand Down Expand Up @@ -317,14 +319,45 @@ bool mlir::wouldOpBeTriviallyDead(Operation *op) {
return wouldOpBeTriviallyDeadImpl(op);
}

llvm::SmallVector<MemoryEffects::EffectInstance>
mlir::MemoryEffects::getMemoryEffectsSorted(Operation *op) {
llvm::SmallVector<MemoryEffects::EffectInstance> effectsSorted;

auto memInterface = dyn_cast<MemoryEffectOpInterface>(op);

if (!memInterface)
return effectsSorted; // return empty vec

memInterface.getEffects(effectsSorted);

auto sortEffects =
[](llvm::SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
llvm::stable_sort(effects, [](const MemoryEffects::EffectInstance &a,
const MemoryEffects::EffectInstance &b) {
if (a.getStage() < b.getStage())
return true;

if (a.getStage() == b.getStage())
return a.getEffect()->getPriority() < b.getEffect()->getPriority();

return false; // b before a
});
};
sortEffects(effectsSorted);

return effectsSorted;
}

bool mlir::isMemoryEffectFree(Operation *op) {
if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
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>())
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
Expand All @@ -338,6 +371,7 @@ bool mlir::isMemoryEffectFree(Operation *op) {
for (Operation &op : region.getOps())
if (!isMemoryEffectFree(&op))
return false;

return true;
}

Expand Down
Loading