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