//===- RaiseMemrefDialect.cpp - raise memref.store and load to affine ops -===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements functionality to convert memref load and store ops to // the corresponding affine ops, inferring the affine map as needed. // //===----------------------------------------------------------------------===// #include "mlir/Dialect/Affine/Analysis/Utils.h" #include "mlir/Dialect/Affine/Passes.h" #include "mlir/Dialect/Affine/Transforms/Transforms.h" #include "mlir/Dialect/Affine/Utils.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/MemRef/IR/MemRef.h" #include "mlir/IR/AffineExpr.h" #include "mlir/IR/Matchers.h" #include "mlir/IR/Operation.h" #include "mlir/Pass/Pass.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" namespace mlir { namespace affine { #define GEN_PASS_DEF_RAISEMEMREFDIALECT #include "mlir/Dialect/Affine/Passes.h.inc" } // namespace affine } // namespace mlir #define DEBUG_TYPE "raise-memref-to-affine" using namespace mlir; using namespace mlir::affine; namespace { /// Find the index of the given value in the `dims` list, /// and append it if it was not already in the list. The /// dims list is a list of symbols or dimensions of the /// affine map. Within the results of an affine map, they /// are identified by their index, which is why we need /// this function. static std::optional findInListOrAdd(Value value, llvm::SmallVectorImpl &dims, function_ref isValidElement) { Value *loopIV = llvm::find(dims, value); if (loopIV != dims.end()) { // We found an IV that already has an index, return that index. return {std::distance(dims.begin(), loopIV)}; } if (isValidElement(value)) { // This is a valid element for the dim/symbol list, push this as a // parameter. size_t idx = dims.size(); dims.push_back(value); return idx; } return std::nullopt; } /// Convert a value to an affine expr if possible. Adds dims and symbols /// if needed. static AffineExpr toAffineExpr(Value value, llvm::SmallVectorImpl &affineDims, llvm::SmallVectorImpl &affineSymbols) { using namespace matchers; IntegerAttr::ValueType cst; if (matchPattern(value, m_ConstantInt(&cst))) { return getAffineConstantExpr(cst.getSExtValue(), value.getContext()); } Operation *definingOp = value.getDefiningOp(); if (llvm::isa_and_nonnull(definingOp) || llvm::isa_and_nonnull(definingOp)) { // TODO: replace recursion with explicit stack. // For the moment this can be tolerated as we only recurse on // arith.addi and arith.muli, so there cannot be any infinite // recursion. The depth of these expressions should be in most // cases very manageable, as affine expressions should be as // simple as `a + b * c`. AffineExpr lhsE = toAffineExpr(definingOp->getOperand(0), affineDims, affineSymbols); AffineExpr rhsE = toAffineExpr(definingOp->getOperand(1), affineDims, affineSymbols); if (lhsE && rhsE) { AffineExprKind kind; if (isa(definingOp)) { kind = mlir::AffineExprKind::Add; } else { kind = mlir::AffineExprKind::Mul; if (!lhsE.isSymbolicOrConstant() && !rhsE.isSymbolicOrConstant()) { // This is not an affine expression, give up. return {}; } } return getAffineBinaryOpExpr(kind, lhsE, rhsE); } return {}; } if (auto dimIx = findInListOrAdd(value, affineSymbols, [](Value v) { return affine::isValidSymbol(v); })) { return getAffineSymbolExpr(*dimIx, value.getContext()); } if (auto dimIx = findInListOrAdd( value, affineDims, [](Value v) { return affine::isValidDim(v); })) { return getAffineDimExpr(*dimIx, value.getContext()); } return {}; } static LogicalResult computeAffineMapAndArgs(MLIRContext *ctx, ValueRange indices, AffineMap &map, llvm::SmallVectorImpl &mapArgs) { SmallVector results; SmallVector symbols; SmallVector dims; for (Value indexExpr : indices) { AffineExpr res = toAffineExpr(indexExpr, dims, symbols); if (!res) { return failure(); } results.push_back(res); } map = AffineMap::get(dims.size(), symbols.size(), results, ctx); dims.append(symbols); mapArgs.swap(dims); return success(); } struct RaiseMemrefDialect : public affine::impl::RaiseMemrefDialectBase { void runOnOperation() override { auto *ctx = &getContext(); Operation *op = getOperation(); IRRewriter rewriter(ctx); AffineMap map; SmallVector mapArgs; op->walk([&](Operation *op) { rewriter.setInsertionPoint(op); if (auto store = llvm::dyn_cast_or_null(op)) { if (succeeded(computeAffineMapAndArgs(ctx, store.getIndices(), map, mapArgs))) { rewriter.replaceOpWithNewOp( op, store.getValueToStore(), store.getMemRef(), map, mapArgs); return; } LLVM_DEBUG(llvm::dbgs() << "[affine] Cannot raise memref op: " << op << "\n"); } else if (auto load = llvm::dyn_cast_or_null(op)) { if (succeeded(computeAffineMapAndArgs(ctx, load.getIndices(), map, mapArgs))) { rewriter.replaceOpWithNewOp(op, load.getMemRef(), map, mapArgs); return; } LLVM_DEBUG(llvm::dbgs() << "[affine] Cannot raise memref op: " << op << "\n"); } }); } }; } // namespace std::unique_ptr> mlir::affine::createRaiseMemrefToAffine() { return std::make_unique(); }