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" 10beeb86bdSValentin Clement #include "flang/Optimizer/Builder/Array.h" 1147f75930SValentin Clement #include "flang/Optimizer/Builder/BoxValue.h" 1247f75930SValentin Clement #include "flang/Optimizer/Builder/FIRBuilder.h" 133ab67c3dSValentin Clement #include "flang/Optimizer/Builder/Factory.h" 143ed899ccSJean Perier #include "flang/Optimizer/Builder/Runtime/Derived.h" 155b66cc10SValentin Clement #include "flang/Optimizer/Builder/Todo.h" 1647f75930SValentin Clement #include "flang/Optimizer/Dialect/FIRDialect.h" 17beeb86bdSValentin Clement #include "flang/Optimizer/Dialect/FIROpsSupport.h" 1847f75930SValentin Clement #include "flang/Optimizer/Support/FIRContext.h" 1947f75930SValentin Clement #include "flang/Optimizer/Transforms/Passes.h" 20ace01605SRiver Riddle #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" 21*8b68da2cSAlex Zinenko #include "mlir/Dialect/SCF/IR/SCF.h" 2247f75930SValentin Clement #include "mlir/Transforms/DialectConversion.h" 2347f75930SValentin Clement #include "llvm/Support/Debug.h" 2447f75930SValentin Clement 2547f75930SValentin Clement #define DEBUG_TYPE "flang-array-value-copy" 2647f75930SValentin Clement 2747f75930SValentin Clement using namespace fir; 28092601d4SAndrzej Warzynski using namespace mlir; 2947f75930SValentin Clement 3047f75930SValentin Clement using OperationUseMapT = llvm::DenseMap<mlir::Operation *, mlir::Operation *>; 3147f75930SValentin Clement 3247f75930SValentin Clement namespace { 3347f75930SValentin Clement 3447f75930SValentin Clement /// Array copy analysis. 3547f75930SValentin Clement /// Perform an interference analysis between array values. 3647f75930SValentin Clement /// 3747f75930SValentin Clement /// Lowering will generate a sequence of the following form. 3847f75930SValentin Clement /// ```mlir 3947f75930SValentin Clement /// %a_1 = fir.array_load %array_1(%shape) : ... 4047f75930SValentin Clement /// ... 4147f75930SValentin Clement /// %a_j = fir.array_load %array_j(%shape) : ... 4247f75930SValentin Clement /// ... 4347f75930SValentin Clement /// %a_n = fir.array_load %array_n(%shape) : ... 4447f75930SValentin Clement /// ... 4547f75930SValentin Clement /// %v_i = fir.array_fetch %a_i, ... 4647f75930SValentin Clement /// %a_j1 = fir.array_update %a_j, ... 4747f75930SValentin Clement /// ... 4847f75930SValentin Clement /// fir.array_merge_store %a_j, %a_jn to %array_j : ... 4947f75930SValentin Clement /// ``` 5047f75930SValentin Clement /// 5147f75930SValentin Clement /// The analysis is to determine if there are any conflicts. A conflict is when 5247f75930SValentin Clement /// one the following cases occurs. 5347f75930SValentin Clement /// 5447f75930SValentin Clement /// 1. There is an `array_update` to an array value, a_j, such that a_j was 5547f75930SValentin Clement /// loaded from the same array memory reference (array_j) but with a different 5647f75930SValentin Clement /// shape as the other array values a_i, where i != j. [Possible overlapping 5747f75930SValentin Clement /// arrays.] 5847f75930SValentin Clement /// 5947f75930SValentin Clement /// 2. There is either an array_fetch or array_update of a_j with a different 6047f75930SValentin Clement /// set of index values. [Possible loop-carried dependence.] 6147f75930SValentin Clement /// 6247f75930SValentin Clement /// If none of the array values overlap in storage and the accesses are not 6347f75930SValentin Clement /// loop-carried, then the arrays are conflict-free and no copies are required. 6447f75930SValentin Clement class ArrayCopyAnalysis { 6547f75930SValentin Clement public: 665e50dd04SRiver Riddle MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(ArrayCopyAnalysis) 675e50dd04SRiver Riddle 6847f75930SValentin Clement using ConflictSetT = llvm::SmallPtrSet<mlir::Operation *, 16>; 6947f75930SValentin Clement using UseSetT = llvm::SmallPtrSet<mlir::OpOperand *, 8>; 70beeb86bdSValentin Clement using LoadMapSetsT = llvm::DenseMap<mlir::Operation *, UseSetT>; 71beeb86bdSValentin Clement using AmendAccessSetT = llvm::SmallPtrSet<mlir::Operation *, 4>; 7247f75930SValentin Clement 7347f75930SValentin Clement ArrayCopyAnalysis(mlir::Operation *op) : operation{op} { construct(op); } 7447f75930SValentin Clement 7547f75930SValentin Clement mlir::Operation *getOperation() const { return operation; } 7647f75930SValentin Clement 7747f75930SValentin Clement /// Return true iff the `array_merge_store` has potential conflicts. 7847f75930SValentin Clement bool hasPotentialConflict(mlir::Operation *op) const { 7947f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 8047f75930SValentin Clement << "looking for a conflict on " << *op 8147f75930SValentin Clement << " and the set has a total of " << conflicts.size() << '\n'); 8247f75930SValentin Clement return conflicts.contains(op); 8347f75930SValentin Clement } 8447f75930SValentin Clement 85beeb86bdSValentin Clement /// Return the use map. 86beeb86bdSValentin Clement /// The use map maps array access, amend, fetch and update operations back to 87beeb86bdSValentin Clement /// the array load that is the original source of the array value. 88beeb86bdSValentin Clement /// It maps an array_load to an array_merge_store, if and only if the loaded 89beeb86bdSValentin Clement /// array value has pending modifications to be merged. 9047f75930SValentin Clement const OperationUseMapT &getUseMap() const { return useMap; } 9147f75930SValentin Clement 92beeb86bdSValentin Clement /// Return the set of array_access ops directly associated with array_amend 93beeb86bdSValentin Clement /// ops. 94beeb86bdSValentin Clement bool inAmendAccessSet(mlir::Operation *op) const { 95beeb86bdSValentin Clement return amendAccesses.count(op); 96beeb86bdSValentin Clement } 97beeb86bdSValentin Clement 98beeb86bdSValentin Clement /// For ArrayLoad `load`, return the transitive set of all OpOperands. 99beeb86bdSValentin Clement UseSetT getLoadUseSet(mlir::Operation *load) const { 100beeb86bdSValentin Clement assert(loadMapSets.count(load) && "analysis missed an array load?"); 101beeb86bdSValentin Clement return loadMapSets.lookup(load); 102beeb86bdSValentin Clement } 103beeb86bdSValentin Clement 104beeb86bdSValentin Clement void arrayMentions(llvm::SmallVectorImpl<mlir::Operation *> &mentions, 105beeb86bdSValentin Clement ArrayLoadOp load); 10647f75930SValentin Clement 10747f75930SValentin Clement private: 10847f75930SValentin Clement void construct(mlir::Operation *topLevelOp); 10947f75930SValentin Clement 11047f75930SValentin Clement mlir::Operation *operation; // operation that analysis ran upon 11147f75930SValentin Clement ConflictSetT conflicts; // set of conflicts (loads and merge stores) 11247f75930SValentin Clement OperationUseMapT useMap; 11347f75930SValentin Clement LoadMapSetsT loadMapSets; 114beeb86bdSValentin Clement // Set of array_access ops associated with array_amend ops. 115beeb86bdSValentin Clement AmendAccessSetT amendAccesses; 11647f75930SValentin Clement }; 11747f75930SValentin Clement } // namespace 11847f75930SValentin Clement 11947f75930SValentin Clement namespace { 12047f75930SValentin Clement /// Helper class to collect all array operations that produced an array value. 12147f75930SValentin Clement class ReachCollector { 122beeb86bdSValentin Clement public: 12347f75930SValentin Clement ReachCollector(llvm::SmallVectorImpl<mlir::Operation *> &reach, 12447f75930SValentin Clement mlir::Region *loopRegion) 12547f75930SValentin Clement : reach{reach}, loopRegion{loopRegion} {} 12647f75930SValentin Clement 127beeb86bdSValentin Clement void collectArrayMentionFrom(mlir::Operation *op, mlir::ValueRange range) { 12847f75930SValentin Clement if (range.empty()) { 129beeb86bdSValentin Clement collectArrayMentionFrom(op, mlir::Value{}); 13047f75930SValentin Clement return; 13147f75930SValentin Clement } 13247f75930SValentin Clement for (mlir::Value v : range) 133beeb86bdSValentin Clement collectArrayMentionFrom(v); 13447f75930SValentin Clement } 13547f75930SValentin Clement 136beeb86bdSValentin Clement // Collect all the array_access ops in `block`. This recursively looks into 137beeb86bdSValentin Clement // blocks in ops with regions. 138beeb86bdSValentin Clement // FIXME: This is temporarily relying on the array_amend appearing in a 139beeb86bdSValentin Clement // do_loop Region. This phase ordering assumption can be eliminated by using 140beeb86bdSValentin Clement // dominance information to find the array_access ops or by scanning the 141beeb86bdSValentin Clement // transitive closure of the amending array_access's users and the defs that 142beeb86bdSValentin Clement // reach them. 143beeb86bdSValentin Clement void collectAccesses(llvm::SmallVector<ArrayAccessOp> &result, 144beeb86bdSValentin Clement mlir::Block *block) { 145beeb86bdSValentin Clement for (auto &op : *block) { 146beeb86bdSValentin Clement if (auto access = mlir::dyn_cast<ArrayAccessOp>(op)) { 147beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "adding access: " << access << '\n'); 148beeb86bdSValentin Clement result.push_back(access); 149beeb86bdSValentin Clement continue; 150beeb86bdSValentin Clement } 151beeb86bdSValentin Clement for (auto ®ion : op.getRegions()) 152beeb86bdSValentin Clement for (auto &bb : region.getBlocks()) 153beeb86bdSValentin Clement collectAccesses(result, &bb); 154beeb86bdSValentin Clement } 155beeb86bdSValentin Clement } 156beeb86bdSValentin Clement 157beeb86bdSValentin Clement void collectArrayMentionFrom(mlir::Operation *op, mlir::Value val) { 15847f75930SValentin Clement // `val` is defined by an Op, process the defining Op. 15947f75930SValentin Clement // If `val` is defined by a region containing Op, we want to drill down 16047f75930SValentin Clement // and through that Op's region(s). 16147f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "popset: " << *op << '\n'); 16247f75930SValentin Clement auto popFn = [&](auto rop) { 16347f75930SValentin Clement assert(val && "op must have a result value"); 16447f75930SValentin Clement auto resNum = val.cast<mlir::OpResult>().getResultNumber(); 16547f75930SValentin Clement llvm::SmallVector<mlir::Value> results; 16647f75930SValentin Clement rop.resultToSourceOps(results, resNum); 16747f75930SValentin Clement for (auto u : results) 168beeb86bdSValentin Clement collectArrayMentionFrom(u); 16947f75930SValentin Clement }; 170beeb86bdSValentin Clement if (auto rop = mlir::dyn_cast<DoLoopOp>(op)) { 171beeb86bdSValentin Clement popFn(rop); 172beeb86bdSValentin Clement return; 173beeb86bdSValentin Clement } 174beeb86bdSValentin Clement if (auto rop = mlir::dyn_cast<IterWhileOp>(op)) { 17547f75930SValentin Clement popFn(rop); 17647f75930SValentin Clement return; 17747f75930SValentin Clement } 17847f75930SValentin Clement if (auto rop = mlir::dyn_cast<fir::IfOp>(op)) { 17947f75930SValentin Clement popFn(rop); 18047f75930SValentin Clement return; 18147f75930SValentin Clement } 182beeb86bdSValentin Clement if (auto box = mlir::dyn_cast<EmboxOp>(op)) { 183beeb86bdSValentin Clement for (auto *user : box.getMemref().getUsers()) 184beeb86bdSValentin Clement if (user != op) 185beeb86bdSValentin Clement collectArrayMentionFrom(user, user->getResults()); 186beeb86bdSValentin Clement return; 187beeb86bdSValentin Clement } 18847f75930SValentin Clement if (auto mergeStore = mlir::dyn_cast<ArrayMergeStoreOp>(op)) { 18947f75930SValentin Clement if (opIsInsideLoops(mergeStore)) 190beeb86bdSValentin Clement collectArrayMentionFrom(mergeStore.getSequence()); 19147f75930SValentin Clement return; 19247f75930SValentin Clement } 19347f75930SValentin Clement 19447f75930SValentin Clement if (mlir::isa<AllocaOp, AllocMemOp>(op)) { 19547f75930SValentin Clement // Look for any stores inside the loops, and collect an array operation 19647f75930SValentin Clement // that produced the value being stored to it. 197beeb86bdSValentin Clement for (auto *user : op->getUsers()) 19847f75930SValentin Clement if (auto store = mlir::dyn_cast<fir::StoreOp>(user)) 19947f75930SValentin Clement if (opIsInsideLoops(store)) 200beeb86bdSValentin Clement collectArrayMentionFrom(store.getValue()); 20147f75930SValentin Clement return; 20247f75930SValentin Clement } 20347f75930SValentin Clement 204beeb86bdSValentin Clement // Scan the uses of amend's memref 205beeb86bdSValentin Clement if (auto amend = mlir::dyn_cast<ArrayAmendOp>(op)) { 206beeb86bdSValentin Clement reach.push_back(op); 207beeb86bdSValentin Clement llvm::SmallVector<ArrayAccessOp> accesses; 208beeb86bdSValentin Clement collectAccesses(accesses, op->getBlock()); 209beeb86bdSValentin Clement for (auto access : accesses) 210beeb86bdSValentin Clement collectArrayMentionFrom(access.getResult()); 21147f75930SValentin Clement } 212beeb86bdSValentin Clement 213beeb86bdSValentin Clement // Otherwise, Op does not contain a region so just chase its operands. 214beeb86bdSValentin Clement if (mlir::isa<ArrayAccessOp, ArrayLoadOp, ArrayUpdateOp, ArrayModifyOp, 215beeb86bdSValentin Clement ArrayFetchOp>(op)) { 216beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "add " << *op << " to reachable set\n"); 217beeb86bdSValentin Clement reach.push_back(op); 218beeb86bdSValentin Clement } 219beeb86bdSValentin Clement 220beeb86bdSValentin Clement // Include all array_access ops using an array_load. 221beeb86bdSValentin Clement if (auto arrLd = mlir::dyn_cast<ArrayLoadOp>(op)) 222beeb86bdSValentin Clement for (auto *user : arrLd.getResult().getUsers()) 223beeb86bdSValentin Clement if (mlir::isa<ArrayAccessOp>(user)) { 224beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "add " << *user << " to reachable set\n"); 225beeb86bdSValentin Clement reach.push_back(user); 226beeb86bdSValentin Clement } 227beeb86bdSValentin Clement 228beeb86bdSValentin Clement // Array modify assignment is performed on the result. So the analysis must 229beeb86bdSValentin Clement // look at the what is done with the result. 23047f75930SValentin Clement if (mlir::isa<ArrayModifyOp>(op)) 231beeb86bdSValentin Clement for (auto *user : op->getResult(0).getUsers()) 23247f75930SValentin Clement followUsers(user); 23347f75930SValentin Clement 234beeb86bdSValentin Clement if (mlir::isa<fir::CallOp>(op)) { 235beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "add " << *op << " to reachable set\n"); 236beeb86bdSValentin Clement reach.push_back(op); 23747f75930SValentin Clement } 23847f75930SValentin Clement 239beeb86bdSValentin Clement for (auto u : op->getOperands()) 240beeb86bdSValentin Clement collectArrayMentionFrom(u); 241beeb86bdSValentin Clement } 242beeb86bdSValentin Clement 243beeb86bdSValentin Clement void collectArrayMentionFrom(mlir::BlockArgument ba) { 24447f75930SValentin Clement auto *parent = ba.getOwner()->getParentOp(); 24547f75930SValentin Clement // If inside an Op holding a region, the block argument corresponds to an 24647f75930SValentin Clement // argument passed to the containing Op. 24747f75930SValentin Clement auto popFn = [&](auto rop) { 248beeb86bdSValentin Clement collectArrayMentionFrom(rop.blockArgToSourceOp(ba.getArgNumber())); 24947f75930SValentin Clement }; 25047f75930SValentin Clement if (auto rop = mlir::dyn_cast<DoLoopOp>(parent)) { 25147f75930SValentin Clement popFn(rop); 25247f75930SValentin Clement return; 25347f75930SValentin Clement } 25447f75930SValentin Clement if (auto rop = mlir::dyn_cast<IterWhileOp>(parent)) { 25547f75930SValentin Clement popFn(rop); 25647f75930SValentin Clement return; 25747f75930SValentin Clement } 25847f75930SValentin Clement // Otherwise, a block argument is provided via the pred blocks. 25947f75930SValentin Clement for (auto *pred : ba.getOwner()->getPredecessors()) { 26047f75930SValentin Clement auto u = pred->getTerminator()->getOperand(ba.getArgNumber()); 261beeb86bdSValentin Clement collectArrayMentionFrom(u); 26247f75930SValentin Clement } 26347f75930SValentin Clement } 26447f75930SValentin Clement 26547f75930SValentin Clement // Recursively trace operands to find all array operations relating to the 26647f75930SValentin Clement // values merged. 267beeb86bdSValentin Clement void collectArrayMentionFrom(mlir::Value val) { 26847f75930SValentin Clement if (!val || visited.contains(val)) 26947f75930SValentin Clement return; 27047f75930SValentin Clement visited.insert(val); 27147f75930SValentin Clement 27247f75930SValentin Clement // Process a block argument. 27347f75930SValentin Clement if (auto ba = val.dyn_cast<mlir::BlockArgument>()) { 274beeb86bdSValentin Clement collectArrayMentionFrom(ba); 27547f75930SValentin Clement return; 27647f75930SValentin Clement } 27747f75930SValentin Clement 27847f75930SValentin Clement // Process an Op. 27947f75930SValentin Clement if (auto *op = val.getDefiningOp()) { 280beeb86bdSValentin Clement collectArrayMentionFrom(op, val); 28147f75930SValentin Clement return; 28247f75930SValentin Clement } 28347f75930SValentin Clement 284beeb86bdSValentin Clement emitFatalError(val.getLoc(), "unhandled value"); 28547f75930SValentin Clement } 28647f75930SValentin Clement 287beeb86bdSValentin Clement /// Return all ops that produce the array value that is stored into the 288beeb86bdSValentin Clement /// `array_merge_store`. 289beeb86bdSValentin Clement static void reachingValues(llvm::SmallVectorImpl<mlir::Operation *> &reach, 290beeb86bdSValentin Clement mlir::Value seq) { 291beeb86bdSValentin Clement reach.clear(); 292beeb86bdSValentin Clement mlir::Region *loopRegion = nullptr; 293beeb86bdSValentin Clement if (auto doLoop = mlir::dyn_cast_or_null<DoLoopOp>(seq.getDefiningOp())) 294beeb86bdSValentin Clement loopRegion = &doLoop->getRegion(0); 295beeb86bdSValentin Clement ReachCollector collector(reach, loopRegion); 296beeb86bdSValentin Clement collector.collectArrayMentionFrom(seq); 297beeb86bdSValentin Clement } 298beeb86bdSValentin Clement 299beeb86bdSValentin Clement private: 30047f75930SValentin Clement /// Is \op inside the loop nest region ? 301beeb86bdSValentin Clement /// FIXME: replace this structural dependence with graph properties. 30247f75930SValentin Clement bool opIsInsideLoops(mlir::Operation *op) const { 303beeb86bdSValentin Clement auto *region = op->getParentRegion(); 304beeb86bdSValentin Clement while (region) { 305beeb86bdSValentin Clement if (region == loopRegion) 306beeb86bdSValentin Clement return true; 307beeb86bdSValentin Clement region = region->getParentRegion(); 308beeb86bdSValentin Clement } 309beeb86bdSValentin Clement return false; 31047f75930SValentin Clement } 31147f75930SValentin Clement 31247f75930SValentin Clement /// Recursively trace the use of an operation results, calling 313beeb86bdSValentin Clement /// collectArrayMentionFrom on the direct and indirect user operands. 31447f75930SValentin Clement void followUsers(mlir::Operation *op) { 31547f75930SValentin Clement for (auto userOperand : op->getOperands()) 316beeb86bdSValentin Clement collectArrayMentionFrom(userOperand); 31747f75930SValentin Clement // Go through potential converts/coordinate_op. 318beeb86bdSValentin Clement for (auto indirectUser : op->getUsers()) 31947f75930SValentin Clement followUsers(indirectUser); 32047f75930SValentin Clement } 32147f75930SValentin Clement 32247f75930SValentin Clement llvm::SmallVectorImpl<mlir::Operation *> &reach; 32347f75930SValentin Clement llvm::SmallPtrSet<mlir::Value, 16> visited; 32447f75930SValentin Clement /// Region of the loops nest that produced the array value. 32547f75930SValentin Clement mlir::Region *loopRegion; 32647f75930SValentin Clement }; 32747f75930SValentin Clement } // namespace 32847f75930SValentin Clement 32947f75930SValentin Clement /// Find all the array operations that access the array value that is loaded by 33047f75930SValentin Clement /// the array load operation, `load`. 331beeb86bdSValentin Clement void ArrayCopyAnalysis::arrayMentions( 332beeb86bdSValentin Clement llvm::SmallVectorImpl<mlir::Operation *> &mentions, ArrayLoadOp load) { 333beeb86bdSValentin Clement mentions.clear(); 33447f75930SValentin Clement auto lmIter = loadMapSets.find(load); 335beeb86bdSValentin Clement if (lmIter != loadMapSets.end()) { 336beeb86bdSValentin Clement for (auto *opnd : lmIter->second) { 337beeb86bdSValentin Clement auto *owner = opnd->getOwner(); 338beeb86bdSValentin Clement if (mlir::isa<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp, ArrayUpdateOp, 339beeb86bdSValentin Clement ArrayModifyOp>(owner)) 340beeb86bdSValentin Clement mentions.push_back(owner); 341beeb86bdSValentin Clement } 342beeb86bdSValentin Clement return; 343beeb86bdSValentin Clement } 34447f75930SValentin Clement 34547f75930SValentin Clement UseSetT visited; 34647f75930SValentin Clement llvm::SmallVector<mlir::OpOperand *> queue; // uses of ArrayLoad[orig] 34747f75930SValentin Clement 34847f75930SValentin Clement auto appendToQueue = [&](mlir::Value val) { 349beeb86bdSValentin Clement for (auto &use : val.getUses()) 35047f75930SValentin Clement if (!visited.count(&use)) { 35147f75930SValentin Clement visited.insert(&use); 35247f75930SValentin Clement queue.push_back(&use); 35347f75930SValentin Clement } 35447f75930SValentin Clement }; 35547f75930SValentin Clement 35647f75930SValentin Clement // Build the set of uses of `original`. 35747f75930SValentin Clement // let USES = { uses of original fir.load } 35847f75930SValentin Clement appendToQueue(load); 35947f75930SValentin Clement 36047f75930SValentin Clement // Process the worklist until done. 36147f75930SValentin Clement while (!queue.empty()) { 36247f75930SValentin Clement mlir::OpOperand *operand = queue.pop_back_val(); 36347f75930SValentin Clement mlir::Operation *owner = operand->getOwner(); 364beeb86bdSValentin Clement if (!owner) 365beeb86bdSValentin Clement continue; 36647f75930SValentin Clement auto structuredLoop = [&](auto ro) { 36747f75930SValentin Clement if (auto blockArg = ro.iterArgToBlockArg(operand->get())) { 36847f75930SValentin Clement int64_t arg = blockArg.getArgNumber(); 369149ad3d5SShraiysh Vaishay mlir::Value output = ro.getResult(ro.getFinalValue() ? arg : arg - 1); 37047f75930SValentin Clement appendToQueue(output); 37147f75930SValentin Clement appendToQueue(blockArg); 37247f75930SValentin Clement } 37347f75930SValentin Clement }; 37447f75930SValentin Clement // TODO: this need to be updated to use the control-flow interface. 37547f75930SValentin Clement auto branchOp = [&](mlir::Block *dest, OperandRange operands) { 37647f75930SValentin Clement if (operands.empty()) 37747f75930SValentin Clement return; 37847f75930SValentin Clement 37947f75930SValentin Clement // Check if this operand is within the range. 38047f75930SValentin Clement unsigned operandIndex = operand->getOperandNumber(); 38147f75930SValentin Clement unsigned operandsStart = operands.getBeginOperandIndex(); 38247f75930SValentin Clement if (operandIndex < operandsStart || 38347f75930SValentin Clement operandIndex >= (operandsStart + operands.size())) 38447f75930SValentin Clement return; 38547f75930SValentin Clement 38647f75930SValentin Clement // Index the successor. 38747f75930SValentin Clement unsigned argIndex = operandIndex - operandsStart; 38847f75930SValentin Clement appendToQueue(dest->getArgument(argIndex)); 38947f75930SValentin Clement }; 39047f75930SValentin Clement // Thread uses into structured loop bodies and return value uses. 39147f75930SValentin Clement if (auto ro = mlir::dyn_cast<DoLoopOp>(owner)) { 39247f75930SValentin Clement structuredLoop(ro); 39347f75930SValentin Clement } else if (auto ro = mlir::dyn_cast<IterWhileOp>(owner)) { 39447f75930SValentin Clement structuredLoop(ro); 39547f75930SValentin Clement } else if (auto rs = mlir::dyn_cast<ResultOp>(owner)) { 39647f75930SValentin Clement // Thread any uses of fir.if that return the marked array value. 397beeb86bdSValentin Clement mlir::Operation *parent = rs->getParentRegion()->getParentOp(); 398beeb86bdSValentin Clement if (auto ifOp = mlir::dyn_cast<fir::IfOp>(parent)) 39947f75930SValentin Clement appendToQueue(ifOp.getResult(operand->getOperandNumber())); 40047f75930SValentin Clement } else if (mlir::isa<ArrayFetchOp>(owner)) { 40147f75930SValentin Clement // Keep track of array value fetches. 40247f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 40347f75930SValentin Clement << "add fetch {" << *owner << "} to array value set\n"); 404beeb86bdSValentin Clement mentions.push_back(owner); 40547f75930SValentin Clement } else if (auto update = mlir::dyn_cast<ArrayUpdateOp>(owner)) { 40647f75930SValentin Clement // Keep track of array value updates and thread the return value uses. 40747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 40847f75930SValentin Clement << "add update {" << *owner << "} to array value set\n"); 409beeb86bdSValentin Clement mentions.push_back(owner); 41047f75930SValentin Clement appendToQueue(update.getResult()); 41147f75930SValentin Clement } else if (auto update = mlir::dyn_cast<ArrayModifyOp>(owner)) { 41247f75930SValentin Clement // Keep track of array value modification and thread the return value 41347f75930SValentin Clement // uses. 41447f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 41547f75930SValentin Clement << "add modify {" << *owner << "} to array value set\n"); 416beeb86bdSValentin Clement mentions.push_back(owner); 41747f75930SValentin Clement appendToQueue(update.getResult(1)); 418beeb86bdSValentin Clement } else if (auto mention = mlir::dyn_cast<ArrayAccessOp>(owner)) { 419beeb86bdSValentin Clement mentions.push_back(owner); 420beeb86bdSValentin Clement } else if (auto amend = mlir::dyn_cast<ArrayAmendOp>(owner)) { 421beeb86bdSValentin Clement mentions.push_back(owner); 422beeb86bdSValentin Clement appendToQueue(amend.getResult()); 423ace01605SRiver Riddle } else if (auto br = mlir::dyn_cast<mlir::cf::BranchOp>(owner)) { 4243012f35fSJacques Pienaar branchOp(br.getDest(), br.getDestOperands()); 425ace01605SRiver Riddle } else if (auto br = mlir::dyn_cast<mlir::cf::CondBranchOp>(owner)) { 42647f75930SValentin Clement branchOp(br.getTrueDest(), br.getTrueOperands()); 42747f75930SValentin Clement branchOp(br.getFalseDest(), br.getFalseOperands()); 42847f75930SValentin Clement } else if (mlir::isa<ArrayMergeStoreOp>(owner)) { 42947f75930SValentin Clement // do nothing 43047f75930SValentin Clement } else { 43147f75930SValentin Clement llvm::report_fatal_error("array value reached unexpected op"); 43247f75930SValentin Clement } 43347f75930SValentin Clement } 434beeb86bdSValentin Clement loadMapSets.insert({load, visited}); 435beeb86bdSValentin Clement } 436beeb86bdSValentin Clement 437beeb86bdSValentin Clement static bool hasPointerType(mlir::Type type) { 438beeb86bdSValentin Clement if (auto boxTy = type.dyn_cast<BoxType>()) 439beeb86bdSValentin Clement type = boxTy.getEleTy(); 440beeb86bdSValentin Clement return type.isa<fir::PointerType>(); 441beeb86bdSValentin Clement } 442beeb86bdSValentin Clement 443beeb86bdSValentin Clement // This is a NF performance hack. It makes a simple test that the slices of the 444beeb86bdSValentin Clement // load, \p ld, and the merge store, \p st, are trivially mutually exclusive. 445beeb86bdSValentin Clement static bool mutuallyExclusiveSliceRange(ArrayLoadOp ld, ArrayMergeStoreOp st) { 446beeb86bdSValentin Clement // If the same array_load, then no further testing is warranted. 447beeb86bdSValentin Clement if (ld.getResult() == st.getOriginal()) 448beeb86bdSValentin Clement return false; 449beeb86bdSValentin Clement 450beeb86bdSValentin Clement auto getSliceOp = [](mlir::Value val) -> SliceOp { 451beeb86bdSValentin Clement if (!val) 452beeb86bdSValentin Clement return {}; 453beeb86bdSValentin Clement auto sliceOp = mlir::dyn_cast_or_null<SliceOp>(val.getDefiningOp()); 454beeb86bdSValentin Clement if (!sliceOp) 455beeb86bdSValentin Clement return {}; 456beeb86bdSValentin Clement return sliceOp; 457beeb86bdSValentin Clement }; 458beeb86bdSValentin Clement 459beeb86bdSValentin Clement auto ldSlice = getSliceOp(ld.getSlice()); 460beeb86bdSValentin Clement auto stSlice = getSliceOp(st.getSlice()); 461beeb86bdSValentin Clement if (!ldSlice || !stSlice) 462beeb86bdSValentin Clement return false; 463beeb86bdSValentin Clement 464beeb86bdSValentin Clement // Resign on subobject slices. 465beeb86bdSValentin Clement if (!ldSlice.getFields().empty() || !stSlice.getFields().empty() || 466beeb86bdSValentin Clement !ldSlice.getSubstr().empty() || !stSlice.getSubstr().empty()) 467beeb86bdSValentin Clement return false; 468beeb86bdSValentin Clement 469beeb86bdSValentin Clement // Crudely test that the two slices do not overlap by looking for the 470beeb86bdSValentin Clement // following general condition. If the slices look like (i:j) and (j+1:k) then 471beeb86bdSValentin Clement // these ranges do not overlap. The addend must be a constant. 472beeb86bdSValentin Clement auto ldTriples = ldSlice.getTriples(); 473beeb86bdSValentin Clement auto stTriples = stSlice.getTriples(); 474beeb86bdSValentin Clement const auto size = ldTriples.size(); 475beeb86bdSValentin Clement if (size != stTriples.size()) 476beeb86bdSValentin Clement return false; 477beeb86bdSValentin Clement 478beeb86bdSValentin Clement auto displacedByConstant = [](mlir::Value v1, mlir::Value v2) { 479beeb86bdSValentin Clement auto removeConvert = [](mlir::Value v) -> mlir::Operation * { 480beeb86bdSValentin Clement auto *op = v.getDefiningOp(); 481beeb86bdSValentin Clement while (auto conv = mlir::dyn_cast_or_null<ConvertOp>(op)) 482beeb86bdSValentin Clement op = conv.getValue().getDefiningOp(); 483beeb86bdSValentin Clement return op; 484beeb86bdSValentin Clement }; 485beeb86bdSValentin Clement 486beeb86bdSValentin Clement auto isPositiveConstant = [](mlir::Value v) -> bool { 487beeb86bdSValentin Clement if (auto conOp = 488beeb86bdSValentin Clement mlir::dyn_cast<mlir::arith::ConstantOp>(v.getDefiningOp())) 489beeb86bdSValentin Clement if (auto iattr = conOp.getValue().dyn_cast<mlir::IntegerAttr>()) 490beeb86bdSValentin Clement return iattr.getInt() > 0; 491beeb86bdSValentin Clement return false; 492beeb86bdSValentin Clement }; 493beeb86bdSValentin Clement 494beeb86bdSValentin Clement auto *op1 = removeConvert(v1); 495beeb86bdSValentin Clement auto *op2 = removeConvert(v2); 496beeb86bdSValentin Clement if (!op1 || !op2) 497beeb86bdSValentin Clement return false; 498beeb86bdSValentin Clement if (auto addi = mlir::dyn_cast<mlir::arith::AddIOp>(op2)) 499beeb86bdSValentin Clement if ((addi.getLhs().getDefiningOp() == op1 && 500beeb86bdSValentin Clement isPositiveConstant(addi.getRhs())) || 501beeb86bdSValentin Clement (addi.getRhs().getDefiningOp() == op1 && 502beeb86bdSValentin Clement isPositiveConstant(addi.getLhs()))) 503beeb86bdSValentin Clement return true; 504beeb86bdSValentin Clement if (auto subi = mlir::dyn_cast<mlir::arith::SubIOp>(op1)) 505beeb86bdSValentin Clement if (subi.getLhs().getDefiningOp() == op2 && 506beeb86bdSValentin Clement isPositiveConstant(subi.getRhs())) 507beeb86bdSValentin Clement return true; 508beeb86bdSValentin Clement return false; 509beeb86bdSValentin Clement }; 510beeb86bdSValentin Clement 511beeb86bdSValentin Clement for (std::remove_const_t<decltype(size)> i = 0; i < size; i += 3) { 512beeb86bdSValentin Clement // If both are loop invariant, skip to the next triple. 513beeb86bdSValentin Clement if (mlir::isa_and_nonnull<fir::UndefOp>(ldTriples[i + 1].getDefiningOp()) && 514beeb86bdSValentin Clement mlir::isa_and_nonnull<fir::UndefOp>(stTriples[i + 1].getDefiningOp())) { 515beeb86bdSValentin Clement // Unless either is a vector index, then be conservative. 516beeb86bdSValentin Clement if (mlir::isa_and_nonnull<fir::UndefOp>(ldTriples[i].getDefiningOp()) || 517beeb86bdSValentin Clement mlir::isa_and_nonnull<fir::UndefOp>(stTriples[i].getDefiningOp())) 518beeb86bdSValentin Clement return false; 519beeb86bdSValentin Clement continue; 520beeb86bdSValentin Clement } 521beeb86bdSValentin Clement // If identical, skip to the next triple. 522beeb86bdSValentin Clement if (ldTriples[i] == stTriples[i] && ldTriples[i + 1] == stTriples[i + 1] && 523beeb86bdSValentin Clement ldTriples[i + 2] == stTriples[i + 2]) 524beeb86bdSValentin Clement continue; 525beeb86bdSValentin Clement // If ubound and lbound are the same with a constant offset, skip to the 526beeb86bdSValentin Clement // next triple. 527beeb86bdSValentin Clement if (displacedByConstant(ldTriples[i + 1], stTriples[i]) || 528beeb86bdSValentin Clement displacedByConstant(stTriples[i + 1], ldTriples[i])) 529beeb86bdSValentin Clement continue; 530beeb86bdSValentin Clement return false; 531beeb86bdSValentin Clement } 532beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "detected non-overlapping slice ranges on " << ld 533beeb86bdSValentin Clement << " and " << st << ", which is not a conflict\n"); 534beeb86bdSValentin Clement return true; 53547f75930SValentin Clement } 53647f75930SValentin Clement 53747f75930SValentin Clement /// Is there a conflict between the array value that was updated and to be 53847f75930SValentin Clement /// stored to `st` and the set of arrays loaded (`reach`) and used to compute 53947f75930SValentin Clement /// the updated value? 54047f75930SValentin Clement static bool conflictOnLoad(llvm::ArrayRef<mlir::Operation *> reach, 54147f75930SValentin Clement ArrayMergeStoreOp st) { 54247f75930SValentin Clement mlir::Value load; 543149ad3d5SShraiysh Vaishay mlir::Value addr = st.getMemref(); 544beeb86bdSValentin Clement const bool storeHasPointerType = hasPointerType(addr.getType()); 545beeb86bdSValentin Clement for (auto *op : reach) 546beeb86bdSValentin Clement if (auto ld = mlir::dyn_cast<ArrayLoadOp>(op)) { 547149ad3d5SShraiysh Vaishay mlir::Type ldTy = ld.getMemref().getType(); 548149ad3d5SShraiysh Vaishay if (ld.getMemref() == addr) { 549beeb86bdSValentin Clement if (mutuallyExclusiveSliceRange(ld, st)) 550beeb86bdSValentin Clement continue; 551149ad3d5SShraiysh Vaishay if (ld.getResult() != st.getOriginal()) 55247f75930SValentin Clement return true; 553beeb86bdSValentin Clement if (load) { 554beeb86bdSValentin Clement // TODO: extend this to allow checking if the first `load` and this 555beeb86bdSValentin Clement // `ld` are mutually exclusive accesses but not identical. 55647f75930SValentin Clement return true; 557beeb86bdSValentin Clement } 55847f75930SValentin Clement load = ld; 559beeb86bdSValentin Clement } else if ((hasPointerType(ldTy) || storeHasPointerType)) { 560beeb86bdSValentin Clement // TODO: Use target attribute to restrict this case further. 561beeb86bdSValentin Clement // TODO: Check if types can also allow ruling out some cases. For now, 562beeb86bdSValentin Clement // the fact that equivalences is using pointer attribute to enforce 563beeb86bdSValentin Clement // aliasing is preventing any attempt to do so, and in general, it may 564beeb86bdSValentin Clement // be wrong to use this if any of the types is a complex or a derived 565beeb86bdSValentin Clement // for which it is possible to create a pointer to a part with a 566beeb86bdSValentin Clement // different type than the whole, although this deserve some more 567beeb86bdSValentin Clement // investigation because existing compiler behavior seem to diverge 568beeb86bdSValentin Clement // here. 569beeb86bdSValentin Clement return true; 57047f75930SValentin Clement } 57147f75930SValentin Clement } 57247f75930SValentin Clement return false; 57347f75930SValentin Clement } 57447f75930SValentin Clement 575beeb86bdSValentin Clement /// Is there an access vector conflict on the array being merged into? If the 576beeb86bdSValentin Clement /// access vectors diverge, then assume that there are potentially overlapping 577beeb86bdSValentin Clement /// loop-carried references. 578beeb86bdSValentin Clement static bool conflictOnMerge(llvm::ArrayRef<mlir::Operation *> mentions) { 579beeb86bdSValentin Clement if (mentions.size() < 2) 58047f75930SValentin Clement return false; 58147f75930SValentin Clement llvm::SmallVector<mlir::Value> indices; 582beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "check merge conflict on with " << mentions.size() 583beeb86bdSValentin Clement << " mentions on the list\n"); 584beeb86bdSValentin Clement bool valSeen = false; 585beeb86bdSValentin Clement bool refSeen = false; 586beeb86bdSValentin Clement for (auto *op : mentions) { 58747f75930SValentin Clement llvm::SmallVector<mlir::Value> compareVector; 58847f75930SValentin Clement if (auto u = mlir::dyn_cast<ArrayUpdateOp>(op)) { 589beeb86bdSValentin Clement valSeen = true; 59047f75930SValentin Clement if (indices.empty()) { 591149ad3d5SShraiysh Vaishay indices = u.getIndices(); 59247f75930SValentin Clement continue; 59347f75930SValentin Clement } 594149ad3d5SShraiysh Vaishay compareVector = u.getIndices(); 59547f75930SValentin Clement } else if (auto f = mlir::dyn_cast<ArrayModifyOp>(op)) { 596beeb86bdSValentin Clement valSeen = true; 59747f75930SValentin Clement if (indices.empty()) { 598149ad3d5SShraiysh Vaishay indices = f.getIndices(); 59947f75930SValentin Clement continue; 60047f75930SValentin Clement } 601149ad3d5SShraiysh Vaishay compareVector = f.getIndices(); 60247f75930SValentin Clement } else if (auto f = mlir::dyn_cast<ArrayFetchOp>(op)) { 603beeb86bdSValentin Clement valSeen = true; 60447f75930SValentin Clement if (indices.empty()) { 605149ad3d5SShraiysh Vaishay indices = f.getIndices(); 60647f75930SValentin Clement continue; 60747f75930SValentin Clement } 608149ad3d5SShraiysh Vaishay compareVector = f.getIndices(); 609beeb86bdSValentin Clement } else if (auto f = mlir::dyn_cast<ArrayAccessOp>(op)) { 610beeb86bdSValentin Clement refSeen = true; 611beeb86bdSValentin Clement if (indices.empty()) { 612beeb86bdSValentin Clement indices = f.getIndices(); 613beeb86bdSValentin Clement continue; 61447f75930SValentin Clement } 615beeb86bdSValentin Clement compareVector = f.getIndices(); 616beeb86bdSValentin Clement } else if (mlir::isa<ArrayAmendOp>(op)) { 617beeb86bdSValentin Clement refSeen = true; 618beeb86bdSValentin Clement continue; 619beeb86bdSValentin Clement } else { 620beeb86bdSValentin Clement mlir::emitError(op->getLoc(), "unexpected operation in analysis"); 621beeb86bdSValentin Clement } 622beeb86bdSValentin Clement if (compareVector.size() != indices.size() || 623beeb86bdSValentin Clement llvm::any_of(llvm::zip(compareVector, indices), [&](auto pair) { 624beeb86bdSValentin Clement return std::get<0>(pair) != std::get<1>(pair); 625beeb86bdSValentin Clement })) 62647f75930SValentin Clement return true; 62747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "vectors compare equal\n"); 62847f75930SValentin Clement } 629beeb86bdSValentin Clement return valSeen && refSeen; 630beeb86bdSValentin Clement } 631beeb86bdSValentin Clement 632beeb86bdSValentin Clement /// With element-by-reference semantics, an amended array with more than once 633beeb86bdSValentin Clement /// access to the same loaded array are conservatively considered a conflict. 634beeb86bdSValentin Clement /// Note: the array copy can still be eliminated in subsequent optimizations. 635beeb86bdSValentin Clement static bool conflictOnReference(llvm::ArrayRef<mlir::Operation *> mentions) { 636beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "checking reference semantics " << mentions.size() 637beeb86bdSValentin Clement << '\n'); 638beeb86bdSValentin Clement if (mentions.size() < 3) 63947f75930SValentin Clement return false; 640beeb86bdSValentin Clement unsigned amendCount = 0; 641beeb86bdSValentin Clement unsigned accessCount = 0; 642beeb86bdSValentin Clement for (auto *op : mentions) { 643beeb86bdSValentin Clement if (mlir::isa<ArrayAmendOp>(op) && ++amendCount > 1) { 644beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << "conflict: multiple amends of array value\n"); 645beeb86bdSValentin Clement return true; 646beeb86bdSValentin Clement } 647beeb86bdSValentin Clement if (mlir::isa<ArrayAccessOp>(op) && ++accessCount > 1) { 648beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() 649beeb86bdSValentin Clement << "conflict: multiple accesses of array value\n"); 650beeb86bdSValentin Clement return true; 651beeb86bdSValentin Clement } 652beeb86bdSValentin Clement if (mlir::isa<ArrayFetchOp, ArrayUpdateOp, ArrayModifyOp>(op)) { 653beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() 654beeb86bdSValentin Clement << "conflict: array value has both uses by-value and uses " 655beeb86bdSValentin Clement "by-reference. conservative assumption.\n"); 656beeb86bdSValentin Clement return true; 657beeb86bdSValentin Clement } 658beeb86bdSValentin Clement } 659beeb86bdSValentin Clement return false; 660beeb86bdSValentin Clement } 661beeb86bdSValentin Clement 662beeb86bdSValentin Clement static mlir::Operation * 663beeb86bdSValentin Clement amendingAccess(llvm::ArrayRef<mlir::Operation *> mentions) { 664beeb86bdSValentin Clement for (auto *op : mentions) 665beeb86bdSValentin Clement if (auto amend = mlir::dyn_cast<ArrayAmendOp>(op)) 666beeb86bdSValentin Clement return amend.getMemref().getDefiningOp(); 667beeb86bdSValentin Clement return {}; 66847f75930SValentin Clement } 66947f75930SValentin Clement 6703260d423SValentin Clement // Are any conflicts present? The conflicts detected here are described above. 6713260d423SValentin Clement static bool conflictDetected(llvm::ArrayRef<mlir::Operation *> reach, 6723260d423SValentin Clement llvm::ArrayRef<mlir::Operation *> mentions, 67347f75930SValentin Clement ArrayMergeStoreOp st) { 6743260d423SValentin Clement return conflictOnLoad(reach, st) || conflictOnMerge(mentions); 67547f75930SValentin Clement } 67647f75930SValentin Clement 677beeb86bdSValentin Clement // Assume that any call to a function that uses host-associations will be 678beeb86bdSValentin Clement // modifying the output array. 679beeb86bdSValentin Clement static bool 680beeb86bdSValentin Clement conservativeCallConflict(llvm::ArrayRef<mlir::Operation *> reaches) { 681beeb86bdSValentin Clement return llvm::any_of(reaches, [](mlir::Operation *op) { 682beeb86bdSValentin Clement if (auto call = mlir::dyn_cast<fir::CallOp>(op)) 683beeb86bdSValentin Clement if (auto callee = 684beeb86bdSValentin Clement call.getCallableForCallee().dyn_cast<mlir::SymbolRefAttr>()) { 685beeb86bdSValentin Clement auto module = op->getParentOfType<mlir::ModuleOp>(); 686beeb86bdSValentin Clement return hasHostAssociationArgument( 68758ceae95SRiver Riddle module.lookupSymbol<mlir::func::FuncOp>(callee)); 688beeb86bdSValentin Clement } 689beeb86bdSValentin Clement return false; 690beeb86bdSValentin Clement }); 691beeb86bdSValentin Clement } 692beeb86bdSValentin Clement 69347f75930SValentin Clement /// Constructor of the array copy analysis. 69447f75930SValentin Clement /// This performs the analysis and saves the intermediate results. 69547f75930SValentin Clement void ArrayCopyAnalysis::construct(mlir::Operation *topLevelOp) { 69647f75930SValentin Clement topLevelOp->walk([&](Operation *op) { 69747f75930SValentin Clement if (auto st = mlir::dyn_cast<fir::ArrayMergeStoreOp>(op)) { 698beeb86bdSValentin Clement llvm::SmallVector<mlir::Operation *> values; 699149ad3d5SShraiysh Vaishay ReachCollector::reachingValues(values, st.getSequence()); 700beeb86bdSValentin Clement bool callConflict = conservativeCallConflict(values); 701beeb86bdSValentin Clement llvm::SmallVector<mlir::Operation *> mentions; 702beeb86bdSValentin Clement arrayMentions(mentions, 703149ad3d5SShraiysh Vaishay mlir::cast<ArrayLoadOp>(st.getOriginal().getDefiningOp())); 704beeb86bdSValentin Clement bool conflict = conflictDetected(values, mentions, st); 705beeb86bdSValentin Clement bool refConflict = conflictOnReference(mentions); 706beeb86bdSValentin Clement if (callConflict || conflict || refConflict) { 70747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 70847f75930SValentin Clement << "CONFLICT: copies required for " << st << '\n' 70947f75930SValentin Clement << " adding conflicts on: " << op << " and " 710149ad3d5SShraiysh Vaishay << st.getOriginal() << '\n'); 71147f75930SValentin Clement conflicts.insert(op); 712149ad3d5SShraiysh Vaishay conflicts.insert(st.getOriginal().getDefiningOp()); 713beeb86bdSValentin Clement if (auto *access = amendingAccess(mentions)) 714beeb86bdSValentin Clement amendAccesses.insert(access); 71547f75930SValentin Clement } 716149ad3d5SShraiysh Vaishay auto *ld = st.getOriginal().getDefiningOp(); 71747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 71847f75930SValentin Clement << "map: adding {" << *ld << " -> " << st << "}\n"); 71947f75930SValentin Clement useMap.insert({ld, op}); 72047f75930SValentin Clement } else if (auto load = mlir::dyn_cast<ArrayLoadOp>(op)) { 721beeb86bdSValentin Clement llvm::SmallVector<mlir::Operation *> mentions; 722beeb86bdSValentin Clement arrayMentions(mentions, load); 72347f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "process load: " << load 724beeb86bdSValentin Clement << ", mentions: " << mentions.size() << '\n'); 725beeb86bdSValentin Clement for (auto *acc : mentions) { 726beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() << " mention: " << *acc << '\n'); 727beeb86bdSValentin Clement if (mlir::isa<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp, ArrayUpdateOp, 728beeb86bdSValentin Clement ArrayModifyOp>(acc)) { 729beeb86bdSValentin Clement if (useMap.count(acc)) { 73047f75930SValentin Clement mlir::emitError( 73147f75930SValentin Clement load.getLoc(), 73247f75930SValentin Clement "The parallel semantics of multiple array_merge_stores per " 73347f75930SValentin Clement "array_load are not supported."); 734beeb86bdSValentin Clement continue; 73547f75930SValentin Clement } 73647f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() 73747f75930SValentin Clement << "map: adding {" << *acc << "} -> {" << load << "}\n"); 738beeb86bdSValentin Clement useMap.insert({acc, op}); 739beeb86bdSValentin Clement } 74047f75930SValentin Clement } 74147f75930SValentin Clement } 74247f75930SValentin Clement }); 74347f75930SValentin Clement } 74447f75930SValentin Clement 745beeb86bdSValentin Clement //===----------------------------------------------------------------------===// 746beeb86bdSValentin Clement // Conversions for converting out of array value form. 747beeb86bdSValentin Clement //===----------------------------------------------------------------------===// 748beeb86bdSValentin Clement 74947f75930SValentin Clement namespace { 75047f75930SValentin Clement class ArrayLoadConversion : public mlir::OpRewritePattern<ArrayLoadOp> { 75147f75930SValentin Clement public: 75247f75930SValentin Clement using OpRewritePattern::OpRewritePattern; 75347f75930SValentin Clement 75447f75930SValentin Clement mlir::LogicalResult 75547f75930SValentin Clement matchAndRewrite(ArrayLoadOp load, 75647f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 75747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "replace load " << load << " with undef.\n"); 75847f75930SValentin Clement rewriter.replaceOpWithNewOp<UndefOp>(load, load.getType()); 75947f75930SValentin Clement return mlir::success(); 76047f75930SValentin Clement } 76147f75930SValentin Clement }; 76247f75930SValentin Clement 76347f75930SValentin Clement class ArrayMergeStoreConversion 76447f75930SValentin Clement : public mlir::OpRewritePattern<ArrayMergeStoreOp> { 76547f75930SValentin Clement public: 76647f75930SValentin Clement using OpRewritePattern::OpRewritePattern; 76747f75930SValentin Clement 76847f75930SValentin Clement mlir::LogicalResult 76947f75930SValentin Clement matchAndRewrite(ArrayMergeStoreOp store, 77047f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 77147f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "marking store " << store << " as dead.\n"); 77247f75930SValentin Clement rewriter.eraseOp(store); 77347f75930SValentin Clement return mlir::success(); 77447f75930SValentin Clement } 77547f75930SValentin Clement }; 77647f75930SValentin Clement } // namespace 77747f75930SValentin Clement 77847f75930SValentin Clement static mlir::Type getEleTy(mlir::Type ty) { 779beeb86bdSValentin Clement auto eleTy = unwrapSequenceType(unwrapPassByRefType(ty)); 78047f75930SValentin Clement // FIXME: keep ptr/heap/ref information. 781beeb86bdSValentin Clement return ReferenceType::get(eleTy); 78247f75930SValentin Clement } 78347f75930SValentin Clement 78447f75930SValentin Clement // Extract extents from the ShapeOp/ShapeShiftOp into the result vector. 785beeb86bdSValentin Clement static bool getAdjustedExtents(mlir::Location loc, 786beeb86bdSValentin Clement mlir::PatternRewriter &rewriter, 787beeb86bdSValentin Clement ArrayLoadOp arrLoad, 788beeb86bdSValentin Clement llvm::SmallVectorImpl<mlir::Value> &result, 78947f75930SValentin Clement mlir::Value shape) { 790beeb86bdSValentin Clement bool copyUsingSlice = false; 79147f75930SValentin Clement auto *shapeOp = shape.getDefiningOp(); 792beeb86bdSValentin Clement if (auto s = mlir::dyn_cast_or_null<ShapeOp>(shapeOp)) { 79347f75930SValentin Clement auto e = s.getExtents(); 79447f75930SValentin Clement result.insert(result.end(), e.begin(), e.end()); 795beeb86bdSValentin Clement } else if (auto s = mlir::dyn_cast_or_null<ShapeShiftOp>(shapeOp)) { 79647f75930SValentin Clement auto e = s.getExtents(); 79747f75930SValentin Clement result.insert(result.end(), e.begin(), e.end()); 798beeb86bdSValentin Clement } else { 799beeb86bdSValentin Clement emitFatalError(loc, "not a fir.shape/fir.shape_shift op"); 80047f75930SValentin Clement } 801beeb86bdSValentin Clement auto idxTy = rewriter.getIndexType(); 802beeb86bdSValentin Clement if (factory::isAssumedSize(result)) { 803beeb86bdSValentin Clement // Use slice information to compute the extent of the column. 804beeb86bdSValentin Clement auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1); 805beeb86bdSValentin Clement mlir::Value size = one; 806beeb86bdSValentin Clement if (mlir::Value sliceArg = arrLoad.getSlice()) { 807beeb86bdSValentin Clement if (auto sliceOp = 808beeb86bdSValentin Clement mlir::dyn_cast_or_null<SliceOp>(sliceArg.getDefiningOp())) { 809beeb86bdSValentin Clement auto triples = sliceOp.getTriples(); 810beeb86bdSValentin Clement const std::size_t tripleSize = triples.size(); 811beeb86bdSValentin Clement auto module = arrLoad->getParentOfType<mlir::ModuleOp>(); 812beeb86bdSValentin Clement FirOpBuilder builder(rewriter, getKindMapping(module)); 813beeb86bdSValentin Clement size = builder.genExtentFromTriplet(loc, triples[tripleSize - 3], 814beeb86bdSValentin Clement triples[tripleSize - 2], 815beeb86bdSValentin Clement triples[tripleSize - 1], idxTy); 816beeb86bdSValentin Clement copyUsingSlice = true; 817beeb86bdSValentin Clement } 818beeb86bdSValentin Clement } 819beeb86bdSValentin Clement result[result.size() - 1] = size; 820beeb86bdSValentin Clement } 821beeb86bdSValentin Clement return copyUsingSlice; 82247f75930SValentin Clement } 82347f75930SValentin Clement 824beeb86bdSValentin Clement /// Place the extents of the array load, \p arrLoad, into \p result and 825beeb86bdSValentin Clement /// return a ShapeOp or ShapeShiftOp with the same extents. If \p arrLoad is 826beeb86bdSValentin Clement /// loading a `!fir.box`, code will be generated to read the extents from the 827beeb86bdSValentin Clement /// boxed value, and the retunred shape Op will be built with the extents read 828beeb86bdSValentin Clement /// from the box. Otherwise, the extents will be extracted from the ShapeOp (or 829beeb86bdSValentin Clement /// ShapeShiftOp) argument of \p arrLoad. \p copyUsingSlice will be set to true 830beeb86bdSValentin Clement /// if slicing of the output array is to be done in the copy-in/copy-out rather 831beeb86bdSValentin Clement /// than in the elemental computation step. 832beeb86bdSValentin Clement static mlir::Value getOrReadExtentsAndShapeOp( 833beeb86bdSValentin Clement mlir::Location loc, mlir::PatternRewriter &rewriter, ArrayLoadOp arrLoad, 834beeb86bdSValentin Clement llvm::SmallVectorImpl<mlir::Value> &result, bool ©UsingSlice) { 83547f75930SValentin Clement assert(result.empty()); 836beeb86bdSValentin Clement if (arrLoad->hasAttr(fir::getOptionalAttrName())) 837beeb86bdSValentin Clement fir::emitFatalError( 838beeb86bdSValentin Clement loc, "shapes from array load of OPTIONAL arrays must not be used"); 839beeb86bdSValentin Clement if (auto boxTy = arrLoad.getMemref().getType().dyn_cast<BoxType>()) { 840beeb86bdSValentin Clement auto rank = 841beeb86bdSValentin Clement dyn_cast_ptrOrBoxEleTy(boxTy).cast<SequenceType>().getDimension(); 84247f75930SValentin Clement auto idxTy = rewriter.getIndexType(); 84347f75930SValentin Clement for (decltype(rank) dim = 0; dim < rank; ++dim) { 844beeb86bdSValentin Clement auto dimVal = rewriter.create<mlir::arith::ConstantIndexOp>(loc, dim); 845beeb86bdSValentin Clement auto dimInfo = rewriter.create<BoxDimsOp>(loc, idxTy, idxTy, idxTy, 846beeb86bdSValentin Clement arrLoad.getMemref(), dimVal); 84747f75930SValentin Clement result.emplace_back(dimInfo.getResult(1)); 84847f75930SValentin Clement } 849beeb86bdSValentin Clement if (!arrLoad.getShape()) { 850beeb86bdSValentin Clement auto shapeType = ShapeType::get(rewriter.getContext(), rank); 851beeb86bdSValentin Clement return rewriter.create<ShapeOp>(loc, shapeType, result); 85247f75930SValentin Clement } 853beeb86bdSValentin Clement auto shiftOp = arrLoad.getShape().getDefiningOp<ShiftOp>(); 854beeb86bdSValentin Clement auto shapeShiftType = ShapeShiftType::get(rewriter.getContext(), rank); 855beeb86bdSValentin Clement llvm::SmallVector<mlir::Value> shapeShiftOperands; 856beeb86bdSValentin Clement for (auto [lb, extent] : llvm::zip(shiftOp.getOrigins(), result)) { 857beeb86bdSValentin Clement shapeShiftOperands.push_back(lb); 858beeb86bdSValentin Clement shapeShiftOperands.push_back(extent); 859beeb86bdSValentin Clement } 860beeb86bdSValentin Clement return rewriter.create<ShapeShiftOp>(loc, shapeShiftType, 861beeb86bdSValentin Clement shapeShiftOperands); 862beeb86bdSValentin Clement } 863beeb86bdSValentin Clement copyUsingSlice = 864beeb86bdSValentin Clement getAdjustedExtents(loc, rewriter, arrLoad, result, arrLoad.getShape()); 865beeb86bdSValentin Clement return arrLoad.getShape(); 86647f75930SValentin Clement } 86747f75930SValentin Clement 86847f75930SValentin Clement static mlir::Type toRefType(mlir::Type ty) { 86947f75930SValentin Clement if (fir::isa_ref_type(ty)) 87047f75930SValentin Clement return ty; 87147f75930SValentin Clement return fir::ReferenceType::get(ty); 87247f75930SValentin Clement } 87347f75930SValentin Clement 8743260d423SValentin Clement static llvm::SmallVector<mlir::Value> 8753260d423SValentin Clement getTypeParamsIfRawData(mlir::Location loc, FirOpBuilder &builder, 8763260d423SValentin Clement ArrayLoadOp arrLoad, mlir::Type ty) { 8773260d423SValentin Clement if (ty.isa<BoxType>()) 8783260d423SValentin Clement return {}; 8793260d423SValentin Clement return fir::factory::getTypeParams(loc, builder, arrLoad); 8803260d423SValentin Clement } 8813260d423SValentin Clement 8823260d423SValentin Clement static mlir::Value genCoorOp(mlir::PatternRewriter &rewriter, 8833260d423SValentin Clement mlir::Location loc, mlir::Type eleTy, 8843260d423SValentin Clement mlir::Type resTy, mlir::Value alloc, 8853260d423SValentin Clement mlir::Value shape, mlir::Value slice, 8863260d423SValentin Clement mlir::ValueRange indices, ArrayLoadOp load, 8873260d423SValentin Clement bool skipOrig = false) { 88847f75930SValentin Clement llvm::SmallVector<mlir::Value> originated; 88947f75930SValentin Clement if (skipOrig) 89047f75930SValentin Clement originated.assign(indices.begin(), indices.end()); 89147f75930SValentin Clement else 8923260d423SValentin Clement originated = factory::originateIndices(loc, rewriter, alloc.getType(), 89347f75930SValentin Clement shape, indices); 8943260d423SValentin Clement auto seqTy = dyn_cast_ptrOrBoxEleTy(alloc.getType()); 8953260d423SValentin Clement assert(seqTy && seqTy.isa<SequenceType>()); 8963260d423SValentin Clement const auto dimension = seqTy.cast<SequenceType>().getDimension(); 8973260d423SValentin Clement auto module = load->getParentOfType<mlir::ModuleOp>(); 8983260d423SValentin Clement FirOpBuilder builder(rewriter, getKindMapping(module)); 8993260d423SValentin Clement auto typeparams = getTypeParamsIfRawData(loc, builder, load, alloc.getType()); 9003260d423SValentin Clement mlir::Value result = rewriter.create<ArrayCoorOp>( 90147f75930SValentin Clement loc, eleTy, alloc, shape, slice, 90247f75930SValentin Clement llvm::ArrayRef<mlir::Value>{originated}.take_front(dimension), 90347f75930SValentin Clement typeparams); 90447f75930SValentin Clement if (dimension < originated.size()) 90547f75930SValentin Clement result = rewriter.create<fir::CoordinateOp>( 90647f75930SValentin Clement loc, resTy, result, 90747f75930SValentin Clement llvm::ArrayRef<mlir::Value>{originated}.drop_front(dimension)); 90847f75930SValentin Clement return result; 90947f75930SValentin Clement } 91047f75930SValentin Clement 911beeb86bdSValentin Clement static mlir::Value getCharacterLen(mlir::Location loc, FirOpBuilder &builder, 912beeb86bdSValentin Clement ArrayLoadOp load, CharacterType charTy) { 913beeb86bdSValentin Clement auto charLenTy = builder.getCharacterLengthType(); 914beeb86bdSValentin Clement if (charTy.hasDynamicLen()) { 915beeb86bdSValentin Clement if (load.getMemref().getType().isa<BoxType>()) { 916beeb86bdSValentin Clement // The loaded array is an emboxed value. Get the CHARACTER length from 917beeb86bdSValentin Clement // the box value. 918beeb86bdSValentin Clement auto eleSzInBytes = 919beeb86bdSValentin Clement builder.create<BoxEleSizeOp>(loc, charLenTy, load.getMemref()); 920beeb86bdSValentin Clement auto kindSize = 921beeb86bdSValentin Clement builder.getKindMap().getCharacterBitsize(charTy.getFKind()); 922beeb86bdSValentin Clement auto kindByteSize = 923beeb86bdSValentin Clement builder.createIntegerConstant(loc, charLenTy, kindSize / 8); 924beeb86bdSValentin Clement return builder.create<mlir::arith::DivSIOp>(loc, eleSzInBytes, 925beeb86bdSValentin Clement kindByteSize); 926beeb86bdSValentin Clement } 927beeb86bdSValentin Clement // The loaded array is a (set of) unboxed values. If the CHARACTER's 928beeb86bdSValentin Clement // length is not a constant, it must be provided as a type parameter to 929beeb86bdSValentin Clement // the array_load. 930beeb86bdSValentin Clement auto typeparams = load.getTypeparams(); 931beeb86bdSValentin Clement assert(typeparams.size() > 0 && "expected type parameters on array_load"); 932beeb86bdSValentin Clement return typeparams.back(); 933beeb86bdSValentin Clement } 934beeb86bdSValentin Clement // The typical case: the length of the CHARACTER is a compile-time 935beeb86bdSValentin Clement // constant that is encoded in the type information. 936beeb86bdSValentin Clement return builder.createIntegerConstant(loc, charLenTy, charTy.getLen()); 937beeb86bdSValentin Clement } 938beeb86bdSValentin Clement /// Generate a shallow array copy. This is used for both copy-in and copy-out. 939beeb86bdSValentin Clement template <bool CopyIn> 940beeb86bdSValentin Clement void genArrayCopy(mlir::Location loc, mlir::PatternRewriter &rewriter, 941beeb86bdSValentin Clement mlir::Value dst, mlir::Value src, mlir::Value shapeOp, 942beeb86bdSValentin Clement mlir::Value sliceOp, ArrayLoadOp arrLoad) { 943beeb86bdSValentin Clement auto insPt = rewriter.saveInsertionPoint(); 944beeb86bdSValentin Clement llvm::SmallVector<mlir::Value> indices; 945beeb86bdSValentin Clement llvm::SmallVector<mlir::Value> extents; 946beeb86bdSValentin Clement bool copyUsingSlice = 947beeb86bdSValentin Clement getAdjustedExtents(loc, rewriter, arrLoad, extents, shapeOp); 948beeb86bdSValentin Clement auto idxTy = rewriter.getIndexType(); 949beeb86bdSValentin Clement // Build loop nest from column to row. 950beeb86bdSValentin Clement for (auto sh : llvm::reverse(extents)) { 951beeb86bdSValentin Clement auto ubi = rewriter.create<ConvertOp>(loc, idxTy, sh); 952beeb86bdSValentin Clement auto zero = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 0); 953beeb86bdSValentin Clement auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1); 954beeb86bdSValentin Clement auto ub = rewriter.create<mlir::arith::SubIOp>(loc, idxTy, ubi, one); 955beeb86bdSValentin Clement auto loop = rewriter.create<DoLoopOp>(loc, zero, ub, one); 956beeb86bdSValentin Clement rewriter.setInsertionPointToStart(loop.getBody()); 957beeb86bdSValentin Clement indices.push_back(loop.getInductionVar()); 958beeb86bdSValentin Clement } 959beeb86bdSValentin Clement // Reverse the indices so they are in column-major order. 960beeb86bdSValentin Clement std::reverse(indices.begin(), indices.end()); 9613260d423SValentin Clement auto module = arrLoad->getParentOfType<mlir::ModuleOp>(); 9623260d423SValentin Clement FirOpBuilder builder(rewriter, getKindMapping(module)); 963beeb86bdSValentin Clement auto fromAddr = rewriter.create<ArrayCoorOp>( 964beeb86bdSValentin Clement loc, getEleTy(src.getType()), src, shapeOp, 965beeb86bdSValentin Clement CopyIn && copyUsingSlice ? sliceOp : mlir::Value{}, 966beeb86bdSValentin Clement factory::originateIndices(loc, rewriter, src.getType(), shapeOp, indices), 9673260d423SValentin Clement getTypeParamsIfRawData(loc, builder, arrLoad, src.getType())); 968beeb86bdSValentin Clement auto toAddr = rewriter.create<ArrayCoorOp>( 969beeb86bdSValentin Clement loc, getEleTy(dst.getType()), dst, shapeOp, 970beeb86bdSValentin Clement !CopyIn && copyUsingSlice ? sliceOp : mlir::Value{}, 971beeb86bdSValentin Clement factory::originateIndices(loc, rewriter, dst.getType(), shapeOp, indices), 9723260d423SValentin Clement getTypeParamsIfRawData(loc, builder, arrLoad, src.getType())); 973beeb86bdSValentin Clement auto eleTy = unwrapSequenceType(unwrapPassByRefType(dst.getType())); 974beeb86bdSValentin Clement // Copy from (to) object to (from) temp copy of same object. 975beeb86bdSValentin Clement if (auto charTy = eleTy.dyn_cast<CharacterType>()) { 976beeb86bdSValentin Clement auto len = getCharacterLen(loc, builder, arrLoad, charTy); 977beeb86bdSValentin Clement CharBoxValue toChar(toAddr, len); 978beeb86bdSValentin Clement CharBoxValue fromChar(fromAddr, len); 979beeb86bdSValentin Clement factory::genScalarAssignment(builder, loc, toChar, fromChar); 980beeb86bdSValentin Clement } else { 981beeb86bdSValentin Clement if (hasDynamicSize(eleTy)) 982beeb86bdSValentin Clement TODO(loc, "copy element of dynamic size"); 983beeb86bdSValentin Clement factory::genScalarAssignment(builder, loc, toAddr, fromAddr); 984beeb86bdSValentin Clement } 985beeb86bdSValentin Clement rewriter.restoreInsertionPoint(insPt); 986beeb86bdSValentin Clement } 987beeb86bdSValentin Clement 988beeb86bdSValentin Clement /// The array load may be either a boxed or unboxed value. If the value is 989beeb86bdSValentin Clement /// boxed, we read the type parameters from the boxed value. 990beeb86bdSValentin Clement static llvm::SmallVector<mlir::Value> 991beeb86bdSValentin Clement genArrayLoadTypeParameters(mlir::Location loc, mlir::PatternRewriter &rewriter, 992beeb86bdSValentin Clement ArrayLoadOp load) { 993beeb86bdSValentin Clement if (load.getTypeparams().empty()) { 994beeb86bdSValentin Clement auto eleTy = 995beeb86bdSValentin Clement unwrapSequenceType(unwrapPassByRefType(load.getMemref().getType())); 996beeb86bdSValentin Clement if (hasDynamicSize(eleTy)) { 997beeb86bdSValentin Clement if (auto charTy = eleTy.dyn_cast<CharacterType>()) { 998beeb86bdSValentin Clement assert(load.getMemref().getType().isa<BoxType>()); 999beeb86bdSValentin Clement auto module = load->getParentOfType<mlir::ModuleOp>(); 1000beeb86bdSValentin Clement FirOpBuilder builder(rewriter, getKindMapping(module)); 1001beeb86bdSValentin Clement return {getCharacterLen(loc, builder, load, charTy)}; 1002beeb86bdSValentin Clement } 1003beeb86bdSValentin Clement TODO(loc, "unhandled dynamic type parameters"); 1004beeb86bdSValentin Clement } 1005beeb86bdSValentin Clement return {}; 1006beeb86bdSValentin Clement } 1007beeb86bdSValentin Clement return load.getTypeparams(); 1008beeb86bdSValentin Clement } 1009beeb86bdSValentin Clement 1010beeb86bdSValentin Clement static llvm::SmallVector<mlir::Value> 1011beeb86bdSValentin Clement findNonconstantExtents(mlir::Type memrefTy, 1012beeb86bdSValentin Clement llvm::ArrayRef<mlir::Value> extents) { 1013beeb86bdSValentin Clement llvm::SmallVector<mlir::Value> nce; 1014beeb86bdSValentin Clement auto arrTy = unwrapPassByRefType(memrefTy); 1015beeb86bdSValentin Clement auto seqTy = arrTy.cast<SequenceType>(); 1016beeb86bdSValentin Clement for (auto [s, x] : llvm::zip(seqTy.getShape(), extents)) 1017beeb86bdSValentin Clement if (s == SequenceType::getUnknownExtent()) 1018beeb86bdSValentin Clement nce.emplace_back(x); 1019beeb86bdSValentin Clement if (extents.size() > seqTy.getShape().size()) 1020beeb86bdSValentin Clement for (auto x : extents.drop_front(seqTy.getShape().size())) 1021beeb86bdSValentin Clement nce.emplace_back(x); 1022beeb86bdSValentin Clement return nce; 1023beeb86bdSValentin Clement } 1024beeb86bdSValentin Clement 10253ed899ccSJean Perier /// Allocate temporary storage for an ArrayLoadOp \load and initialize any 10263ed899ccSJean Perier /// allocatable direct components of the array elements with an unallocated 10273ed899ccSJean Perier /// status. Returns the temporary address as well as a callback to generate the 10283ed899ccSJean Perier /// temporary clean-up once it has been used. The clean-up will take care of 10293ed899ccSJean Perier /// deallocating all the element allocatable components that may have been 10303ed899ccSJean Perier /// allocated while using the temporary. 10313ed899ccSJean Perier static std::pair<mlir::Value, 10323ed899ccSJean Perier std::function<void(mlir::PatternRewriter &rewriter)>> 10333ed899ccSJean Perier allocateArrayTemp(mlir::Location loc, mlir::PatternRewriter &rewriter, 10343ed899ccSJean Perier ArrayLoadOp load, llvm::ArrayRef<mlir::Value> extents, 10353ed899ccSJean Perier mlir::Value shape) { 10363ed899ccSJean Perier mlir::Type baseType = load.getMemref().getType(); 10373ed899ccSJean Perier llvm::SmallVector<mlir::Value> nonconstantExtents = 10383ed899ccSJean Perier findNonconstantExtents(baseType, extents); 10393ed899ccSJean Perier llvm::SmallVector<mlir::Value> typeParams = 10403ed899ccSJean Perier genArrayLoadTypeParameters(loc, rewriter, load); 10413ed899ccSJean Perier mlir::Value allocmem = rewriter.create<AllocMemOp>( 10423ed899ccSJean Perier loc, dyn_cast_ptrOrBoxEleTy(baseType), typeParams, nonconstantExtents); 10433ed899ccSJean Perier mlir::Type eleType = 10443ed899ccSJean Perier fir::unwrapSequenceType(fir::unwrapPassByRefType(baseType)); 10453ed899ccSJean Perier if (fir::isRecordWithAllocatableMember(eleType)) { 10463ed899ccSJean Perier // The allocatable component descriptors need to be set to a clean 10473ed899ccSJean Perier // deallocated status before anything is done with them. 10483ed899ccSJean Perier mlir::Value box = rewriter.create<fir::EmboxOp>( 10493ed899ccSJean Perier loc, fir::BoxType::get(baseType), allocmem, shape, 10503ed899ccSJean Perier /*slice=*/mlir::Value{}, typeParams); 10513ed899ccSJean Perier auto module = load->getParentOfType<mlir::ModuleOp>(); 10523ed899ccSJean Perier FirOpBuilder builder(rewriter, getKindMapping(module)); 10533ed899ccSJean Perier runtime::genDerivedTypeInitialize(builder, loc, box); 10543ed899ccSJean Perier // Any allocatable component that may have been allocated must be 10553ed899ccSJean Perier // deallocated during the clean-up. 10563ed899ccSJean Perier auto cleanup = [=](mlir::PatternRewriter &r) { 10573ed899ccSJean Perier FirOpBuilder builder(r, getKindMapping(module)); 10583ed899ccSJean Perier runtime::genDerivedTypeDestroy(builder, loc, box); 10593ed899ccSJean Perier r.create<FreeMemOp>(loc, allocmem); 10603ed899ccSJean Perier }; 10613ed899ccSJean Perier return {allocmem, cleanup}; 10623ed899ccSJean Perier } 10633ed899ccSJean Perier auto cleanup = [=](mlir::PatternRewriter &r) { 10643ed899ccSJean Perier r.create<FreeMemOp>(loc, allocmem); 10653ed899ccSJean Perier }; 10663ed899ccSJean Perier return {allocmem, cleanup}; 10673ed899ccSJean Perier } 10683ed899ccSJean Perier 106947f75930SValentin Clement namespace { 107047f75930SValentin Clement /// Conversion of fir.array_update and fir.array_modify Ops. 107147f75930SValentin Clement /// If there is a conflict for the update, then we need to perform a 107247f75930SValentin Clement /// copy-in/copy-out to preserve the original values of the array. If there is 107347f75930SValentin Clement /// no conflict, then it is save to eschew making any copies. 107447f75930SValentin Clement template <typename ArrayOp> 107547f75930SValentin Clement class ArrayUpdateConversionBase : public mlir::OpRewritePattern<ArrayOp> { 107647f75930SValentin Clement public: 1077beeb86bdSValentin Clement // TODO: Implement copy/swap semantics? 107847f75930SValentin Clement explicit ArrayUpdateConversionBase(mlir::MLIRContext *ctx, 107947f75930SValentin Clement const ArrayCopyAnalysis &a, 108047f75930SValentin Clement const OperationUseMapT &m) 108147f75930SValentin Clement : mlir::OpRewritePattern<ArrayOp>{ctx}, analysis{a}, useMap{m} {} 108247f75930SValentin Clement 1083beeb86bdSValentin Clement /// The array_access, \p access, is to be to a cloned copy due to a potential 1084beeb86bdSValentin Clement /// conflict. Uses copy-in/copy-out semantics and not copy/swap. 1085beeb86bdSValentin Clement mlir::Value referenceToClone(mlir::Location loc, 1086beeb86bdSValentin Clement mlir::PatternRewriter &rewriter, 1087beeb86bdSValentin Clement ArrayOp access) const { 1088beeb86bdSValentin Clement LLVM_DEBUG(llvm::dbgs() 1089beeb86bdSValentin Clement << "generating copy-in/copy-out loops for " << access << '\n'); 1090beeb86bdSValentin Clement auto *op = access.getOperation(); 1091beeb86bdSValentin Clement auto *loadOp = useMap.lookup(op); 1092beeb86bdSValentin Clement auto load = mlir::cast<ArrayLoadOp>(loadOp); 1093beeb86bdSValentin Clement auto eleTy = access.getType(); 1094beeb86bdSValentin Clement rewriter.setInsertionPoint(loadOp); 1095beeb86bdSValentin Clement // Copy in. 109647f75930SValentin Clement llvm::SmallVector<mlir::Value> extents; 1097beeb86bdSValentin Clement bool copyUsingSlice = false; 1098beeb86bdSValentin Clement auto shapeOp = getOrReadExtentsAndShapeOp(loc, rewriter, load, extents, 1099beeb86bdSValentin Clement copyUsingSlice); 11003ed899ccSJean Perier auto [allocmem, genTempCleanUp] = 11013ed899ccSJean Perier allocateArrayTemp(loc, rewriter, load, extents, shapeOp); 1102beeb86bdSValentin Clement genArrayCopy</*copyIn=*/true>(load.getLoc(), rewriter, allocmem, 1103beeb86bdSValentin Clement load.getMemref(), shapeOp, load.getSlice(), 1104beeb86bdSValentin Clement load); 1105beeb86bdSValentin Clement // Generate the reference for the access. 1106beeb86bdSValentin Clement rewriter.setInsertionPoint(op); 11073260d423SValentin Clement auto coor = genCoorOp( 11083260d423SValentin Clement rewriter, loc, getEleTy(load.getType()), eleTy, allocmem, shapeOp, 11093260d423SValentin Clement copyUsingSlice ? mlir::Value{} : load.getSlice(), access.getIndices(), 11103260d423SValentin Clement load, access->hasAttr(factory::attrFortranArrayOffsets())); 1111beeb86bdSValentin Clement // Copy out. 1112beeb86bdSValentin Clement auto *storeOp = useMap.lookup(loadOp); 1113beeb86bdSValentin Clement auto store = mlir::cast<ArrayMergeStoreOp>(storeOp); 1114beeb86bdSValentin Clement rewriter.setInsertionPoint(storeOp); 1115beeb86bdSValentin Clement // Copy out. 1116beeb86bdSValentin Clement genArrayCopy</*copyIn=*/false>(store.getLoc(), rewriter, store.getMemref(), 1117beeb86bdSValentin Clement allocmem, shapeOp, store.getSlice(), load); 11183ed899ccSJean Perier genTempCleanUp(rewriter); 1119beeb86bdSValentin Clement return coor; 112047f75930SValentin Clement } 112147f75930SValentin Clement 112247f75930SValentin Clement /// Copy the RHS element into the LHS and insert copy-in/copy-out between a 112347f75930SValentin Clement /// temp and the LHS if the analysis found potential overlaps between the RHS 1124beeb86bdSValentin Clement /// and LHS arrays. The element copy generator must be provided in \p 112547f75930SValentin Clement /// assignElement. \p update must be the ArrayUpdateOp or the ArrayModifyOp. 112647f75930SValentin Clement /// Returns the address of the LHS element inside the loop and the LHS 112747f75930SValentin Clement /// ArrayLoad result. 112847f75930SValentin Clement std::pair<mlir::Value, mlir::Value> 112947f75930SValentin Clement materializeAssignment(mlir::Location loc, mlir::PatternRewriter &rewriter, 113047f75930SValentin Clement ArrayOp update, 1131beeb86bdSValentin Clement const std::function<void(mlir::Value)> &assignElement, 113247f75930SValentin Clement mlir::Type lhsEltRefType) const { 113347f75930SValentin Clement auto *op = update.getOperation(); 1134beeb86bdSValentin Clement auto *loadOp = useMap.lookup(op); 113547f75930SValentin Clement auto load = mlir::cast<ArrayLoadOp>(loadOp); 113647f75930SValentin Clement LLVM_DEBUG(llvm::outs() << "does " << load << " have a conflict?\n"); 113747f75930SValentin Clement if (analysis.hasPotentialConflict(loadOp)) { 113847f75930SValentin Clement // If there is a conflict between the arrays, then we copy the lhs array 113947f75930SValentin Clement // to a temporary, update the temporary, and copy the temporary back to 114047f75930SValentin Clement // the lhs array. This yields Fortran's copy-in copy-out array semantics. 114147f75930SValentin Clement LLVM_DEBUG(llvm::outs() << "Yes, conflict was found\n"); 114247f75930SValentin Clement rewriter.setInsertionPoint(loadOp); 114347f75930SValentin Clement // Copy in. 114447f75930SValentin Clement llvm::SmallVector<mlir::Value> extents; 1145beeb86bdSValentin Clement bool copyUsingSlice = false; 1146beeb86bdSValentin Clement auto shapeOp = getOrReadExtentsAndShapeOp(loc, rewriter, load, extents, 1147beeb86bdSValentin Clement copyUsingSlice); 11483ed899ccSJean Perier auto [allocmem, genTempCleanUp] = 11493ed899ccSJean Perier allocateArrayTemp(loc, rewriter, load, extents, shapeOp); 11503ed899ccSJean Perier 1151beeb86bdSValentin Clement genArrayCopy</*copyIn=*/true>(load.getLoc(), rewriter, allocmem, 1152beeb86bdSValentin Clement load.getMemref(), shapeOp, load.getSlice(), 1153beeb86bdSValentin Clement load); 115447f75930SValentin Clement rewriter.setInsertionPoint(op); 1155beeb86bdSValentin Clement auto coor = genCoorOp( 115647f75930SValentin Clement rewriter, loc, getEleTy(load.getType()), lhsEltRefType, allocmem, 1157beeb86bdSValentin Clement shapeOp, copyUsingSlice ? mlir::Value{} : load.getSlice(), 11583260d423SValentin Clement update.getIndices(), load, 1159beeb86bdSValentin Clement update->hasAttr(factory::attrFortranArrayOffsets())); 116047f75930SValentin Clement assignElement(coor); 1161beeb86bdSValentin Clement auto *storeOp = useMap.lookup(loadOp); 116247f75930SValentin Clement auto store = mlir::cast<ArrayMergeStoreOp>(storeOp); 116347f75930SValentin Clement rewriter.setInsertionPoint(storeOp); 116447f75930SValentin Clement // Copy out. 1165beeb86bdSValentin Clement genArrayCopy</*copyIn=*/false>(store.getLoc(), rewriter, 1166beeb86bdSValentin Clement store.getMemref(), allocmem, shapeOp, 1167beeb86bdSValentin Clement store.getSlice(), load); 11683ed899ccSJean Perier genTempCleanUp(rewriter); 116947f75930SValentin Clement return {coor, load.getResult()}; 117047f75930SValentin Clement } 117147f75930SValentin Clement // Otherwise, when there is no conflict (a possible loop-carried 117247f75930SValentin Clement // dependence), the lhs array can be updated in place. 117347f75930SValentin Clement LLVM_DEBUG(llvm::outs() << "No, conflict wasn't found\n"); 117447f75930SValentin Clement rewriter.setInsertionPoint(op); 117547f75930SValentin Clement auto coorTy = getEleTy(load.getType()); 11763260d423SValentin Clement auto coor = 11773260d423SValentin Clement genCoorOp(rewriter, loc, coorTy, lhsEltRefType, load.getMemref(), 11783260d423SValentin Clement load.getShape(), load.getSlice(), update.getIndices(), load, 1179beeb86bdSValentin Clement update->hasAttr(factory::attrFortranArrayOffsets())); 118047f75930SValentin Clement assignElement(coor); 118147f75930SValentin Clement return {coor, load.getResult()}; 118247f75930SValentin Clement } 118347f75930SValentin Clement 1184beeb86bdSValentin Clement protected: 118547f75930SValentin Clement const ArrayCopyAnalysis &analysis; 118647f75930SValentin Clement const OperationUseMapT &useMap; 118747f75930SValentin Clement }; 118847f75930SValentin Clement 118947f75930SValentin Clement class ArrayUpdateConversion : public ArrayUpdateConversionBase<ArrayUpdateOp> { 119047f75930SValentin Clement public: 119147f75930SValentin Clement explicit ArrayUpdateConversion(mlir::MLIRContext *ctx, 119247f75930SValentin Clement const ArrayCopyAnalysis &a, 119347f75930SValentin Clement const OperationUseMapT &m) 119447f75930SValentin Clement : ArrayUpdateConversionBase{ctx, a, m} {} 119547f75930SValentin Clement 119647f75930SValentin Clement mlir::LogicalResult 119747f75930SValentin Clement matchAndRewrite(ArrayUpdateOp update, 119847f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 119947f75930SValentin Clement auto loc = update.getLoc(); 120047f75930SValentin Clement auto assignElement = [&](mlir::Value coor) { 1201beeb86bdSValentin Clement auto input = update.getMerge(); 1202beeb86bdSValentin Clement if (auto inEleTy = dyn_cast_ptrEleTy(input.getType())) { 1203beeb86bdSValentin Clement emitFatalError(loc, "array_update on references not supported"); 1204beeb86bdSValentin Clement } else { 1205beeb86bdSValentin Clement rewriter.create<fir::StoreOp>(loc, input, coor); 1206beeb86bdSValentin Clement } 120747f75930SValentin Clement }; 1208149ad3d5SShraiysh Vaishay auto lhsEltRefType = toRefType(update.getMerge().getType()); 120947f75930SValentin Clement auto [_, lhsLoadResult] = materializeAssignment( 121047f75930SValentin Clement loc, rewriter, update, assignElement, lhsEltRefType); 121147f75930SValentin Clement update.replaceAllUsesWith(lhsLoadResult); 121247f75930SValentin Clement rewriter.replaceOp(update, lhsLoadResult); 121347f75930SValentin Clement return mlir::success(); 121447f75930SValentin Clement } 121547f75930SValentin Clement }; 121647f75930SValentin Clement 121747f75930SValentin Clement class ArrayModifyConversion : public ArrayUpdateConversionBase<ArrayModifyOp> { 121847f75930SValentin Clement public: 121947f75930SValentin Clement explicit ArrayModifyConversion(mlir::MLIRContext *ctx, 122047f75930SValentin Clement const ArrayCopyAnalysis &a, 122147f75930SValentin Clement const OperationUseMapT &m) 122247f75930SValentin Clement : ArrayUpdateConversionBase{ctx, a, m} {} 122347f75930SValentin Clement 122447f75930SValentin Clement mlir::LogicalResult 122547f75930SValentin Clement matchAndRewrite(ArrayModifyOp modify, 122647f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 122747f75930SValentin Clement auto loc = modify.getLoc(); 122847f75930SValentin Clement auto assignElement = [](mlir::Value) { 122947f75930SValentin Clement // Assignment already materialized by lowering using lhs element address. 123047f75930SValentin Clement }; 123147f75930SValentin Clement auto lhsEltRefType = modify.getResult(0).getType(); 123247f75930SValentin Clement auto [lhsEltCoor, lhsLoadResult] = materializeAssignment( 123347f75930SValentin Clement loc, rewriter, modify, assignElement, lhsEltRefType); 123447f75930SValentin Clement modify.replaceAllUsesWith(mlir::ValueRange{lhsEltCoor, lhsLoadResult}); 123547f75930SValentin Clement rewriter.replaceOp(modify, mlir::ValueRange{lhsEltCoor, lhsLoadResult}); 123647f75930SValentin Clement return mlir::success(); 123747f75930SValentin Clement } 123847f75930SValentin Clement }; 123947f75930SValentin Clement 124047f75930SValentin Clement class ArrayFetchConversion : public mlir::OpRewritePattern<ArrayFetchOp> { 124147f75930SValentin Clement public: 124247f75930SValentin Clement explicit ArrayFetchConversion(mlir::MLIRContext *ctx, 124347f75930SValentin Clement const OperationUseMapT &m) 124447f75930SValentin Clement : OpRewritePattern{ctx}, useMap{m} {} 124547f75930SValentin Clement 124647f75930SValentin Clement mlir::LogicalResult 124747f75930SValentin Clement matchAndRewrite(ArrayFetchOp fetch, 124847f75930SValentin Clement mlir::PatternRewriter &rewriter) const override { 124947f75930SValentin Clement auto *op = fetch.getOperation(); 125047f75930SValentin Clement rewriter.setInsertionPoint(op); 125147f75930SValentin Clement auto load = mlir::cast<ArrayLoadOp>(useMap.lookup(op)); 125247f75930SValentin Clement auto loc = fetch.getLoc(); 12533260d423SValentin Clement auto coor = genCoorOp( 12543260d423SValentin Clement rewriter, loc, getEleTy(load.getType()), toRefType(fetch.getType()), 12553260d423SValentin Clement load.getMemref(), load.getShape(), load.getSlice(), fetch.getIndices(), 12563260d423SValentin Clement load, fetch->hasAttr(factory::attrFortranArrayOffsets())); 1257beeb86bdSValentin Clement if (isa_ref_type(fetch.getType())) 1258beeb86bdSValentin Clement rewriter.replaceOp(fetch, coor); 1259beeb86bdSValentin Clement else 126047f75930SValentin Clement rewriter.replaceOpWithNewOp<fir::LoadOp>(fetch, coor); 126147f75930SValentin Clement return mlir::success(); 126247f75930SValentin Clement } 126347f75930SValentin Clement 126447f75930SValentin Clement private: 126547f75930SValentin Clement const OperationUseMapT &useMap; 126647f75930SValentin Clement }; 126747f75930SValentin Clement 1268beeb86bdSValentin Clement /// As array_access op is like an array_fetch op, except that it does not imply 1269beeb86bdSValentin Clement /// a load op. (It operates in the reference domain.) 1270beeb86bdSValentin Clement class ArrayAccessConversion : public ArrayUpdateConversionBase<ArrayAccessOp> { 1271beeb86bdSValentin Clement public: 1272beeb86bdSValentin Clement explicit ArrayAccessConversion(mlir::MLIRContext *ctx, 1273beeb86bdSValentin Clement const ArrayCopyAnalysis &a, 1274beeb86bdSValentin Clement const OperationUseMapT &m) 1275beeb86bdSValentin Clement : ArrayUpdateConversionBase{ctx, a, m} {} 1276beeb86bdSValentin Clement 1277beeb86bdSValentin Clement mlir::LogicalResult 1278beeb86bdSValentin Clement matchAndRewrite(ArrayAccessOp access, 1279beeb86bdSValentin Clement mlir::PatternRewriter &rewriter) const override { 1280beeb86bdSValentin Clement auto *op = access.getOperation(); 1281beeb86bdSValentin Clement auto loc = access.getLoc(); 1282beeb86bdSValentin Clement if (analysis.inAmendAccessSet(op)) { 1283beeb86bdSValentin Clement // This array_access is associated with an array_amend and there is a 1284beeb86bdSValentin Clement // conflict. Make a copy to store into. 1285beeb86bdSValentin Clement auto result = referenceToClone(loc, rewriter, access); 1286beeb86bdSValentin Clement access.replaceAllUsesWith(result); 1287beeb86bdSValentin Clement rewriter.replaceOp(access, result); 1288beeb86bdSValentin Clement return mlir::success(); 1289beeb86bdSValentin Clement } 1290beeb86bdSValentin Clement rewriter.setInsertionPoint(op); 1291beeb86bdSValentin Clement auto load = mlir::cast<ArrayLoadOp>(useMap.lookup(op)); 12923260d423SValentin Clement auto coor = genCoorOp( 12933260d423SValentin Clement rewriter, loc, getEleTy(load.getType()), toRefType(access.getType()), 12943260d423SValentin Clement load.getMemref(), load.getShape(), load.getSlice(), access.getIndices(), 12953260d423SValentin Clement load, access->hasAttr(factory::attrFortranArrayOffsets())); 1296beeb86bdSValentin Clement rewriter.replaceOp(access, coor); 1297beeb86bdSValentin Clement return mlir::success(); 1298beeb86bdSValentin Clement } 1299beeb86bdSValentin Clement }; 1300beeb86bdSValentin Clement 1301beeb86bdSValentin Clement /// An array_amend op is a marker to record which array access is being used to 1302beeb86bdSValentin Clement /// update an array value. After this pass runs, an array_amend has no 1303beeb86bdSValentin Clement /// semantics. We rewrite these to undefined values here to remove them while 1304beeb86bdSValentin Clement /// preserving SSA form. 1305beeb86bdSValentin Clement class ArrayAmendConversion : public mlir::OpRewritePattern<ArrayAmendOp> { 1306beeb86bdSValentin Clement public: 1307beeb86bdSValentin Clement explicit ArrayAmendConversion(mlir::MLIRContext *ctx) 1308beeb86bdSValentin Clement : OpRewritePattern{ctx} {} 1309beeb86bdSValentin Clement 1310beeb86bdSValentin Clement mlir::LogicalResult 1311beeb86bdSValentin Clement matchAndRewrite(ArrayAmendOp amend, 1312beeb86bdSValentin Clement mlir::PatternRewriter &rewriter) const override { 1313beeb86bdSValentin Clement auto *op = amend.getOperation(); 1314beeb86bdSValentin Clement rewriter.setInsertionPoint(op); 1315beeb86bdSValentin Clement auto loc = amend.getLoc(); 1316beeb86bdSValentin Clement auto undef = rewriter.create<UndefOp>(loc, amend.getType()); 1317beeb86bdSValentin Clement rewriter.replaceOp(amend, undef.getResult()); 1318beeb86bdSValentin Clement return mlir::success(); 1319beeb86bdSValentin Clement } 1320beeb86bdSValentin Clement }; 1321beeb86bdSValentin Clement 132247f75930SValentin Clement class ArrayValueCopyConverter 132347f75930SValentin Clement : public ArrayValueCopyBase<ArrayValueCopyConverter> { 132447f75930SValentin Clement public: 1325196c4279SRiver Riddle void runOnOperation() override { 1326196c4279SRiver Riddle auto func = getOperation(); 132747f75930SValentin Clement LLVM_DEBUG(llvm::dbgs() << "\n\narray-value-copy pass on function '" 132847f75930SValentin Clement << func.getName() << "'\n"); 132947f75930SValentin Clement auto *context = &getContext(); 133047f75930SValentin Clement 133147f75930SValentin Clement // Perform the conflict analysis. 1332beeb86bdSValentin Clement const auto &analysis = getAnalysis<ArrayCopyAnalysis>(); 133347f75930SValentin Clement const auto &useMap = analysis.getUseMap(); 133447f75930SValentin Clement 13359f85c198SRiver Riddle mlir::RewritePatternSet patterns1(context); 133647f75930SValentin Clement patterns1.insert<ArrayFetchConversion>(context, useMap); 133747f75930SValentin Clement patterns1.insert<ArrayUpdateConversion>(context, analysis, useMap); 133847f75930SValentin Clement patterns1.insert<ArrayModifyConversion>(context, analysis, useMap); 1339beeb86bdSValentin Clement patterns1.insert<ArrayAccessConversion>(context, analysis, useMap); 1340beeb86bdSValentin Clement patterns1.insert<ArrayAmendConversion>(context); 134147f75930SValentin Clement mlir::ConversionTarget target(*context); 1342beeb86bdSValentin Clement target.addLegalDialect<FIROpsDialect, mlir::scf::SCFDialect, 1343beeb86bdSValentin Clement mlir::arith::ArithmeticDialect, 1344beeb86bdSValentin Clement mlir::func::FuncDialect>(); 1345beeb86bdSValentin Clement target.addIllegalOp<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp, 1346beeb86bdSValentin Clement ArrayUpdateOp, ArrayModifyOp>(); 134747f75930SValentin Clement // Rewrite the array fetch and array update ops. 134847f75930SValentin Clement if (mlir::failed( 134947f75930SValentin Clement mlir::applyPartialConversion(func, target, std::move(patterns1)))) { 135047f75930SValentin Clement mlir::emitError(mlir::UnknownLoc::get(context), 135147f75930SValentin Clement "failure in array-value-copy pass, phase 1"); 135247f75930SValentin Clement signalPassFailure(); 135347f75930SValentin Clement } 135447f75930SValentin Clement 13559f85c198SRiver Riddle mlir::RewritePatternSet patterns2(context); 135647f75930SValentin Clement patterns2.insert<ArrayLoadConversion>(context); 135747f75930SValentin Clement patterns2.insert<ArrayMergeStoreConversion>(context); 135847f75930SValentin Clement target.addIllegalOp<ArrayLoadOp, ArrayMergeStoreOp>(); 135947f75930SValentin Clement if (mlir::failed( 136047f75930SValentin Clement mlir::applyPartialConversion(func, target, std::move(patterns2)))) { 136147f75930SValentin Clement mlir::emitError(mlir::UnknownLoc::get(context), 136247f75930SValentin Clement "failure in array-value-copy pass, phase 2"); 136347f75930SValentin Clement signalPassFailure(); 136447f75930SValentin Clement } 136547f75930SValentin Clement } 136647f75930SValentin Clement }; 136747f75930SValentin Clement } // namespace 136847f75930SValentin Clement 136947f75930SValentin Clement std::unique_ptr<mlir::Pass> fir::createArrayValueCopyPass() { 137047f75930SValentin Clement return std::make_unique<ArrayValueCopyConverter>(); 137147f75930SValentin Clement } 1372