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