2019-05-25 01:54:22 +08:00
|
|
|
//===- TestLoopFusion.cpp - Test loop fusion ------------------------------===//
|
|
|
|
//
|
|
|
|
// Copyright 2019 The MLIR Authors.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
// =============================================================================
|
|
|
|
//
|
|
|
|
// This file implements a pass to test various loop fusion utility functions.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "mlir/Analysis/AffineAnalysis.h"
|
|
|
|
#include "mlir/Analysis/AffineStructures.h"
|
|
|
|
#include "mlir/Analysis/Passes.h"
|
|
|
|
#include "mlir/Analysis/Utils.h"
|
2019-08-21 06:36:08 +08:00
|
|
|
#include "mlir/Dialect/AffineOps/AffineOps.h"
|
2019-08-20 02:00:47 +08:00
|
|
|
#include "mlir/Dialect/StandardOps/Ops.h"
|
2019-05-25 01:54:22 +08:00
|
|
|
#include "mlir/IR/Builders.h"
|
|
|
|
#include "mlir/Pass/Pass.h"
|
|
|
|
#include "mlir/Transforms/LoopFusionUtils.h"
|
|
|
|
#include "mlir/Transforms/Passes.h"
|
|
|
|
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "llvm/Support/Debug.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));
|
|
|
|
|
2019-06-18 00:59:35 +08:00
|
|
|
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));
|
|
|
|
|
2019-05-25 01:54:22 +08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct TestLoopFusion : public FunctionPass<TestLoopFusion> {
|
|
|
|
void runOnFunction() override;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2019-09-14 04:33:46 +08:00
|
|
|
std::unique_ptr<OpPassBase<FuncOp>> mlir::createTestLoopFusionPass() {
|
2019-08-18 02:05:35 +08:00
|
|
|
return std::make_unique<TestLoopFusion>();
|
2019-05-25 01:54:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Gathers all AffineForOps in 'block' at 'currLoopDepth' in 'depthToLoops'.
|
|
|
|
static void
|
|
|
|
gatherLoops(Block *block, unsigned currLoopDepth,
|
|
|
|
DenseMap<unsigned, SmallVector<AffineForOp, 2>> &depthToLoops) {
|
|
|
|
auto &loopsAtDepth = depthToLoops[currLoopDepth];
|
|
|
|
for (auto &op : *block) {
|
|
|
|
if (auto forOp = dyn_cast<AffineForOp>(op)) {
|
|
|
|
loopsAtDepth.push_back(forOp);
|
|
|
|
gatherLoops(forOp.getBody(), currLoopDepth + 1, depthToLoops);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-18 00:59:35 +08:00
|
|
|
// Run fusion dependence check on 'loops[i]' and 'loops[j]' at loop depths
|
|
|
|
// in range ['loopDepth' + 1, 'maxLoopDepth'].
|
2019-05-25 01:54:22 +08:00
|
|
|
// Emits a remark on 'loops[i]' if a fusion-preventing dependence exists.
|
|
|
|
static void testDependenceCheck(SmallVector<AffineForOp, 2> &loops, unsigned i,
|
2019-06-18 00:59:35 +08:00
|
|
|
unsigned j, unsigned loopDepth,
|
|
|
|
unsigned maxLoopDepth) {
|
2019-05-25 01:54:22 +08:00
|
|
|
AffineForOp srcForOp = loops[i];
|
|
|
|
AffineForOp dstForOp = loops[j];
|
2019-05-30 05:02:14 +08:00
|
|
|
mlir::ComputationSliceState sliceUnion;
|
2019-06-18 00:59:35 +08:00
|
|
|
for (unsigned d = loopDepth + 1; d <= maxLoopDepth; ++d) {
|
|
|
|
FusionResult result =
|
|
|
|
mlir::canFuseLoops(srcForOp, dstForOp, d, &sliceUnion);
|
|
|
|
if (result.value == FusionResult::FailBlockDependence) {
|
|
|
|
srcForOp.getOperation()->emitRemark("block-level dependence preventing"
|
|
|
|
" fusion of loop nest ")
|
|
|
|
<< i << " into loop nest " << j << " at depth " << loopDepth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the index of 'op' in its block.
|
|
|
|
static unsigned getBlockIndex(Operation &op) {
|
|
|
|
unsigned index = 0;
|
|
|
|
for (auto &opX : *op.getBlock()) {
|
|
|
|
if (&op == &opX)
|
|
|
|
break;
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a string representation of 'sliceUnion'.
|
|
|
|
static std::string getSliceStr(const mlir::ComputationSliceState &sliceUnion) {
|
|
|
|
std::string result;
|
|
|
|
llvm::raw_string_ostream os(result);
|
|
|
|
// Slice insertion point format [loop-depth, operation-block-index]
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Computes fusion slice union on 'loops[i]' and 'loops[j]' at loop depths
|
|
|
|
// in range ['loopDepth' + 1, 'maxLoopDepth'].
|
2019-10-21 00:44:06 +08:00
|
|
|
// Emits a string representation of the slice union as a remark on 'loops[j]'.
|
2019-06-18 00:59:35 +08:00
|
|
|
static void testSliceComputation(SmallVector<AffineForOp, 2> &loops, unsigned i,
|
|
|
|
unsigned j, unsigned loopDepth,
|
|
|
|
unsigned maxLoopDepth) {
|
|
|
|
AffineForOp forOpA = loops[i];
|
|
|
|
AffineForOp forOpB = loops[j];
|
|
|
|
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.getOperation()->emitRemark("slice (")
|
|
|
|
<< " src loop: " << i << ", dst loop: " << j << ", depth: " << d
|
|
|
|
<< " : " << getSliceStr(sliceUnion) << ")";
|
|
|
|
}
|
2019-05-25 01:54:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TestLoopFusion::runOnFunction() {
|
|
|
|
// Gather all AffineForOps by loop depth.
|
|
|
|
DenseMap<unsigned, SmallVector<AffineForOp, 2>> depthToLoops;
|
|
|
|
for (auto &block : getFunction()) {
|
|
|
|
gatherLoops(&block, /*currLoopDepth=*/0, depthToLoops);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run tests on all combinations of src/dst loop nests in 'depthToLoops'.
|
|
|
|
for (auto &depthAndLoops : depthToLoops) {
|
|
|
|
unsigned loopDepth = depthAndLoops.first;
|
|
|
|
auto &loops = depthAndLoops.second;
|
|
|
|
unsigned numLoops = loops.size();
|
|
|
|
for (unsigned j = 0; j < numLoops; ++j) {
|
|
|
|
for (unsigned k = 0; k < numLoops; ++k) {
|
|
|
|
if (j == k)
|
|
|
|
continue;
|
|
|
|
if (clTestDependenceCheck)
|
2019-06-18 00:59:35 +08:00
|
|
|
testDependenceCheck(loops, j, k, loopDepth, depthToLoops.size());
|
|
|
|
if (clTestSliceComputation)
|
|
|
|
testSliceComputation(loops, j, k, loopDepth, depthToLoops.size());
|
2019-05-25 01:54:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static PassRegistration<TestLoopFusion>
|
|
|
|
pass("test-loop-fusion", "Tests loop fusion utility functions.");
|