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/Lower/Todo.h"
11beeb86bdSValentin Clement #include "flang/Optimizer/Builder/Array.h"
1247f75930SValentin Clement #include "flang/Optimizer/Builder/BoxValue.h"
1347f75930SValentin Clement #include "flang/Optimizer/Builder/FIRBuilder.h"
143ab67c3dSValentin Clement #include "flang/Optimizer/Builder/Factory.h"
15*3ed899ccSJean Perier #include "flang/Optimizer/Builder/Runtime/Derived.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"
2147f75930SValentin Clement #include "mlir/Dialect/SCF/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:
6647f75930SValentin Clement   using ConflictSetT = llvm::SmallPtrSet<mlir::Operation *, 16>;
6747f75930SValentin Clement   using UseSetT = llvm::SmallPtrSet<mlir::OpOperand *, 8>;
68beeb86bdSValentin Clement   using LoadMapSetsT = llvm::DenseMap<mlir::Operation *, UseSetT>;
69beeb86bdSValentin Clement   using AmendAccessSetT = llvm::SmallPtrSet<mlir::Operation *, 4>;
7047f75930SValentin Clement 
7147f75930SValentin Clement   ArrayCopyAnalysis(mlir::Operation *op) : operation{op} { construct(op); }
7247f75930SValentin Clement 
7347f75930SValentin Clement   mlir::Operation *getOperation() const { return operation; }
7447f75930SValentin Clement 
7547f75930SValentin Clement   /// Return true iff the `array_merge_store` has potential conflicts.
7647f75930SValentin Clement   bool hasPotentialConflict(mlir::Operation *op) const {
7747f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs()
7847f75930SValentin Clement                << "looking for a conflict on " << *op
7947f75930SValentin Clement                << " and the set has a total of " << conflicts.size() << '\n');
8047f75930SValentin Clement     return conflicts.contains(op);
8147f75930SValentin Clement   }
8247f75930SValentin Clement 
83beeb86bdSValentin Clement   /// Return the use map.
84beeb86bdSValentin Clement   /// The use map maps array access, amend, fetch and update operations back to
85beeb86bdSValentin Clement   /// the array load that is the original source of the array value.
86beeb86bdSValentin Clement   /// It maps an array_load to an array_merge_store, if and only if the loaded
87beeb86bdSValentin Clement   /// array value has pending modifications to be merged.
8847f75930SValentin Clement   const OperationUseMapT &getUseMap() const { return useMap; }
8947f75930SValentin Clement 
90beeb86bdSValentin Clement   /// Return the set of array_access ops directly associated with array_amend
91beeb86bdSValentin Clement   /// ops.
92beeb86bdSValentin Clement   bool inAmendAccessSet(mlir::Operation *op) const {
93beeb86bdSValentin Clement     return amendAccesses.count(op);
94beeb86bdSValentin Clement   }
95beeb86bdSValentin Clement 
96beeb86bdSValentin Clement   /// For ArrayLoad `load`, return the transitive set of all OpOperands.
97beeb86bdSValentin Clement   UseSetT getLoadUseSet(mlir::Operation *load) const {
98beeb86bdSValentin Clement     assert(loadMapSets.count(load) && "analysis missed an array load?");
99beeb86bdSValentin Clement     return loadMapSets.lookup(load);
100beeb86bdSValentin Clement   }
101beeb86bdSValentin Clement 
102beeb86bdSValentin Clement   void arrayMentions(llvm::SmallVectorImpl<mlir::Operation *> &mentions,
103beeb86bdSValentin Clement                      ArrayLoadOp load);
10447f75930SValentin Clement 
10547f75930SValentin Clement private:
10647f75930SValentin Clement   void construct(mlir::Operation *topLevelOp);
10747f75930SValentin Clement 
10847f75930SValentin Clement   mlir::Operation *operation; // operation that analysis ran upon
10947f75930SValentin Clement   ConflictSetT conflicts;     // set of conflicts (loads and merge stores)
11047f75930SValentin Clement   OperationUseMapT useMap;
11147f75930SValentin Clement   LoadMapSetsT loadMapSets;
112beeb86bdSValentin Clement   // Set of array_access ops associated with array_amend ops.
113beeb86bdSValentin Clement   AmendAccessSetT amendAccesses;
11447f75930SValentin Clement };
11547f75930SValentin Clement } // namespace
11647f75930SValentin Clement 
11747f75930SValentin Clement namespace {
11847f75930SValentin Clement /// Helper class to collect all array operations that produced an array value.
11947f75930SValentin Clement class ReachCollector {
120beeb86bdSValentin Clement public:
12147f75930SValentin Clement   ReachCollector(llvm::SmallVectorImpl<mlir::Operation *> &reach,
12247f75930SValentin Clement                  mlir::Region *loopRegion)
12347f75930SValentin Clement       : reach{reach}, loopRegion{loopRegion} {}
12447f75930SValentin Clement 
125beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::Operation *op, mlir::ValueRange range) {
12647f75930SValentin Clement     if (range.empty()) {
127beeb86bdSValentin Clement       collectArrayMentionFrom(op, mlir::Value{});
12847f75930SValentin Clement       return;
12947f75930SValentin Clement     }
13047f75930SValentin Clement     for (mlir::Value v : range)
131beeb86bdSValentin Clement       collectArrayMentionFrom(v);
13247f75930SValentin Clement   }
13347f75930SValentin Clement 
134beeb86bdSValentin Clement   // Collect all the array_access ops in `block`. This recursively looks into
135beeb86bdSValentin Clement   // blocks in ops with regions.
136beeb86bdSValentin Clement   // FIXME: This is temporarily relying on the array_amend appearing in a
137beeb86bdSValentin Clement   // do_loop Region.  This phase ordering assumption can be eliminated by using
138beeb86bdSValentin Clement   // dominance information to find the array_access ops or by scanning the
139beeb86bdSValentin Clement   // transitive closure of the amending array_access's users and the defs that
140beeb86bdSValentin Clement   // reach them.
141beeb86bdSValentin Clement   void collectAccesses(llvm::SmallVector<ArrayAccessOp> &result,
142beeb86bdSValentin Clement                        mlir::Block *block) {
143beeb86bdSValentin Clement     for (auto &op : *block) {
144beeb86bdSValentin Clement       if (auto access = mlir::dyn_cast<ArrayAccessOp>(op)) {
145beeb86bdSValentin Clement         LLVM_DEBUG(llvm::dbgs() << "adding access: " << access << '\n');
146beeb86bdSValentin Clement         result.push_back(access);
147beeb86bdSValentin Clement         continue;
148beeb86bdSValentin Clement       }
149beeb86bdSValentin Clement       for (auto &region : op.getRegions())
150beeb86bdSValentin Clement         for (auto &bb : region.getBlocks())
151beeb86bdSValentin Clement           collectAccesses(result, &bb);
152beeb86bdSValentin Clement     }
153beeb86bdSValentin Clement   }
154beeb86bdSValentin Clement 
155beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::Operation *op, mlir::Value val) {
15647f75930SValentin Clement     // `val` is defined by an Op, process the defining Op.
15747f75930SValentin Clement     // If `val` is defined by a region containing Op, we want to drill down
15847f75930SValentin Clement     // and through that Op's region(s).
15947f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "popset: " << *op << '\n');
16047f75930SValentin Clement     auto popFn = [&](auto rop) {
16147f75930SValentin Clement       assert(val && "op must have a result value");
16247f75930SValentin Clement       auto resNum = val.cast<mlir::OpResult>().getResultNumber();
16347f75930SValentin Clement       llvm::SmallVector<mlir::Value> results;
16447f75930SValentin Clement       rop.resultToSourceOps(results, resNum);
16547f75930SValentin Clement       for (auto u : results)
166beeb86bdSValentin Clement         collectArrayMentionFrom(u);
16747f75930SValentin Clement     };
168beeb86bdSValentin Clement     if (auto rop = mlir::dyn_cast<DoLoopOp>(op)) {
169beeb86bdSValentin Clement       popFn(rop);
170beeb86bdSValentin Clement       return;
171beeb86bdSValentin Clement     }
172beeb86bdSValentin Clement     if (auto rop = mlir::dyn_cast<IterWhileOp>(op)) {
17347f75930SValentin Clement       popFn(rop);
17447f75930SValentin Clement       return;
17547f75930SValentin Clement     }
17647f75930SValentin Clement     if (auto rop = mlir::dyn_cast<fir::IfOp>(op)) {
17747f75930SValentin Clement       popFn(rop);
17847f75930SValentin Clement       return;
17947f75930SValentin Clement     }
180beeb86bdSValentin Clement     if (auto box = mlir::dyn_cast<EmboxOp>(op)) {
181beeb86bdSValentin Clement       for (auto *user : box.getMemref().getUsers())
182beeb86bdSValentin Clement         if (user != op)
183beeb86bdSValentin Clement           collectArrayMentionFrom(user, user->getResults());
184beeb86bdSValentin Clement       return;
185beeb86bdSValentin Clement     }
18647f75930SValentin Clement     if (auto mergeStore = mlir::dyn_cast<ArrayMergeStoreOp>(op)) {
18747f75930SValentin Clement       if (opIsInsideLoops(mergeStore))
188beeb86bdSValentin Clement         collectArrayMentionFrom(mergeStore.getSequence());
18947f75930SValentin Clement       return;
19047f75930SValentin Clement     }
19147f75930SValentin Clement 
19247f75930SValentin Clement     if (mlir::isa<AllocaOp, AllocMemOp>(op)) {
19347f75930SValentin Clement       // Look for any stores inside the loops, and collect an array operation
19447f75930SValentin Clement       // that produced the value being stored to it.
195beeb86bdSValentin Clement       for (auto *user : op->getUsers())
19647f75930SValentin Clement         if (auto store = mlir::dyn_cast<fir::StoreOp>(user))
19747f75930SValentin Clement           if (opIsInsideLoops(store))
198beeb86bdSValentin Clement             collectArrayMentionFrom(store.getValue());
19947f75930SValentin Clement       return;
20047f75930SValentin Clement     }
20147f75930SValentin Clement 
202beeb86bdSValentin Clement     // Scan the uses of amend's memref
203beeb86bdSValentin Clement     if (auto amend = mlir::dyn_cast<ArrayAmendOp>(op)) {
204beeb86bdSValentin Clement       reach.push_back(op);
205beeb86bdSValentin Clement       llvm::SmallVector<ArrayAccessOp> accesses;
206beeb86bdSValentin Clement       collectAccesses(accesses, op->getBlock());
207beeb86bdSValentin Clement       for (auto access : accesses)
208beeb86bdSValentin Clement         collectArrayMentionFrom(access.getResult());
20947f75930SValentin Clement     }
210beeb86bdSValentin Clement 
211beeb86bdSValentin Clement     // Otherwise, Op does not contain a region so just chase its operands.
212beeb86bdSValentin Clement     if (mlir::isa<ArrayAccessOp, ArrayLoadOp, ArrayUpdateOp, ArrayModifyOp,
213beeb86bdSValentin Clement                   ArrayFetchOp>(op)) {
214beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs() << "add " << *op << " to reachable set\n");
215beeb86bdSValentin Clement       reach.push_back(op);
216beeb86bdSValentin Clement     }
217beeb86bdSValentin Clement 
218beeb86bdSValentin Clement     // Include all array_access ops using an array_load.
219beeb86bdSValentin Clement     if (auto arrLd = mlir::dyn_cast<ArrayLoadOp>(op))
220beeb86bdSValentin Clement       for (auto *user : arrLd.getResult().getUsers())
221beeb86bdSValentin Clement         if (mlir::isa<ArrayAccessOp>(user)) {
222beeb86bdSValentin Clement           LLVM_DEBUG(llvm::dbgs() << "add " << *user << " to reachable set\n");
223beeb86bdSValentin Clement           reach.push_back(user);
224beeb86bdSValentin Clement         }
225beeb86bdSValentin Clement 
226beeb86bdSValentin Clement     // Array modify assignment is performed on the result. So the analysis must
227beeb86bdSValentin Clement     // look at the what is done with the result.
22847f75930SValentin Clement     if (mlir::isa<ArrayModifyOp>(op))
229beeb86bdSValentin Clement       for (auto *user : op->getResult(0).getUsers())
23047f75930SValentin Clement         followUsers(user);
23147f75930SValentin Clement 
232beeb86bdSValentin Clement     if (mlir::isa<fir::CallOp>(op)) {
233beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs() << "add " << *op << " to reachable set\n");
234beeb86bdSValentin Clement       reach.push_back(op);
23547f75930SValentin Clement     }
23647f75930SValentin Clement 
237beeb86bdSValentin Clement     for (auto u : op->getOperands())
238beeb86bdSValentin Clement       collectArrayMentionFrom(u);
239beeb86bdSValentin Clement   }
240beeb86bdSValentin Clement 
241beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::BlockArgument ba) {
24247f75930SValentin Clement     auto *parent = ba.getOwner()->getParentOp();
24347f75930SValentin Clement     // If inside an Op holding a region, the block argument corresponds to an
24447f75930SValentin Clement     // argument passed to the containing Op.
24547f75930SValentin Clement     auto popFn = [&](auto rop) {
246beeb86bdSValentin Clement       collectArrayMentionFrom(rop.blockArgToSourceOp(ba.getArgNumber()));
24747f75930SValentin Clement     };
24847f75930SValentin Clement     if (auto rop = mlir::dyn_cast<DoLoopOp>(parent)) {
24947f75930SValentin Clement       popFn(rop);
25047f75930SValentin Clement       return;
25147f75930SValentin Clement     }
25247f75930SValentin Clement     if (auto rop = mlir::dyn_cast<IterWhileOp>(parent)) {
25347f75930SValentin Clement       popFn(rop);
25447f75930SValentin Clement       return;
25547f75930SValentin Clement     }
25647f75930SValentin Clement     // Otherwise, a block argument is provided via the pred blocks.
25747f75930SValentin Clement     for (auto *pred : ba.getOwner()->getPredecessors()) {
25847f75930SValentin Clement       auto u = pred->getTerminator()->getOperand(ba.getArgNumber());
259beeb86bdSValentin Clement       collectArrayMentionFrom(u);
26047f75930SValentin Clement     }
26147f75930SValentin Clement   }
26247f75930SValentin Clement 
26347f75930SValentin Clement   // Recursively trace operands to find all array operations relating to the
26447f75930SValentin Clement   // values merged.
265beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::Value val) {
26647f75930SValentin Clement     if (!val || visited.contains(val))
26747f75930SValentin Clement       return;
26847f75930SValentin Clement     visited.insert(val);
26947f75930SValentin Clement 
27047f75930SValentin Clement     // Process a block argument.
27147f75930SValentin Clement     if (auto ba = val.dyn_cast<mlir::BlockArgument>()) {
272beeb86bdSValentin Clement       collectArrayMentionFrom(ba);
27347f75930SValentin Clement       return;
27447f75930SValentin Clement     }
27547f75930SValentin Clement 
27647f75930SValentin Clement     // Process an Op.
27747f75930SValentin Clement     if (auto *op = val.getDefiningOp()) {
278beeb86bdSValentin Clement       collectArrayMentionFrom(op, val);
27947f75930SValentin Clement       return;
28047f75930SValentin Clement     }
28147f75930SValentin Clement 
282beeb86bdSValentin Clement     emitFatalError(val.getLoc(), "unhandled value");
28347f75930SValentin Clement   }
28447f75930SValentin Clement 
285beeb86bdSValentin Clement   /// Return all ops that produce the array value that is stored into the
286beeb86bdSValentin Clement   /// `array_merge_store`.
287beeb86bdSValentin Clement   static void reachingValues(llvm::SmallVectorImpl<mlir::Operation *> &reach,
288beeb86bdSValentin Clement                              mlir::Value seq) {
289beeb86bdSValentin Clement     reach.clear();
290beeb86bdSValentin Clement     mlir::Region *loopRegion = nullptr;
291beeb86bdSValentin Clement     if (auto doLoop = mlir::dyn_cast_or_null<DoLoopOp>(seq.getDefiningOp()))
292beeb86bdSValentin Clement       loopRegion = &doLoop->getRegion(0);
293beeb86bdSValentin Clement     ReachCollector collector(reach, loopRegion);
294beeb86bdSValentin Clement     collector.collectArrayMentionFrom(seq);
295beeb86bdSValentin Clement   }
296beeb86bdSValentin Clement 
297beeb86bdSValentin Clement private:
29847f75930SValentin Clement   /// Is \op inside the loop nest region ?
299beeb86bdSValentin Clement   /// FIXME: replace this structural dependence with graph properties.
30047f75930SValentin Clement   bool opIsInsideLoops(mlir::Operation *op) const {
301beeb86bdSValentin Clement     auto *region = op->getParentRegion();
302beeb86bdSValentin Clement     while (region) {
303beeb86bdSValentin Clement       if (region == loopRegion)
304beeb86bdSValentin Clement         return true;
305beeb86bdSValentin Clement       region = region->getParentRegion();
306beeb86bdSValentin Clement     }
307beeb86bdSValentin Clement     return false;
30847f75930SValentin Clement   }
30947f75930SValentin Clement 
31047f75930SValentin Clement   /// Recursively trace the use of an operation results, calling
311beeb86bdSValentin Clement   /// collectArrayMentionFrom on the direct and indirect user operands.
31247f75930SValentin Clement   void followUsers(mlir::Operation *op) {
31347f75930SValentin Clement     for (auto userOperand : op->getOperands())
314beeb86bdSValentin Clement       collectArrayMentionFrom(userOperand);
31547f75930SValentin Clement     // Go through potential converts/coordinate_op.
316beeb86bdSValentin Clement     for (auto indirectUser : op->getUsers())
31747f75930SValentin Clement       followUsers(indirectUser);
31847f75930SValentin Clement   }
31947f75930SValentin Clement 
32047f75930SValentin Clement   llvm::SmallVectorImpl<mlir::Operation *> &reach;
32147f75930SValentin Clement   llvm::SmallPtrSet<mlir::Value, 16> visited;
32247f75930SValentin Clement   /// Region of the loops nest that produced the array value.
32347f75930SValentin Clement   mlir::Region *loopRegion;
32447f75930SValentin Clement };
32547f75930SValentin Clement } // namespace
32647f75930SValentin Clement 
32747f75930SValentin Clement /// Find all the array operations that access the array value that is loaded by
32847f75930SValentin Clement /// the array load operation, `load`.
329beeb86bdSValentin Clement void ArrayCopyAnalysis::arrayMentions(
330beeb86bdSValentin Clement     llvm::SmallVectorImpl<mlir::Operation *> &mentions, ArrayLoadOp load) {
331beeb86bdSValentin Clement   mentions.clear();
33247f75930SValentin Clement   auto lmIter = loadMapSets.find(load);
333beeb86bdSValentin Clement   if (lmIter != loadMapSets.end()) {
334beeb86bdSValentin Clement     for (auto *opnd : lmIter->second) {
335beeb86bdSValentin Clement       auto *owner = opnd->getOwner();
336beeb86bdSValentin Clement       if (mlir::isa<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp, ArrayUpdateOp,
337beeb86bdSValentin Clement                     ArrayModifyOp>(owner))
338beeb86bdSValentin Clement         mentions.push_back(owner);
339beeb86bdSValentin Clement     }
340beeb86bdSValentin Clement     return;
341beeb86bdSValentin Clement   }
34247f75930SValentin Clement 
34347f75930SValentin Clement   UseSetT visited;
34447f75930SValentin Clement   llvm::SmallVector<mlir::OpOperand *> queue; // uses of ArrayLoad[orig]
34547f75930SValentin Clement 
34647f75930SValentin Clement   auto appendToQueue = [&](mlir::Value val) {
347beeb86bdSValentin Clement     for (auto &use : val.getUses())
34847f75930SValentin Clement       if (!visited.count(&use)) {
34947f75930SValentin Clement         visited.insert(&use);
35047f75930SValentin Clement         queue.push_back(&use);
35147f75930SValentin Clement       }
35247f75930SValentin Clement   };
35347f75930SValentin Clement 
35447f75930SValentin Clement   // Build the set of uses of `original`.
35547f75930SValentin Clement   // let USES = { uses of original fir.load }
35647f75930SValentin Clement   appendToQueue(load);
35747f75930SValentin Clement 
35847f75930SValentin Clement   // Process the worklist until done.
35947f75930SValentin Clement   while (!queue.empty()) {
36047f75930SValentin Clement     mlir::OpOperand *operand = queue.pop_back_val();
36147f75930SValentin Clement     mlir::Operation *owner = operand->getOwner();
362beeb86bdSValentin Clement     if (!owner)
363beeb86bdSValentin Clement       continue;
36447f75930SValentin Clement     auto structuredLoop = [&](auto ro) {
36547f75930SValentin Clement       if (auto blockArg = ro.iterArgToBlockArg(operand->get())) {
36647f75930SValentin Clement         int64_t arg = blockArg.getArgNumber();
367149ad3d5SShraiysh Vaishay         mlir::Value output = ro.getResult(ro.getFinalValue() ? arg : arg - 1);
36847f75930SValentin Clement         appendToQueue(output);
36947f75930SValentin Clement         appendToQueue(blockArg);
37047f75930SValentin Clement       }
37147f75930SValentin Clement     };
37247f75930SValentin Clement     // TODO: this need to be updated to use the control-flow interface.
37347f75930SValentin Clement     auto branchOp = [&](mlir::Block *dest, OperandRange operands) {
37447f75930SValentin Clement       if (operands.empty())
37547f75930SValentin Clement         return;
37647f75930SValentin Clement 
37747f75930SValentin Clement       // Check if this operand is within the range.
37847f75930SValentin Clement       unsigned operandIndex = operand->getOperandNumber();
37947f75930SValentin Clement       unsigned operandsStart = operands.getBeginOperandIndex();
38047f75930SValentin Clement       if (operandIndex < operandsStart ||
38147f75930SValentin Clement           operandIndex >= (operandsStart + operands.size()))
38247f75930SValentin Clement         return;
38347f75930SValentin Clement 
38447f75930SValentin Clement       // Index the successor.
38547f75930SValentin Clement       unsigned argIndex = operandIndex - operandsStart;
38647f75930SValentin Clement       appendToQueue(dest->getArgument(argIndex));
38747f75930SValentin Clement     };
38847f75930SValentin Clement     // Thread uses into structured loop bodies and return value uses.
38947f75930SValentin Clement     if (auto ro = mlir::dyn_cast<DoLoopOp>(owner)) {
39047f75930SValentin Clement       structuredLoop(ro);
39147f75930SValentin Clement     } else if (auto ro = mlir::dyn_cast<IterWhileOp>(owner)) {
39247f75930SValentin Clement       structuredLoop(ro);
39347f75930SValentin Clement     } else if (auto rs = mlir::dyn_cast<ResultOp>(owner)) {
39447f75930SValentin Clement       // Thread any uses of fir.if that return the marked array value.
395beeb86bdSValentin Clement       mlir::Operation *parent = rs->getParentRegion()->getParentOp();
396beeb86bdSValentin Clement       if (auto ifOp = mlir::dyn_cast<fir::IfOp>(parent))
39747f75930SValentin Clement         appendToQueue(ifOp.getResult(operand->getOperandNumber()));
39847f75930SValentin Clement     } else if (mlir::isa<ArrayFetchOp>(owner)) {
39947f75930SValentin Clement       // Keep track of array value fetches.
40047f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
40147f75930SValentin Clement                  << "add fetch {" << *owner << "} to array value set\n");
402beeb86bdSValentin Clement       mentions.push_back(owner);
40347f75930SValentin Clement     } else if (auto update = mlir::dyn_cast<ArrayUpdateOp>(owner)) {
40447f75930SValentin Clement       // Keep track of array value updates and thread the return value uses.
40547f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
40647f75930SValentin Clement                  << "add update {" << *owner << "} to array value set\n");
407beeb86bdSValentin Clement       mentions.push_back(owner);
40847f75930SValentin Clement       appendToQueue(update.getResult());
40947f75930SValentin Clement     } else if (auto update = mlir::dyn_cast<ArrayModifyOp>(owner)) {
41047f75930SValentin Clement       // Keep track of array value modification and thread the return value
41147f75930SValentin Clement       // uses.
41247f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
41347f75930SValentin Clement                  << "add modify {" << *owner << "} to array value set\n");
414beeb86bdSValentin Clement       mentions.push_back(owner);
41547f75930SValentin Clement       appendToQueue(update.getResult(1));
416beeb86bdSValentin Clement     } else if (auto mention = mlir::dyn_cast<ArrayAccessOp>(owner)) {
417beeb86bdSValentin Clement       mentions.push_back(owner);
418beeb86bdSValentin Clement     } else if (auto amend = mlir::dyn_cast<ArrayAmendOp>(owner)) {
419beeb86bdSValentin Clement       mentions.push_back(owner);
420beeb86bdSValentin Clement       appendToQueue(amend.getResult());
421ace01605SRiver Riddle     } else if (auto br = mlir::dyn_cast<mlir::cf::BranchOp>(owner)) {
4223012f35fSJacques Pienaar       branchOp(br.getDest(), br.getDestOperands());
423ace01605SRiver Riddle     } else if (auto br = mlir::dyn_cast<mlir::cf::CondBranchOp>(owner)) {
42447f75930SValentin Clement       branchOp(br.getTrueDest(), br.getTrueOperands());
42547f75930SValentin Clement       branchOp(br.getFalseDest(), br.getFalseOperands());
42647f75930SValentin Clement     } else if (mlir::isa<ArrayMergeStoreOp>(owner)) {
42747f75930SValentin Clement       // do nothing
42847f75930SValentin Clement     } else {
42947f75930SValentin Clement       llvm::report_fatal_error("array value reached unexpected op");
43047f75930SValentin Clement     }
43147f75930SValentin Clement   }
432beeb86bdSValentin Clement   loadMapSets.insert({load, visited});
433beeb86bdSValentin Clement }
434beeb86bdSValentin Clement 
435beeb86bdSValentin Clement static bool hasPointerType(mlir::Type type) {
436beeb86bdSValentin Clement   if (auto boxTy = type.dyn_cast<BoxType>())
437beeb86bdSValentin Clement     type = boxTy.getEleTy();
438beeb86bdSValentin Clement   return type.isa<fir::PointerType>();
439beeb86bdSValentin Clement }
440beeb86bdSValentin Clement 
441beeb86bdSValentin Clement // This is a NF performance hack. It makes a simple test that the slices of the
442beeb86bdSValentin Clement // load, \p ld, and the merge store, \p st, are trivially mutually exclusive.
443beeb86bdSValentin Clement static bool mutuallyExclusiveSliceRange(ArrayLoadOp ld, ArrayMergeStoreOp st) {
444beeb86bdSValentin Clement   // If the same array_load, then no further testing is warranted.
445beeb86bdSValentin Clement   if (ld.getResult() == st.getOriginal())
446beeb86bdSValentin Clement     return false;
447beeb86bdSValentin Clement 
448beeb86bdSValentin Clement   auto getSliceOp = [](mlir::Value val) -> SliceOp {
449beeb86bdSValentin Clement     if (!val)
450beeb86bdSValentin Clement       return {};
451beeb86bdSValentin Clement     auto sliceOp = mlir::dyn_cast_or_null<SliceOp>(val.getDefiningOp());
452beeb86bdSValentin Clement     if (!sliceOp)
453beeb86bdSValentin Clement       return {};
454beeb86bdSValentin Clement     return sliceOp;
455beeb86bdSValentin Clement   };
456beeb86bdSValentin Clement 
457beeb86bdSValentin Clement   auto ldSlice = getSliceOp(ld.getSlice());
458beeb86bdSValentin Clement   auto stSlice = getSliceOp(st.getSlice());
459beeb86bdSValentin Clement   if (!ldSlice || !stSlice)
460beeb86bdSValentin Clement     return false;
461beeb86bdSValentin Clement 
462beeb86bdSValentin Clement   // Resign on subobject slices.
463beeb86bdSValentin Clement   if (!ldSlice.getFields().empty() || !stSlice.getFields().empty() ||
464beeb86bdSValentin Clement       !ldSlice.getSubstr().empty() || !stSlice.getSubstr().empty())
465beeb86bdSValentin Clement     return false;
466beeb86bdSValentin Clement 
467beeb86bdSValentin Clement   // Crudely test that the two slices do not overlap by looking for the
468beeb86bdSValentin Clement   // following general condition. If the slices look like (i:j) and (j+1:k) then
469beeb86bdSValentin Clement   // these ranges do not overlap. The addend must be a constant.
470beeb86bdSValentin Clement   auto ldTriples = ldSlice.getTriples();
471beeb86bdSValentin Clement   auto stTriples = stSlice.getTriples();
472beeb86bdSValentin Clement   const auto size = ldTriples.size();
473beeb86bdSValentin Clement   if (size != stTriples.size())
474beeb86bdSValentin Clement     return false;
475beeb86bdSValentin Clement 
476beeb86bdSValentin Clement   auto displacedByConstant = [](mlir::Value v1, mlir::Value v2) {
477beeb86bdSValentin Clement     auto removeConvert = [](mlir::Value v) -> mlir::Operation * {
478beeb86bdSValentin Clement       auto *op = v.getDefiningOp();
479beeb86bdSValentin Clement       while (auto conv = mlir::dyn_cast_or_null<ConvertOp>(op))
480beeb86bdSValentin Clement         op = conv.getValue().getDefiningOp();
481beeb86bdSValentin Clement       return op;
482beeb86bdSValentin Clement     };
483beeb86bdSValentin Clement 
484beeb86bdSValentin Clement     auto isPositiveConstant = [](mlir::Value v) -> bool {
485beeb86bdSValentin Clement       if (auto conOp =
486beeb86bdSValentin Clement               mlir::dyn_cast<mlir::arith::ConstantOp>(v.getDefiningOp()))
487beeb86bdSValentin Clement         if (auto iattr = conOp.getValue().dyn_cast<mlir::IntegerAttr>())
488beeb86bdSValentin Clement           return iattr.getInt() > 0;
489beeb86bdSValentin Clement       return false;
490beeb86bdSValentin Clement     };
491beeb86bdSValentin Clement 
492beeb86bdSValentin Clement     auto *op1 = removeConvert(v1);
493beeb86bdSValentin Clement     auto *op2 = removeConvert(v2);
494beeb86bdSValentin Clement     if (!op1 || !op2)
495beeb86bdSValentin Clement       return false;
496beeb86bdSValentin Clement     if (auto addi = mlir::dyn_cast<mlir::arith::AddIOp>(op2))
497beeb86bdSValentin Clement       if ((addi.getLhs().getDefiningOp() == op1 &&
498beeb86bdSValentin Clement            isPositiveConstant(addi.getRhs())) ||
499beeb86bdSValentin Clement           (addi.getRhs().getDefiningOp() == op1 &&
500beeb86bdSValentin Clement            isPositiveConstant(addi.getLhs())))
501beeb86bdSValentin Clement         return true;
502beeb86bdSValentin Clement     if (auto subi = mlir::dyn_cast<mlir::arith::SubIOp>(op1))
503beeb86bdSValentin Clement       if (subi.getLhs().getDefiningOp() == op2 &&
504beeb86bdSValentin Clement           isPositiveConstant(subi.getRhs()))
505beeb86bdSValentin Clement         return true;
506beeb86bdSValentin Clement     return false;
507beeb86bdSValentin Clement   };
508beeb86bdSValentin Clement 
509beeb86bdSValentin Clement   for (std::remove_const_t<decltype(size)> i = 0; i < size; i += 3) {
510beeb86bdSValentin Clement     // If both are loop invariant, skip to the next triple.
511beeb86bdSValentin Clement     if (mlir::isa_and_nonnull<fir::UndefOp>(ldTriples[i + 1].getDefiningOp()) &&
512beeb86bdSValentin Clement         mlir::isa_and_nonnull<fir::UndefOp>(stTriples[i + 1].getDefiningOp())) {
513beeb86bdSValentin Clement       // Unless either is a vector index, then be conservative.
514beeb86bdSValentin Clement       if (mlir::isa_and_nonnull<fir::UndefOp>(ldTriples[i].getDefiningOp()) ||
515beeb86bdSValentin Clement           mlir::isa_and_nonnull<fir::UndefOp>(stTriples[i].getDefiningOp()))
516beeb86bdSValentin Clement         return false;
517beeb86bdSValentin Clement       continue;
518beeb86bdSValentin Clement     }
519beeb86bdSValentin Clement     // If identical, skip to the next triple.
520beeb86bdSValentin Clement     if (ldTriples[i] == stTriples[i] && ldTriples[i + 1] == stTriples[i + 1] &&
521beeb86bdSValentin Clement         ldTriples[i + 2] == stTriples[i + 2])
522beeb86bdSValentin Clement       continue;
523beeb86bdSValentin Clement     // If ubound and lbound are the same with a constant offset, skip to the
524beeb86bdSValentin Clement     // next triple.
525beeb86bdSValentin Clement     if (displacedByConstant(ldTriples[i + 1], stTriples[i]) ||
526beeb86bdSValentin Clement         displacedByConstant(stTriples[i + 1], ldTriples[i]))
527beeb86bdSValentin Clement       continue;
528beeb86bdSValentin Clement     return false;
529beeb86bdSValentin Clement   }
530beeb86bdSValentin Clement   LLVM_DEBUG(llvm::dbgs() << "detected non-overlapping slice ranges on " << ld
531beeb86bdSValentin Clement                           << " and " << st << ", which is not a conflict\n");
532beeb86bdSValentin Clement   return true;
53347f75930SValentin Clement }
53447f75930SValentin Clement 
53547f75930SValentin Clement /// Is there a conflict between the array value that was updated and to be
53647f75930SValentin Clement /// stored to `st` and the set of arrays loaded (`reach`) and used to compute
53747f75930SValentin Clement /// the updated value?
53847f75930SValentin Clement static bool conflictOnLoad(llvm::ArrayRef<mlir::Operation *> reach,
53947f75930SValentin Clement                            ArrayMergeStoreOp st) {
54047f75930SValentin Clement   mlir::Value load;
541149ad3d5SShraiysh Vaishay   mlir::Value addr = st.getMemref();
542beeb86bdSValentin Clement   const bool storeHasPointerType = hasPointerType(addr.getType());
543beeb86bdSValentin Clement   for (auto *op : reach)
544beeb86bdSValentin Clement     if (auto ld = mlir::dyn_cast<ArrayLoadOp>(op)) {
545149ad3d5SShraiysh Vaishay       mlir::Type ldTy = ld.getMemref().getType();
546149ad3d5SShraiysh Vaishay       if (ld.getMemref() == addr) {
547beeb86bdSValentin Clement         if (mutuallyExclusiveSliceRange(ld, st))
548beeb86bdSValentin Clement           continue;
549149ad3d5SShraiysh Vaishay         if (ld.getResult() != st.getOriginal())
55047f75930SValentin Clement           return true;
551beeb86bdSValentin Clement         if (load) {
552beeb86bdSValentin Clement           // TODO: extend this to allow checking if the first `load` and this
553beeb86bdSValentin Clement           // `ld` are mutually exclusive accesses but not identical.
55447f75930SValentin Clement           return true;
555beeb86bdSValentin Clement         }
55647f75930SValentin Clement         load = ld;
557beeb86bdSValentin Clement       } else if ((hasPointerType(ldTy) || storeHasPointerType)) {
558beeb86bdSValentin Clement         // TODO: Use target attribute to restrict this case further.
559beeb86bdSValentin Clement         // TODO: Check if types can also allow ruling out some cases. For now,
560beeb86bdSValentin Clement         // the fact that equivalences is using pointer attribute to enforce
561beeb86bdSValentin Clement         // aliasing is preventing any attempt to do so, and in general, it may
562beeb86bdSValentin Clement         // be wrong to use this if any of the types is a complex or a derived
563beeb86bdSValentin Clement         // for which it is possible to create a pointer to a part with a
564beeb86bdSValentin Clement         // different type than the whole, although this deserve some more
565beeb86bdSValentin Clement         // investigation because existing compiler behavior seem to diverge
566beeb86bdSValentin Clement         // here.
567beeb86bdSValentin Clement         return true;
56847f75930SValentin Clement       }
56947f75930SValentin Clement     }
57047f75930SValentin Clement   return false;
57147f75930SValentin Clement }
57247f75930SValentin Clement 
573beeb86bdSValentin Clement /// Is there an access vector conflict on the array being merged into? If the
574beeb86bdSValentin Clement /// access vectors diverge, then assume that there are potentially overlapping
575beeb86bdSValentin Clement /// loop-carried references.
576beeb86bdSValentin Clement static bool conflictOnMerge(llvm::ArrayRef<mlir::Operation *> mentions) {
577beeb86bdSValentin Clement   if (mentions.size() < 2)
57847f75930SValentin Clement     return false;
57947f75930SValentin Clement   llvm::SmallVector<mlir::Value> indices;
580beeb86bdSValentin Clement   LLVM_DEBUG(llvm::dbgs() << "check merge conflict on with " << mentions.size()
581beeb86bdSValentin Clement                           << " mentions on the list\n");
582beeb86bdSValentin Clement   bool valSeen = false;
583beeb86bdSValentin Clement   bool refSeen = false;
584beeb86bdSValentin Clement   for (auto *op : mentions) {
58547f75930SValentin Clement     llvm::SmallVector<mlir::Value> compareVector;
58647f75930SValentin Clement     if (auto u = mlir::dyn_cast<ArrayUpdateOp>(op)) {
587beeb86bdSValentin Clement       valSeen = true;
58847f75930SValentin Clement       if (indices.empty()) {
589149ad3d5SShraiysh Vaishay         indices = u.getIndices();
59047f75930SValentin Clement         continue;
59147f75930SValentin Clement       }
592149ad3d5SShraiysh Vaishay       compareVector = u.getIndices();
59347f75930SValentin Clement     } else if (auto f = mlir::dyn_cast<ArrayModifyOp>(op)) {
594beeb86bdSValentin Clement       valSeen = true;
59547f75930SValentin Clement       if (indices.empty()) {
596149ad3d5SShraiysh Vaishay         indices = f.getIndices();
59747f75930SValentin Clement         continue;
59847f75930SValentin Clement       }
599149ad3d5SShraiysh Vaishay       compareVector = f.getIndices();
60047f75930SValentin Clement     } else if (auto f = mlir::dyn_cast<ArrayFetchOp>(op)) {
601beeb86bdSValentin Clement       valSeen = true;
60247f75930SValentin Clement       if (indices.empty()) {
603149ad3d5SShraiysh Vaishay         indices = f.getIndices();
60447f75930SValentin Clement         continue;
60547f75930SValentin Clement       }
606149ad3d5SShraiysh Vaishay       compareVector = f.getIndices();
607beeb86bdSValentin Clement     } else if (auto f = mlir::dyn_cast<ArrayAccessOp>(op)) {
608beeb86bdSValentin Clement       refSeen = true;
609beeb86bdSValentin Clement       if (indices.empty()) {
610beeb86bdSValentin Clement         indices = f.getIndices();
611beeb86bdSValentin Clement         continue;
61247f75930SValentin Clement       }
613beeb86bdSValentin Clement       compareVector = f.getIndices();
614beeb86bdSValentin Clement     } else if (mlir::isa<ArrayAmendOp>(op)) {
615beeb86bdSValentin Clement       refSeen = true;
616beeb86bdSValentin Clement       continue;
617beeb86bdSValentin Clement     } else {
618beeb86bdSValentin Clement       mlir::emitError(op->getLoc(), "unexpected operation in analysis");
619beeb86bdSValentin Clement     }
620beeb86bdSValentin Clement     if (compareVector.size() != indices.size() ||
621beeb86bdSValentin Clement         llvm::any_of(llvm::zip(compareVector, indices), [&](auto pair) {
622beeb86bdSValentin Clement           return std::get<0>(pair) != std::get<1>(pair);
623beeb86bdSValentin Clement         }))
62447f75930SValentin Clement       return true;
62547f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "vectors compare equal\n");
62647f75930SValentin Clement   }
627beeb86bdSValentin Clement   return valSeen && refSeen;
628beeb86bdSValentin Clement }
629beeb86bdSValentin Clement 
630beeb86bdSValentin Clement /// With element-by-reference semantics, an amended array with more than once
631beeb86bdSValentin Clement /// access to the same loaded array are conservatively considered a conflict.
632beeb86bdSValentin Clement /// Note: the array copy can still be eliminated in subsequent optimizations.
633beeb86bdSValentin Clement static bool conflictOnReference(llvm::ArrayRef<mlir::Operation *> mentions) {
634beeb86bdSValentin Clement   LLVM_DEBUG(llvm::dbgs() << "checking reference semantics " << mentions.size()
635beeb86bdSValentin Clement                           << '\n');
636beeb86bdSValentin Clement   if (mentions.size() < 3)
63747f75930SValentin Clement     return false;
638beeb86bdSValentin Clement   unsigned amendCount = 0;
639beeb86bdSValentin Clement   unsigned accessCount = 0;
640beeb86bdSValentin Clement   for (auto *op : mentions) {
641beeb86bdSValentin Clement     if (mlir::isa<ArrayAmendOp>(op) && ++amendCount > 1) {
642beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs() << "conflict: multiple amends of array value\n");
643beeb86bdSValentin Clement       return true;
644beeb86bdSValentin Clement     }
645beeb86bdSValentin Clement     if (mlir::isa<ArrayAccessOp>(op) && ++accessCount > 1) {
646beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs()
647beeb86bdSValentin Clement                  << "conflict: multiple accesses of array value\n");
648beeb86bdSValentin Clement       return true;
649beeb86bdSValentin Clement     }
650beeb86bdSValentin Clement     if (mlir::isa<ArrayFetchOp, ArrayUpdateOp, ArrayModifyOp>(op)) {
651beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs()
652beeb86bdSValentin Clement                  << "conflict: array value has both uses by-value and uses "
653beeb86bdSValentin Clement                     "by-reference. conservative assumption.\n");
654beeb86bdSValentin Clement       return true;
655beeb86bdSValentin Clement     }
656beeb86bdSValentin Clement   }
657beeb86bdSValentin Clement   return false;
658beeb86bdSValentin Clement }
659beeb86bdSValentin Clement 
660beeb86bdSValentin Clement static mlir::Operation *
661beeb86bdSValentin Clement amendingAccess(llvm::ArrayRef<mlir::Operation *> mentions) {
662beeb86bdSValentin Clement   for (auto *op : mentions)
663beeb86bdSValentin Clement     if (auto amend = mlir::dyn_cast<ArrayAmendOp>(op))
664beeb86bdSValentin Clement       return amend.getMemref().getDefiningOp();
665beeb86bdSValentin Clement   return {};
66647f75930SValentin Clement }
66747f75930SValentin Clement 
66847f75930SValentin Clement // Are either of types of conflicts present?
66947f75930SValentin Clement inline bool conflictDetected(llvm::ArrayRef<mlir::Operation *> reach,
67047f75930SValentin Clement                              llvm::ArrayRef<mlir::Operation *> accesses,
67147f75930SValentin Clement                              ArrayMergeStoreOp st) {
67247f75930SValentin Clement   return conflictOnLoad(reach, st) || conflictOnMerge(accesses);
67347f75930SValentin Clement }
67447f75930SValentin Clement 
675beeb86bdSValentin Clement // Assume that any call to a function that uses host-associations will be
676beeb86bdSValentin Clement // modifying the output array.
677beeb86bdSValentin Clement static bool
678beeb86bdSValentin Clement conservativeCallConflict(llvm::ArrayRef<mlir::Operation *> reaches) {
679beeb86bdSValentin Clement   return llvm::any_of(reaches, [](mlir::Operation *op) {
680beeb86bdSValentin Clement     if (auto call = mlir::dyn_cast<fir::CallOp>(op))
681beeb86bdSValentin Clement       if (auto callee =
682beeb86bdSValentin Clement               call.getCallableForCallee().dyn_cast<mlir::SymbolRefAttr>()) {
683beeb86bdSValentin Clement         auto module = op->getParentOfType<mlir::ModuleOp>();
684beeb86bdSValentin Clement         return hasHostAssociationArgument(
685beeb86bdSValentin Clement             module.lookupSymbol<mlir::FuncOp>(callee));
686beeb86bdSValentin Clement       }
687beeb86bdSValentin Clement     return false;
688beeb86bdSValentin Clement   });
689beeb86bdSValentin Clement }
690beeb86bdSValentin Clement 
69147f75930SValentin Clement /// Constructor of the array copy analysis.
69247f75930SValentin Clement /// This performs the analysis and saves the intermediate results.
69347f75930SValentin Clement void ArrayCopyAnalysis::construct(mlir::Operation *topLevelOp) {
69447f75930SValentin Clement   topLevelOp->walk([&](Operation *op) {
69547f75930SValentin Clement     if (auto st = mlir::dyn_cast<fir::ArrayMergeStoreOp>(op)) {
696beeb86bdSValentin Clement       llvm::SmallVector<mlir::Operation *> values;
697149ad3d5SShraiysh Vaishay       ReachCollector::reachingValues(values, st.getSequence());
698beeb86bdSValentin Clement       bool callConflict = conservativeCallConflict(values);
699beeb86bdSValentin Clement       llvm::SmallVector<mlir::Operation *> mentions;
700beeb86bdSValentin Clement       arrayMentions(mentions,
701149ad3d5SShraiysh Vaishay                     mlir::cast<ArrayLoadOp>(st.getOriginal().getDefiningOp()));
702beeb86bdSValentin Clement       bool conflict = conflictDetected(values, mentions, st);
703beeb86bdSValentin Clement       bool refConflict = conflictOnReference(mentions);
704beeb86bdSValentin Clement       if (callConflict || conflict || refConflict) {
70547f75930SValentin Clement         LLVM_DEBUG(llvm::dbgs()
70647f75930SValentin Clement                    << "CONFLICT: copies required for " << st << '\n'
70747f75930SValentin Clement                    << "   adding conflicts on: " << op << " and "
708149ad3d5SShraiysh Vaishay                    << st.getOriginal() << '\n');
70947f75930SValentin Clement         conflicts.insert(op);
710149ad3d5SShraiysh Vaishay         conflicts.insert(st.getOriginal().getDefiningOp());
711beeb86bdSValentin Clement         if (auto *access = amendingAccess(mentions))
712beeb86bdSValentin Clement           amendAccesses.insert(access);
71347f75930SValentin Clement       }
714149ad3d5SShraiysh Vaishay       auto *ld = st.getOriginal().getDefiningOp();
71547f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
71647f75930SValentin Clement                  << "map: adding {" << *ld << " -> " << st << "}\n");
71747f75930SValentin Clement       useMap.insert({ld, op});
71847f75930SValentin Clement     } else if (auto load = mlir::dyn_cast<ArrayLoadOp>(op)) {
719beeb86bdSValentin Clement       llvm::SmallVector<mlir::Operation *> mentions;
720beeb86bdSValentin Clement       arrayMentions(mentions, load);
72147f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs() << "process load: " << load
722beeb86bdSValentin Clement                               << ", mentions: " << mentions.size() << '\n');
723beeb86bdSValentin Clement       for (auto *acc : mentions) {
724beeb86bdSValentin Clement         LLVM_DEBUG(llvm::dbgs() << " mention: " << *acc << '\n');
725beeb86bdSValentin Clement         if (mlir::isa<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp, ArrayUpdateOp,
726beeb86bdSValentin Clement                       ArrayModifyOp>(acc)) {
727beeb86bdSValentin Clement           if (useMap.count(acc)) {
72847f75930SValentin Clement             mlir::emitError(
72947f75930SValentin Clement                 load.getLoc(),
73047f75930SValentin Clement                 "The parallel semantics of multiple array_merge_stores per "
73147f75930SValentin Clement                 "array_load are not supported.");
732beeb86bdSValentin Clement             continue;
73347f75930SValentin Clement           }
73447f75930SValentin Clement           LLVM_DEBUG(llvm::dbgs()
73547f75930SValentin Clement                      << "map: adding {" << *acc << "} -> {" << load << "}\n");
736beeb86bdSValentin Clement           useMap.insert({acc, op});
737beeb86bdSValentin Clement         }
73847f75930SValentin Clement       }
73947f75930SValentin Clement     }
74047f75930SValentin Clement   });
74147f75930SValentin Clement }
74247f75930SValentin Clement 
743beeb86bdSValentin Clement //===----------------------------------------------------------------------===//
744beeb86bdSValentin Clement // Conversions for converting out of array value form.
745beeb86bdSValentin Clement //===----------------------------------------------------------------------===//
746beeb86bdSValentin Clement 
74747f75930SValentin Clement namespace {
74847f75930SValentin Clement class ArrayLoadConversion : public mlir::OpRewritePattern<ArrayLoadOp> {
74947f75930SValentin Clement public:
75047f75930SValentin Clement   using OpRewritePattern::OpRewritePattern;
75147f75930SValentin Clement 
75247f75930SValentin Clement   mlir::LogicalResult
75347f75930SValentin Clement   matchAndRewrite(ArrayLoadOp load,
75447f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
75547f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "replace load " << load << " with undef.\n");
75647f75930SValentin Clement     rewriter.replaceOpWithNewOp<UndefOp>(load, load.getType());
75747f75930SValentin Clement     return mlir::success();
75847f75930SValentin Clement   }
75947f75930SValentin Clement };
76047f75930SValentin Clement 
76147f75930SValentin Clement class ArrayMergeStoreConversion
76247f75930SValentin Clement     : public mlir::OpRewritePattern<ArrayMergeStoreOp> {
76347f75930SValentin Clement public:
76447f75930SValentin Clement   using OpRewritePattern::OpRewritePattern;
76547f75930SValentin Clement 
76647f75930SValentin Clement   mlir::LogicalResult
76747f75930SValentin Clement   matchAndRewrite(ArrayMergeStoreOp store,
76847f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
76947f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "marking store " << store << " as dead.\n");
77047f75930SValentin Clement     rewriter.eraseOp(store);
77147f75930SValentin Clement     return mlir::success();
77247f75930SValentin Clement   }
77347f75930SValentin Clement };
77447f75930SValentin Clement } // namespace
77547f75930SValentin Clement 
77647f75930SValentin Clement static mlir::Type getEleTy(mlir::Type ty) {
777beeb86bdSValentin Clement   auto eleTy = unwrapSequenceType(unwrapPassByRefType(ty));
77847f75930SValentin Clement   // FIXME: keep ptr/heap/ref information.
779beeb86bdSValentin Clement   return ReferenceType::get(eleTy);
78047f75930SValentin Clement }
78147f75930SValentin Clement 
78247f75930SValentin Clement // Extract extents from the ShapeOp/ShapeShiftOp into the result vector.
783beeb86bdSValentin Clement static bool getAdjustedExtents(mlir::Location loc,
784beeb86bdSValentin Clement                                mlir::PatternRewriter &rewriter,
785beeb86bdSValentin Clement                                ArrayLoadOp arrLoad,
786beeb86bdSValentin Clement                                llvm::SmallVectorImpl<mlir::Value> &result,
78747f75930SValentin Clement                                mlir::Value shape) {
788beeb86bdSValentin Clement   bool copyUsingSlice = false;
78947f75930SValentin Clement   auto *shapeOp = shape.getDefiningOp();
790beeb86bdSValentin Clement   if (auto s = mlir::dyn_cast_or_null<ShapeOp>(shapeOp)) {
79147f75930SValentin Clement     auto e = s.getExtents();
79247f75930SValentin Clement     result.insert(result.end(), e.begin(), e.end());
793beeb86bdSValentin Clement   } else if (auto s = mlir::dyn_cast_or_null<ShapeShiftOp>(shapeOp)) {
79447f75930SValentin Clement     auto e = s.getExtents();
79547f75930SValentin Clement     result.insert(result.end(), e.begin(), e.end());
796beeb86bdSValentin Clement   } else {
797beeb86bdSValentin Clement     emitFatalError(loc, "not a fir.shape/fir.shape_shift op");
79847f75930SValentin Clement   }
799beeb86bdSValentin Clement   auto idxTy = rewriter.getIndexType();
800beeb86bdSValentin Clement   if (factory::isAssumedSize(result)) {
801beeb86bdSValentin Clement     // Use slice information to compute the extent of the column.
802beeb86bdSValentin Clement     auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1);
803beeb86bdSValentin Clement     mlir::Value size = one;
804beeb86bdSValentin Clement     if (mlir::Value sliceArg = arrLoad.getSlice()) {
805beeb86bdSValentin Clement       if (auto sliceOp =
806beeb86bdSValentin Clement               mlir::dyn_cast_or_null<SliceOp>(sliceArg.getDefiningOp())) {
807beeb86bdSValentin Clement         auto triples = sliceOp.getTriples();
808beeb86bdSValentin Clement         const std::size_t tripleSize = triples.size();
809beeb86bdSValentin Clement         auto module = arrLoad->getParentOfType<mlir::ModuleOp>();
810beeb86bdSValentin Clement         FirOpBuilder builder(rewriter, getKindMapping(module));
811beeb86bdSValentin Clement         size = builder.genExtentFromTriplet(loc, triples[tripleSize - 3],
812beeb86bdSValentin Clement                                             triples[tripleSize - 2],
813beeb86bdSValentin Clement                                             triples[tripleSize - 1], idxTy);
814beeb86bdSValentin Clement         copyUsingSlice = true;
815beeb86bdSValentin Clement       }
816beeb86bdSValentin Clement     }
817beeb86bdSValentin Clement     result[result.size() - 1] = size;
818beeb86bdSValentin Clement   }
819beeb86bdSValentin Clement   return copyUsingSlice;
82047f75930SValentin Clement }
82147f75930SValentin Clement 
822beeb86bdSValentin Clement /// Place the extents of the array load, \p arrLoad, into \p result and
823beeb86bdSValentin Clement /// return a ShapeOp or ShapeShiftOp with the same extents. If \p arrLoad is
824beeb86bdSValentin Clement /// loading a `!fir.box`, code will be generated to read the extents from the
825beeb86bdSValentin Clement /// boxed value, and the retunred shape Op will be built with the extents read
826beeb86bdSValentin Clement /// from the box. Otherwise, the extents will be extracted from the ShapeOp (or
827beeb86bdSValentin Clement /// ShapeShiftOp) argument of \p arrLoad. \p copyUsingSlice will be set to true
828beeb86bdSValentin Clement /// if slicing of the output array is to be done in the copy-in/copy-out rather
829beeb86bdSValentin Clement /// than in the elemental computation step.
830beeb86bdSValentin Clement static mlir::Value getOrReadExtentsAndShapeOp(
831beeb86bdSValentin Clement     mlir::Location loc, mlir::PatternRewriter &rewriter, ArrayLoadOp arrLoad,
832beeb86bdSValentin Clement     llvm::SmallVectorImpl<mlir::Value> &result, bool &copyUsingSlice) {
83347f75930SValentin Clement   assert(result.empty());
834beeb86bdSValentin Clement   if (arrLoad->hasAttr(fir::getOptionalAttrName()))
835beeb86bdSValentin Clement     fir::emitFatalError(
836beeb86bdSValentin Clement         loc, "shapes from array load of OPTIONAL arrays must not be used");
837beeb86bdSValentin Clement   if (auto boxTy = arrLoad.getMemref().getType().dyn_cast<BoxType>()) {
838beeb86bdSValentin Clement     auto rank =
839beeb86bdSValentin Clement         dyn_cast_ptrOrBoxEleTy(boxTy).cast<SequenceType>().getDimension();
84047f75930SValentin Clement     auto idxTy = rewriter.getIndexType();
84147f75930SValentin Clement     for (decltype(rank) dim = 0; dim < rank; ++dim) {
842beeb86bdSValentin Clement       auto dimVal = rewriter.create<mlir::arith::ConstantIndexOp>(loc, dim);
843beeb86bdSValentin Clement       auto dimInfo = rewriter.create<BoxDimsOp>(loc, idxTy, idxTy, idxTy,
844beeb86bdSValentin Clement                                                 arrLoad.getMemref(), dimVal);
84547f75930SValentin Clement       result.emplace_back(dimInfo.getResult(1));
84647f75930SValentin Clement     }
847beeb86bdSValentin Clement     if (!arrLoad.getShape()) {
848beeb86bdSValentin Clement       auto shapeType = ShapeType::get(rewriter.getContext(), rank);
849beeb86bdSValentin Clement       return rewriter.create<ShapeOp>(loc, shapeType, result);
85047f75930SValentin Clement     }
851beeb86bdSValentin Clement     auto shiftOp = arrLoad.getShape().getDefiningOp<ShiftOp>();
852beeb86bdSValentin Clement     auto shapeShiftType = ShapeShiftType::get(rewriter.getContext(), rank);
853beeb86bdSValentin Clement     llvm::SmallVector<mlir::Value> shapeShiftOperands;
854beeb86bdSValentin Clement     for (auto [lb, extent] : llvm::zip(shiftOp.getOrigins(), result)) {
855beeb86bdSValentin Clement       shapeShiftOperands.push_back(lb);
856beeb86bdSValentin Clement       shapeShiftOperands.push_back(extent);
857beeb86bdSValentin Clement     }
858beeb86bdSValentin Clement     return rewriter.create<ShapeShiftOp>(loc, shapeShiftType,
859beeb86bdSValentin Clement                                          shapeShiftOperands);
860beeb86bdSValentin Clement   }
861beeb86bdSValentin Clement   copyUsingSlice =
862beeb86bdSValentin Clement       getAdjustedExtents(loc, rewriter, arrLoad, result, arrLoad.getShape());
863beeb86bdSValentin Clement   return arrLoad.getShape();
86447f75930SValentin Clement }
86547f75930SValentin Clement 
86647f75930SValentin Clement static mlir::Type toRefType(mlir::Type ty) {
86747f75930SValentin Clement   if (fir::isa_ref_type(ty))
86847f75930SValentin Clement     return ty;
86947f75930SValentin Clement   return fir::ReferenceType::get(ty);
87047f75930SValentin Clement }
87147f75930SValentin Clement 
87247f75930SValentin Clement static mlir::Value
87347f75930SValentin Clement genCoorOp(mlir::PatternRewriter &rewriter, mlir::Location loc, mlir::Type eleTy,
87447f75930SValentin Clement           mlir::Type resTy, mlir::Value alloc, mlir::Value shape,
87547f75930SValentin Clement           mlir::Value slice, mlir::ValueRange indices,
87647f75930SValentin Clement           mlir::ValueRange typeparams, bool skipOrig = false) {
87747f75930SValentin Clement   llvm::SmallVector<mlir::Value> originated;
87847f75930SValentin Clement   if (skipOrig)
87947f75930SValentin Clement     originated.assign(indices.begin(), indices.end());
88047f75930SValentin Clement   else
88147f75930SValentin Clement     originated = fir::factory::originateIndices(loc, rewriter, alloc.getType(),
88247f75930SValentin Clement                                                 shape, indices);
88347f75930SValentin Clement   auto seqTy = fir::dyn_cast_ptrOrBoxEleTy(alloc.getType());
88447f75930SValentin Clement   assert(seqTy && seqTy.isa<fir::SequenceType>());
88547f75930SValentin Clement   const auto dimension = seqTy.cast<fir::SequenceType>().getDimension();
88647f75930SValentin Clement   mlir::Value result = rewriter.create<fir::ArrayCoorOp>(
88747f75930SValentin Clement       loc, eleTy, alloc, shape, slice,
88847f75930SValentin Clement       llvm::ArrayRef<mlir::Value>{originated}.take_front(dimension),
88947f75930SValentin Clement       typeparams);
89047f75930SValentin Clement   if (dimension < originated.size())
89147f75930SValentin Clement     result = rewriter.create<fir::CoordinateOp>(
89247f75930SValentin Clement         loc, resTy, result,
89347f75930SValentin Clement         llvm::ArrayRef<mlir::Value>{originated}.drop_front(dimension));
89447f75930SValentin Clement   return result;
89547f75930SValentin Clement }
89647f75930SValentin Clement 
897beeb86bdSValentin Clement static mlir::Value getCharacterLen(mlir::Location loc, FirOpBuilder &builder,
898beeb86bdSValentin Clement                                    ArrayLoadOp load, CharacterType charTy) {
899beeb86bdSValentin Clement   auto charLenTy = builder.getCharacterLengthType();
900beeb86bdSValentin Clement   if (charTy.hasDynamicLen()) {
901beeb86bdSValentin Clement     if (load.getMemref().getType().isa<BoxType>()) {
902beeb86bdSValentin Clement       // The loaded array is an emboxed value. Get the CHARACTER length from
903beeb86bdSValentin Clement       // the box value.
904beeb86bdSValentin Clement       auto eleSzInBytes =
905beeb86bdSValentin Clement           builder.create<BoxEleSizeOp>(loc, charLenTy, load.getMemref());
906beeb86bdSValentin Clement       auto kindSize =
907beeb86bdSValentin Clement           builder.getKindMap().getCharacterBitsize(charTy.getFKind());
908beeb86bdSValentin Clement       auto kindByteSize =
909beeb86bdSValentin Clement           builder.createIntegerConstant(loc, charLenTy, kindSize / 8);
910beeb86bdSValentin Clement       return builder.create<mlir::arith::DivSIOp>(loc, eleSzInBytes,
911beeb86bdSValentin Clement                                                   kindByteSize);
912beeb86bdSValentin Clement     }
913beeb86bdSValentin Clement     // The loaded array is a (set of) unboxed values. If the CHARACTER's
914beeb86bdSValentin Clement     // length is not a constant, it must be provided as a type parameter to
915beeb86bdSValentin Clement     // the array_load.
916beeb86bdSValentin Clement     auto typeparams = load.getTypeparams();
917beeb86bdSValentin Clement     assert(typeparams.size() > 0 && "expected type parameters on array_load");
918beeb86bdSValentin Clement     return typeparams.back();
919beeb86bdSValentin Clement   }
920beeb86bdSValentin Clement   // The typical case: the length of the CHARACTER is a compile-time
921beeb86bdSValentin Clement   // constant that is encoded in the type information.
922beeb86bdSValentin Clement   return builder.createIntegerConstant(loc, charLenTy, charTy.getLen());
923beeb86bdSValentin Clement }
924beeb86bdSValentin Clement /// Generate a shallow array copy. This is used for both copy-in and copy-out.
925beeb86bdSValentin Clement template <bool CopyIn>
926beeb86bdSValentin Clement void genArrayCopy(mlir::Location loc, mlir::PatternRewriter &rewriter,
927beeb86bdSValentin Clement                   mlir::Value dst, mlir::Value src, mlir::Value shapeOp,
928beeb86bdSValentin Clement                   mlir::Value sliceOp, ArrayLoadOp arrLoad) {
929beeb86bdSValentin Clement   auto insPt = rewriter.saveInsertionPoint();
930beeb86bdSValentin Clement   llvm::SmallVector<mlir::Value> indices;
931beeb86bdSValentin Clement   llvm::SmallVector<mlir::Value> extents;
932beeb86bdSValentin Clement   bool copyUsingSlice =
933beeb86bdSValentin Clement       getAdjustedExtents(loc, rewriter, arrLoad, extents, shapeOp);
934beeb86bdSValentin Clement   auto idxTy = rewriter.getIndexType();
935beeb86bdSValentin Clement   // Build loop nest from column to row.
936beeb86bdSValentin Clement   for (auto sh : llvm::reverse(extents)) {
937beeb86bdSValentin Clement     auto ubi = rewriter.create<ConvertOp>(loc, idxTy, sh);
938beeb86bdSValentin Clement     auto zero = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 0);
939beeb86bdSValentin Clement     auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1);
940beeb86bdSValentin Clement     auto ub = rewriter.create<mlir::arith::SubIOp>(loc, idxTy, ubi, one);
941beeb86bdSValentin Clement     auto loop = rewriter.create<DoLoopOp>(loc, zero, ub, one);
942beeb86bdSValentin Clement     rewriter.setInsertionPointToStart(loop.getBody());
943beeb86bdSValentin Clement     indices.push_back(loop.getInductionVar());
944beeb86bdSValentin Clement   }
945beeb86bdSValentin Clement   // Reverse the indices so they are in column-major order.
946beeb86bdSValentin Clement   std::reverse(indices.begin(), indices.end());
947beeb86bdSValentin Clement   auto typeparams = arrLoad.getTypeparams();
948beeb86bdSValentin Clement   auto fromAddr = rewriter.create<ArrayCoorOp>(
949beeb86bdSValentin Clement       loc, getEleTy(src.getType()), src, shapeOp,
950beeb86bdSValentin Clement       CopyIn && copyUsingSlice ? sliceOp : mlir::Value{},
951beeb86bdSValentin Clement       factory::originateIndices(loc, rewriter, src.getType(), shapeOp, indices),
952beeb86bdSValentin Clement       typeparams);
953beeb86bdSValentin Clement   auto toAddr = rewriter.create<ArrayCoorOp>(
954beeb86bdSValentin Clement       loc, getEleTy(dst.getType()), dst, shapeOp,
955beeb86bdSValentin Clement       !CopyIn && copyUsingSlice ? sliceOp : mlir::Value{},
956beeb86bdSValentin Clement       factory::originateIndices(loc, rewriter, dst.getType(), shapeOp, indices),
957beeb86bdSValentin Clement       typeparams);
958beeb86bdSValentin Clement   auto eleTy = unwrapSequenceType(unwrapPassByRefType(dst.getType()));
959beeb86bdSValentin Clement   auto module = toAddr->getParentOfType<mlir::ModuleOp>();
960beeb86bdSValentin Clement   FirOpBuilder builder(rewriter, getKindMapping(module));
961beeb86bdSValentin Clement   // Copy from (to) object to (from) temp copy of same object.
962beeb86bdSValentin Clement   if (auto charTy = eleTy.dyn_cast<CharacterType>()) {
963beeb86bdSValentin Clement     auto len = getCharacterLen(loc, builder, arrLoad, charTy);
964beeb86bdSValentin Clement     CharBoxValue toChar(toAddr, len);
965beeb86bdSValentin Clement     CharBoxValue fromChar(fromAddr, len);
966beeb86bdSValentin Clement     factory::genScalarAssignment(builder, loc, toChar, fromChar);
967beeb86bdSValentin Clement   } else {
968beeb86bdSValentin Clement     if (hasDynamicSize(eleTy))
969beeb86bdSValentin Clement       TODO(loc, "copy element of dynamic size");
970beeb86bdSValentin Clement     factory::genScalarAssignment(builder, loc, toAddr, fromAddr);
971beeb86bdSValentin Clement   }
972beeb86bdSValentin Clement   rewriter.restoreInsertionPoint(insPt);
973beeb86bdSValentin Clement }
974beeb86bdSValentin Clement 
975beeb86bdSValentin Clement /// The array load may be either a boxed or unboxed value. If the value is
976beeb86bdSValentin Clement /// boxed, we read the type parameters from the boxed value.
977beeb86bdSValentin Clement static llvm::SmallVector<mlir::Value>
978beeb86bdSValentin Clement genArrayLoadTypeParameters(mlir::Location loc, mlir::PatternRewriter &rewriter,
979beeb86bdSValentin Clement                            ArrayLoadOp load) {
980beeb86bdSValentin Clement   if (load.getTypeparams().empty()) {
981beeb86bdSValentin Clement     auto eleTy =
982beeb86bdSValentin Clement         unwrapSequenceType(unwrapPassByRefType(load.getMemref().getType()));
983beeb86bdSValentin Clement     if (hasDynamicSize(eleTy)) {
984beeb86bdSValentin Clement       if (auto charTy = eleTy.dyn_cast<CharacterType>()) {
985beeb86bdSValentin Clement         assert(load.getMemref().getType().isa<BoxType>());
986beeb86bdSValentin Clement         auto module = load->getParentOfType<mlir::ModuleOp>();
987beeb86bdSValentin Clement         FirOpBuilder builder(rewriter, getKindMapping(module));
988beeb86bdSValentin Clement         return {getCharacterLen(loc, builder, load, charTy)};
989beeb86bdSValentin Clement       }
990beeb86bdSValentin Clement       TODO(loc, "unhandled dynamic type parameters");
991beeb86bdSValentin Clement     }
992beeb86bdSValentin Clement     return {};
993beeb86bdSValentin Clement   }
994beeb86bdSValentin Clement   return load.getTypeparams();
995beeb86bdSValentin Clement }
996beeb86bdSValentin Clement 
997beeb86bdSValentin Clement static llvm::SmallVector<mlir::Value>
998beeb86bdSValentin Clement findNonconstantExtents(mlir::Type memrefTy,
999beeb86bdSValentin Clement                        llvm::ArrayRef<mlir::Value> extents) {
1000beeb86bdSValentin Clement   llvm::SmallVector<mlir::Value> nce;
1001beeb86bdSValentin Clement   auto arrTy = unwrapPassByRefType(memrefTy);
1002beeb86bdSValentin Clement   auto seqTy = arrTy.cast<SequenceType>();
1003beeb86bdSValentin Clement   for (auto [s, x] : llvm::zip(seqTy.getShape(), extents))
1004beeb86bdSValentin Clement     if (s == SequenceType::getUnknownExtent())
1005beeb86bdSValentin Clement       nce.emplace_back(x);
1006beeb86bdSValentin Clement   if (extents.size() > seqTy.getShape().size())
1007beeb86bdSValentin Clement     for (auto x : extents.drop_front(seqTy.getShape().size()))
1008beeb86bdSValentin Clement       nce.emplace_back(x);
1009beeb86bdSValentin Clement   return nce;
1010beeb86bdSValentin Clement }
1011beeb86bdSValentin Clement 
1012*3ed899ccSJean Perier /// Allocate temporary storage for an ArrayLoadOp \load and initialize any
1013*3ed899ccSJean Perier /// allocatable direct components of the array elements with an unallocated
1014*3ed899ccSJean Perier /// status. Returns the temporary address as well as a callback to generate the
1015*3ed899ccSJean Perier /// temporary clean-up once it has been used. The clean-up will take care of
1016*3ed899ccSJean Perier /// deallocating all the element allocatable components that may have been
1017*3ed899ccSJean Perier /// allocated while using the temporary.
1018*3ed899ccSJean Perier static std::pair<mlir::Value,
1019*3ed899ccSJean Perier                  std::function<void(mlir::PatternRewriter &rewriter)>>
1020*3ed899ccSJean Perier allocateArrayTemp(mlir::Location loc, mlir::PatternRewriter &rewriter,
1021*3ed899ccSJean Perier                   ArrayLoadOp load, llvm::ArrayRef<mlir::Value> extents,
1022*3ed899ccSJean Perier                   mlir::Value shape) {
1023*3ed899ccSJean Perier   mlir::Type baseType = load.getMemref().getType();
1024*3ed899ccSJean Perier   llvm::SmallVector<mlir::Value> nonconstantExtents =
1025*3ed899ccSJean Perier       findNonconstantExtents(baseType, extents);
1026*3ed899ccSJean Perier   llvm::SmallVector<mlir::Value> typeParams =
1027*3ed899ccSJean Perier       genArrayLoadTypeParameters(loc, rewriter, load);
1028*3ed899ccSJean Perier   mlir::Value allocmem = rewriter.create<AllocMemOp>(
1029*3ed899ccSJean Perier       loc, dyn_cast_ptrOrBoxEleTy(baseType), typeParams, nonconstantExtents);
1030*3ed899ccSJean Perier   mlir::Type eleType =
1031*3ed899ccSJean Perier       fir::unwrapSequenceType(fir::unwrapPassByRefType(baseType));
1032*3ed899ccSJean Perier   if (fir::isRecordWithAllocatableMember(eleType)) {
1033*3ed899ccSJean Perier     // The allocatable component descriptors need to be set to a clean
1034*3ed899ccSJean Perier     // deallocated status before anything is done with them.
1035*3ed899ccSJean Perier     mlir::Value box = rewriter.create<fir::EmboxOp>(
1036*3ed899ccSJean Perier         loc, fir::BoxType::get(baseType), allocmem, shape,
1037*3ed899ccSJean Perier         /*slice=*/mlir::Value{}, typeParams);
1038*3ed899ccSJean Perier     auto module = load->getParentOfType<mlir::ModuleOp>();
1039*3ed899ccSJean Perier     FirOpBuilder builder(rewriter, getKindMapping(module));
1040*3ed899ccSJean Perier     runtime::genDerivedTypeInitialize(builder, loc, box);
1041*3ed899ccSJean Perier     // Any allocatable component that may have been allocated must be
1042*3ed899ccSJean Perier     // deallocated during the clean-up.
1043*3ed899ccSJean Perier     auto cleanup = [=](mlir::PatternRewriter &r) {
1044*3ed899ccSJean Perier       FirOpBuilder builder(r, getKindMapping(module));
1045*3ed899ccSJean Perier       runtime::genDerivedTypeDestroy(builder, loc, box);
1046*3ed899ccSJean Perier       r.create<FreeMemOp>(loc, allocmem);
1047*3ed899ccSJean Perier     };
1048*3ed899ccSJean Perier     return {allocmem, cleanup};
1049*3ed899ccSJean Perier   }
1050*3ed899ccSJean Perier   auto cleanup = [=](mlir::PatternRewriter &r) {
1051*3ed899ccSJean Perier     r.create<FreeMemOp>(loc, allocmem);
1052*3ed899ccSJean Perier   };
1053*3ed899ccSJean Perier   return {allocmem, cleanup};
1054*3ed899ccSJean Perier }
1055*3ed899ccSJean Perier 
105647f75930SValentin Clement namespace {
105747f75930SValentin Clement /// Conversion of fir.array_update and fir.array_modify Ops.
105847f75930SValentin Clement /// If there is a conflict for the update, then we need to perform a
105947f75930SValentin Clement /// copy-in/copy-out to preserve the original values of the array. If there is
106047f75930SValentin Clement /// no conflict, then it is save to eschew making any copies.
106147f75930SValentin Clement template <typename ArrayOp>
106247f75930SValentin Clement class ArrayUpdateConversionBase : public mlir::OpRewritePattern<ArrayOp> {
106347f75930SValentin Clement public:
1064beeb86bdSValentin Clement   // TODO: Implement copy/swap semantics?
106547f75930SValentin Clement   explicit ArrayUpdateConversionBase(mlir::MLIRContext *ctx,
106647f75930SValentin Clement                                      const ArrayCopyAnalysis &a,
106747f75930SValentin Clement                                      const OperationUseMapT &m)
106847f75930SValentin Clement       : mlir::OpRewritePattern<ArrayOp>{ctx}, analysis{a}, useMap{m} {}
106947f75930SValentin Clement 
1070beeb86bdSValentin Clement   /// The array_access, \p access, is to be to a cloned copy due to a potential
1071beeb86bdSValentin Clement   /// conflict. Uses copy-in/copy-out semantics and not copy/swap.
1072beeb86bdSValentin Clement   mlir::Value referenceToClone(mlir::Location loc,
1073beeb86bdSValentin Clement                                mlir::PatternRewriter &rewriter,
1074beeb86bdSValentin Clement                                ArrayOp access) const {
1075beeb86bdSValentin Clement     LLVM_DEBUG(llvm::dbgs()
1076beeb86bdSValentin Clement                << "generating copy-in/copy-out loops for " << access << '\n');
1077beeb86bdSValentin Clement     auto *op = access.getOperation();
1078beeb86bdSValentin Clement     auto *loadOp = useMap.lookup(op);
1079beeb86bdSValentin Clement     auto load = mlir::cast<ArrayLoadOp>(loadOp);
1080beeb86bdSValentin Clement     auto eleTy = access.getType();
1081beeb86bdSValentin Clement     rewriter.setInsertionPoint(loadOp);
1082beeb86bdSValentin Clement     // Copy in.
108347f75930SValentin Clement     llvm::SmallVector<mlir::Value> extents;
1084beeb86bdSValentin Clement     bool copyUsingSlice = false;
1085beeb86bdSValentin Clement     auto shapeOp = getOrReadExtentsAndShapeOp(loc, rewriter, load, extents,
1086beeb86bdSValentin Clement                                               copyUsingSlice);
1087*3ed899ccSJean Perier     auto [allocmem, genTempCleanUp] =
1088*3ed899ccSJean Perier         allocateArrayTemp(loc, rewriter, load, extents, shapeOp);
1089beeb86bdSValentin Clement     genArrayCopy</*copyIn=*/true>(load.getLoc(), rewriter, allocmem,
1090beeb86bdSValentin Clement                                   load.getMemref(), shapeOp, load.getSlice(),
1091beeb86bdSValentin Clement                                   load);
1092beeb86bdSValentin Clement     // Generate the reference for the access.
1093beeb86bdSValentin Clement     rewriter.setInsertionPoint(op);
1094beeb86bdSValentin Clement     auto coor =
1095beeb86bdSValentin Clement         genCoorOp(rewriter, loc, getEleTy(load.getType()), eleTy, allocmem,
1096beeb86bdSValentin Clement                   shapeOp, copyUsingSlice ? mlir::Value{} : load.getSlice(),
1097beeb86bdSValentin Clement                   access.getIndices(), load.getTypeparams(),
1098beeb86bdSValentin Clement                   access->hasAttr(factory::attrFortranArrayOffsets()));
1099beeb86bdSValentin Clement     // Copy out.
1100beeb86bdSValentin Clement     auto *storeOp = useMap.lookup(loadOp);
1101beeb86bdSValentin Clement     auto store = mlir::cast<ArrayMergeStoreOp>(storeOp);
1102beeb86bdSValentin Clement     rewriter.setInsertionPoint(storeOp);
1103beeb86bdSValentin Clement     // Copy out.
1104beeb86bdSValentin Clement     genArrayCopy</*copyIn=*/false>(store.getLoc(), rewriter, store.getMemref(),
1105beeb86bdSValentin Clement                                    allocmem, shapeOp, store.getSlice(), load);
1106*3ed899ccSJean Perier     genTempCleanUp(rewriter);
1107beeb86bdSValentin Clement     return coor;
110847f75930SValentin Clement   }
110947f75930SValentin Clement 
111047f75930SValentin Clement   /// Copy the RHS element into the LHS and insert copy-in/copy-out between a
111147f75930SValentin Clement   /// temp and the LHS if the analysis found potential overlaps between the RHS
1112beeb86bdSValentin Clement   /// and LHS arrays. The element copy generator must be provided in \p
111347f75930SValentin Clement   /// assignElement. \p update must be the ArrayUpdateOp or the ArrayModifyOp.
111447f75930SValentin Clement   /// Returns the address of the LHS element inside the loop and the LHS
111547f75930SValentin Clement   /// ArrayLoad result.
111647f75930SValentin Clement   std::pair<mlir::Value, mlir::Value>
111747f75930SValentin Clement   materializeAssignment(mlir::Location loc, mlir::PatternRewriter &rewriter,
111847f75930SValentin Clement                         ArrayOp update,
1119beeb86bdSValentin Clement                         const std::function<void(mlir::Value)> &assignElement,
112047f75930SValentin Clement                         mlir::Type lhsEltRefType) const {
112147f75930SValentin Clement     auto *op = update.getOperation();
1122beeb86bdSValentin Clement     auto *loadOp = useMap.lookup(op);
112347f75930SValentin Clement     auto load = mlir::cast<ArrayLoadOp>(loadOp);
112447f75930SValentin Clement     LLVM_DEBUG(llvm::outs() << "does " << load << " have a conflict?\n");
112547f75930SValentin Clement     if (analysis.hasPotentialConflict(loadOp)) {
112647f75930SValentin Clement       // If there is a conflict between the arrays, then we copy the lhs array
112747f75930SValentin Clement       // to a temporary, update the temporary, and copy the temporary back to
112847f75930SValentin Clement       // the lhs array. This yields Fortran's copy-in copy-out array semantics.
112947f75930SValentin Clement       LLVM_DEBUG(llvm::outs() << "Yes, conflict was found\n");
113047f75930SValentin Clement       rewriter.setInsertionPoint(loadOp);
113147f75930SValentin Clement       // Copy in.
113247f75930SValentin Clement       llvm::SmallVector<mlir::Value> extents;
1133beeb86bdSValentin Clement       bool copyUsingSlice = false;
1134beeb86bdSValentin Clement       auto shapeOp = getOrReadExtentsAndShapeOp(loc, rewriter, load, extents,
1135beeb86bdSValentin Clement                                                 copyUsingSlice);
1136*3ed899ccSJean Perier       auto [allocmem, genTempCleanUp] =
1137*3ed899ccSJean Perier           allocateArrayTemp(loc, rewriter, load, extents, shapeOp);
1138*3ed899ccSJean Perier 
1139beeb86bdSValentin Clement       genArrayCopy</*copyIn=*/true>(load.getLoc(), rewriter, allocmem,
1140beeb86bdSValentin Clement                                     load.getMemref(), shapeOp, load.getSlice(),
1141beeb86bdSValentin Clement                                     load);
114247f75930SValentin Clement       rewriter.setInsertionPoint(op);
1143beeb86bdSValentin Clement       auto coor = genCoorOp(
114447f75930SValentin Clement           rewriter, loc, getEleTy(load.getType()), lhsEltRefType, allocmem,
1145beeb86bdSValentin Clement           shapeOp, copyUsingSlice ? mlir::Value{} : load.getSlice(),
1146beeb86bdSValentin Clement           update.getIndices(), load.getTypeparams(),
1147beeb86bdSValentin Clement           update->hasAttr(factory::attrFortranArrayOffsets()));
114847f75930SValentin Clement       assignElement(coor);
1149beeb86bdSValentin Clement       auto *storeOp = useMap.lookup(loadOp);
115047f75930SValentin Clement       auto store = mlir::cast<ArrayMergeStoreOp>(storeOp);
115147f75930SValentin Clement       rewriter.setInsertionPoint(storeOp);
115247f75930SValentin Clement       // Copy out.
1153beeb86bdSValentin Clement       genArrayCopy</*copyIn=*/false>(store.getLoc(), rewriter,
1154beeb86bdSValentin Clement                                      store.getMemref(), allocmem, shapeOp,
1155beeb86bdSValentin Clement                                      store.getSlice(), load);
1156*3ed899ccSJean Perier       genTempCleanUp(rewriter);
115747f75930SValentin Clement       return {coor, load.getResult()};
115847f75930SValentin Clement     }
115947f75930SValentin Clement     // Otherwise, when there is no conflict (a possible loop-carried
116047f75930SValentin Clement     // dependence), the lhs array can be updated in place.
116147f75930SValentin Clement     LLVM_DEBUG(llvm::outs() << "No, conflict wasn't found\n");
116247f75930SValentin Clement     rewriter.setInsertionPoint(op);
116347f75930SValentin Clement     auto coorTy = getEleTy(load.getType());
1164beeb86bdSValentin Clement     auto coor = genCoorOp(rewriter, loc, coorTy, lhsEltRefType,
1165beeb86bdSValentin Clement                           load.getMemref(), load.getShape(), load.getSlice(),
1166beeb86bdSValentin Clement                           update.getIndices(), load.getTypeparams(),
1167beeb86bdSValentin Clement                           update->hasAttr(factory::attrFortranArrayOffsets()));
116847f75930SValentin Clement     assignElement(coor);
116947f75930SValentin Clement     return {coor, load.getResult()};
117047f75930SValentin Clement   }
117147f75930SValentin Clement 
1172beeb86bdSValentin Clement protected:
117347f75930SValentin Clement   const ArrayCopyAnalysis &analysis;
117447f75930SValentin Clement   const OperationUseMapT &useMap;
117547f75930SValentin Clement };
117647f75930SValentin Clement 
117747f75930SValentin Clement class ArrayUpdateConversion : public ArrayUpdateConversionBase<ArrayUpdateOp> {
117847f75930SValentin Clement public:
117947f75930SValentin Clement   explicit ArrayUpdateConversion(mlir::MLIRContext *ctx,
118047f75930SValentin Clement                                  const ArrayCopyAnalysis &a,
118147f75930SValentin Clement                                  const OperationUseMapT &m)
118247f75930SValentin Clement       : ArrayUpdateConversionBase{ctx, a, m} {}
118347f75930SValentin Clement 
118447f75930SValentin Clement   mlir::LogicalResult
118547f75930SValentin Clement   matchAndRewrite(ArrayUpdateOp update,
118647f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
118747f75930SValentin Clement     auto loc = update.getLoc();
118847f75930SValentin Clement     auto assignElement = [&](mlir::Value coor) {
1189beeb86bdSValentin Clement       auto input = update.getMerge();
1190beeb86bdSValentin Clement       if (auto inEleTy = dyn_cast_ptrEleTy(input.getType())) {
1191beeb86bdSValentin Clement         emitFatalError(loc, "array_update on references not supported");
1192beeb86bdSValentin Clement       } else {
1193beeb86bdSValentin Clement         rewriter.create<fir::StoreOp>(loc, input, coor);
1194beeb86bdSValentin Clement       }
119547f75930SValentin Clement     };
1196149ad3d5SShraiysh Vaishay     auto lhsEltRefType = toRefType(update.getMerge().getType());
119747f75930SValentin Clement     auto [_, lhsLoadResult] = materializeAssignment(
119847f75930SValentin Clement         loc, rewriter, update, assignElement, lhsEltRefType);
119947f75930SValentin Clement     update.replaceAllUsesWith(lhsLoadResult);
120047f75930SValentin Clement     rewriter.replaceOp(update, lhsLoadResult);
120147f75930SValentin Clement     return mlir::success();
120247f75930SValentin Clement   }
120347f75930SValentin Clement };
120447f75930SValentin Clement 
120547f75930SValentin Clement class ArrayModifyConversion : public ArrayUpdateConversionBase<ArrayModifyOp> {
120647f75930SValentin Clement public:
120747f75930SValentin Clement   explicit ArrayModifyConversion(mlir::MLIRContext *ctx,
120847f75930SValentin Clement                                  const ArrayCopyAnalysis &a,
120947f75930SValentin Clement                                  const OperationUseMapT &m)
121047f75930SValentin Clement       : ArrayUpdateConversionBase{ctx, a, m} {}
121147f75930SValentin Clement 
121247f75930SValentin Clement   mlir::LogicalResult
121347f75930SValentin Clement   matchAndRewrite(ArrayModifyOp modify,
121447f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
121547f75930SValentin Clement     auto loc = modify.getLoc();
121647f75930SValentin Clement     auto assignElement = [](mlir::Value) {
121747f75930SValentin Clement       // Assignment already materialized by lowering using lhs element address.
121847f75930SValentin Clement     };
121947f75930SValentin Clement     auto lhsEltRefType = modify.getResult(0).getType();
122047f75930SValentin Clement     auto [lhsEltCoor, lhsLoadResult] = materializeAssignment(
122147f75930SValentin Clement         loc, rewriter, modify, assignElement, lhsEltRefType);
122247f75930SValentin Clement     modify.replaceAllUsesWith(mlir::ValueRange{lhsEltCoor, lhsLoadResult});
122347f75930SValentin Clement     rewriter.replaceOp(modify, mlir::ValueRange{lhsEltCoor, lhsLoadResult});
122447f75930SValentin Clement     return mlir::success();
122547f75930SValentin Clement   }
122647f75930SValentin Clement };
122747f75930SValentin Clement 
122847f75930SValentin Clement class ArrayFetchConversion : public mlir::OpRewritePattern<ArrayFetchOp> {
122947f75930SValentin Clement public:
123047f75930SValentin Clement   explicit ArrayFetchConversion(mlir::MLIRContext *ctx,
123147f75930SValentin Clement                                 const OperationUseMapT &m)
123247f75930SValentin Clement       : OpRewritePattern{ctx}, useMap{m} {}
123347f75930SValentin Clement 
123447f75930SValentin Clement   mlir::LogicalResult
123547f75930SValentin Clement   matchAndRewrite(ArrayFetchOp fetch,
123647f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
123747f75930SValentin Clement     auto *op = fetch.getOperation();
123847f75930SValentin Clement     rewriter.setInsertionPoint(op);
123947f75930SValentin Clement     auto load = mlir::cast<ArrayLoadOp>(useMap.lookup(op));
124047f75930SValentin Clement     auto loc = fetch.getLoc();
1241beeb86bdSValentin Clement     auto coor =
124247f75930SValentin Clement         genCoorOp(rewriter, loc, getEleTy(load.getType()),
1243149ad3d5SShraiysh Vaishay                   toRefType(fetch.getType()), load.getMemref(), load.getShape(),
1244149ad3d5SShraiysh Vaishay                   load.getSlice(), fetch.getIndices(), load.getTypeparams(),
1245beeb86bdSValentin Clement                   fetch->hasAttr(factory::attrFortranArrayOffsets()));
1246beeb86bdSValentin Clement     if (isa_ref_type(fetch.getType()))
1247beeb86bdSValentin Clement       rewriter.replaceOp(fetch, coor);
1248beeb86bdSValentin Clement     else
124947f75930SValentin Clement       rewriter.replaceOpWithNewOp<fir::LoadOp>(fetch, coor);
125047f75930SValentin Clement     return mlir::success();
125147f75930SValentin Clement   }
125247f75930SValentin Clement 
125347f75930SValentin Clement private:
125447f75930SValentin Clement   const OperationUseMapT &useMap;
125547f75930SValentin Clement };
125647f75930SValentin Clement 
1257beeb86bdSValentin Clement /// As array_access op is like an array_fetch op, except that it does not imply
1258beeb86bdSValentin Clement /// a load op. (It operates in the reference domain.)
1259beeb86bdSValentin Clement class ArrayAccessConversion : public ArrayUpdateConversionBase<ArrayAccessOp> {
1260beeb86bdSValentin Clement public:
1261beeb86bdSValentin Clement   explicit ArrayAccessConversion(mlir::MLIRContext *ctx,
1262beeb86bdSValentin Clement                                  const ArrayCopyAnalysis &a,
1263beeb86bdSValentin Clement                                  const OperationUseMapT &m)
1264beeb86bdSValentin Clement       : ArrayUpdateConversionBase{ctx, a, m} {}
1265beeb86bdSValentin Clement 
1266beeb86bdSValentin Clement   mlir::LogicalResult
1267beeb86bdSValentin Clement   matchAndRewrite(ArrayAccessOp access,
1268beeb86bdSValentin Clement                   mlir::PatternRewriter &rewriter) const override {
1269beeb86bdSValentin Clement     auto *op = access.getOperation();
1270beeb86bdSValentin Clement     auto loc = access.getLoc();
1271beeb86bdSValentin Clement     if (analysis.inAmendAccessSet(op)) {
1272beeb86bdSValentin Clement       // This array_access is associated with an array_amend and there is a
1273beeb86bdSValentin Clement       // conflict. Make a copy to store into.
1274beeb86bdSValentin Clement       auto result = referenceToClone(loc, rewriter, access);
1275beeb86bdSValentin Clement       access.replaceAllUsesWith(result);
1276beeb86bdSValentin Clement       rewriter.replaceOp(access, result);
1277beeb86bdSValentin Clement       return mlir::success();
1278beeb86bdSValentin Clement     }
1279beeb86bdSValentin Clement     rewriter.setInsertionPoint(op);
1280beeb86bdSValentin Clement     auto load = mlir::cast<ArrayLoadOp>(useMap.lookup(op));
1281beeb86bdSValentin Clement     auto coor = genCoorOp(rewriter, loc, getEleTy(load.getType()),
1282beeb86bdSValentin Clement                           toRefType(access.getType()), load.getMemref(),
1283beeb86bdSValentin Clement                           load.getShape(), load.getSlice(), access.getIndices(),
1284beeb86bdSValentin Clement                           load.getTypeparams(),
1285beeb86bdSValentin Clement                           access->hasAttr(factory::attrFortranArrayOffsets()));
1286beeb86bdSValentin Clement     rewriter.replaceOp(access, coor);
1287beeb86bdSValentin Clement     return mlir::success();
1288beeb86bdSValentin Clement   }
1289beeb86bdSValentin Clement };
1290beeb86bdSValentin Clement 
1291beeb86bdSValentin Clement /// An array_amend op is a marker to record which array access is being used to
1292beeb86bdSValentin Clement /// update an array value. After this pass runs, an array_amend has no
1293beeb86bdSValentin Clement /// semantics. We rewrite these to undefined values here to remove them while
1294beeb86bdSValentin Clement /// preserving SSA form.
1295beeb86bdSValentin Clement class ArrayAmendConversion : public mlir::OpRewritePattern<ArrayAmendOp> {
1296beeb86bdSValentin Clement public:
1297beeb86bdSValentin Clement   explicit ArrayAmendConversion(mlir::MLIRContext *ctx)
1298beeb86bdSValentin Clement       : OpRewritePattern{ctx} {}
1299beeb86bdSValentin Clement 
1300beeb86bdSValentin Clement   mlir::LogicalResult
1301beeb86bdSValentin Clement   matchAndRewrite(ArrayAmendOp amend,
1302beeb86bdSValentin Clement                   mlir::PatternRewriter &rewriter) const override {
1303beeb86bdSValentin Clement     auto *op = amend.getOperation();
1304beeb86bdSValentin Clement     rewriter.setInsertionPoint(op);
1305beeb86bdSValentin Clement     auto loc = amend.getLoc();
1306beeb86bdSValentin Clement     auto undef = rewriter.create<UndefOp>(loc, amend.getType());
1307beeb86bdSValentin Clement     rewriter.replaceOp(amend, undef.getResult());
1308beeb86bdSValentin Clement     return mlir::success();
1309beeb86bdSValentin Clement   }
1310beeb86bdSValentin Clement };
1311beeb86bdSValentin Clement 
131247f75930SValentin Clement class ArrayValueCopyConverter
131347f75930SValentin Clement     : public ArrayValueCopyBase<ArrayValueCopyConverter> {
131447f75930SValentin Clement public:
1315196c4279SRiver Riddle   void runOnOperation() override {
1316196c4279SRiver Riddle     auto func = getOperation();
131747f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "\n\narray-value-copy pass on function '"
131847f75930SValentin Clement                             << func.getName() << "'\n");
131947f75930SValentin Clement     auto *context = &getContext();
132047f75930SValentin Clement 
132147f75930SValentin Clement     // Perform the conflict analysis.
1322beeb86bdSValentin Clement     const auto &analysis = getAnalysis<ArrayCopyAnalysis>();
132347f75930SValentin Clement     const auto &useMap = analysis.getUseMap();
132447f75930SValentin Clement 
13259f85c198SRiver Riddle     mlir::RewritePatternSet patterns1(context);
132647f75930SValentin Clement     patterns1.insert<ArrayFetchConversion>(context, useMap);
132747f75930SValentin Clement     patterns1.insert<ArrayUpdateConversion>(context, analysis, useMap);
132847f75930SValentin Clement     patterns1.insert<ArrayModifyConversion>(context, analysis, useMap);
1329beeb86bdSValentin Clement     patterns1.insert<ArrayAccessConversion>(context, analysis, useMap);
1330beeb86bdSValentin Clement     patterns1.insert<ArrayAmendConversion>(context);
133147f75930SValentin Clement     mlir::ConversionTarget target(*context);
1332beeb86bdSValentin Clement     target.addLegalDialect<FIROpsDialect, mlir::scf::SCFDialect,
1333beeb86bdSValentin Clement                            mlir::arith::ArithmeticDialect,
1334beeb86bdSValentin Clement                            mlir::func::FuncDialect>();
1335beeb86bdSValentin Clement     target.addIllegalOp<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp,
1336beeb86bdSValentin Clement                         ArrayUpdateOp, ArrayModifyOp>();
133747f75930SValentin Clement     // Rewrite the array fetch and array update ops.
133847f75930SValentin Clement     if (mlir::failed(
133947f75930SValentin Clement             mlir::applyPartialConversion(func, target, std::move(patterns1)))) {
134047f75930SValentin Clement       mlir::emitError(mlir::UnknownLoc::get(context),
134147f75930SValentin Clement                       "failure in array-value-copy pass, phase 1");
134247f75930SValentin Clement       signalPassFailure();
134347f75930SValentin Clement     }
134447f75930SValentin Clement 
13459f85c198SRiver Riddle     mlir::RewritePatternSet patterns2(context);
134647f75930SValentin Clement     patterns2.insert<ArrayLoadConversion>(context);
134747f75930SValentin Clement     patterns2.insert<ArrayMergeStoreConversion>(context);
134847f75930SValentin Clement     target.addIllegalOp<ArrayLoadOp, ArrayMergeStoreOp>();
134947f75930SValentin Clement     if (mlir::failed(
135047f75930SValentin Clement             mlir::applyPartialConversion(func, target, std::move(patterns2)))) {
135147f75930SValentin Clement       mlir::emitError(mlir::UnknownLoc::get(context),
135247f75930SValentin Clement                       "failure in array-value-copy pass, phase 2");
135347f75930SValentin Clement       signalPassFailure();
135447f75930SValentin Clement     }
135547f75930SValentin Clement   }
135647f75930SValentin Clement };
135747f75930SValentin Clement } // namespace
135847f75930SValentin Clement 
135947f75930SValentin Clement std::unique_ptr<mlir::Pass> fir::createArrayValueCopyPass() {
136047f75930SValentin Clement   return std::make_unique<ArrayValueCopyConverter>();
136147f75930SValentin Clement }
1362