147f75930SValentin Clement //===-- ArrayValueCopy.cpp ------------------------------------------------===// 247f75930SValentin Clement // 347f75930SValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 447f75930SValentin Clement // See https://llvm.org/LICENSE.txt for license information. 547f75930SValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 647f75930SValentin Clement // 747f75930SValentin Clement //===----------------------------------------------------------------------===// 847f75930SValentin Clement 947f75930SValentin Clement #include "PassDetail.h" 1047f75930SValentin Clement #include "flang/Optimizer/Builder/BoxValue.h" 1147f75930SValentin Clement #include "flang/Optimizer/Builder/FIRBuilder.h" 123ab67c3dSValentin Clement #include "flang/Optimizer/Builder/Factory.h" 1347f75930SValentin Clement #include "flang/Optimizer/Dialect/FIRDialect.h" 1447f75930SValentin Clement #include "flang/Optimizer/Support/FIRContext.h" 1547f75930SValentin Clement #include "flang/Optimizer/Transforms/Passes.h" 16ace01605SRiver Riddle #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" 1747f75930SValentin Clement #include "mlir/Dialect/SCF/SCF.h" 1847f75930SValentin Clement #include "mlir/Transforms/DialectConversion.h" 1947f75930SValentin Clement #include "llvm/Support/Debug.h" 2047f75930SValentin Clement 2147f75930SValentin Clement #define DEBUG_TYPE "flang-array-value-copy" 2247f75930SValentin Clement 2347f75930SValentin Clement using namespace fir; 24*092601d4SAndrzej Warzynski using namespace mlir; 2547f75930SValentin Clement 2647f75930SValentin Clement using OperationUseMapT = llvm::DenseMap<mlir::Operation *, mlir::Operation *>; 2747f75930SValentin Clement 2847f75930SValentin Clement namespace { 2947f75930SValentin Clement 3047f75930SValentin Clement /// Array copy analysis. 3147f75930SValentin Clement /// Perform an interference analysis between array values. 3247f75930SValentin Clement /// 3347f75930SValentin Clement /// Lowering will generate a sequence of the following form. 3447f75930SValentin Clement /// ```mlir 3547f75930SValentin Clement /// %a_1 = fir.array_load %array_1(%shape) : ... 3647f75930SValentin Clement /// ... 3747f75930SValentin Clement /// %a_j = fir.array_load %array_j(%shape) : ... 3847f75930SValentin Clement /// ... 3947f75930SValentin Clement /// %a_n = fir.array_load %array_n(%shape) : ... 4047f75930SValentin Clement /// ... 4147f75930SValentin Clement /// %v_i = fir.array_fetch %a_i, ... 4247f75930SValentin Clement /// %a_j1 = fir.array_update %a_j, ... 4347f75930SValentin Clement /// ... 4447f75930SValentin Clement /// fir.array_merge_store %a_j, %a_jn to %array_j : ... 4547f75930SValentin Clement /// ``` 4647f75930SValentin Clement /// 4747f75930SValentin Clement /// The analysis is to determine if there are any conflicts. A conflict is when 4847f75930SValentin Clement /// one the following cases occurs. 4947f75930SValentin Clement /// 5047f75930SValentin Clement /// 1. There is an `array_update` to an array value, a_j, such that a_j was 5147f75930SValentin Clement /// loaded from the same array memory reference (array_j) but with a different 5247f75930SValentin Clement /// shape as the other array values a_i, where i != j. [Possible overlapping 5347f75930SValentin Clement /// arrays.] 5447f75930SValentin Clement /// 5547f75930SValentin Clement /// 2. There is either an array_fetch or array_update of a_j with a different 5647f75930SValentin Clement /// set of index values. [Possible loop-carried dependence.] 5747f75930SValentin Clement /// 5847f75930SValentin Clement /// If none of the array values overlap in storage and the accesses are not 5947f75930SValentin Clement /// loop-carried, then the arrays are conflict-free and no copies are required. 6047f75930SValentin Clement class ArrayCopyAnalysis { 6147f75930SValentin Clement public: 6247f75930SValentin Clement using ConflictSetT = llvm::SmallPtrSet<mlir::Operation *, 16>; 6347f75930SValentin Clement using UseSetT = llvm::SmallPtrSet<mlir::OpOperand *, 8>; 6447f75930SValentin Clement using LoadMapSetsT = 6547f75930SValentin Clement llvm::DenseMap<mlir::Operation *, SmallVector<Operation *>>; 6647f75930SValentin Clement 6747f75930SValentin Clement ArrayCopyAnalysis(mlir::Operation *op) : operation{op} { construct(op); } 6847f75930SValentin Clement 6947f75930SValentin Clement mlir::Operation *getOperation() const { return operation; } 7047f75930SValentin Clement 7147f75930SValentin Clement /// Return true iff the `array_merge_store` has potential conflicts. 7247f75930SValentin Clement bool hasPotentialConflict(mlir::Operation *op) const { 7347f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 7447f75930SValentin Clement << "looking for a conflict on " << *op 7547f75930SValentin Clement << " and the set has a total of " << conflicts.size() << '\n'); 7647f75930SValentin Clement return conflicts.contains(op); 7747f75930SValentin Clement } 7847f75930SValentin Clement 7947f75930SValentin Clement /// Return the use map. The use map maps array fetch and update operations 8047f75930SValentin Clement /// back to the array load that is the original source of the array value. 8147f75930SValentin Clement const OperationUseMapT &getUseMap() const { return useMap; } 8247f75930SValentin Clement 8347f75930SValentin Clement /// Find all the array operations that access the array value that is loaded 8447f75930SValentin Clement /// by the array load operation, `load`. 8547f75930SValentin Clement const llvm::SmallVector<mlir::Operation *> &arrayAccesses(ArrayLoadOp load); 8647f75930SValentin Clement 8747f75930SValentin Clement private: 8847f75930SValentin Clement void construct(mlir::Operation *topLevelOp); 8947f75930SValentin Clement 9047f75930SValentin Clement mlir::Operation *operation; // operation that analysis ran upon 9147f75930SValentin Clement ConflictSetT conflicts; // set of conflicts (loads and merge stores) 9247f75930SValentin Clement OperationUseMapT useMap; 9347f75930SValentin Clement LoadMapSetsT loadMapSets; 9447f75930SValentin Clement }; 9547f75930SValentin Clement } // namespace 9647f75930SValentin Clement 9747f75930SValentin Clement namespace { 9847f75930SValentin Clement /// Helper class to collect all array operations that produced an array value. 9947f75930SValentin Clement class ReachCollector { 10047f75930SValentin Clement private: 10147f75930SValentin Clement // If provided, the `loopRegion` is the body of a loop that produces the array 10247f75930SValentin Clement // of interest. 10347f75930SValentin Clement ReachCollector(llvm::SmallVectorImpl<mlir::Operation *> &reach, 10447f75930SValentin Clement mlir::Region *loopRegion) 10547f75930SValentin Clement : reach{reach}, loopRegion{loopRegion} {} 10647f75930SValentin Clement 10747f75930SValentin Clement void collectArrayAccessFrom(mlir::Operation *op, mlir::ValueRange range) { 10847f75930SValentin Clement llvm::errs() << "COLLECT " << *op << "\n"; 10947f75930SValentin Clement if (range.empty()) { 11047f75930SValentin Clement collectArrayAccessFrom(op, mlir::Value{}); 11147f75930SValentin Clement return; 11247f75930SValentin Clement } 11347f75930SValentin Clement for (mlir::Value v : range) 11447f75930SValentin Clement collectArrayAccessFrom(v); 11547f75930SValentin Clement } 11647f75930SValentin Clement 11747f75930SValentin Clement // TODO: Replace recursive algorithm on def-use chain with an iterative one 11847f75930SValentin Clement // with an explicit stack. 11947f75930SValentin Clement void collectArrayAccessFrom(mlir::Operation *op, mlir::Value val) { 12047f75930SValentin Clement // `val` is defined by an Op, process the defining Op. 12147f75930SValentin Clement // If `val` is defined by a region containing Op, we want to drill down 12247f75930SValentin Clement // and through that Op's region(s). 12347f75930SValentin Clement llvm::errs() << "COLLECT " << *op << "\n"; 12447f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "popset: " << *op << '\n'); 12547f75930SValentin Clement auto popFn = [&](auto rop) { 12647f75930SValentin Clement assert(val && "op must have a result value"); 12747f75930SValentin Clement auto resNum = val.cast<mlir::OpResult>().getResultNumber(); 12847f75930SValentin Clement llvm::SmallVector<mlir::Value> results; 12947f75930SValentin Clement rop.resultToSourceOps(results, resNum); 13047f75930SValentin Clement for (auto u : results) 13147f75930SValentin Clement collectArrayAccessFrom(u); 13247f75930SValentin Clement }; 13347f75930SValentin Clement if (auto rop = mlir::dyn_cast<fir::DoLoopOp>(op)) { 13447f75930SValentin Clement popFn(rop); 13547f75930SValentin Clement return; 13647f75930SValentin Clement } 13747f75930SValentin Clement if (auto rop = mlir::dyn_cast<fir::IfOp>(op)) { 13847f75930SValentin Clement popFn(rop); 13947f75930SValentin Clement return; 14047f75930SValentin Clement } 14147f75930SValentin Clement if (auto mergeStore = mlir::dyn_cast<ArrayMergeStoreOp>(op)) { 14247f75930SValentin Clement if (opIsInsideLoops(mergeStore)) 143149ad3d5SShraiysh Vaishay collectArrayAccessFrom(mergeStore.getSequence()); 14447f75930SValentin Clement return; 14547f75930SValentin Clement } 14647f75930SValentin Clement 14747f75930SValentin Clement if (mlir::isa<AllocaOp, AllocMemOp>(op)) { 14847f75930SValentin Clement // Look for any stores inside the loops, and collect an array operation 14947f75930SValentin Clement // that produced the value being stored to it. 15047f75930SValentin Clement for (mlir::Operation *user : op->getUsers()) 15147f75930SValentin Clement if (auto store = mlir::dyn_cast<fir::StoreOp>(user)) 15247f75930SValentin Clement if (opIsInsideLoops(store)) 153149ad3d5SShraiysh Vaishay collectArrayAccessFrom(store.getValue()); 15447f75930SValentin Clement return; 15547f75930SValentin Clement } 15647f75930SValentin Clement 15747f75930SValentin Clement // Otherwise, Op does not contain a region so just chase its operands. 15847f75930SValentin Clement if (mlir::isa<ArrayLoadOp, ArrayUpdateOp, ArrayModifyOp, ArrayFetchOp>( 15947f75930SValentin Clement op)) { 16047f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "add " << *op << " to reachable set\n"); 16147f75930SValentin Clement reach.emplace_back(op); 16247f75930SValentin Clement } 16347f75930SValentin Clement // Array modify assignment is performed on the result. So the analysis 16447f75930SValentin Clement // must look at the what is done with the result. 16547f75930SValentin Clement if (mlir::isa<ArrayModifyOp>(op)) 16647f75930SValentin Clement for (mlir::Operation *user : op->getResult(0).getUsers()) 16747f75930SValentin Clement followUsers(user); 16847f75930SValentin Clement 16947f75930SValentin Clement for (auto u : op->getOperands()) 17047f75930SValentin Clement collectArrayAccessFrom(u); 17147f75930SValentin Clement } 17247f75930SValentin Clement 17347f75930SValentin Clement void collectArrayAccessFrom(mlir::BlockArgument ba) { 17447f75930SValentin Clement auto *parent = ba.getOwner()->getParentOp(); 17547f75930SValentin Clement // If inside an Op holding a region, the block argument corresponds to an 17647f75930SValentin Clement // argument passed to the containing Op. 17747f75930SValentin Clement auto popFn = [&](auto rop) { 17847f75930SValentin Clement collectArrayAccessFrom(rop.blockArgToSourceOp(ba.getArgNumber())); 17947f75930SValentin Clement }; 18047f75930SValentin Clement if (auto rop = mlir::dyn_cast<DoLoopOp>(parent)) { 18147f75930SValentin Clement popFn(rop); 18247f75930SValentin Clement return; 18347f75930SValentin Clement } 18447f75930SValentin Clement if (auto rop = mlir::dyn_cast<IterWhileOp>(parent)) { 18547f75930SValentin Clement popFn(rop); 18647f75930SValentin Clement return; 18747f75930SValentin Clement } 18847f75930SValentin Clement // Otherwise, a block argument is provided via the pred blocks. 18947f75930SValentin Clement for (auto *pred : ba.getOwner()->getPredecessors()) { 19047f75930SValentin Clement auto u = pred->getTerminator()->getOperand(ba.getArgNumber()); 19147f75930SValentin Clement collectArrayAccessFrom(u); 19247f75930SValentin Clement } 19347f75930SValentin Clement } 19447f75930SValentin Clement 19547f75930SValentin Clement // Recursively trace operands to find all array operations relating to the 19647f75930SValentin Clement // values merged. 19747f75930SValentin Clement void collectArrayAccessFrom(mlir::Value val) { 19847f75930SValentin Clement if (!val || visited.contains(val)) 19947f75930SValentin Clement return; 20047f75930SValentin Clement visited.insert(val); 20147f75930SValentin Clement 20247f75930SValentin Clement // Process a block argument. 20347f75930SValentin Clement if (auto ba = val.dyn_cast<mlir::BlockArgument>()) { 20447f75930SValentin Clement collectArrayAccessFrom(ba); 20547f75930SValentin Clement return; 20647f75930SValentin Clement } 20747f75930SValentin Clement 20847f75930SValentin Clement // Process an Op. 20947f75930SValentin Clement if (auto *op = val.getDefiningOp()) { 21047f75930SValentin Clement collectArrayAccessFrom(op, val); 21147f75930SValentin Clement return; 21247f75930SValentin Clement } 21347f75930SValentin Clement 21447f75930SValentin Clement fir::emitFatalError(val.getLoc(), "unhandled value"); 21547f75930SValentin Clement } 21647f75930SValentin Clement 21747f75930SValentin Clement /// Is \op inside the loop nest region ? 21847f75930SValentin Clement bool opIsInsideLoops(mlir::Operation *op) const { 21947f75930SValentin Clement return loopRegion && loopRegion->isAncestor(op->getParentRegion()); 22047f75930SValentin Clement } 22147f75930SValentin Clement 22247f75930SValentin Clement /// Recursively trace the use of an operation results, calling 22347f75930SValentin Clement /// collectArrayAccessFrom on the direct and indirect user operands. 22447f75930SValentin Clement /// TODO: Replace recursive algorithm on def-use chain with an iterative one 22547f75930SValentin Clement /// with an explicit stack. 22647f75930SValentin Clement void followUsers(mlir::Operation *op) { 22747f75930SValentin Clement for (auto userOperand : op->getOperands()) 22847f75930SValentin Clement collectArrayAccessFrom(userOperand); 22947f75930SValentin Clement // Go through potential converts/coordinate_op. 23047f75930SValentin Clement for (mlir::Operation *indirectUser : op->getUsers()) 23147f75930SValentin Clement followUsers(indirectUser); 23247f75930SValentin Clement } 23347f75930SValentin Clement 23447f75930SValentin Clement llvm::SmallVectorImpl<mlir::Operation *> &reach; 23547f75930SValentin Clement llvm::SmallPtrSet<mlir::Value, 16> visited; 23647f75930SValentin Clement /// Region of the loops nest that produced the array value. 23747f75930SValentin Clement mlir::Region *loopRegion; 23847f75930SValentin Clement 23947f75930SValentin Clement public: 24047f75930SValentin Clement /// Return all ops that produce the array value that is stored into the 24147f75930SValentin Clement /// `array_merge_store`. 24247f75930SValentin Clement static void reachingValues(llvm::SmallVectorImpl<mlir::Operation *> &reach, 24347f75930SValentin Clement mlir::Value seq) { 24447f75930SValentin Clement reach.clear(); 24547f75930SValentin Clement mlir::Region *loopRegion = nullptr; 24647f75930SValentin Clement // Only `DoLoopOp` is tested here since array operations are currently only 24747f75930SValentin Clement // associated with this kind of loop. 24847f75930SValentin Clement if (auto doLoop = 24947f75930SValentin Clement mlir::dyn_cast_or_null<fir::DoLoopOp>(seq.getDefiningOp())) 25047f75930SValentin Clement loopRegion = &doLoop->getRegion(0); 25147f75930SValentin Clement ReachCollector collector(reach, loopRegion); 25247f75930SValentin Clement collector.collectArrayAccessFrom(seq); 25347f75930SValentin Clement } 25447f75930SValentin Clement }; 25547f75930SValentin Clement } // namespace 25647f75930SValentin Clement 25747f75930SValentin Clement /// Find all the array operations that access the array value that is loaded by 25847f75930SValentin Clement /// the array load operation, `load`. 25947f75930SValentin Clement const llvm::SmallVector<mlir::Operation *> & 26047f75930SValentin Clement ArrayCopyAnalysis::arrayAccesses(ArrayLoadOp load) { 26147f75930SValentin Clement auto lmIter = loadMapSets.find(load); 26247f75930SValentin Clement if (lmIter != loadMapSets.end()) 26347f75930SValentin Clement return lmIter->getSecond(); 26447f75930SValentin Clement 26547f75930SValentin Clement llvm::SmallVector<mlir::Operation *> accesses; 26647f75930SValentin Clement UseSetT visited; 26747f75930SValentin Clement llvm::SmallVector<mlir::OpOperand *> queue; // uses of ArrayLoad[orig] 26847f75930SValentin Clement 26947f75930SValentin Clement auto appendToQueue = [&](mlir::Value val) { 27047f75930SValentin Clement for (mlir::OpOperand &use : val.getUses()) 27147f75930SValentin Clement if (!visited.count(&use)) { 27247f75930SValentin Clement visited.insert(&use); 27347f75930SValentin Clement queue.push_back(&use); 27447f75930SValentin Clement } 27547f75930SValentin Clement }; 27647f75930SValentin Clement 27747f75930SValentin Clement // Build the set of uses of `original`. 27847f75930SValentin Clement // let USES = { uses of original fir.load } 27947f75930SValentin Clement appendToQueue(load); 28047f75930SValentin Clement 28147f75930SValentin Clement // Process the worklist until done. 28247f75930SValentin Clement while (!queue.empty()) { 28347f75930SValentin Clement mlir::OpOperand *operand = queue.pop_back_val(); 28447f75930SValentin Clement mlir::Operation *owner = operand->getOwner(); 28547f75930SValentin Clement 28647f75930SValentin Clement auto structuredLoop = [&](auto ro) { 28747f75930SValentin Clement if (auto blockArg = ro.iterArgToBlockArg(operand->get())) { 28847f75930SValentin Clement int64_t arg = blockArg.getArgNumber(); 289149ad3d5SShraiysh Vaishay mlir::Value output = ro.getResult(ro.getFinalValue() ? arg : arg - 1); 29047f75930SValentin Clement appendToQueue(output); 29147f75930SValentin Clement appendToQueue(blockArg); 29247f75930SValentin Clement } 29347f75930SValentin Clement }; 29447f75930SValentin Clement // TODO: this need to be updated to use the control-flow interface. 29547f75930SValentin Clement auto branchOp = [&](mlir::Block *dest, OperandRange operands) { 29647f75930SValentin Clement if (operands.empty()) 29747f75930SValentin Clement return; 29847f75930SValentin Clement 29947f75930SValentin Clement // Check if this operand is within the range. 30047f75930SValentin Clement unsigned operandIndex = operand->getOperandNumber(); 30147f75930SValentin Clement unsigned operandsStart = operands.getBeginOperandIndex(); 30247f75930SValentin Clement if (operandIndex < operandsStart || 30347f75930SValentin Clement operandIndex >= (operandsStart + operands.size())) 30447f75930SValentin Clement return; 30547f75930SValentin Clement 30647f75930SValentin Clement // Index the successor. 30747f75930SValentin Clement unsigned argIndex = operandIndex - operandsStart; 30847f75930SValentin Clement appendToQueue(dest->getArgument(argIndex)); 30947f75930SValentin Clement }; 31047f75930SValentin Clement // Thread uses into structured loop bodies and return value uses. 31147f75930SValentin Clement if (auto ro = mlir::dyn_cast<DoLoopOp>(owner)) { 31247f75930SValentin Clement structuredLoop(ro); 31347f75930SValentin Clement } else if (auto ro = mlir::dyn_cast<IterWhileOp>(owner)) { 31447f75930SValentin Clement structuredLoop(ro); 31547f75930SValentin Clement } else if (auto rs = mlir::dyn_cast<ResultOp>(owner)) { 31647f75930SValentin Clement // Thread any uses of fir.if that return the marked array value. 31747f75930SValentin Clement if (auto ifOp = rs->getParentOfType<fir::IfOp>()) 31847f75930SValentin Clement appendToQueue(ifOp.getResult(operand->getOperandNumber())); 31947f75930SValentin Clement } else if (mlir::isa<ArrayFetchOp>(owner)) { 32047f75930SValentin Clement // Keep track of array value fetches. 32147f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 32247f75930SValentin Clement << "add fetch {" << *owner << "} to array value set\n"); 32347f75930SValentin Clement accesses.push_back(owner); 32447f75930SValentin Clement } else if (auto update = mlir::dyn_cast<ArrayUpdateOp>(owner)) { 32547f75930SValentin Clement // Keep track of array value updates and thread the return value uses. 32647f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 32747f75930SValentin Clement << "add update {" << *owner << "} to array value set\n"); 32847f75930SValentin Clement accesses.push_back(owner); 32947f75930SValentin Clement appendToQueue(update.getResult()); 33047f75930SValentin Clement } else if (auto update = mlir::dyn_cast<ArrayModifyOp>(owner)) { 33147f75930SValentin Clement // Keep track of array value modification and thread the return value 33247f75930SValentin Clement // uses. 33347f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 33447f75930SValentin Clement << "add modify {" << *owner << "} to array value set\n"); 33547f75930SValentin Clement accesses.push_back(owner); 33647f75930SValentin Clement appendToQueue(update.getResult(1)); 337ace01605SRiver Riddle } else if (auto br = mlir::dyn_cast<mlir::cf::BranchOp>(owner)) { 3383012f35fSJacques Pienaar branchOp(br.getDest(), br.getDestOperands()); 339ace01605SRiver Riddle } else if (auto br = mlir::dyn_cast<mlir::cf::CondBranchOp>(owner)) { 34047f75930SValentin Clement branchOp(br.getTrueDest(), br.getTrueOperands()); 34147f75930SValentin Clement branchOp(br.getFalseDest(), br.getFalseOperands()); 34247f75930SValentin Clement } else if (mlir::isa<ArrayMergeStoreOp>(owner)) { 34347f75930SValentin Clement // do nothing 34447f75930SValentin Clement } else { 34547f75930SValentin Clement llvm::report_fatal_error("array value reached unexpected op"); 34647f75930SValentin Clement } 34747f75930SValentin Clement } 34847f75930SValentin Clement return loadMapSets.insert({load, accesses}).first->getSecond(); 34947f75930SValentin Clement } 35047f75930SValentin Clement 35147f75930SValentin Clement /// Is there a conflict between the array value that was updated and to be 35247f75930SValentin Clement /// stored to `st` and the set of arrays loaded (`reach`) and used to compute 35347f75930SValentin Clement /// the updated value? 35447f75930SValentin Clement static bool conflictOnLoad(llvm::ArrayRef<mlir::Operation *> reach, 35547f75930SValentin Clement ArrayMergeStoreOp st) { 35647f75930SValentin Clement mlir::Value load; 357149ad3d5SShraiysh Vaishay mlir::Value addr = st.getMemref(); 35847f75930SValentin Clement auto stEleTy = fir::dyn_cast_ptrOrBoxEleTy(addr.getType()); 35947f75930SValentin Clement for (auto *op : reach) { 36047f75930SValentin Clement auto ld = mlir::dyn_cast<ArrayLoadOp>(op); 36147f75930SValentin Clement if (!ld) 36247f75930SValentin Clement continue; 363149ad3d5SShraiysh Vaishay mlir::Type ldTy = ld.getMemref().getType(); 36447f75930SValentin Clement if (auto boxTy = ldTy.dyn_cast<fir::BoxType>()) 36547f75930SValentin Clement ldTy = boxTy.getEleTy(); 36647f75930SValentin Clement if (ldTy.isa<fir::PointerType>() && stEleTy == dyn_cast_ptrEleTy(ldTy)) 36747f75930SValentin Clement return true; 368149ad3d5SShraiysh Vaishay if (ld.getMemref() == addr) { 369149ad3d5SShraiysh Vaishay if (ld.getResult() != st.getOriginal()) 37047f75930SValentin Clement return true; 37147f75930SValentin Clement if (load) 37247f75930SValentin Clement return true; 37347f75930SValentin Clement load = ld; 37447f75930SValentin Clement } 37547f75930SValentin Clement } 37647f75930SValentin Clement return false; 37747f75930SValentin Clement } 37847f75930SValentin Clement 37947f75930SValentin Clement /// Check if there is any potential conflict in the chained update operations 38047f75930SValentin Clement /// (ArrayFetchOp, ArrayUpdateOp, ArrayModifyOp) while merging back to the 38147f75930SValentin Clement /// array. A potential conflict is detected if two operations work on the same 38247f75930SValentin Clement /// indices. 38347f75930SValentin Clement static bool conflictOnMerge(llvm::ArrayRef<mlir::Operation *> accesses) { 38447f75930SValentin Clement if (accesses.size() < 2) 38547f75930SValentin Clement return false; 38647f75930SValentin Clement llvm::SmallVector<mlir::Value> indices; 38747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "check merge conflict on with " << accesses.size() 38847f75930SValentin Clement << " accesses on the list\n"); 38947f75930SValentin Clement for (auto *op : accesses) { 39047f75930SValentin Clement assert((mlir::isa<ArrayFetchOp, ArrayUpdateOp, ArrayModifyOp>(op)) && 39147f75930SValentin Clement "unexpected operation in analysis"); 39247f75930SValentin Clement llvm::SmallVector<mlir::Value> compareVector; 39347f75930SValentin Clement if (auto u = mlir::dyn_cast<ArrayUpdateOp>(op)) { 39447f75930SValentin Clement if (indices.empty()) { 395149ad3d5SShraiysh Vaishay indices = u.getIndices(); 39647f75930SValentin Clement continue; 39747f75930SValentin Clement } 398149ad3d5SShraiysh Vaishay compareVector = u.getIndices(); 39947f75930SValentin Clement } else if (auto f = mlir::dyn_cast<ArrayModifyOp>(op)) { 40047f75930SValentin Clement if (indices.empty()) { 401149ad3d5SShraiysh Vaishay indices = f.getIndices(); 40247f75930SValentin Clement continue; 40347f75930SValentin Clement } 404149ad3d5SShraiysh Vaishay compareVector = f.getIndices(); 40547f75930SValentin Clement } else if (auto f = mlir::dyn_cast<ArrayFetchOp>(op)) { 40647f75930SValentin Clement if (indices.empty()) { 407149ad3d5SShraiysh Vaishay indices = f.getIndices(); 40847f75930SValentin Clement continue; 40947f75930SValentin Clement } 410149ad3d5SShraiysh Vaishay compareVector = f.getIndices(); 41147f75930SValentin Clement } 41247f75930SValentin Clement if (compareVector != indices) 41347f75930SValentin Clement return true; 41447f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "vectors compare equal\n"); 41547f75930SValentin Clement } 41647f75930SValentin Clement return false; 41747f75930SValentin Clement } 41847f75930SValentin Clement 41947f75930SValentin Clement // Are either of types of conflicts present? 42047f75930SValentin Clement inline bool conflictDetected(llvm::ArrayRef<mlir::Operation *> reach, 42147f75930SValentin Clement llvm::ArrayRef<mlir::Operation *> accesses, 42247f75930SValentin Clement ArrayMergeStoreOp st) { 42347f75930SValentin Clement return conflictOnLoad(reach, st) || conflictOnMerge(accesses); 42447f75930SValentin Clement } 42547f75930SValentin Clement 42647f75930SValentin Clement /// Constructor of the array copy analysis. 42747f75930SValentin Clement /// This performs the analysis and saves the intermediate results. 42847f75930SValentin Clement void ArrayCopyAnalysis::construct(mlir::Operation *topLevelOp) { 42947f75930SValentin Clement topLevelOp->walk([&](Operation *op) { 43047f75930SValentin Clement if (auto st = mlir::dyn_cast<fir::ArrayMergeStoreOp>(op)) { 43147f75930SValentin Clement llvm::SmallVector<Operation *> values; 432149ad3d5SShraiysh Vaishay ReachCollector::reachingValues(values, st.getSequence()); 433149ad3d5SShraiysh Vaishay const llvm::SmallVector<Operation *> &accesses = arrayAccesses( 434149ad3d5SShraiysh Vaishay mlir::cast<ArrayLoadOp>(st.getOriginal().getDefiningOp())); 43547f75930SValentin Clement if (conflictDetected(values, accesses, st)) { 43647f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 43747f75930SValentin Clement << "CONFLICT: copies required for " << st << '\n' 43847f75930SValentin Clement << " adding conflicts on: " << op << " and " 439149ad3d5SShraiysh Vaishay << st.getOriginal() << '\n'); 44047f75930SValentin Clement conflicts.insert(op); 441149ad3d5SShraiysh Vaishay conflicts.insert(st.getOriginal().getDefiningOp()); 44247f75930SValentin Clement } 443149ad3d5SShraiysh Vaishay auto *ld = st.getOriginal().getDefiningOp(); 44447f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 44547f75930SValentin Clement << "map: adding {" << *ld << " -> " << st << "}\n"); 44647f75930SValentin Clement useMap.insert({ld, op}); 44747f75930SValentin Clement } else if (auto load = mlir::dyn_cast<ArrayLoadOp>(op)) { 44847f75930SValentin Clement const llvm::SmallVector<mlir::Operation *> &accesses = 44947f75930SValentin Clement arrayAccesses(load); 45047f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "process load: " << load 45147f75930SValentin Clement << ", accesses: " << accesses.size() << '\n'); 45247f75930SValentin Clement for (auto *acc : accesses) { 45347f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << " access: " << *acc << '\n'); 45447f75930SValentin Clement assert((mlir::isa<ArrayFetchOp, ArrayUpdateOp, ArrayModifyOp>(acc))); 45547f75930SValentin Clement if (!useMap.insert({acc, op}).second) { 45647f75930SValentin Clement mlir::emitError( 45747f75930SValentin Clement load.getLoc(), 45847f75930SValentin Clement "The parallel semantics of multiple array_merge_stores per " 45947f75930SValentin Clement "array_load are not supported."); 46047f75930SValentin Clement return; 46147f75930SValentin Clement } 46247f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 46347f75930SValentin Clement << "map: adding {" << *acc << "} -> {" << load << "}\n"); 46447f75930SValentin Clement } 46547f75930SValentin Clement } 46647f75930SValentin Clement }); 46747f75930SValentin Clement } 46847f75930SValentin Clement 46947f75930SValentin Clement namespace { 47047f75930SValentin Clement class ArrayLoadConversion : public mlir::OpRewritePattern<ArrayLoadOp> { 47147f75930SValentin Clement public: 47247f75930SValentin Clement using OpRewritePattern::OpRewritePattern; 47347f75930SValentin Clement 47447f75930SValentin Clement mlir::LogicalResult 47547f75930SValentin Clement matchAndRewrite(ArrayLoadOp load, 47647f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 47747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "replace load " << load << " with undef.\n"); 47847f75930SValentin Clement rewriter.replaceOpWithNewOp<UndefOp>(load, load.getType()); 47947f75930SValentin Clement return mlir::success(); 48047f75930SValentin Clement } 48147f75930SValentin Clement }; 48247f75930SValentin Clement 48347f75930SValentin Clement class ArrayMergeStoreConversion 48447f75930SValentin Clement : public mlir::OpRewritePattern<ArrayMergeStoreOp> { 48547f75930SValentin Clement public: 48647f75930SValentin Clement using OpRewritePattern::OpRewritePattern; 48747f75930SValentin Clement 48847f75930SValentin Clement mlir::LogicalResult 48947f75930SValentin Clement matchAndRewrite(ArrayMergeStoreOp store, 49047f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 49147f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "marking store " << store << " as dead.\n"); 49247f75930SValentin Clement rewriter.eraseOp(store); 49347f75930SValentin Clement return mlir::success(); 49447f75930SValentin Clement } 49547f75930SValentin Clement }; 49647f75930SValentin Clement } // namespace 49747f75930SValentin Clement 49847f75930SValentin Clement static mlir::Type getEleTy(mlir::Type ty) { 49947f75930SValentin Clement if (auto t = dyn_cast_ptrEleTy(ty)) 50047f75930SValentin Clement ty = t; 50147f75930SValentin Clement if (auto t = ty.dyn_cast<SequenceType>()) 50247f75930SValentin Clement ty = t.getEleTy(); 50347f75930SValentin Clement // FIXME: keep ptr/heap/ref information. 50447f75930SValentin Clement return ReferenceType::get(ty); 50547f75930SValentin Clement } 50647f75930SValentin Clement 50747f75930SValentin Clement // Extract extents from the ShapeOp/ShapeShiftOp into the result vector. 50847f75930SValentin Clement // TODO: getExtents on op should return a ValueRange instead of a vector. 50947f75930SValentin Clement static void getExtents(llvm::SmallVectorImpl<mlir::Value> &result, 51047f75930SValentin Clement mlir::Value shape) { 51147f75930SValentin Clement auto *shapeOp = shape.getDefiningOp(); 51247f75930SValentin Clement if (auto s = mlir::dyn_cast<fir::ShapeOp>(shapeOp)) { 51347f75930SValentin Clement auto e = s.getExtents(); 51447f75930SValentin Clement result.insert(result.end(), e.begin(), e.end()); 51547f75930SValentin Clement return; 51647f75930SValentin Clement } 51747f75930SValentin Clement if (auto s = mlir::dyn_cast<fir::ShapeShiftOp>(shapeOp)) { 51847f75930SValentin Clement auto e = s.getExtents(); 51947f75930SValentin Clement result.insert(result.end(), e.begin(), e.end()); 52047f75930SValentin Clement return; 52147f75930SValentin Clement } 52247f75930SValentin Clement llvm::report_fatal_error("not a fir.shape/fir.shape_shift op"); 52347f75930SValentin Clement } 52447f75930SValentin Clement 52547f75930SValentin Clement // Place the extents of the array loaded by an ArrayLoadOp into the result 52647f75930SValentin Clement // vector and return a ShapeOp/ShapeShiftOp with the corresponding extents. If 52747f75930SValentin Clement // the ArrayLoadOp is loading a fir.box, code will be generated to read the 52847f75930SValentin Clement // extents from the fir.box, and a the retunred ShapeOp is built with the read 52947f75930SValentin Clement // extents. 53047f75930SValentin Clement // Otherwise, the extents will be extracted from the ShapeOp/ShapeShiftOp 53147f75930SValentin Clement // argument of the ArrayLoadOp that is returned. 53247f75930SValentin Clement static mlir::Value 53347f75930SValentin Clement getOrReadExtentsAndShapeOp(mlir::Location loc, mlir::PatternRewriter &rewriter, 53447f75930SValentin Clement fir::ArrayLoadOp loadOp, 53547f75930SValentin Clement llvm::SmallVectorImpl<mlir::Value> &result) { 53647f75930SValentin Clement assert(result.empty()); 537149ad3d5SShraiysh Vaishay if (auto boxTy = loadOp.getMemref().getType().dyn_cast<fir::BoxType>()) { 53847f75930SValentin Clement auto rank = fir::dyn_cast_ptrOrBoxEleTy(boxTy) 53947f75930SValentin Clement .cast<fir::SequenceType>() 54047f75930SValentin Clement .getDimension(); 54147f75930SValentin Clement auto idxTy = rewriter.getIndexType(); 54247f75930SValentin Clement for (decltype(rank) dim = 0; dim < rank; ++dim) { 54347f75930SValentin Clement auto dimVal = rewriter.create<arith::ConstantIndexOp>(loc, dim); 544149ad3d5SShraiysh Vaishay auto dimInfo = rewriter.create<fir::BoxDimsOp>( 545149ad3d5SShraiysh Vaishay loc, idxTy, idxTy, idxTy, loadOp.getMemref(), dimVal); 54647f75930SValentin Clement result.emplace_back(dimInfo.getResult(1)); 54747f75930SValentin Clement } 54847f75930SValentin Clement auto shapeType = fir::ShapeType::get(rewriter.getContext(), rank); 54947f75930SValentin Clement return rewriter.create<fir::ShapeOp>(loc, shapeType, result); 55047f75930SValentin Clement } 551149ad3d5SShraiysh Vaishay getExtents(result, loadOp.getShape()); 552149ad3d5SShraiysh Vaishay return loadOp.getShape(); 55347f75930SValentin Clement } 55447f75930SValentin Clement 55547f75930SValentin Clement static mlir::Type toRefType(mlir::Type ty) { 55647f75930SValentin Clement if (fir::isa_ref_type(ty)) 55747f75930SValentin Clement return ty; 55847f75930SValentin Clement return fir::ReferenceType::get(ty); 55947f75930SValentin Clement } 56047f75930SValentin Clement 56147f75930SValentin Clement static mlir::Value 56247f75930SValentin Clement genCoorOp(mlir::PatternRewriter &rewriter, mlir::Location loc, mlir::Type eleTy, 56347f75930SValentin Clement mlir::Type resTy, mlir::Value alloc, mlir::Value shape, 56447f75930SValentin Clement mlir::Value slice, mlir::ValueRange indices, 56547f75930SValentin Clement mlir::ValueRange typeparams, bool skipOrig = false) { 56647f75930SValentin Clement llvm::SmallVector<mlir::Value> originated; 56747f75930SValentin Clement if (skipOrig) 56847f75930SValentin Clement originated.assign(indices.begin(), indices.end()); 56947f75930SValentin Clement else 57047f75930SValentin Clement originated = fir::factory::originateIndices(loc, rewriter, alloc.getType(), 57147f75930SValentin Clement shape, indices); 57247f75930SValentin Clement auto seqTy = fir::dyn_cast_ptrOrBoxEleTy(alloc.getType()); 57347f75930SValentin Clement assert(seqTy && seqTy.isa<fir::SequenceType>()); 57447f75930SValentin Clement const auto dimension = seqTy.cast<fir::SequenceType>().getDimension(); 57547f75930SValentin Clement mlir::Value result = rewriter.create<fir::ArrayCoorOp>( 57647f75930SValentin Clement loc, eleTy, alloc, shape, slice, 57747f75930SValentin Clement llvm::ArrayRef<mlir::Value>{originated}.take_front(dimension), 57847f75930SValentin Clement typeparams); 57947f75930SValentin Clement if (dimension < originated.size()) 58047f75930SValentin Clement result = rewriter.create<fir::CoordinateOp>( 58147f75930SValentin Clement loc, resTy, result, 58247f75930SValentin Clement llvm::ArrayRef<mlir::Value>{originated}.drop_front(dimension)); 58347f75930SValentin Clement return result; 58447f75930SValentin Clement } 58547f75930SValentin Clement 58647f75930SValentin Clement namespace { 58747f75930SValentin Clement /// Conversion of fir.array_update and fir.array_modify Ops. 58847f75930SValentin Clement /// If there is a conflict for the update, then we need to perform a 58947f75930SValentin Clement /// copy-in/copy-out to preserve the original values of the array. If there is 59047f75930SValentin Clement /// no conflict, then it is save to eschew making any copies. 59147f75930SValentin Clement template <typename ArrayOp> 59247f75930SValentin Clement class ArrayUpdateConversionBase : public mlir::OpRewritePattern<ArrayOp> { 59347f75930SValentin Clement public: 59447f75930SValentin Clement explicit ArrayUpdateConversionBase(mlir::MLIRContext *ctx, 59547f75930SValentin Clement const ArrayCopyAnalysis &a, 59647f75930SValentin Clement const OperationUseMapT &m) 59747f75930SValentin Clement : mlir::OpRewritePattern<ArrayOp>{ctx}, analysis{a}, useMap{m} {} 59847f75930SValentin Clement 59947f75930SValentin Clement void genArrayCopy(mlir::Location loc, mlir::PatternRewriter &rewriter, 60047f75930SValentin Clement mlir::Value dst, mlir::Value src, mlir::Value shapeOp, 60147f75930SValentin Clement mlir::Type arrTy) const { 60247f75930SValentin Clement auto insPt = rewriter.saveInsertionPoint(); 60347f75930SValentin Clement llvm::SmallVector<mlir::Value> indices; 60447f75930SValentin Clement llvm::SmallVector<mlir::Value> extents; 60547f75930SValentin Clement getExtents(extents, shapeOp); 60647f75930SValentin Clement // Build loop nest from column to row. 60747f75930SValentin Clement for (auto sh : llvm::reverse(extents)) { 60847f75930SValentin Clement auto idxTy = rewriter.getIndexType(); 60947f75930SValentin Clement auto ubi = rewriter.create<fir::ConvertOp>(loc, idxTy, sh); 61047f75930SValentin Clement auto zero = rewriter.create<arith::ConstantIndexOp>(loc, 0); 61147f75930SValentin Clement auto one = rewriter.create<arith::ConstantIndexOp>(loc, 1); 61247f75930SValentin Clement auto ub = rewriter.create<arith::SubIOp>(loc, idxTy, ubi, one); 61347f75930SValentin Clement auto loop = rewriter.create<fir::DoLoopOp>(loc, zero, ub, one); 61447f75930SValentin Clement rewriter.setInsertionPointToStart(loop.getBody()); 61547f75930SValentin Clement indices.push_back(loop.getInductionVar()); 61647f75930SValentin Clement } 61747f75930SValentin Clement // Reverse the indices so they are in column-major order. 61847f75930SValentin Clement std::reverse(indices.begin(), indices.end()); 61947f75930SValentin Clement auto ty = getEleTy(arrTy); 62047f75930SValentin Clement auto fromAddr = rewriter.create<fir::ArrayCoorOp>( 62147f75930SValentin Clement loc, ty, src, shapeOp, mlir::Value{}, 62247f75930SValentin Clement fir::factory::originateIndices(loc, rewriter, src.getType(), shapeOp, 62347f75930SValentin Clement indices), 62447f75930SValentin Clement mlir::ValueRange{}); 62547f75930SValentin Clement auto load = rewriter.create<fir::LoadOp>(loc, fromAddr); 62647f75930SValentin Clement auto toAddr = rewriter.create<fir::ArrayCoorOp>( 62747f75930SValentin Clement loc, ty, dst, shapeOp, mlir::Value{}, 62847f75930SValentin Clement fir::factory::originateIndices(loc, rewriter, dst.getType(), shapeOp, 62947f75930SValentin Clement indices), 63047f75930SValentin Clement mlir::ValueRange{}); 63147f75930SValentin Clement rewriter.create<fir::StoreOp>(loc, load, toAddr); 63247f75930SValentin Clement rewriter.restoreInsertionPoint(insPt); 63347f75930SValentin Clement } 63447f75930SValentin Clement 63547f75930SValentin Clement /// Copy the RHS element into the LHS and insert copy-in/copy-out between a 63647f75930SValentin Clement /// temp and the LHS if the analysis found potential overlaps between the RHS 63747f75930SValentin Clement /// and LHS arrays. The element copy generator must be provided through \p 63847f75930SValentin Clement /// assignElement. \p update must be the ArrayUpdateOp or the ArrayModifyOp. 63947f75930SValentin Clement /// Returns the address of the LHS element inside the loop and the LHS 64047f75930SValentin Clement /// ArrayLoad result. 64147f75930SValentin Clement std::pair<mlir::Value, mlir::Value> 64247f75930SValentin Clement materializeAssignment(mlir::Location loc, mlir::PatternRewriter &rewriter, 64347f75930SValentin Clement ArrayOp update, 64447f75930SValentin Clement llvm::function_ref<void(mlir::Value)> assignElement, 64547f75930SValentin Clement mlir::Type lhsEltRefType) const { 64647f75930SValentin Clement auto *op = update.getOperation(); 64747f75930SValentin Clement mlir::Operation *loadOp = useMap.lookup(op); 64847f75930SValentin Clement auto load = mlir::cast<ArrayLoadOp>(loadOp); 64947f75930SValentin Clement LLVM_DEBUG(llvm::outs() << "does " << load << " have a conflict?\n"); 65047f75930SValentin Clement if (analysis.hasPotentialConflict(loadOp)) { 65147f75930SValentin Clement // If there is a conflict between the arrays, then we copy the lhs array 65247f75930SValentin Clement // to a temporary, update the temporary, and copy the temporary back to 65347f75930SValentin Clement // the lhs array. This yields Fortran's copy-in copy-out array semantics. 65447f75930SValentin Clement LLVM_DEBUG(llvm::outs() << "Yes, conflict was found\n"); 65547f75930SValentin Clement rewriter.setInsertionPoint(loadOp); 65647f75930SValentin Clement // Copy in. 65747f75930SValentin Clement llvm::SmallVector<mlir::Value> extents; 65847f75930SValentin Clement mlir::Value shapeOp = 65947f75930SValentin Clement getOrReadExtentsAndShapeOp(loc, rewriter, load, extents); 66047f75930SValentin Clement auto allocmem = rewriter.create<AllocMemOp>( 661149ad3d5SShraiysh Vaishay loc, dyn_cast_ptrOrBoxEleTy(load.getMemref().getType()), 662149ad3d5SShraiysh Vaishay load.getTypeparams(), extents); 663149ad3d5SShraiysh Vaishay genArrayCopy(load.getLoc(), rewriter, allocmem, load.getMemref(), shapeOp, 66447f75930SValentin Clement load.getType()); 66547f75930SValentin Clement rewriter.setInsertionPoint(op); 66647f75930SValentin Clement mlir::Value coor = genCoorOp( 66747f75930SValentin Clement rewriter, loc, getEleTy(load.getType()), lhsEltRefType, allocmem, 668149ad3d5SShraiysh Vaishay shapeOp, load.getSlice(), update.getIndices(), load.getTypeparams(), 66947f75930SValentin Clement update->hasAttr(fir::factory::attrFortranArrayOffsets())); 67047f75930SValentin Clement assignElement(coor); 67147f75930SValentin Clement mlir::Operation *storeOp = useMap.lookup(loadOp); 67247f75930SValentin Clement auto store = mlir::cast<ArrayMergeStoreOp>(storeOp); 67347f75930SValentin Clement rewriter.setInsertionPoint(storeOp); 67447f75930SValentin Clement // Copy out. 675149ad3d5SShraiysh Vaishay genArrayCopy(store.getLoc(), rewriter, store.getMemref(), allocmem, 676149ad3d5SShraiysh Vaishay shapeOp, load.getType()); 67747f75930SValentin Clement rewriter.create<FreeMemOp>(loc, allocmem); 67847f75930SValentin Clement return {coor, load.getResult()}; 67947f75930SValentin Clement } 68047f75930SValentin Clement // Otherwise, when there is no conflict (a possible loop-carried 68147f75930SValentin Clement // dependence), the lhs array can be updated in place. 68247f75930SValentin Clement LLVM_DEBUG(llvm::outs() << "No, conflict wasn't found\n"); 68347f75930SValentin Clement rewriter.setInsertionPoint(op); 68447f75930SValentin Clement auto coorTy = getEleTy(load.getType()); 68547f75930SValentin Clement mlir::Value coor = genCoorOp( 686149ad3d5SShraiysh Vaishay rewriter, loc, coorTy, lhsEltRefType, load.getMemref(), load.getShape(), 687149ad3d5SShraiysh Vaishay load.getSlice(), update.getIndices(), load.getTypeparams(), 68847f75930SValentin Clement update->hasAttr(fir::factory::attrFortranArrayOffsets())); 68947f75930SValentin Clement assignElement(coor); 69047f75930SValentin Clement return {coor, load.getResult()}; 69147f75930SValentin Clement } 69247f75930SValentin Clement 69347f75930SValentin Clement private: 69447f75930SValentin Clement const ArrayCopyAnalysis &analysis; 69547f75930SValentin Clement const OperationUseMapT &useMap; 69647f75930SValentin Clement }; 69747f75930SValentin Clement 69847f75930SValentin Clement class ArrayUpdateConversion : public ArrayUpdateConversionBase<ArrayUpdateOp> { 69947f75930SValentin Clement public: 70047f75930SValentin Clement explicit ArrayUpdateConversion(mlir::MLIRContext *ctx, 70147f75930SValentin Clement const ArrayCopyAnalysis &a, 70247f75930SValentin Clement const OperationUseMapT &m) 70347f75930SValentin Clement : ArrayUpdateConversionBase{ctx, a, m} {} 70447f75930SValentin Clement 70547f75930SValentin Clement mlir::LogicalResult 70647f75930SValentin Clement matchAndRewrite(ArrayUpdateOp update, 70747f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 70847f75930SValentin Clement auto loc = update.getLoc(); 70947f75930SValentin Clement auto assignElement = [&](mlir::Value coor) { 710149ad3d5SShraiysh Vaishay rewriter.create<fir::StoreOp>(loc, update.getMerge(), coor); 71147f75930SValentin Clement }; 712149ad3d5SShraiysh Vaishay auto lhsEltRefType = toRefType(update.getMerge().getType()); 71347f75930SValentin Clement auto [_, lhsLoadResult] = materializeAssignment( 71447f75930SValentin Clement loc, rewriter, update, assignElement, lhsEltRefType); 71547f75930SValentin Clement update.replaceAllUsesWith(lhsLoadResult); 71647f75930SValentin Clement rewriter.replaceOp(update, lhsLoadResult); 71747f75930SValentin Clement return mlir::success(); 71847f75930SValentin Clement } 71947f75930SValentin Clement }; 72047f75930SValentin Clement 72147f75930SValentin Clement class ArrayModifyConversion : public ArrayUpdateConversionBase<ArrayModifyOp> { 72247f75930SValentin Clement public: 72347f75930SValentin Clement explicit ArrayModifyConversion(mlir::MLIRContext *ctx, 72447f75930SValentin Clement const ArrayCopyAnalysis &a, 72547f75930SValentin Clement const OperationUseMapT &m) 72647f75930SValentin Clement : ArrayUpdateConversionBase{ctx, a, m} {} 72747f75930SValentin Clement 72847f75930SValentin Clement mlir::LogicalResult 72947f75930SValentin Clement matchAndRewrite(ArrayModifyOp modify, 73047f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 73147f75930SValentin Clement auto loc = modify.getLoc(); 73247f75930SValentin Clement auto assignElement = [](mlir::Value) { 73347f75930SValentin Clement // Assignment already materialized by lowering using lhs element address. 73447f75930SValentin Clement }; 73547f75930SValentin Clement auto lhsEltRefType = modify.getResult(0).getType(); 73647f75930SValentin Clement auto [lhsEltCoor, lhsLoadResult] = materializeAssignment( 73747f75930SValentin Clement loc, rewriter, modify, assignElement, lhsEltRefType); 73847f75930SValentin Clement modify.replaceAllUsesWith(mlir::ValueRange{lhsEltCoor, lhsLoadResult}); 73947f75930SValentin Clement rewriter.replaceOp(modify, mlir::ValueRange{lhsEltCoor, lhsLoadResult}); 74047f75930SValentin Clement return mlir::success(); 74147f75930SValentin Clement } 74247f75930SValentin Clement }; 74347f75930SValentin Clement 74447f75930SValentin Clement class ArrayFetchConversion : public mlir::OpRewritePattern<ArrayFetchOp> { 74547f75930SValentin Clement public: 74647f75930SValentin Clement explicit ArrayFetchConversion(mlir::MLIRContext *ctx, 74747f75930SValentin Clement const OperationUseMapT &m) 74847f75930SValentin Clement : OpRewritePattern{ctx}, useMap{m} {} 74947f75930SValentin Clement 75047f75930SValentin Clement mlir::LogicalResult 75147f75930SValentin Clement matchAndRewrite(ArrayFetchOp fetch, 75247f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 75347f75930SValentin Clement auto *op = fetch.getOperation(); 75447f75930SValentin Clement rewriter.setInsertionPoint(op); 75547f75930SValentin Clement auto load = mlir::cast<ArrayLoadOp>(useMap.lookup(op)); 75647f75930SValentin Clement auto loc = fetch.getLoc(); 75747f75930SValentin Clement mlir::Value coor = 75847f75930SValentin Clement genCoorOp(rewriter, loc, getEleTy(load.getType()), 759149ad3d5SShraiysh Vaishay toRefType(fetch.getType()), load.getMemref(), load.getShape(), 760149ad3d5SShraiysh Vaishay load.getSlice(), fetch.getIndices(), load.getTypeparams(), 76147f75930SValentin Clement fetch->hasAttr(fir::factory::attrFortranArrayOffsets())); 76247f75930SValentin Clement rewriter.replaceOpWithNewOp<fir::LoadOp>(fetch, coor); 76347f75930SValentin Clement return mlir::success(); 76447f75930SValentin Clement } 76547f75930SValentin Clement 76647f75930SValentin Clement private: 76747f75930SValentin Clement const OperationUseMapT &useMap; 76847f75930SValentin Clement }; 76947f75930SValentin Clement } // namespace 77047f75930SValentin Clement 77147f75930SValentin Clement namespace { 77247f75930SValentin Clement class ArrayValueCopyConverter 77347f75930SValentin Clement : public ArrayValueCopyBase<ArrayValueCopyConverter> { 77447f75930SValentin Clement public: 775196c4279SRiver Riddle void runOnOperation() override { 776196c4279SRiver Riddle auto func = getOperation(); 77747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "\n\narray-value-copy pass on function '" 77847f75930SValentin Clement << func.getName() << "'\n"); 77947f75930SValentin Clement auto *context = &getContext(); 78047f75930SValentin Clement 78147f75930SValentin Clement // Perform the conflict analysis. 78247f75930SValentin Clement auto &analysis = getAnalysis<ArrayCopyAnalysis>(); 78347f75930SValentin Clement const auto &useMap = analysis.getUseMap(); 78447f75930SValentin Clement 78547f75930SValentin Clement // Phase 1 is performing a rewrite on the array accesses. Once all the 78647f75930SValentin Clement // array accesses are rewritten we can go on phase 2. 78747f75930SValentin Clement // Phase 2 gets rid of the useless copy-in/copyout operations. The copy-in 78847f75930SValentin Clement // /copy-out refers the Fortran copy-in/copy-out semantics on statements. 7899f85c198SRiver Riddle mlir::RewritePatternSet patterns1(context); 79047f75930SValentin Clement patterns1.insert<ArrayFetchConversion>(context, useMap); 79147f75930SValentin Clement patterns1.insert<ArrayUpdateConversion>(context, analysis, useMap); 79247f75930SValentin Clement patterns1.insert<ArrayModifyConversion>(context, analysis, useMap); 79347f75930SValentin Clement mlir::ConversionTarget target(*context); 794ace01605SRiver Riddle target.addLegalDialect< 795ace01605SRiver Riddle FIROpsDialect, mlir::scf::SCFDialect, mlir::arith::ArithmeticDialect, 79623aa5a74SRiver Riddle mlir::cf::ControlFlowDialect, mlir::func::FuncDialect>(); 79747f75930SValentin Clement target.addIllegalOp<ArrayFetchOp, ArrayUpdateOp, ArrayModifyOp>(); 79847f75930SValentin Clement // Rewrite the array fetch and array update ops. 79947f75930SValentin Clement if (mlir::failed( 80047f75930SValentin Clement mlir::applyPartialConversion(func, target, std::move(patterns1)))) { 80147f75930SValentin Clement mlir::emitError(mlir::UnknownLoc::get(context), 80247f75930SValentin Clement "failure in array-value-copy pass, phase 1"); 80347f75930SValentin Clement signalPassFailure(); 80447f75930SValentin Clement } 80547f75930SValentin Clement 8069f85c198SRiver Riddle mlir::RewritePatternSet patterns2(context); 80747f75930SValentin Clement patterns2.insert<ArrayLoadConversion>(context); 80847f75930SValentin Clement patterns2.insert<ArrayMergeStoreConversion>(context); 80947f75930SValentin Clement target.addIllegalOp<ArrayLoadOp, ArrayMergeStoreOp>(); 81047f75930SValentin Clement if (mlir::failed( 81147f75930SValentin Clement mlir::applyPartialConversion(func, target, std::move(patterns2)))) { 81247f75930SValentin Clement mlir::emitError(mlir::UnknownLoc::get(context), 81347f75930SValentin Clement "failure in array-value-copy pass, phase 2"); 81447f75930SValentin Clement signalPassFailure(); 81547f75930SValentin Clement } 81647f75930SValentin Clement } 81747f75930SValentin Clement }; 81847f75930SValentin Clement } // namespace 81947f75930SValentin Clement 82047f75930SValentin Clement std::unique_ptr<mlir::Pass> fir::createArrayValueCopyPass() { 82147f75930SValentin Clement return std::make_unique<ArrayValueCopyConverter>(); 82247f75930SValentin Clement } 823