37#include "mlir/Dialect/Arith/IR/Arith.h"
38#include "mlir/Dialect/Func/IR/FuncOps.h"
39#include "mlir/Dialect/MemRef/IR/MemRef.h"
40#include "mlir/Dialect/Ptr/IR/PtrOps.h"
41#include "mlir/Dialect/SCF/IR/SCF.h"
42#include "mlir/Dialect/Vector/IR/VectorOps.h"
43#include "mlir/IR/PatternMatch.h"
44#include "mlir/Pass/Pass.h"
45#include "mlir/Transforms/DialectConversion.h"
46#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
48#include "llvm/ADT/DenseMap.h"
49#include "llvm/ADT/DenseSet.h"
50#include "llvm/ADT/SetVector.h"
52#define DEBUG_TYPE "aie-vector-to-pointer-loops"
61static bool isLoopCarriedValue(Value val, scf::ForOp forOp) {
62 auto blockArgs = forOp.getRegionIterArgs();
63 return llvm::is_contained(blockArgs, val);
67struct MemrefVectorAccess {
69 SmallVector<vector::LoadOp> loads;
70 SmallVector<vector::StoreOp> stores;
71 SmallVector<Value> indices;
75static bool analyzeLoopForVectorAccesses(
76 scf::ForOp forOp, DenseMap<Value, MemrefVectorAccess> &memrefAccesses) {
78 bool foundPattern =
false;
80 forOp.walk([&](vector::LoadOp loadOp) {
81 Value base = loadOp.getBase();
82 auto indices = loadOp.getIndices();
86 if (indices.size() != 1)
89 Value idx = indices[0];
92 if (isLoopCarriedValue(idx, forOp)) {
93 memrefAccesses[base].memref = base;
94 memrefAccesses[base].loads.push_back(loadOp);
95 memrefAccesses[base].indices.push_back(idx);
100 forOp.walk([&](vector::StoreOp storeOp) {
101 Value base = storeOp.getBase();
102 auto indices = storeOp.getIndices();
104 if (indices.size() != 1)
107 Value idx = indices[0];
109 if (isLoopCarriedValue(idx, forOp)) {
110 memrefAccesses[base].memref = base;
111 memrefAccesses[base].stores.push_back(storeOp);
112 memrefAccesses[base].indices.push_back(idx);
127static bool canConvertIndexIterArg(scf::ForOp forOp,
unsigned pos, Value
memref,
128 DenseSet<Value> &chain) {
129 if (!forOp.isDefinedOutsideOfLoop(
memref))
131 if (!forOp.getResult(pos).use_empty())
134 Block *body = forOp.getBody();
135 Operation *yield = body->getTerminator();
136 SmallVector<Value> worklist = {forOp.getRegionIterArgs()[pos]};
137 chain.insert(worklist.begin(), worklist.end());
138 while (!worklist.empty()) {
139 Value v = worklist.pop_back_val();
140 for (OpOperand &use : v.getUses()) {
141 Operation *user = use.getOwner();
142 if (user->getBlock() != body)
145 if (use.getOperandNumber() != pos)
149 if (
auto load = dyn_cast<vector::LoadOp>(user)) {
150 if (load.getBase() !=
memref || load.getIndices().size() != 1 ||
151 load.getIndices()[0] != v)
155 if (
auto store = dyn_cast<vector::StoreOp>(user)) {
156 if (store.getBase() !=
memref || store.getIndices().size() != 1 ||
157 store.getIndices()[0] != v || store.getValueToStore() == v)
161 if (
auto addi = dyn_cast<arith::AddIOp>(user)) {
163 if (chain.contains(addi.getLhs()) && chain.contains(addi.getRhs()))
165 if (chain.insert(addi.getResult()).second)
166 worklist.push_back(addi.getResult());
173 return chain.contains(yield->getOperand(pos));
180 LogicalResult matchAndRewrite(scf::ForOp forOp,
181 PatternRewriter &rewriter)
const override {
184 for (Value iterArg : forOp.getRegionIterArgs()) {
185 if (llvm::isa<ptr::PtrType>(iterArg.getType()))
190 DenseMap<Value, MemrefVectorAccess> memrefAccesses;
191 if (!analyzeLoopForVectorAccesses(forOp, memrefAccesses))
194 if (memrefAccesses.empty())
200 SmallVector<unsigned> indexIterArgPositions;
201 SmallVector<Value> correspondingMemrefs;
205 DenseSet<Value> converted;
206 for (
auto [idx, iterArg] : llvm::enumerate(forOp.getRegionIterArgs())) {
208 for (
auto &[
memref, access] : memrefAccesses) {
209 if (!llvm::is_contained(access.indices, iterArg)) {
220 DenseSet<Value> chain;
221 if (!canConvertIndexIterArg(forOp, idx, owner, chain)) {
224 for (Value v : chain) {
225 if (!converted.insert(v).second) {
229 indexIterArgPositions.push_back(idx);
230 correspondingMemrefs.push_back(owner);
234 if (indexIterArgPositions.empty())
237 Location loc = forOp.getLoc();
238 OpBuilder::InsertionGuard guard(rewriter);
239 rewriter.setInsertionPoint(forOp);
242 DenseMap<Value, Value> memrefToPtrMap;
243 DenseMap<Value, Type>
244 memrefToGenericTypeMap;
245 DenseMap<Value, Value>
246 memrefToConvertedMap;
247 auto genericSpace = ptr::GenericSpaceAttr::get(rewriter.getContext());
249 for (
auto &[
memref, access] : memrefAccesses) {
251 auto memrefType = cast<MemRefType>(
memref.getType());
252 Attribute memorySpace = memrefType.getMemorySpace();
254 Value memrefToConvert =
memref;
255 Type genericMemrefType = memrefType;
260 if (!llvm::isa_and_nonnull<ptr::GenericSpaceAttr>(memorySpace)) {
263 MemRefType::get(memrefType.getShape(), memrefType.getElementType(),
264 memrefType.getLayout(), genericSpace);
267 auto castOp = UnrealizedConversionCastOp::create(rewriter, loc,
269 memrefToConvert = castOp.getResult(0);
270 genericMemrefType = newMemrefType;
274 auto ptrType = ptr::PtrType::get(rewriter.getContext(), genericSpace);
276 ptr::ToPtrOp::create(rewriter, loc, ptrType, memrefToConvert);
277 memrefToPtrMap[
memref] = ptrOp.getResult();
278 memrefToGenericTypeMap[
memref] = genericMemrefType;
279 memrefToConvertedMap[
memref] =
284 SmallVector<Value> newInitArgs;
285 Value c0 = arith::ConstantIndexOp::create(rewriter, loc, 0);
287 for (
auto [idx, initArg] : llvm::enumerate(forOp.getInitArgs())) {
288 auto *it = llvm::find(indexIterArgPositions, idx);
289 if (it != indexIterArgPositions.end()) {
291 size_t pos = std::distance(indexIterArgPositions.begin(), it);
292 Value
memref = correspondingMemrefs[pos];
293 Value basePtr = memrefToPtrMap[
memref];
296 auto memrefType = cast<MemRefType>(
memref.getType());
297 unsigned elementSizeBits = memrefType.getElementTypeBitWidth();
298 unsigned elementSizeBytes = (elementSizeBits + 7) / 8;
301 Value byteOffset = initArg;
302 if (elementSizeBytes != 1) {
304 arith::ConstantIndexOp::create(rewriter, loc, elementSizeBytes);
306 arith::MulIOp::create(rewriter, loc, initArg, elementSize);
311 ptr::PtrAddOp::create(rewriter, loc, basePtr, byteOffset);
312 newInitArgs.push_back(initPtrOp.getResult());
315 newInitArgs.push_back(initArg);
321 scf::ForOp::create(rewriter, loc, forOp.getLowerBound(),
322 forOp.getUpperBound(), forOp.getStep(), newInitArgs);
325 newForOp->setAttrs(forOp->getAttrDictionary());
329 mapper.map(forOp.getInductionVar(), newForOp.getInductionVar());
331 for (
auto [oldArg, newArg] :
332 llvm::zip(forOp.getRegionIterArgs(), newForOp.getRegionIterArgs())) {
333 mapper.map(oldArg, newArg);
339 DenseMap<Value, Value> ptrToMemref;
341 llvm::zip(indexIterArgPositions, correspondingMemrefs)) {
342 ptrToMemref[newForOp.getRegionIterArgs()[pos]] =
memref;
345 rewriter.setInsertionPointToStart(newForOp.getBody());
348 for (Operation &op : forOp.getBody()->without_terminator()) {
350 if (
auto loadOp = dyn_cast<vector::LoadOp>(&op)) {
351 Value idx = loadOp.getIndices()[0];
352 Value mappedIdx = mapper.lookupOrDefault(idx);
355 if (llvm::isa<ptr::PtrType>(mappedIdx.getType())) {
357 Type genericType = memrefToGenericTypeMap[loadOp.getBase()];
360 Value convertedMemref = memrefToConvertedMap[loadOp.getBase()];
361 auto metadataOp = ptr::GetMetadataOp::create(
362 rewriter, loadOp.getLoc(), convertedMemref);
367 ptr::FromPtrOp::create(rewriter, loadOp.getLoc(), genericType,
368 mappedIdx, metadataOp.getResult());
369 auto newLoad = vector::LoadOp::create(
370 rewriter, loadOp.getLoc(), loadOp.getVectorType(),
371 fromPtrOp.getResult(), ValueRange{c0});
372 mapper.map(loadOp.getResult(), newLoad.getResult());
378 if (
auto storeOp = dyn_cast<vector::StoreOp>(&op)) {
379 Value idx = storeOp.getIndices()[0];
380 Value mappedIdx = mapper.lookupOrDefault(idx);
382 if (llvm::isa<ptr::PtrType>(mappedIdx.getType())) {
384 Type genericType = memrefToGenericTypeMap[storeOp.getBase()];
387 Value convertedMemref = memrefToConvertedMap[storeOp.getBase()];
388 auto metadataOp = ptr::GetMetadataOp::create(
389 rewriter, storeOp.getLoc(), convertedMemref);
394 ptr::FromPtrOp::create(rewriter, storeOp.getLoc(), genericType,
395 mappedIdx, metadataOp.getResult());
397 mapper.lookupOrDefault(storeOp.getValueToStore());
398 vector::StoreOp::create(rewriter, storeOp.getLoc(), valueToStore,
399 fromPtrOp.getResult(), ValueRange{c0});
405 if (
auto addiOp = dyn_cast<arith::AddIOp>(&op)) {
406 Value lhs = mapper.lookupOrDefault(addiOp.getLhs());
407 Value rhs = mapper.lookupOrDefault(addiOp.getRhs());
408 if (llvm::isa<ptr::PtrType>(rhs.getType())) {
413 if (llvm::isa<ptr::PtrType>(lhs.getType())) {
414 Value memrefForPtr = ptrToMemref.lookup(lhs);
416 Value byteOffset = rhs;
418 auto memrefType = cast<MemRefType>(memrefForPtr.getType());
419 unsigned elementSizeBits = memrefType.getElementTypeBitWidth();
420 unsigned elementSizeBytes = (elementSizeBits + 7) / 8;
423 if (elementSizeBytes != 1) {
424 Value elementSize = arith::ConstantIndexOp::create(
425 rewriter, addiOp.getLoc(), elementSizeBytes);
426 byteOffset = arith::MulIOp::create(rewriter, addiOp.getLoc(), rhs,
432 ptr::PtrAddOp::create(rewriter, addiOp.getLoc(), lhs, byteOffset);
433 ptrToMemref[ptrAddOp.getResult()] = memrefForPtr;
434 mapper.map(addiOp.getResult(), ptrAddOp.getResult());
440 rewriter.clone(op, mapper);
444 auto oldYield = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
445 SmallVector<Value> newYieldOperands;
446 for (Value operand : oldYield.getOperands()) {
447 newYieldOperands.push_back(mapper.lookupOrDefault(operand));
449 scf::YieldOp::create(rewriter, loc, newYieldOperands);
452 rewriter.replaceOp(forOp, newForOp.getResults());
461struct AIEVectorToPointerLoopsPass
462 :
public PassWrapper<AIEVectorToPointerLoopsPass, OperationPass<DeviceOp>> {
464 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(AIEVectorToPointerLoopsPass)
466 StringRef getArgument()
const override {
467 return "aie-vector-to-pointer-loops";
470 StringRef getDescription()
const override {
471 return "Transform vector.load/store with loop-carried indices to use ptr "
475 void getDependentDialects(DialectRegistry ®istry)
const override {
476 registry.insert<ptr::PtrDialect>();
477 registry.insert<scf::SCFDialect>();
478 registry.insert<vector::VectorDialect>();
479 registry.insert<arith::ArithDialect>();
480 registry.insert<memref::MemRefDialect>();
483 void runOnOperation()
override {
484 DeviceOp deviceOp = getOperation();
486 RewritePatternSet patterns(&getContext());
487 patterns.add<VectorToPointerLoopsPattern>(&getContext());
491 if (failed(applyPatternsGreedily(deviceOp, std::move(patterns)))) {
503 return std::make_unique<AIEVectorToPointerLoopsPass>();
std::unique_ptr< mlir::OperationPass< DeviceOp > > createAIEVectorToPointerLoopsPass()