Skip to content
Merged
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
8 changes: 7 additions & 1 deletion mlir/include/mlir/Dialect/Affine/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class FuncOp;

namespace memref {
class AllocOp;
class AllocaOp;
} // namespace memref

namespace affine {
Expand Down Expand Up @@ -245,7 +246,12 @@ LogicalResult replaceAllMemRefUsesWith(Value oldMemRef, Value newMemRef,
/// Rewrites the memref defined by this alloc op to have an identity layout map
/// and updates all its indexing uses. Returns failure if any of its uses
/// escape (while leaving the IR in a valid state).
LogicalResult normalizeMemRef(memref::AllocOp *op);
template <typename AllocLikeOp>
LogicalResult normalizeMemRef(AllocLikeOp *op);
extern template LogicalResult
normalizeMemRef<memref::AllocaOp>(memref::AllocaOp *op);
extern template LogicalResult
normalizeMemRef<memref::AllocOp>(memref::AllocOp *op);

/// Normalizes `memrefType` so that the affine layout map of the memref is
/// transformed to an identity map with a new shape being computed for the
Expand Down
21 changes: 14 additions & 7 deletions mlir/lib/Dialect/Affine/Utils/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1737,9 +1737,10 @@ static AffineExpr createDimSizeExprForTiledLayout(AffineExpr oldMapOutput,
/// %c4 = arith.constant 4 : index
/// %1 = affine.apply #map1(%c4, %0)
/// %2 = affine.apply #map2(%c4, %0)
template <typename AllocLikeOp>
static void createNewDynamicSizes(MemRefType oldMemRefType,
MemRefType newMemRefType, AffineMap map,
memref::AllocOp *allocOp, OpBuilder b,
AllocLikeOp *allocOp, OpBuilder b,
SmallVectorImpl<Value> &newDynamicSizes) {
// Create new input for AffineApplyOp.
SmallVector<Value, 4> inAffineApply;
Expand Down Expand Up @@ -1786,7 +1787,8 @@ static void createNewDynamicSizes(MemRefType oldMemRefType,
}

// TODO: Currently works for static memrefs with a single layout map.
LogicalResult mlir::affine::normalizeMemRef(memref::AllocOp *allocOp) {
template <typename AllocLikeOp>
LogicalResult mlir::affine::normalizeMemRef(AllocLikeOp *allocOp) {
MemRefType memrefType = allocOp->getType();
OpBuilder b(*allocOp);

Expand All @@ -1802,7 +1804,7 @@ LogicalResult mlir::affine::normalizeMemRef(memref::AllocOp *allocOp) {

SmallVector<Value, 4> symbolOperands(allocOp->getSymbolOperands());
AffineMap layoutMap = memrefType.getLayout().getAffineMap();
memref::AllocOp newAlloc;
AllocLikeOp newAlloc;
// Check if `layoutMap` is a tiled layout. Only single layout map is
// supported for normalizing dynamic memrefs.
SmallVector<std::tuple<AffineExpr, unsigned, unsigned>> tileSizePos;
Expand All @@ -1814,11 +1816,11 @@ LogicalResult mlir::affine::normalizeMemRef(memref::AllocOp *allocOp) {
newDynamicSizes);
// Add the new dynamic sizes in new AllocOp.
newAlloc =
b.create<memref::AllocOp>(allocOp->getLoc(), newMemRefType,
newDynamicSizes, allocOp->getAlignmentAttr());
b.create<AllocLikeOp>(allocOp->getLoc(), newMemRefType, newDynamicSizes,
allocOp->getAlignmentAttr());
} else {
newAlloc = b.create<memref::AllocOp>(allocOp->getLoc(), newMemRefType,
allocOp->getAlignmentAttr());
newAlloc = b.create<AllocLikeOp>(allocOp->getLoc(), newMemRefType,
allocOp->getAlignmentAttr());
}
// Replace all uses of the old memref.
if (failed(replaceAllMemRefUsesWith(oldMemRef, /*newMemRef=*/newAlloc,
Expand All @@ -1843,6 +1845,11 @@ LogicalResult mlir::affine::normalizeMemRef(memref::AllocOp *allocOp) {
return success();
}

template LogicalResult
mlir::affine::normalizeMemRef<memref::AllocaOp>(memref::AllocaOp *op);
template LogicalResult
mlir::affine::normalizeMemRef<memref::AllocOp>(memref::AllocOp *op);

MemRefType mlir::affine::normalizeMemRefType(MemRefType memrefType) {
unsigned rank = memrefType.getRank();
if (rank == 0)
Expand Down
34 changes: 25 additions & 9 deletions mlir/lib/Dialect/MemRef/Transforms/NormalizeMemRefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,11 @@ void NormalizeMemRefs::setCalleesAndCallersNonNormalizable(
});
}

/// Check whether all the uses of AllocOps, CallOps and function arguments of a
/// function are either of dereferencing type or are uses in: DeallocOp, CallOp
/// or ReturnOp. Only if these constraints are satisfied will the function
/// become a candidate for normalization. When the uses of a memref are
/// non-normalizable and the memref map layout is trivial (identity), we can
/// Check whether all the uses of AllocOps, AllocaOps, CallOps and function
/// arguments of a function are either of dereferencing type or are uses in:
/// DeallocOp, CallOp or ReturnOp. Only if these constraints are satisfied will
/// the function become a candidate for normalization. When the uses of a memref
/// are non-normalizable and the memref map layout is trivial (identity), we can
/// still label the entire function as normalizable. We assume external
/// functions to be normalizable.
bool NormalizeMemRefs::areMemRefsNormalizable(func::FuncOp funcOp) {
Expand All @@ -174,6 +174,17 @@ bool NormalizeMemRefs::areMemRefsNormalizable(func::FuncOp funcOp) {
.wasInterrupted())
return false;

if (funcOp
.walk([&](memref::AllocaOp allocaOp) -> WalkResult {
Value oldMemRef = allocaOp.getResult();
if (!allocaOp.getType().getLayout().isIdentity() &&
!isMemRefNormalizable(oldMemRef.getUsers()))
return WalkResult::interrupt();
return WalkResult::advance();
})
.wasInterrupted())
return false;

if (funcOp
.walk([&](func::CallOp callOp) -> WalkResult {
for (unsigned resIndex :
Expand Down Expand Up @@ -335,18 +346,23 @@ void NormalizeMemRefs::updateFunctionSignature(func::FuncOp funcOp,
}

/// Normalizes the memrefs within a function which includes those arising as a
/// result of AllocOps, CallOps and function's argument. The ModuleOp argument
/// is used to help update function's signature after normalization.
/// result of AllocOps, AllocaOps, CallOps and function's argument. The ModuleOp
/// argument is used to help update function's signature after normalization.
void NormalizeMemRefs::normalizeFuncOpMemRefs(func::FuncOp funcOp,
ModuleOp moduleOp) {
// Turn memrefs' non-identity layouts maps into ones with identity. Collect
// alloc ops first and then process since normalizeMemRef replaces/erases ops
// during memref rewriting.
// alloc/alloca ops first and then process since normalizeMemRef
// replaces/erases ops during memref rewriting.
SmallVector<memref::AllocOp, 4> allocOps;
funcOp.walk([&](memref::AllocOp op) { allocOps.push_back(op); });
for (memref::AllocOp allocOp : allocOps)
(void)normalizeMemRef(&allocOp);

SmallVector<memref::AllocaOp> allocaOps;
funcOp.walk([&](memref::AllocaOp op) { allocaOps.push_back(op); });
for (memref::AllocaOp allocaOp : allocaOps)
(void)normalizeMemRef(&allocaOp);

// We use this OpBuilder to create new memref layout later.
OpBuilder b(funcOp);

Expand Down
14 changes: 14 additions & 0 deletions mlir/test/Dialect/MemRef/normalize-memrefs.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ func.func @permute() {
// CHECK-NEXT: memref.dealloc [[MEM]]
// CHECK-NEXT: return

// CHECK-LABEL: func @alloca
func.func @alloca(%idx : index) {
// CHECK-NEXT: memref.alloca() : memref<65xf32>
%A = memref.alloca() : memref<64xf32, affine_map<(d0) -> (d0 + 1)>>
// CHECK-NEXT: affine.load %{{.*}}[symbol(%arg0) + 1] : memref<65xf32>
affine.load %A[%idx] : memref<64xf32, affine_map<(d0) -> (d0 + 1)>>
affine.for %i = 0 to 64 {
%1 = affine.load %A[%i] : memref<64xf32, affine_map<(d0) -> (d0 + 1)>>
"prevent.dce"(%1) : (f32) -> ()
// CHECK: %{{.*}} = affine.load %{{.*}}[%arg{{.*}} + 1] : memref<65xf32>
}
return
}

// CHECK-LABEL: func @shift
func.func @shift(%idx : index) {
// CHECK-NEXT: memref.alloc() : memref<65xf32>
Expand Down
Loading