Add a basic model to set tile sizes + some cleanup
- compute tile sizes based on a simple model that looks at memory footprints
(instead of using the hardcoded default value)
- adjust tile sizes to make them factors of trip counts based on an option
- update loop fusion CL options to allow setting maximal fusion at pass creation
- change an emitError to emitWarning (since it's not a hard error unless the client
treats it that way, in which case, it can emit one)
$ mlir-opt -debug-only=loop-tile -loop-tile test/Transforms/loop-tiling.mlir
test/Transforms/loop-tiling.mlir:81:3: note: using tile sizes [4 4 5 ]
for %i = 0 to 256 {
for %i0 = 0 to 256 step 4 {
for %i1 = 0 to 256 step 4 {
for %i2 = 0 to 250 step 5 {
for %i3 = #map4(%i0) to #map11(%i0) {
for %i4 = #map4(%i1) to #map11(%i1) {
for %i5 = #map4(%i2) to #map12(%i2) {
%0 = load %arg0[%i3, %i5] : memref<8x8xvector<64xf32>>
%1 = load %arg1[%i5, %i4] : memref<8x8xvector<64xf32>>
%2 = load %arg2[%i3, %i4] : memref<8x8xvector<64xf32>>
%3 = mulf %0, %1 : vector<64xf32>
%4 = addf %2, %3 : vector<64xf32>
store %4, %arg2[%i3, %i4] : memref<8x8xvector<64xf32>>
}
}
}
}
}
}
PiperOrigin-RevId: 237461836
diff --git a/mlir/lib/Transforms/LoopFusion.cpp b/mlir/lib/Transforms/LoopFusion.cpp
index 3786177..4e619ef 100644
--- a/mlir/lib/Transforms/LoopFusion.cpp
+++ b/mlir/lib/Transforms/LoopFusion.cpp
@@ -48,7 +48,9 @@
static llvm::cl::OptionCategory clOptionsCategory(DEBUG_TYPE " options");
-/// Disables fusion profitability check and fuses if valid.
+/// Disables fusion profitability check and fuses if valid. Ignore any
+/// additional (redundant) computation tolerance threshold
+/// that would have prevented fusion.
static llvm::cl::opt<bool>
clMaximalLoopFusion("fusion-maximal",
llvm::cl::desc("Enables maximal loop fusion"),
@@ -66,8 +68,8 @@
llvm::cl::desc("Faster memory space number to promote fusion buffers to"),
llvm::cl::cat(clOptionsCategory));
-// A local buffer of size less than or equal to this size is promoted to fast
-// memory.
+// A local buffer of size less than or equal to this size is automatically
+// promoted to fast memory after producer-consumer fusion.
static llvm::cl::opt<unsigned long long> clFusionLocalBufThreshold(
"fusion-local-buf-threshold",
llvm::cl::desc("Threshold size (KiB) for promoting local buffers to fast "
@@ -86,9 +88,10 @@
// and add support for more general loop fusion algorithms.
struct LoopFusion : public FunctionPass<LoopFusion> {
- LoopFusion(unsigned fastMemorySpace = 0, uint64_t localBufSizeThreshold = 0)
+ LoopFusion(unsigned fastMemorySpace = 0, uint64_t localBufSizeThreshold = 0,
+ bool maximalFusion = false)
: localBufSizeThreshold(localBufSizeThreshold),
- fastMemorySpace(fastMemorySpace) {}
+ fastMemorySpace(fastMemorySpace), maximalFusion(maximalFusion) {}
void runOnFunction() override;
@@ -96,6 +99,9 @@
// `fastMemorySpace` if provided.
uint64_t localBufSizeThreshold;
Optional<unsigned> fastMemorySpace = None;
+ // If true, ignore any additional (redundant) computation tolerance threshold
+ // that would have prevented fusion.
+ bool maximalFusion;
// The amount of additional computation that is tolerated while fusing
// pair-wise as a fraction of the total computation.
@@ -105,8 +111,9 @@
} // end anonymous namespace
FunctionPassBase *mlir::createLoopFusionPass(unsigned fastMemorySpace,
- uint64_t localBufSizeThreshold) {
- return new LoopFusion(fastMemorySpace, localBufSizeThreshold);
+ uint64_t localBufSizeThreshold,
+ bool maximalFusion) {
+ return new LoopFusion(fastMemorySpace, localBufSizeThreshold, maximalFusion);
}
namespace {
@@ -1411,7 +1418,7 @@
ArrayRef<Instruction *> dstLoadOpInsts,
ArrayRef<Instruction *> dstStoreOpInsts,
ComputationSliceState *sliceState,
- unsigned *dstLoopDepth) {
+ unsigned *dstLoopDepth, bool maximalFusion) {
LLVM_DEBUG({
llvm::dbgs() << "Checking whether fusion is profitable between:\n";
llvm::dbgs() << " " << *srcOpInst << " and \n";
@@ -1620,7 +1627,7 @@
// (as per computeToleranceThreshold), we will simply pick the one that
// reduces the intermediary size the most.
if ((storageReduction > maxStorageReduction) &&
- (clMaximalLoopFusion ||
+ (maximalFusion ||
(additionalComputeFraction < computeToleranceThreshold))) {
maxStorageReduction = storageReduction;
bestDstLoopDepth = i;
@@ -1632,7 +1639,7 @@
// A simple cost model: fuse if it reduces the memory footprint. If
// -maximal-fusion is set, fuse nevertheless.
- if (!clMaximalLoopFusion && !bestDstLoopDepth.hasValue()) {
+ if (!maximalFusion && !bestDstLoopDepth.hasValue()) {
LLVM_DEBUG(
llvm::dbgs()
<< "All fusion choices involve more than the threshold amount of "
@@ -1661,7 +1668,7 @@
Optional<double> storageReduction = None;
- if (!clMaximalLoopFusion) {
+ if (!maximalFusion) {
if (!dstMemSize.hasValue() || !srcMemSize.hasValue()) {
LLVM_DEBUG(
llvm::dbgs()
@@ -1785,13 +1792,16 @@
unsigned localBufSizeThreshold;
// Parameter for fast memory space.
Optional<unsigned> fastMemorySpace;
+ // If true, ignore any additional (redundant) computation tolerance threshold
+ // that would have prevented fusion.
+ bool maximalFusion;
using Node = MemRefDependenceGraph::Node;
GreedyFusion(MemRefDependenceGraph *mdg, unsigned localBufSizeThreshold,
- Optional<unsigned> fastMemorySpace)
+ Optional<unsigned> fastMemorySpace, bool maximalFusion)
: mdg(mdg), localBufSizeThreshold(localBufSizeThreshold),
- fastMemorySpace(fastMemorySpace) {}
+ fastMemorySpace(fastMemorySpace), maximalFusion(maximalFusion) {}
// Initializes 'worklist' with nodes from 'mdg'
void init() {
@@ -1917,7 +1927,7 @@
// Check if fusion would be profitable.
if (!isFusionProfitable(srcStoreOpInst, srcStoreOpInst,
dstLoadOpInsts, dstStoreOpInsts, &sliceState,
- &bestDstLoopDepth))
+ &bestDstLoopDepth, maximalFusion))
continue;
// Fuse computation slice of 'srcLoopNest' into 'dstLoopNest'.
@@ -2076,7 +2086,8 @@
// Check if fusion would be profitable.
if (!isFusionProfitable(sibLoadOpInst, sibStoreOpInst, dstLoadOpInsts,
- dstStoreOpInsts, &sliceState, &bestDstLoopDepth))
+ dstStoreOpInsts, &sliceState, &bestDstLoopDepth,
+ maximalFusion))
continue;
// Fuse computation slice of 'sibLoopNest' into 'dstLoopNest'.
@@ -2231,9 +2242,13 @@
localBufSizeThreshold = clFusionLocalBufThreshold * 1024;
}
+ if (clMaximalLoopFusion.getNumOccurrences() > 0)
+ maximalFusion = clMaximalLoopFusion;
+
MemRefDependenceGraph g;
if (g.init(getFunction()))
- GreedyFusion(&g, localBufSizeThreshold, fastMemorySpace).run();
+ GreedyFusion(&g, localBufSizeThreshold, fastMemorySpace, maximalFusion)
+ .run();
}
static PassRegistration<LoopFusion> pass("loop-fusion", "Fuse loop nests");