#include "mlir/Dialect/Affine/Analysis/Utils.h"
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Affine/LoopFusionUtils.h"
#include "mlir/Dialect/Affine/LoopUtils.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Pass/Pass.h"
#define DEBUG_TYPE "test-loop-fusion"
using namespace mlir;
static llvm::cl::OptionCategory clOptionsCategory(DEBUG_TYPE " options");
static llvm::cl::opt<bool> clTestDependenceCheck(
"test-loop-fusion-dependence-check",
llvm::cl::desc("Enable testing of loop fusion dependence check"),
llvm::cl::cat(clOptionsCategory));
static llvm::cl::opt<bool> clTestSliceComputation(
"test-loop-fusion-slice-computation",
llvm::cl::desc("Enable testing of loop fusion slice computation"),
llvm::cl::cat(clOptionsCategory));
static llvm::cl::opt<bool> clTestLoopFusionTransformation(
"test-loop-fusion-transformation",
llvm::cl::desc("Enable testing of loop fusion transformation"),
llvm::cl::cat(clOptionsCategory));
namespace {
struct TestLoopFusion
: public PassWrapper<TestLoopFusion, OperationPass<func::FuncOp>> {
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestLoopFusion)
StringRef getArgument() const final { return "test-loop-fusion"; }
StringRef getDescription() const final {
return "Tests loop fusion utility functions.";
}
void runOnOperation() override;
};
}
static bool testDependenceCheck(AffineForOp srcForOp, AffineForOp dstForOp,
unsigned i, unsigned j, unsigned loopDepth,
unsigned maxLoopDepth) {
mlir::ComputationSliceState sliceUnion;
for (unsigned d = loopDepth + 1; d <= maxLoopDepth; ++d) {
FusionResult result =
mlir::canFuseLoops(srcForOp, dstForOp, d, &sliceUnion);
if (result.value == FusionResult::FailBlockDependence) {
srcForOp->emitRemark("block-level dependence preventing"
" fusion of loop nest ")
<< i << " into loop nest " << j << " at depth " << loopDepth;
}
}
return false;
}
static unsigned getBlockIndex(Operation &op) {
unsigned index = 0;
for (auto &opX : *op.getBlock()) {
if (&op == &opX)
break;
++index;
}
return index;
}
static std::string getSliceStr(const mlir::ComputationSliceState &sliceUnion) {
std::string result;
llvm::raw_string_ostream os(result);
unsigned ipd = getNestingDepth(&*sliceUnion.insertPoint);
unsigned ipb = getBlockIndex(*sliceUnion.insertPoint);
os << "insert point: (" << std::to_string(ipd) << ", " << std::to_string(ipb)
<< ")";
assert(sliceUnion.lbs.size() == sliceUnion.ubs.size());
os << " loop bounds: ";
for (unsigned k = 0, e = sliceUnion.lbs.size(); k < e; ++k) {
os << '[';
sliceUnion.lbs[k].print(os);
os << ", ";
sliceUnion.ubs[k].print(os);
os << "] ";
}
return os.str();
}
static bool testSliceComputation(AffineForOp forOpA, AffineForOp forOpB,
unsigned i, unsigned j, unsigned loopDepth,
unsigned maxLoopDepth) {
for (unsigned d = loopDepth + 1; d <= maxLoopDepth; ++d) {
mlir::ComputationSliceState sliceUnion;
FusionResult result = mlir::canFuseLoops(forOpA, forOpB, d, &sliceUnion);
if (result.value == FusionResult::Success) {
forOpB->emitRemark("slice (")
<< " src loop: " << i << ", dst loop: " << j << ", depth: " << d
<< " : " << getSliceStr(sliceUnion) << ")";
} else if (result.value == FusionResult::FailIncorrectSlice) {
forOpB->emitRemark("Incorrect slice (")
<< " src loop: " << i << ", dst loop: " << j << ", depth: " << d
<< " : " << getSliceStr(sliceUnion) << ")";
}
}
return false;
}
static bool testLoopFusionTransformation(AffineForOp forOpA, AffineForOp forOpB,
unsigned i, unsigned j,
unsigned loopDepth,
unsigned maxLoopDepth) {
for (unsigned d = loopDepth + 1; d <= maxLoopDepth; ++d) {
mlir::ComputationSliceState sliceUnion;
FusionResult result = mlir::canFuseLoops(forOpA, forOpB, d, &sliceUnion);
if (result.value == FusionResult::Success) {
mlir::fuseLoops(forOpA, forOpB, sliceUnion);
forOpA.erase();
return true;
}
}
return false;
}
using LoopFunc = function_ref<bool(AffineForOp, AffineForOp, unsigned, unsigned,
unsigned, unsigned)>;
static bool iterateLoops(ArrayRef<SmallVector<AffineForOp, 2>> depthToLoops,
LoopFunc fn, bool returnOnChange = false) {
bool changed = false;
for (unsigned loopDepth = 0, end = depthToLoops.size(); loopDepth < end;
++loopDepth) {
auto &loops = depthToLoops[loopDepth];
unsigned numLoops = loops.size();
for (unsigned j = 0; j < numLoops; ++j) {
for (unsigned k = 0; k < numLoops; ++k) {
if (j != k)
changed |=
fn(loops[j], loops[k], j, k, loopDepth, depthToLoops.size());
if (changed && returnOnChange)
return true;
}
}
}
return changed;
}
void TestLoopFusion::runOnOperation() {
std::vector<SmallVector<AffineForOp, 2>> depthToLoops;
if (clTestLoopFusionTransformation) {
do {
depthToLoops.clear();
gatherLoops(getOperation(), depthToLoops);
} while (iterateLoops(depthToLoops, testLoopFusionTransformation,
true));
return;
}
gatherLoops(getOperation(), depthToLoops);
if (clTestDependenceCheck)
iterateLoops(depthToLoops, testDependenceCheck);
if (clTestSliceComputation)
iterateLoops(depthToLoops, testSliceComputation);
}
namespace mlir {
namespace test {
void registerTestLoopFusion() { PassRegistration<TestLoopFusion>(); }
}
}