Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mlir/include/mlir/Dialect/Affine/IR/AffineOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ def AffineForOp : Affine_Op<"for",
Speculation::Speculatability getSpeculatability();
}];

let hasCanonicalizer = 1;
let hasCustomAssemblyFormat = 1;
let hasFolder = 1;
let hasRegionVerifier = 1;
Expand Down
3 changes: 1 addition & 2 deletions mlir/include/mlir/Dialect/Affine/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ std::unique_ptr<OperationPass<func::FuncOp>> createRaiseMemrefToAffine();
/// Apply normalization transformations to affine loop-like ops. If
/// `promoteSingleIter` is true, single iteration loops are promoted (i.e., the
/// loop is replaced by its loop body).
std::unique_ptr<OperationPass<func::FuncOp>>
createAffineLoopNormalizePass(bool promoteSingleIter = false);
std::unique_ptr<OperationPass<func::FuncOp>> createAffineLoopNormalizePass();

/// Performs packing (or explicit copying) of accessed memref regions into
/// buffers in the specified faster memory space through either pointwise copies
Expand Down
4 changes: 0 additions & 4 deletions mlir/include/mlir/Dialect/Affine/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -383,10 +383,6 @@ def AffineParallelize : Pass<"affine-parallelize", "func::FuncOp"> {
def AffineLoopNormalize : Pass<"affine-loop-normalize", "func::FuncOp"> {
let summary = "Apply normalization transformations to affine loop-like ops";
let constructor = "mlir::affine::createAffineLoopNormalizePass()";
let options = [
Option<"promoteSingleIter", "promote-single-iter", "bool",
/*default=*/"true", "Promote single iteration loops">,
];
}

def LoopCoalescing : Pass<"affine-loop-coalescing", "func::FuncOp"> {
Expand Down
7 changes: 2 additions & 5 deletions mlir/include/mlir/Dialect/Affine/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,8 @@ void normalizeAffineParallel(AffineParallelOp op);
/// lower bound to zero and loop step to one. The upper bound is set to the trip
/// count of the loop. Original loops must have a lower bound with only a single
/// result. There is no such restriction on upper bounds. Returns success if the
/// loop has been normalized (or is already in the normal form). If
/// `promoteSingleIter` is true, the loop is simply promoted if it has a single
/// iteration.
LogicalResult normalizeAffineFor(AffineForOp op,
bool promoteSingleIter = false);
/// loop has been normalized (or is already in the normal form).
LogicalResult normalizeAffineFor(AffineForOp op);

/// Traverse `e` and return an AffineExpr where all occurrences of `dim` have
/// been replaced by either:
Expand Down
40 changes: 40 additions & 0 deletions mlir/lib/Dialect/Affine/IR/AffineOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2716,6 +2716,46 @@ LogicalResult AffineForOp::fold(FoldAdaptor adaptor,
return success(folded);
}

/// Replaces the given op with the contents of the given single-block region,
/// using the operands of the block terminator to replace operation results.
static void replaceOpWithRegion(PatternRewriter &rewriter, Operation *op,
Region &region, ValueRange blockArgs = {}) {
assert(region.hasOneBlock() && "expected single-block region");
Block *block = &region.front();
Operation *terminator = block->getTerminator();
ValueRange results = terminator->getOperands();
rewriter.inlineBlockBefore(block, op, blockArgs);
rewriter.replaceOp(op, results);
rewriter.eraseOp(terminator);
}

struct SimplifyTrivialLoops : public OpRewritePattern<AffineForOp> {
using OpRewritePattern<AffineForOp>::OpRewritePattern;
LogicalResult matchAndRewrite(AffineForOp forOp,
PatternRewriter &rewriter) const override {
std::optional<uint64_t> tripCount = getTrivialConstantTripCount(forOp);
if (!tripCount.has_value() || tripCount != 1)
return failure();

SmallVector<Value> blockArgs;
blockArgs.reserve(forOp.getInits().size() + 1);
rewriter.setInsertionPointToStart(forOp.getBody());
Value lower = AffineApplyOp::create(
rewriter, forOp.getLoc(), forOp.getLowerBoundMap(),
ValueRange(forOp.getLowerBoundOperands()));
forOp.getInductionVar().replaceAllUsesWith(lower);
blockArgs.push_back(lower);
llvm::append_range(blockArgs, forOp.getInits());
replaceOpWithRegion(rewriter, forOp, forOp.getRegion(), blockArgs);
return success();
}
};

void AffineForOp::getCanonicalizationPatterns(RewritePatternSet &results,
MLIRContext *context) {
results.add<SimplifyTrivialLoops>(context);
}

OperandRange AffineForOp::getEntrySuccessorOperands(RegionBranchPoint point) {
assert((point.isParent() || point == getRegion()) && "invalid region point");

Expand Down
10 changes: 4 additions & 6 deletions mlir/lib/Dialect/Affine/Transforms/AffineLoopNormalize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,21 @@ namespace {
/// that are already in a normalized form.
struct AffineLoopNormalizePass
: public affine::impl::AffineLoopNormalizeBase<AffineLoopNormalizePass> {
explicit AffineLoopNormalizePass(bool promoteSingleIter) {
this->promoteSingleIter = promoteSingleIter;
}
using Base::Base;

void runOnOperation() override {
getOperation().walk([&](Operation *op) {
if (auto affineParallel = dyn_cast<AffineParallelOp>(op))
normalizeAffineParallel(affineParallel);
else if (auto affineFor = dyn_cast<AffineForOp>(op))
(void)normalizeAffineFor(affineFor, promoteSingleIter);
(void)normalizeAffineFor(affineFor);
});
}
};

} // namespace

std::unique_ptr<OperationPass<func::FuncOp>>
mlir::affine::createAffineLoopNormalizePass(bool promoteSingleIter) {
return std::make_unique<AffineLoopNormalizePass>(promoteSingleIter);
mlir::affine::createAffineLoopNormalizePass() {
return std::make_unique<AffineLoopNormalizePass>();
}
5 changes: 1 addition & 4 deletions mlir/lib/Dialect/Affine/Utils/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,10 +556,7 @@ void mlir::affine::normalizeAffineParallel(AffineParallelOp op) {
op.setUpperBounds(ranges.getOperands(), newUpperMap);
}

LogicalResult mlir::affine::normalizeAffineFor(AffineForOp op,
bool promoteSingleIter) {
if (promoteSingleIter && succeeded(promoteIfSingleIteration(op)))
return success();
LogicalResult mlir::affine::normalizeAffineFor(AffineForOp op) {

// Check if the forop is already normalized.
if (op.hasConstantLowerBound() && (op.getConstantLowerBound() == 0) &&
Expand Down
21 changes: 0 additions & 21 deletions mlir/test/Dialect/Affine/affine-loop-normalize.mlir
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// RUN: mlir-opt %s -affine-loop-normalize -split-input-file | FileCheck %s
// RUN: mlir-opt %s -affine-loop-normalize='promote-single-iter=1' -split-input-file | FileCheck %s --check-prefix=PROMOTE-SINGLE-ITER

// Normalize steps to 1 and lower bounds to 0.

Expand Down Expand Up @@ -37,26 +36,6 @@ func.func @relative_bounds(%arg: index) {

// -----

// Check that single iteration loop is removed and its body is promoted to the
// parent block.

// CHECK-LABEL: func @promote_single_iter_loop
// PROMOTE-SINGLE-ITER-LABEL: func @promote_single_iter_loop
func.func @promote_single_iter_loop(%in: memref<1xf32>, %out: memref<1xf32>) {
affine.for %i = 0 to 1 {
%1 = affine.load %in[%i] : memref<1xf32>
affine.store %1, %out[%i] : memref<1xf32>
}
return
}

// PROMOTE-SINGLE-ITER-NEXT: arith.constant
// PROMOTE-SINGLE-ITER-NEXT: affine.load
// PROMOTE-SINGLE-ITER-NEXT: affine.store
// PROMOTE-SINGLE-ITER-NEXT: return

// -----

// CHECK-DAG: [[$IV0:#map[0-9]*]] = affine_map<(d0) -> (d0 * 2 + 2)>
// CHECK-DAG: [[$IV1:#map[0-9]*]] = affine_map<(d0) -> (d0 * 3)>

Expand Down
31 changes: 22 additions & 9 deletions mlir/test/Dialect/Affine/canonicalize.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -1323,12 +1323,10 @@ func.func @simplify_bounds_tiled() {
}
}
}
// CHECK: affine.for
// CHECK-NEXT: affine.for
// CHECK: affine.for %{{.*}} 0 to 2
// CHECK-NEXT: affine.for %{{.*}} = 0 to 32 step 16
// CHECK-NEXT: affine.for %{{.*}} = 0 to 32 step 16
// CHECK-NEXT: affine.for %{{.*}} = 0 to 32 step 16
// CHECK-NEXT: affine.for %{{.*}} = 0 to 2
// CHECK-NEXT: affine.for %{{.*}} = 0 to 16 step 16
// CHECK-NEXT: affine.for %{{.*}} = 0 to 2

return
}
Expand All @@ -1348,9 +1346,7 @@ func.func @simplify_min_max_multi_expr() {
// CHECK: affine.for
affine.for %i = 0 to 2 {
// CHECK: affine.for
affine.for %j = 0 to 4 {
// The first upper bound expression will not be lower than -9. So, it's redundant.
// CHECK-NEXT: affine.for %{{.*}} = -10 to -9
affine.for %j = 0 to 4 {
affine.for %k = -10 to min affine_map<(d0, d1) -> (4 * d0 - 3 * d1, -9)>(%i, %j) {
"test.foo"() : () -> ()
}
Expand All @@ -1370,7 +1366,6 @@ func.func @simplify_min_max_multi_expr() {
}
}

// CHECK: affine.for %{{.*}} = 0 to 1
affine.for %i = 0 to 2 {
affine.for %j = max affine_map<(d0) -> (d0 floordiv 2, 0)>(%i) to 1 {
"test.foo"() : () -> ()
Expand Down Expand Up @@ -2401,3 +2396,21 @@ func.func @for_empty_body_folder_iv_yield() -> index {
}
return %10 : index
}

// -----

// Check that single iteration loop is removed and its body is promoted to the
// parent block.

// CHECK-LABEL: func @promote_single_iter_loop
// CHECK-SAME: %[[IN:.*]]: memref<1xf32>, %[[OUT:.*]]: memref<1xf32>
func.func @promote_single_iter_loop(%in: memref<1xf32>, %out: memref<1xf32>) {
affine.for %i = 0 to 1 {
%1 = affine.load %in[%i] : memref<1xf32>
affine.store %1, %out[%i] : memref<1xf32>
}
return
}

// CHECK-NEXT: %[[DATA:.*]] = affine.load %[[IN]][0]
// CHECK-NEXT: affine.store %[[DATA]], %[[OUT]][0]
16 changes: 6 additions & 10 deletions mlir/test/Dialect/Affine/raise-memref.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,17 @@ func.func @reduce_window_max() {
// CHECK: affine.for %[[arg0:.*]] =
// CHECK: affine.for %[[arg1:.*]] =
// CHECK: affine.for %[[arg2:.*]] =
// CHECK: affine.for %[[arg3:.*]] =
// CHECK: affine.store %[[cst]], %[[v0]][%[[arg0]], %[[arg1]], %[[arg2]], %[[arg3]]] :
// CHECK: affine.store %[[cst]], %[[v0]][0, %[[arg0]], %[[arg1]], %[[arg2]]] :
// CHECK: affine.for %[[a0:.*]] =
// CHECK: affine.for %[[a1:.*]] =
// CHECK: affine.for %[[a2:.*]] =
// CHECK: affine.for %[[a3:.*]] =
// CHECK: affine.for %[[a4:.*]] =
// CHECK: affine.for %[[a5:.*]] =
// CHECK: affine.for %[[a6:.*]] =
// CHECK: affine.for %[[a7:.*]] =
// CHECK: %[[lhs:.*]] = affine.load %[[v0]][%[[a0]], %[[a1]], %[[a2]], %[[a3]]] :
// CHECK: %[[rhs:.*]] = affine.load %[[v1]][%[[a0]] + %[[a4]], %[[a1]] * 2 + %[[a5]], %[[a2]] * 2 + %[[a6]], %[[a3]] + %[[a7]]] :
// CHECK: %[[res:.*]] = arith.cmpf ogt, %[[lhs]], %[[rhs]] : f32
// CHECK: %[[sel:.*]] = arith.select %[[res]], %[[lhs]], %[[rhs]] : f32
// CHECK: affine.store %[[sel]], %[[v0]][%[[a0]], %[[a1]], %[[a2]], %[[a3]]] :
// CHECK: %[[lhs:.*]] = affine.load %[[v0]][0, %[[a0]], %[[a1]], %[[a2]]] :
// CHECK: %[[rhs:.*]] = affine.load %[[v1]][0, %[[a0]] * 2 + %[[a3]], %[[a1]] * 2 + %[[a4]], %[[a2]]] :
// CHECK: %[[res:.*]] = arith.cmpf ogt, %[[lhs]], %[[rhs]] : f32
// CHECK: %[[sel:.*]] = arith.select %[[res]], %[[lhs]], %[[rhs]] : f32
// CHECK: affine.store %[[sel]], %[[v0]][0, %[[a0]], %[[a1]], %[[a2]]] :

// CHECK-LABEL: func @symbols(
func.func @symbols(%N : index) {
Expand Down