23#include "llvm/Support/CommandLine.h"
24#include "llvm/Support/Debug.h"
29#define GEN_PASS_DEF_AFFINELOOPTILING
30#include "mlir/Dialect/Affine/Transforms/Passes.h.inc"
37#define DEBUG_TYPE "affine-loop-tile"
43 LoopTiling() =
default;
44 explicit LoopTiling(uint64_t cacheSizeBytes,
bool avoidMaxMinBounds =
true)
45 : avoidMaxMinBounds(avoidMaxMinBounds) {
46 this->cacheSizeInKiB = cacheSizeBytes / 1024;
49 void runOnOperation()
override;
54 constexpr static unsigned kDefaultTileSize = 4;
57 bool avoidMaxMinBounds =
true;
68 for (AffineForOp forOp : f.getOps<AffineForOp>()) {
72 bands.push_back(band);
78std::unique_ptr<OperationPass<func::FuncOp>>
80 return std::make_unique<LoopTiling>(cacheSizeBytes);
82std::unique_ptr<OperationPass<func::FuncOp>>
84 return std::make_unique<LoopTiling>();
91 assert(band.size() == tileSizes->size() &&
"invalid tile size count");
92 for (
unsigned i = 0, e = band.size(); i < e; i++) {
93 unsigned &tSizeAdjusted = (*tileSizes)[i];
94 AffineForOp forOp = band[i];
95 std::optional<APInt> mayConst = forOp.getStaticTripCount();
100 uint64_t constTripCount = mayConst->getZExtValue();
101 if (constTripCount > 1 && tSizeAdjusted > constTripCount / 2)
102 tSizeAdjusted = constTripCount / 2;
103 while (constTripCount % tSizeAdjusted != 0)
121 tileSizes->assign(band.size(), tileSize);
126 if (!this->tileSizes.empty()) {
127 tileSizes->assign(this->tileSizes.begin(), this->tileSizes.end());
128 tileSizes->resize(band.size(), kDefaultTileSize);
131 tileSizes->resize(band.size());
135 if (cacheSizeInKiB == 0) {
136 llvm::fill(*tileSizes, 1);
147 llvm::fill(*tileSizes, LoopTiling::kDefaultTileSize);
148 if (avoidMaxMinBounds)
151 AffineForOp rootForOp = band[0];
154 rootForOp.emitWarning(
"memory footprint unknown: using default tile "
155 "sizes adjusted to trip count divisors"));
160 uint64_t cacheSizeBytes = cacheSizeInKiB * 1024;
161 uint64_t excessFactor = llvm::divideCeil(*fp, cacheSizeBytes);
162 if (excessFactor <= 1) {
164 llvm::fill(*tileSizes, 1);
176 static_cast<unsigned>(floorl(std::pow(excessFactor, 1.0 / band.size())));
178 unsigned cumulProductOfTileSizes = 1;
179 for (
unsigned i = 0, e = band.size(); i < e; i++) {
181 (*tileSizes)[i] = tSize;
184 (*tileSizes)[i] = std::max(
185 1U,
static_cast<unsigned>(excessFactor / cumulProductOfTileSizes));
186 cumulProductOfTileSizes *= (*tileSizes)[i];
188 if (avoidMaxMinBounds)
192void LoopTiling::runOnOperation() {
194 std::vector<SmallVector<AffineForOp, 6>> bands;
198 for (
auto &band : bands) {
201 SmallVector<unsigned, 6> tileSizes;
203 if (llvm::DebugFlag) {
204 auto diag = band[0].emitRemark(
"using tile sizes [");
205 llvm::interleaveComma(tileSizes, llvm::dbgs());
208 SmallVector<AffineForOp, 6> tiledNest;
211 assert(!band.empty() &&
"guaranteed to succeed on empty bands");
212 LLVM_DEBUG(band.front()->emitRemark(
"loop tiling failed!\n"));
218 auto intraTileLoops =
219 MutableArrayRef<AffineForOp>(tiledNest).drop_front(band.size());
220 if (
failed(separateFullTiles(intraTileLoops))) {
221 assert(!intraTileLoops.empty() &&
222 "guaranteed to succeed on empty bands");
223 LLVM_DEBUG(intraTileLoops.front()->emitRemark(
224 "separation post tiling failed!"));
static void adjustToDivisorsOfTripCounts(ArrayRef< AffineForOp > band, SmallVectorImpl< unsigned > *tileSizes)
Reduces each tile size to the largest divisor of the corresponding trip count (if the trip count is k...
static void getTopLevelTileableBands(func::FuncOp f, std::vector< SmallVector< AffineForOp, 6 > > &bands)
Get bands of loops that are valid to tile from the top-level of f.
static std::string diag(const llvm::Value &value)
static SmallVector< Value > getTileSizes(Location loc, x86::amx::TileType tType, RewriterBase &rewriter)
Maps the 2-dim vector shape to the two 16-bit tile sizes.
bool isTilingValid(ArrayRef< AffineForOp > loops)
Checks whether hyper-rectangular loop tiling of the nest represented by loops is valid.
void getPerfectlyNestedLoops(SmallVectorImpl< AffineForOp > &nestedLoops, AffineForOp root)
Get perfectly nested sequence of loops starting at root of loop nest (the first op being another Affi...
std::optional< int64_t > getMemoryFootprintBytes(AffineForOp forOp, int memorySpace=-1)
Gets the memory footprint of all data touched in the specified memory space in bytes; if the memory s...
LogicalResult tilePerfectlyNested(MutableArrayRef< AffineForOp > input, ArrayRef< unsigned > tileSizes, SmallVectorImpl< AffineForOp > *tiledNest=nullptr)
Tiles the specified band of perfectly nested loops creating tile-space loops and intra-tile loops.
std::unique_ptr< OperationPass< func::FuncOp > > createLoopTilingPass()
Overload relying on pass options for initialization.
Include the generated interface declarations.