Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions include/Conversion/ConversionPasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ std::unique_ptr<mlir::Pass> createLowerAffineToNeuraPass();
// TaskFlow Conversion Passes.
std::unique_ptr<mlir::Pass> createConvertAffineToTaskflowPass();
std::unique_ptr<mlir::Pass> createConvertTaskflowToNeuraPass();

// Memref SubView and Copy Conversion Passes.
std::unique_ptr<mlir::Pass> createFoldSubViewPass();
std::unique_ptr<mlir::Pass> createConvertCopyToAffineLoopsPass();
#define GEN_PASS_REGISTRATION
#include "Conversion/ConversionPasses.h.inc"

Expand Down
31 changes: 31 additions & 0 deletions include/Conversion/ConversionPasses.td
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,35 @@ def ConvertTaskflowToNeura : Pass<"convert-taskflow-to-neura", "ModuleOp">{
];
}

def FoldSubView : Pass<"fold-subview", "func::FuncOp"> {
let summary = "Folds memref.subview into affine load/store operations";
let description = [{
Eliminates memref.subview operations by folding them into their users
(affine.load and affine.store). Adjusts indices to account for subview
offsets and strides, enabling direct access to the source memref.
}];
let constructor = "mlir::createFoldSubViewPass()";
let dependentDialects = [
"mlir::affine::AffineDialect",
"mlir::memref::MemRefDialect",
"mlir::arith::ArithDialect",
"mlir::func::FuncDialect"
];
}

def ConvertCopyToAffineLoops : Pass<"convert-copy-to-affine-loops", "func::FuncOp"> {
let summary = "Converts memref.copy to explicit affine loop nests";
let description = [{
Converts memref.copy operations into explicit affine.for loop nests
with affine.load and affine.store operations. This makes data movement
explicit for CGRA compilation with global addressing.
}];
let constructor = "mlir::createConvertCopyToAffineLoopsPass()";
let dependentDialects = [
"mlir::affine::AffineDialect",
"mlir::memref::MemRefDialect",
"mlir::func::FuncDialect"
];
}

#endif // CONVERSION_PASSES_TD
4 changes: 4 additions & 0 deletions include/TaskflowDialect/TaskflowPasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
#include <memory>
namespace mlir {
namespace taskflow {

void registerTaskflowConversionPassPipeline();
void registerTosaToAffineConversionPassPipeline();

// Passes defined in TaskflowPasses.td
#define GEN_PASS_DECL
#include "TaskflowDialect/TaskflowPasses.h.inc"
Expand Down
2 changes: 2 additions & 0 deletions lib/Conversion/AffineToTaskflow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})

add_mlir_conversion_library(MLIRAffineToTaskflowPass
AffineToTaskflowPass.cpp
FoldSubViewPass.cpp
ConvertCopyToAffineLoopsPass.cpp

DEPENDS
MLIRConversionIncGen
Expand Down
99 changes: 99 additions & 0 deletions lib/Conversion/AffineToTaskflow/ConvertCopyToAffineLoopsPass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//===- ConvertCopyToAffineLoopsPass.cpp - Converts memref.copy to loops ---===//
//
// This pass converts memref.copy operations into explicit affine loop nests.
//
//===----------------------------------------------------------------------===//

#include "Conversion/ConversionPasses.h"
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"

using namespace mlir;

namespace {
// Converts memref.copy to nested affine loops with affine.load/store.
struct CopyOpLoweringPattern : public OpRewritePattern<memref::CopyOp> {
using OpRewritePattern<memref::CopyOp>::OpRewritePattern;

LogicalResult matchAndRewrite(memref::CopyOp copy,
PatternRewriter &rewriter) const override {
// Checks if the target has any users besides this copy.
// If the target (e.g., a subview) is only used by this copy and nothing
// else, this copy is dead code and should be removed without conversion.
Value target = copy.getTarget();
bool has_other_users = false;
for (auto *user : target.getUsers()) {
if (user != copy.getOperation()) {
has_other_users = true;
break;
}
}

if (!has_other_users) {
// Target has no users besides this copy, so just erase the copy.
rewriter.eraseOp(copy);
return success();
}

// Target has other users, convert copy to affine loops.
rewriter.setInsertionPoint(copy);
auto loc = copy.getLoc();
MemRefType memref_type = dyn_cast<MemRefType>(copy.getSource().getType());

// Creates explicit memory copy using an affine loop nest.
SmallVector<Value> ivs;
for (auto dim_size : memref_type.getShape()) {
auto loop = rewriter.create<affine::AffineForOp>(loc, 0, dim_size);
rewriter.setInsertionPointToStart(loop.getBody());
ivs.push_back(loop.getInductionVar());
}

// Creates affine load from source and store to target.
Value value =
rewriter.create<affine::AffineLoadOp>(loc, copy.getSource(), ivs);
rewriter.create<affine::AffineStoreOp>(loc, value, copy.getTarget(), ivs);

rewriter.eraseOp(copy);
return success();
}
};
} // namespace

//===----------------------------------------------------------------------===//
// Pass implementation
//===----------------------------------------------------------------------===//

namespace {
struct ConvertCopyToAffineLoopsPass
: public PassWrapper<ConvertCopyToAffineLoopsPass,
OperationPass<func::FuncOp>> {
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ConvertCopyToAffineLoopsPass)

StringRef getArgument() const final { return "convert-copy-to-affine-loops"; }

StringRef getDescription() const final {
return "Convert memref.copy to explicit affine loop nests";
}

void getDependentDialects(DialectRegistry &registry) const override {
registry.insert<affine::AffineDialect, memref::MemRefDialect,
func::FuncDialect>();
}

void runOnOperation() override {
RewritePatternSet patterns(&getContext());
patterns.add<CopyOpLoweringPattern>(&getContext());
if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) {
signalPassFailure();
}
}
};
} // namespace

std::unique_ptr<Pass> mlir::createConvertCopyToAffineLoopsPass() {
return std::make_unique<ConvertCopyToAffineLoopsPass>();
}
191 changes: 191 additions & 0 deletions lib/Conversion/AffineToTaskflow/FoldSubViewPass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
//===- FoldSubViewPass.cpp - Fold memref.subview into load/store ---------===//
//
// This pass folds memref.subview operations into their affine.load and
// affine.store users by adjusting the access indices. Designed for CGRA
// systems with global addressing.
//
//===----------------------------------------------------------------------===//

#include "Conversion/ConversionPasses.h"
#include "mlir/Dialect/Affine/IR/AffineOps.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "llvm/ADT/SmallBitVector.h"

using namespace mlir;

//===----------------------------------------------------------------------===//
// Utility functions.
//===----------------------------------------------------------------------===//

// Resolves the source indices for a load/store operation that accesses a
// subview. Computes the adjusted indices to access the source memref directly.
//
// For example:
// %subview = memref.subview %source[%offset0, %offset1][...][%stride0,
// %stride1] %val = affine.load %subview[%i, %j]
// becomes:
// %val = affine.load %source[%i * %stride0 + %offset0, %j * %stride1 +
// %offset1]
static LogicalResult
resolveSourceIndices(Location loc, PatternRewriter &rewriter,
memref::SubViewOp sub_view_op, ValueRange indices,
SmallVectorImpl<Value> &source_indices) {
SmallVector<OpFoldResult> mixed_offsets = sub_view_op.getMixedOffsets();
SmallVector<OpFoldResult> mixed_sizes = sub_view_op.getMixedSizes();
SmallVector<OpFoldResult> mixed_strides = sub_view_op.getMixedStrides();

SmallVector<Value> use_indices;
// Handles rank-reducing subviews: for every unit-dim size, adds a zero index.
unsigned result_dim = 0;
llvm::SmallBitVector unused_dims = sub_view_op.getDroppedDims();
for (auto dim :
llvm::seq<unsigned>(0, sub_view_op.getSourceType().getRank())) {
if (unused_dims.test(dim)) {
use_indices.push_back(rewriter.create<arith::ConstantIndexOp>(loc, 0));
} else {
use_indices.push_back(indices[result_dim++]);
}
}

if (use_indices.size() != mixed_offsets.size()) {
return failure();
}

source_indices.resize(use_indices.size());
for (auto index : llvm::seq<size_t>(0, mixed_offsets.size())) {
SmallVector<Value> dynamic_operands;
AffineExpr expr = rewriter.getAffineDimExpr(0);
unsigned num_symbols = 0;
dynamic_operands.push_back(use_indices[index]);

// Multiplies by stride: index * stride.
if (auto attr = mixed_strides[index].dyn_cast<Attribute>()) {
int64_t stride_val = dyn_cast<IntegerAttr>(attr).getInt();
if (stride_val != 1) {
expr = expr * stride_val;
}
} else {
dynamic_operands.push_back(dyn_cast<Value>(mixed_strides[index]));
expr = expr * rewriter.getAffineSymbolExpr(num_symbols++);
}

// Adds offset: index * stride + offset.
if (auto attr = mixed_offsets[index].dyn_cast<Attribute>()) {
int64_t offset_val = dyn_cast<IntegerAttr>(attr).getInt();
if (offset_val != 0) {
expr = expr + offset_val;
}
} else {
dynamic_operands.push_back(dyn_cast<Value>(mixed_offsets[index]));
expr = expr + rewriter.getAffineSymbolExpr(num_symbols++);
}

source_indices[index] = rewriter.create<affine::AffineApplyOp>(
loc, AffineMap::get(1, num_symbols, expr), dynamic_operands);
}
return success();
}

//===----------------------------------------------------------------------===//
// Patterns
//===----------------------------------------------------------------------===//

namespace {
/// Folds affine.load from a subview into a load from the source memref.
class LoadOpOfSubViewFolder final
: public OpRewritePattern<affine::AffineLoadOp> {
public:
using OpRewritePattern<affine::AffineLoadOp>::OpRewritePattern;

LogicalResult matchAndRewrite(affine::AffineLoadOp loadOp,
PatternRewriter &rewriter) const override {
auto subViewOp = loadOp.getMemRef().getDefiningOp<memref::SubViewOp>();
if (!subViewOp)
return failure();

SmallVector<Value, 4> sourceIndices;
if (failed(resolveSourceIndices(loadOp.getLoc(), rewriter, subViewOp,
loadOp.getIndices(), sourceIndices)))
return failure();

rewriter.replaceOpWithNewOp<affine::AffineLoadOp>(
loadOp, subViewOp.getSource(), sourceIndices);
return success();
}
};

/// Folds affine.store to a subview into a store to the source memref.
class StoreOpOfSubViewFolder final
: public OpRewritePattern<affine::AffineStoreOp> {
public:
using OpRewritePattern<affine::AffineStoreOp>::OpRewritePattern;

LogicalResult matchAndRewrite(affine::AffineStoreOp storeOp,
PatternRewriter &rewriter) const override {
auto subViewOp = storeOp.getMemRef().getDefiningOp<memref::SubViewOp>();
if (!subViewOp)
return failure();

SmallVector<Value, 4> sourceIndices;
if (failed(resolveSourceIndices(storeOp.getLoc(), rewriter, subViewOp,
storeOp.getIndices(), sourceIndices)))
return failure();

rewriter.replaceOpWithNewOp<affine::AffineStoreOp>(
storeOp, storeOp.getValue(), subViewOp.getSource(), sourceIndices);
return success();
}
};
} // namespace

//===----------------------------------------------------------------------===//
// Pass implementation
//===----------------------------------------------------------------------===//

namespace {
struct FoldSubViewPass
: public PassWrapper<FoldSubViewPass, OperationPass<func::FuncOp>> {
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(FoldSubViewPass)

StringRef getArgument() const final { return "fold-subview"; }

StringRef getDescription() const final {
return "Fold memref.subview into affine load/store operations";
}

void getDependentDialects(DialectRegistry &registry) const override {
registry.insert<affine::AffineDialect, memref::MemRefDialect,
arith::ArithDialect, func::FuncDialect>();
}

void runOnOperation() override {
// Step 1: Folds subviews into their load/store users.
RewritePatternSet patterns(&getContext());
patterns.add<LoadOpOfSubViewFolder, StoreOpOfSubViewFolder>(&getContext());
if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) {
signalPassFailure();
}

// Step 2: Cleans up dead subview operations that have no remaining users.
SmallVector<memref::SubViewOp> dead_sub_views;
getOperation().walk([&](memref::SubViewOp sub_view_op) {
if (sub_view_op->use_empty()) {
dead_sub_views.push_back(sub_view_op);
}
});

for (auto sub_view_op : dead_sub_views) {
sub_view_op.erase();
}
}
};
} // namespace

std::unique_ptr<Pass> mlir::createFoldSubViewPass() {
return std::make_unique<FoldSubViewPass>();
}
Loading