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