Skip to content

Commit 9463b0c

Browse files
committed
wip
1 parent efa9550 commit 9463b0c

File tree

4 files changed

+216
-152
lines changed

4 files changed

+216
-152
lines changed

mlir/include/mlir/Interfaces/SideEffectInterfaces.h

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,14 @@ struct AlwaysSpeculatableImplTrait
348348
//===----------------------------------------------------------------------===//
349349

350350
namespace MemoryEffects {
351+
enum Priority {
352+
kDefault = 0,
353+
kAllocPriority = 1,
354+
kFreePriority = 2,
355+
kReadPriority = 3,
356+
kWritePriority = 4
357+
};
358+
351359
/// This class represents the base class used for memory effects.
352360
struct Effect : public SideEffects::Effect {
353361
using SideEffects::Effect::Effect;
@@ -357,28 +365,48 @@ struct Effect : public SideEffects::Effect {
357365
using Base = SideEffects::Effect::Base<DerivedEffect, Effect>;
358366

359367
static bool classof(const SideEffects::Effect *effect);
368+
369+
/// Return the priority associated with this memory effect.
370+
Priority getPriority() const { return priority; }
371+
372+
protected:
373+
/// Priority value for this effect. Lower numbers indicate higher precedence.
374+
Priority priority = kDefault;
360375
};
361376
using EffectInstance = SideEffects::EffectInstance<Effect>;
362377

378+
/// Returns vector of effects sorted by effect stage then priority
379+
/// priority order: allocate -> free -> read -> write
380+
llvm::SmallVector<MemoryEffects::EffectInstance>
381+
getMemoryEffectsSorted(Operation *op);
382+
363383
/// The following effect indicates that the operation allocates from some
364384
/// resource. An 'allocate' effect implies only allocation of the resource, and
365385
/// not any visible mutation or dereference.
366-
struct Allocate : public Effect::Base<Allocate> {};
386+
struct Allocate : public Effect::Base<Allocate> {
387+
Allocate() : Effect::Base<Allocate>() { this->priority = kAllocPriority; }
388+
};
367389

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

373397
/// The following effect indicates that the operation reads from some resource.
374398
/// A 'read' effect implies only dereferencing of the resource, and not any
375399
/// visible mutation.
376-
struct Read : public Effect::Base<Read> {};
400+
struct Read : public Effect::Base<Read> {
401+
Read() : Effect::Base<Read>() { this->priority = kReadPriority; }
402+
};
377403

378404
/// The following effect indicates that the operation writes to some resource. A
379405
/// 'write' effect implies only mutating a resource, and not any visible
380406
/// dereference or read.
381-
struct Write : public Effect::Base<Write> {};
407+
struct Write : public Effect::Base<Write> {
408+
Write() : Effect::Base<Write>() { this->priority = kWritePriority; }
409+
};
382410
} // namespace MemoryEffects
383411

384412
//===----------------------------------------------------------------------===//
@@ -470,17 +498,6 @@ bool wouldOpBeTriviallyDead(Operation *op);
470498
///
471499
std::optional<bool> isZeroTrip(mlir::LoopLikeOpInterface &loop);
472500

473-
/// Returns true if the given operation is allowed to be moved under the
474-
/// memory effects interface.
475-
///
476-
/// An operation is movable if either case is true:
477-
/// (a) free of memory effects as defined in isMemoryEffectFree()
478-
/// (b) if the operation does have memory effects, it must be conflict-free
479-
/// as defined in isMemoryEffectConflictFree()
480-
///
481-
/// If the operation meets either criteria, then it is movable under memory effects
482-
bool isMemoryEffectMovable(Operation *op);
483-
484501
/// Returns true if the given operation is free of memory effects.
485502
///
486503
/// An operation is free of memory effects if its implementation of
@@ -493,35 +510,6 @@ bool isMemoryEffectMovable(Operation *op);
493510
/// conditions are satisfied.
494511
bool isMemoryEffectFree(Operation *op);
495512

496-
/// Returns true if the given operation has conflict-free write effects
497-
///
498-
/// An operation is conflict free:
499-
/// (1) Parent is a loop with the LoopLikeOpInterface
500-
/// (2) Parent loop is not a zero trip loop and has constant bounds/steps
501-
/// (3) all of the op's memory effects are of type Write
502-
/// (4) there are no other ops with Alloc/Free/Write effects on the same
503-
/// resources within the op's parent loop region
504-
/// (5) all ops in the parent loop region with Read effects on the same
505-
/// resources are dominated by the operation
506-
///
507-
/// If the operation meets all criteria, then it is conflict free
508-
bool isMemoryEffectConflictFree(Operation *op);
509-
510-
/// Returns true if op and/or any operations within its nested regions
511-
/// have a memory effect conflict with mainOp as defined below:
512-
///
513-
/// op has a memory effect conflict with mainOp if op and/or any of
514-
/// the operations in its nested regions meet any of these criteria:
515-
/// (a) they have any Alloc/Free/Write effects on the resources used by mainOp
516-
/// (b) they dominate mainOp and have any read effect on the resources used by mainOp
517-
///
518-
/// Function mutates resources map
519-
///
520-
/// If none of the critera above are met, mainOp and op are conflict free
521-
bool hasMemoryEffectConflict(
522-
Operation *mainOp, Operation *op,
523-
mlir::DominanceInfo &dom, DenseMap<TypeID, int> &resourceCounts);
524-
525513
/// Returns the side effects of an operation. If the operation has
526514
/// RecursiveMemoryEffects, include all side effects of child operations.
527515
///

mlir/include/mlir/Transforms/LoopInvariantCodeMotionUtils.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99
#ifndef MLIR_TRANSFORMS_LOOPINVARIANTCODEMOTIONUTILS_H
1010
#define MLIR_TRANSFORMS_LOOPINVARIANTCODEMOTIONUTILS_H
1111

12+
#include "mlir/Interfaces/SideEffectInterfaces.h"
1213
#include "mlir/Support/LLVM.h"
14+
#include "mlir/Support/TypeID.h"
1315

1416
#include "llvm/ADT/SmallVector.h"
17+
#include <utility>
1518

1619
namespace mlir {
1720

@@ -21,6 +24,17 @@ class Region;
2124
class RewriterBase;
2225
class Value;
2326

27+
/// Gathers potential conflicts on all memory resources used within loop
28+
///
29+
/// Given a target loop and an op within it (or the loop op itself),
30+
/// gathers op's memory effects and flags potential resource conflicts
31+
/// in a map and then recurses into the op's regions to gather nested
32+
/// resource conflicts
33+
///
34+
/// First call should use loop = someLoop and op = someLoop.getOperation()
35+
void gatherResourceConflicts(LoopLikeOpInterface loop, Operation *op,
36+
DenseMap<TypeID, std::pair<bool, MemoryEffects::EffectInstance>> &resourceConflicts);
37+
2438
/// Given a list of regions, perform loop-invariant code motion. An operation is
2539
/// loop-invariant if it depends only of values defined outside of the loop.
2640
/// LICM moves these operations out of the loop body so that they are not
@@ -63,7 +77,7 @@ class Value;
6377
///
6478
/// Returns the number of operations moved.
6579
size_t moveLoopInvariantCode(
66-
ArrayRef<Region *> regions,
80+
LoopLikeOpInterface loopLike,
6781
function_ref<bool(Value, Region *)> isDefinedOutsideRegion,
6882
function_ref<bool(Operation *, Region *)> shouldMoveOutOfRegion,
6983
function_ref<void(Operation *, Region *)> moveOutOfRegion);

mlir/lib/Interfaces/SideEffectInterfaces.cpp

Lines changed: 22 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "mlir/Dialect/Utils/StaticValueUtils.h"
1212
#include "mlir/IR/Dominance.h"
1313
#include "mlir/IR/SymbolTable.h"
14+
#include <optional>
1415
#include <utility>
1516

1617
using namespace mlir;
@@ -346,14 +347,29 @@ std::optional<bool> mlir::isZeroTrip(mlir::LoopLikeOpInterface &loop) {
346347
return false;
347348
}
348349

349-
bool mlir::isMemoryEffectMovable(Operation *op) {
350-
if (isMemoryEffectFree(op))
351-
return true;
350+
llvm::SmallVector<MemoryEffects::EffectInstance>
351+
mlir::MemoryEffects::getMemoryEffectsSorted(Operation *op) {
352+
auto memInterface = dyn_cast<MemoryEffectOpInterface>(op);
352353

353-
if (isMemoryEffectConflictFree(op))
354-
return true;
354+
llvm::SmallVector<MemoryEffects::EffectInstance> effectsSorted;
355+
memInterface.getEffects(effectsSorted);
355356

356-
return false;
357+
auto sortEffects =
358+
[](llvm::SmallVectorImpl<MemoryEffects::EffectInstance> &effects) {
359+
llvm::stable_sort(effects, [](const MemoryEffects::EffectInstance &a,
360+
const MemoryEffects::EffectInstance &b) {
361+
if (a.getStage() < b.getStage())
362+
return true;
363+
364+
if (a.getStage() == b.getStage())
365+
return a.getEffect()->getPriority() < b.getEffect()->getPriority();
366+
367+
return false; // b before a
368+
});
369+
};
370+
sortEffects(effectsSorted);
371+
372+
return effectsSorted;
357373
}
358374

359375
bool mlir::isMemoryEffectFree(Operation *op) {
@@ -383,101 +399,6 @@ bool mlir::isMemoryEffectFree(Operation *op) {
383399
return true;
384400
}
385401

386-
bool mlir::isMemoryEffectConflictFree(Operation *op) {
387-
auto memInterface = dyn_cast<MemoryEffectOpInterface>(op);
388-
// op does not implement the memory effect op interface
389-
// shouldn't be flagged as movable to be conservative
390-
if (!memInterface) return false;
391-
392-
// check parent loop to make sure it's not dead
393-
Operation *parent = op->getParentOp();
394-
if (!parent)
395-
return false;
396-
397-
auto loopInterface = dyn_cast<LoopLikeOpInterface>(parent);
398-
if (!loopInterface)
399-
return false;
400-
401-
auto isDead = isZeroTrip(loopInterface);
402-
if (!isDead.has_value() || isDead.value())
403-
return false;
404-
405-
// gather all effects on op
406-
llvm::SmallVector<MemoryEffects::EffectInstance> effects;
407-
memInterface.getEffects(effects);
408-
409-
// op has interface but no effects, be conservative
410-
if (effects.empty()) return false;
411-
412-
DenseMap<TypeID, int> resourceCounts;
413-
414-
// ensure op only has Write effects and gather unique
415-
// resource names
416-
for (const MemoryEffects::EffectInstance &effect : effects) {
417-
if (!isa<MemoryEffects::Write>(effect.getEffect()))
418-
return false;
419-
420-
resourceCounts.try_emplace(effect.getResource()->getResourceID(), 0);
421-
}
422-
423-
mlir::DominanceInfo dom(parent);
424-
425-
for (Region &region : parent->getRegions())
426-
for (Operation &opI : region.getOps())
427-
if (hasMemoryEffectConflict(op, &opI, dom, resourceCounts))
428-
return false;
429-
430-
return true;
431-
}
432-
433-
bool mlir::hasMemoryEffectConflict(
434-
Operation *mainOp, Operation *op,
435-
mlir::DominanceInfo &dom, DenseMap<TypeID, int> &resourceCounts) {
436-
437-
if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
438-
439-
llvm::SmallVector<MemoryEffects::EffectInstance> effects;
440-
memInterface.getEffects(effects);
441-
442-
bool isDominated = dom.properlyDominates(mainOp, op);
443-
444-
// ensure op only has Write or dominated Read effects
445-
// check used resources
446-
for (const MemoryEffects::EffectInstance &effect : effects) {
447-
auto resourceID = effect.getResource()->getResourceID();
448-
449-
if (resourceCounts.contains(resourceID)) {
450-
if (isa<MemoryEffects::Read>(effect.getEffect())) {
451-
if (isDominated)
452-
continue; // skip dominated reads
453-
}
454-
else if (!isa<MemoryEffects::Write>(effect.getEffect())) {
455-
return true; // count alloc/free in same region as conflict, be conservative
456-
}
457-
458-
// update write counts, should always be <=1 per resource in region
459-
if (++resourceCounts[resourceID] > 1) {
460-
return true;
461-
}
462-
}
463-
}
464-
} else if (!op->hasTrait<OpTrait::HasRecursiveMemoryEffects>()) {
465-
// Otherwise, if the op does not implement the memory effect interface and
466-
// it does not have recursive side effects, then it cannot be known that the
467-
// op is conflicting or not.
468-
return true;
469-
}
470-
471-
// Recurse into the regions and ensure that nested ops don't
472-
// conflict with each other's MemWrites
473-
for (Region &region : op->getRegions())
474-
for (Operation &opI : region.getOps())
475-
if (hasMemoryEffectConflict(mainOp, &opI, dom, resourceCounts))
476-
return true;
477-
478-
return false;
479-
}
480-
481402
// the returned vector may contain duplicate effects
482403
std::optional<llvm::SmallVector<MemoryEffects::EffectInstance>>
483404
mlir::getEffectsRecursively(Operation *rootOp) {

0 commit comments

Comments
 (0)