diff --git a/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td b/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td --- a/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td +++ b/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td @@ -109,7 +109,8 @@ def AffineForOp : Affine_Op<"for", [AutomaticAllocationScope, ImplicitAffineTerminator, RecursiveSideEffects, DeclareOpInterfaceMethods]> { + ["getSingleInductionVar", "getSingleLowerBound", "getSingleStep"]>, + DeclareOpInterfaceMethods]> { let summary = "for operation"; let description = [{ Syntax: @@ -339,6 +340,12 @@ /// Returns true if both the lower and upper bound have the same operand /// lists (same operands in the same order). bool matchingBoundOperandList(); + + /// Return operands used when entering the region at 'index'. These operands + /// correspond to the loop iterator operands, i.e., those excluding the + /// induction variable. AffineForOp only has one region, so zero is the only + /// valid value for `index`. + OperandRange getSuccessorEntryOperands(unsigned index); }]; let hasCanonicalizer = 1; diff --git a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp --- a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp +++ b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp @@ -1723,6 +1723,39 @@ results.add(context); } +/// Return operands used when entering the region at 'index'. These operands +/// correspond to the loop iterator operands, i.e., those excluding the +/// induction variable. LoopOp only has one region, so 0 is the only valid value +/// for `index`. +OperandRange AffineForOp::getSuccessorEntryOperands(unsigned index) { + assert(index == 0 && "invalid region index"); + + // The initial operands map to the loop arguments after the induction + // variable. + return getIterOperands(); +} + +/// Given the region at `index`, or the parent operation if `index` is None, +/// return the successor regions. These are the regions that may be selected +/// during the flow of control. `operands` is a set of optional attributes that +/// correspond to a constant value for each operand, or null if that operand is +/// not a constant. +void AffineForOp::getSuccessorRegions( + Optional index, ArrayRef operands, + SmallVectorImpl ®ions) { + // If the predecessor is the affine.for op, branch into the body using the + // iterator arguments. + if (!index.hasValue()) { + regions.push_back(RegionSuccessor(&getLoopBody(), getRegionIterArgs())); + return; + } + + // Otherwise, the loop may branch back to itself or the parent operation. + assert(index.getValue() == 0 && "expected loop region"); + regions.push_back(RegionSuccessor(&getLoopBody(), getRegionIterArgs())); + regions.push_back(RegionSuccessor(getResults())); +} + /// Returns true if the affine.for has zero iterations in trivial cases. static bool hasTrivialZeroTripCount(AffineForOp op) { Optional tripCount = getTrivialConstantTripCount(op); diff --git a/mlir/test/Dialect/Bufferization/Transforms/buffer-deallocation.mlir b/mlir/test/Dialect/Bufferization/Transforms/buffer-deallocation.mlir --- a/mlir/test/Dialect/Bufferization/Transforms/buffer-deallocation.mlir +++ b/mlir/test/Dialect/Bufferization/Transforms/buffer-deallocation.mlir @@ -1092,6 +1092,24 @@ // ----- +// CHECK-LABEL: func @affine_loop +func @affine_loop() { + %buffer = memref.alloc() : memref<1024xf32> + %sum_init_0 = arith.constant 0.0 : f32 + %res = affine.for %i = 0 to 10 step 2 iter_args(%sum_iter = %sum_init_0) -> f32 { + %t = affine.load %buffer[%i] : memref<1024xf32> + %sum_next = arith.addf %sum_iter, %t : f32 + affine.yield %sum_next : f32 + } + // CHECK: %[[M:.*]] = memref.alloc + // CHECK: affine.for + // CHECK: } + // CHECK-NEXT: memref.dealloc %[[M]] + return +} + +// ----- + // Test Case: explicit control-flow loop with a dynamically allocated buffer. // The BufferDeallocation transformation should fail on this explicit // control-flow loop since they are not supported.