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"
10*beeb86bdSValentin Clement #include "flang/Lower/Todo.h"
11*beeb86bdSValentin 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"
1547f75930SValentin Clement #include "flang/Optimizer/Dialect/FIRDialect.h"
16*beeb86bdSValentin Clement #include "flang/Optimizer/Dialect/FIROpsSupport.h"
1747f75930SValentin Clement #include "flang/Optimizer/Support/FIRContext.h"
1847f75930SValentin Clement #include "flang/Optimizer/Transforms/Passes.h"
19ace01605SRiver Riddle #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
2047f75930SValentin Clement #include "mlir/Dialect/SCF/SCF.h"
2147f75930SValentin Clement #include "mlir/Transforms/DialectConversion.h"
2247f75930SValentin Clement #include "llvm/Support/Debug.h"
2347f75930SValentin Clement 
2447f75930SValentin Clement #define DEBUG_TYPE "flang-array-value-copy"
2547f75930SValentin Clement 
2647f75930SValentin Clement using namespace fir;
27092601d4SAndrzej Warzynski using namespace mlir;
2847f75930SValentin Clement 
2947f75930SValentin Clement using OperationUseMapT = llvm::DenseMap<mlir::Operation *, mlir::Operation *>;
3047f75930SValentin Clement 
3147f75930SValentin Clement namespace {
3247f75930SValentin Clement 
3347f75930SValentin Clement /// Array copy analysis.
3447f75930SValentin Clement /// Perform an interference analysis between array values.
3547f75930SValentin Clement ///
3647f75930SValentin Clement /// Lowering will generate a sequence of the following form.
3747f75930SValentin Clement /// ```mlir
3847f75930SValentin Clement ///   %a_1 = fir.array_load %array_1(%shape) : ...
3947f75930SValentin Clement ///   ...
4047f75930SValentin Clement ///   %a_j = fir.array_load %array_j(%shape) : ...
4147f75930SValentin Clement ///   ...
4247f75930SValentin Clement ///   %a_n = fir.array_load %array_n(%shape) : ...
4347f75930SValentin Clement ///     ...
4447f75930SValentin Clement ///     %v_i = fir.array_fetch %a_i, ...
4547f75930SValentin Clement ///     %a_j1 = fir.array_update %a_j, ...
4647f75930SValentin Clement ///     ...
4747f75930SValentin Clement ///   fir.array_merge_store %a_j, %a_jn to %array_j : ...
4847f75930SValentin Clement /// ```
4947f75930SValentin Clement ///
5047f75930SValentin Clement /// The analysis is to determine if there are any conflicts. A conflict is when
5147f75930SValentin Clement /// one the following cases occurs.
5247f75930SValentin Clement ///
5347f75930SValentin Clement /// 1. There is an `array_update` to an array value, a_j, such that a_j was
5447f75930SValentin Clement /// loaded from the same array memory reference (array_j) but with a different
5547f75930SValentin Clement /// shape as the other array values a_i, where i != j. [Possible overlapping
5647f75930SValentin Clement /// arrays.]
5747f75930SValentin Clement ///
5847f75930SValentin Clement /// 2. There is either an array_fetch or array_update of a_j with a different
5947f75930SValentin Clement /// set of index values. [Possible loop-carried dependence.]
6047f75930SValentin Clement ///
6147f75930SValentin Clement /// If none of the array values overlap in storage and the accesses are not
6247f75930SValentin Clement /// loop-carried, then the arrays are conflict-free and no copies are required.
6347f75930SValentin Clement class ArrayCopyAnalysis {
6447f75930SValentin Clement public:
6547f75930SValentin Clement   using ConflictSetT = llvm::SmallPtrSet<mlir::Operation *, 16>;
6647f75930SValentin Clement   using UseSetT = llvm::SmallPtrSet<mlir::OpOperand *, 8>;
67*beeb86bdSValentin Clement   using LoadMapSetsT = llvm::DenseMap<mlir::Operation *, UseSetT>;
68*beeb86bdSValentin Clement   using AmendAccessSetT = llvm::SmallPtrSet<mlir::Operation *, 4>;
6947f75930SValentin Clement 
7047f75930SValentin Clement   ArrayCopyAnalysis(mlir::Operation *op) : operation{op} { construct(op); }
7147f75930SValentin Clement 
7247f75930SValentin Clement   mlir::Operation *getOperation() const { return operation; }
7347f75930SValentin Clement 
7447f75930SValentin Clement   /// Return true iff the `array_merge_store` has potential conflicts.
7547f75930SValentin Clement   bool hasPotentialConflict(mlir::Operation *op) const {
7647f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs()
7747f75930SValentin Clement                << "looking for a conflict on " << *op
7847f75930SValentin Clement                << " and the set has a total of " << conflicts.size() << '\n');
7947f75930SValentin Clement     return conflicts.contains(op);
8047f75930SValentin Clement   }
8147f75930SValentin Clement 
82*beeb86bdSValentin Clement   /// Return the use map.
83*beeb86bdSValentin Clement   /// The use map maps array access, amend, fetch and update operations back to
84*beeb86bdSValentin Clement   /// the array load that is the original source of the array value.
85*beeb86bdSValentin Clement   /// It maps an array_load to an array_merge_store, if and only if the loaded
86*beeb86bdSValentin Clement   /// array value has pending modifications to be merged.
8747f75930SValentin Clement   const OperationUseMapT &getUseMap() const { return useMap; }
8847f75930SValentin Clement 
89*beeb86bdSValentin Clement   /// Return the set of array_access ops directly associated with array_amend
90*beeb86bdSValentin Clement   /// ops.
91*beeb86bdSValentin Clement   bool inAmendAccessSet(mlir::Operation *op) const {
92*beeb86bdSValentin Clement     return amendAccesses.count(op);
93*beeb86bdSValentin Clement   }
94*beeb86bdSValentin Clement 
95*beeb86bdSValentin Clement   /// For ArrayLoad `load`, return the transitive set of all OpOperands.
96*beeb86bdSValentin Clement   UseSetT getLoadUseSet(mlir::Operation *load) const {
97*beeb86bdSValentin Clement     assert(loadMapSets.count(load) && "analysis missed an array load?");
98*beeb86bdSValentin Clement     return loadMapSets.lookup(load);
99*beeb86bdSValentin Clement   }
100*beeb86bdSValentin Clement 
101*beeb86bdSValentin Clement   void arrayMentions(llvm::SmallVectorImpl<mlir::Operation *> &mentions,
102*beeb86bdSValentin Clement                      ArrayLoadOp load);
10347f75930SValentin Clement 
10447f75930SValentin Clement private:
10547f75930SValentin Clement   void construct(mlir::Operation *topLevelOp);
10647f75930SValentin Clement 
10747f75930SValentin Clement   mlir::Operation *operation; // operation that analysis ran upon
10847f75930SValentin Clement   ConflictSetT conflicts;     // set of conflicts (loads and merge stores)
10947f75930SValentin Clement   OperationUseMapT useMap;
11047f75930SValentin Clement   LoadMapSetsT loadMapSets;
111*beeb86bdSValentin Clement   // Set of array_access ops associated with array_amend ops.
112*beeb86bdSValentin Clement   AmendAccessSetT amendAccesses;
11347f75930SValentin Clement };
11447f75930SValentin Clement } // namespace
11547f75930SValentin Clement 
11647f75930SValentin Clement namespace {
11747f75930SValentin Clement /// Helper class to collect all array operations that produced an array value.
11847f75930SValentin Clement class ReachCollector {
119*beeb86bdSValentin Clement public:
12047f75930SValentin Clement   ReachCollector(llvm::SmallVectorImpl<mlir::Operation *> &reach,
12147f75930SValentin Clement                  mlir::Region *loopRegion)
12247f75930SValentin Clement       : reach{reach}, loopRegion{loopRegion} {}
12347f75930SValentin Clement 
124*beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::Operation *op, mlir::ValueRange range) {
12547f75930SValentin Clement     if (range.empty()) {
126*beeb86bdSValentin Clement       collectArrayMentionFrom(op, mlir::Value{});
12747f75930SValentin Clement       return;
12847f75930SValentin Clement     }
12947f75930SValentin Clement     for (mlir::Value v : range)
130*beeb86bdSValentin Clement       collectArrayMentionFrom(v);
13147f75930SValentin Clement   }
13247f75930SValentin Clement 
133*beeb86bdSValentin Clement   // Collect all the array_access ops in `block`. This recursively looks into
134*beeb86bdSValentin Clement   // blocks in ops with regions.
135*beeb86bdSValentin Clement   // FIXME: This is temporarily relying on the array_amend appearing in a
136*beeb86bdSValentin Clement   // do_loop Region.  This phase ordering assumption can be eliminated by using
137*beeb86bdSValentin Clement   // dominance information to find the array_access ops or by scanning the
138*beeb86bdSValentin Clement   // transitive closure of the amending array_access's users and the defs that
139*beeb86bdSValentin Clement   // reach them.
140*beeb86bdSValentin Clement   void collectAccesses(llvm::SmallVector<ArrayAccessOp> &result,
141*beeb86bdSValentin Clement                        mlir::Block *block) {
142*beeb86bdSValentin Clement     for (auto &op : *block) {
143*beeb86bdSValentin Clement       if (auto access = mlir::dyn_cast<ArrayAccessOp>(op)) {
144*beeb86bdSValentin Clement         LLVM_DEBUG(llvm::dbgs() << "adding access: " << access << '\n');
145*beeb86bdSValentin Clement         result.push_back(access);
146*beeb86bdSValentin Clement         continue;
147*beeb86bdSValentin Clement       }
148*beeb86bdSValentin Clement       for (auto &region : op.getRegions())
149*beeb86bdSValentin Clement         for (auto &bb : region.getBlocks())
150*beeb86bdSValentin Clement           collectAccesses(result, &bb);
151*beeb86bdSValentin Clement     }
152*beeb86bdSValentin Clement   }
153*beeb86bdSValentin Clement 
154*beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::Operation *op, mlir::Value val) {
15547f75930SValentin Clement     // `val` is defined by an Op, process the defining Op.
15647f75930SValentin Clement     // If `val` is defined by a region containing Op, we want to drill down
15747f75930SValentin Clement     // and through that Op's region(s).
15847f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "popset: " << *op << '\n');
15947f75930SValentin Clement     auto popFn = [&](auto rop) {
16047f75930SValentin Clement       assert(val && "op must have a result value");
16147f75930SValentin Clement       auto resNum = val.cast<mlir::OpResult>().getResultNumber();
16247f75930SValentin Clement       llvm::SmallVector<mlir::Value> results;
16347f75930SValentin Clement       rop.resultToSourceOps(results, resNum);
16447f75930SValentin Clement       for (auto u : results)
165*beeb86bdSValentin Clement         collectArrayMentionFrom(u);
16647f75930SValentin Clement     };
167*beeb86bdSValentin Clement     if (auto rop = mlir::dyn_cast<DoLoopOp>(op)) {
168*beeb86bdSValentin Clement       popFn(rop);
169*beeb86bdSValentin Clement       return;
170*beeb86bdSValentin Clement     }
171*beeb86bdSValentin Clement     if (auto rop = mlir::dyn_cast<IterWhileOp>(op)) {
17247f75930SValentin Clement       popFn(rop);
17347f75930SValentin Clement       return;
17447f75930SValentin Clement     }
17547f75930SValentin Clement     if (auto rop = mlir::dyn_cast<fir::IfOp>(op)) {
17647f75930SValentin Clement       popFn(rop);
17747f75930SValentin Clement       return;
17847f75930SValentin Clement     }
179*beeb86bdSValentin Clement     if (auto box = mlir::dyn_cast<EmboxOp>(op)) {
180*beeb86bdSValentin Clement       for (auto *user : box.getMemref().getUsers())
181*beeb86bdSValentin Clement         if (user != op)
182*beeb86bdSValentin Clement           collectArrayMentionFrom(user, user->getResults());
183*beeb86bdSValentin Clement       return;
184*beeb86bdSValentin Clement     }
18547f75930SValentin Clement     if (auto mergeStore = mlir::dyn_cast<ArrayMergeStoreOp>(op)) {
18647f75930SValentin Clement       if (opIsInsideLoops(mergeStore))
187*beeb86bdSValentin Clement         collectArrayMentionFrom(mergeStore.getSequence());
18847f75930SValentin Clement       return;
18947f75930SValentin Clement     }
19047f75930SValentin Clement 
19147f75930SValentin Clement     if (mlir::isa<AllocaOp, AllocMemOp>(op)) {
19247f75930SValentin Clement       // Look for any stores inside the loops, and collect an array operation
19347f75930SValentin Clement       // that produced the value being stored to it.
194*beeb86bdSValentin Clement       for (auto *user : op->getUsers())
19547f75930SValentin Clement         if (auto store = mlir::dyn_cast<fir::StoreOp>(user))
19647f75930SValentin Clement           if (opIsInsideLoops(store))
197*beeb86bdSValentin Clement             collectArrayMentionFrom(store.getValue());
19847f75930SValentin Clement       return;
19947f75930SValentin Clement     }
20047f75930SValentin Clement 
201*beeb86bdSValentin Clement     // Scan the uses of amend's memref
202*beeb86bdSValentin Clement     if (auto amend = mlir::dyn_cast<ArrayAmendOp>(op)) {
203*beeb86bdSValentin Clement       reach.push_back(op);
204*beeb86bdSValentin Clement       llvm::SmallVector<ArrayAccessOp> accesses;
205*beeb86bdSValentin Clement       collectAccesses(accesses, op->getBlock());
206*beeb86bdSValentin Clement       for (auto access : accesses)
207*beeb86bdSValentin Clement         collectArrayMentionFrom(access.getResult());
20847f75930SValentin Clement     }
209*beeb86bdSValentin Clement 
210*beeb86bdSValentin Clement     // Otherwise, Op does not contain a region so just chase its operands.
211*beeb86bdSValentin Clement     if (mlir::isa<ArrayAccessOp, ArrayLoadOp, ArrayUpdateOp, ArrayModifyOp,
212*beeb86bdSValentin Clement                   ArrayFetchOp>(op)) {
213*beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs() << "add " << *op << " to reachable set\n");
214*beeb86bdSValentin Clement       reach.push_back(op);
215*beeb86bdSValentin Clement     }
216*beeb86bdSValentin Clement 
217*beeb86bdSValentin Clement     // Include all array_access ops using an array_load.
218*beeb86bdSValentin Clement     if (auto arrLd = mlir::dyn_cast<ArrayLoadOp>(op))
219*beeb86bdSValentin Clement       for (auto *user : arrLd.getResult().getUsers())
220*beeb86bdSValentin Clement         if (mlir::isa<ArrayAccessOp>(user)) {
221*beeb86bdSValentin Clement           LLVM_DEBUG(llvm::dbgs() << "add " << *user << " to reachable set\n");
222*beeb86bdSValentin Clement           reach.push_back(user);
223*beeb86bdSValentin Clement         }
224*beeb86bdSValentin Clement 
225*beeb86bdSValentin Clement     // Array modify assignment is performed on the result. So the analysis must
226*beeb86bdSValentin Clement     // look at the what is done with the result.
22747f75930SValentin Clement     if (mlir::isa<ArrayModifyOp>(op))
228*beeb86bdSValentin Clement       for (auto *user : op->getResult(0).getUsers())
22947f75930SValentin Clement         followUsers(user);
23047f75930SValentin Clement 
231*beeb86bdSValentin Clement     if (mlir::isa<fir::CallOp>(op)) {
232*beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs() << "add " << *op << " to reachable set\n");
233*beeb86bdSValentin Clement       reach.push_back(op);
23447f75930SValentin Clement     }
23547f75930SValentin Clement 
236*beeb86bdSValentin Clement     for (auto u : op->getOperands())
237*beeb86bdSValentin Clement       collectArrayMentionFrom(u);
238*beeb86bdSValentin Clement   }
239*beeb86bdSValentin Clement 
240*beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::BlockArgument ba) {
24147f75930SValentin Clement     auto *parent = ba.getOwner()->getParentOp();
24247f75930SValentin Clement     // If inside an Op holding a region, the block argument corresponds to an
24347f75930SValentin Clement     // argument passed to the containing Op.
24447f75930SValentin Clement     auto popFn = [&](auto rop) {
245*beeb86bdSValentin Clement       collectArrayMentionFrom(rop.blockArgToSourceOp(ba.getArgNumber()));
24647f75930SValentin Clement     };
24747f75930SValentin Clement     if (auto rop = mlir::dyn_cast<DoLoopOp>(parent)) {
24847f75930SValentin Clement       popFn(rop);
24947f75930SValentin Clement       return;
25047f75930SValentin Clement     }
25147f75930SValentin Clement     if (auto rop = mlir::dyn_cast<IterWhileOp>(parent)) {
25247f75930SValentin Clement       popFn(rop);
25347f75930SValentin Clement       return;
25447f75930SValentin Clement     }
25547f75930SValentin Clement     // Otherwise, a block argument is provided via the pred blocks.
25647f75930SValentin Clement     for (auto *pred : ba.getOwner()->getPredecessors()) {
25747f75930SValentin Clement       auto u = pred->getTerminator()->getOperand(ba.getArgNumber());
258*beeb86bdSValentin Clement       collectArrayMentionFrom(u);
25947f75930SValentin Clement     }
26047f75930SValentin Clement   }
26147f75930SValentin Clement 
26247f75930SValentin Clement   // Recursively trace operands to find all array operations relating to the
26347f75930SValentin Clement   // values merged.
264*beeb86bdSValentin Clement   void collectArrayMentionFrom(mlir::Value val) {
26547f75930SValentin Clement     if (!val || visited.contains(val))
26647f75930SValentin Clement       return;
26747f75930SValentin Clement     visited.insert(val);
26847f75930SValentin Clement 
26947f75930SValentin Clement     // Process a block argument.
27047f75930SValentin Clement     if (auto ba = val.dyn_cast<mlir::BlockArgument>()) {
271*beeb86bdSValentin Clement       collectArrayMentionFrom(ba);
27247f75930SValentin Clement       return;
27347f75930SValentin Clement     }
27447f75930SValentin Clement 
27547f75930SValentin Clement     // Process an Op.
27647f75930SValentin Clement     if (auto *op = val.getDefiningOp()) {
277*beeb86bdSValentin Clement       collectArrayMentionFrom(op, val);
27847f75930SValentin Clement       return;
27947f75930SValentin Clement     }
28047f75930SValentin Clement 
281*beeb86bdSValentin Clement     emitFatalError(val.getLoc(), "unhandled value");
28247f75930SValentin Clement   }
28347f75930SValentin Clement 
284*beeb86bdSValentin Clement   /// Return all ops that produce the array value that is stored into the
285*beeb86bdSValentin Clement   /// `array_merge_store`.
286*beeb86bdSValentin Clement   static void reachingValues(llvm::SmallVectorImpl<mlir::Operation *> &reach,
287*beeb86bdSValentin Clement                              mlir::Value seq) {
288*beeb86bdSValentin Clement     reach.clear();
289*beeb86bdSValentin Clement     mlir::Region *loopRegion = nullptr;
290*beeb86bdSValentin Clement     if (auto doLoop = mlir::dyn_cast_or_null<DoLoopOp>(seq.getDefiningOp()))
291*beeb86bdSValentin Clement       loopRegion = &doLoop->getRegion(0);
292*beeb86bdSValentin Clement     ReachCollector collector(reach, loopRegion);
293*beeb86bdSValentin Clement     collector.collectArrayMentionFrom(seq);
294*beeb86bdSValentin Clement   }
295*beeb86bdSValentin Clement 
296*beeb86bdSValentin Clement private:
29747f75930SValentin Clement   /// Is \op inside the loop nest region ?
298*beeb86bdSValentin Clement   /// FIXME: replace this structural dependence with graph properties.
29947f75930SValentin Clement   bool opIsInsideLoops(mlir::Operation *op) const {
300*beeb86bdSValentin Clement     auto *region = op->getParentRegion();
301*beeb86bdSValentin Clement     while (region) {
302*beeb86bdSValentin Clement       if (region == loopRegion)
303*beeb86bdSValentin Clement         return true;
304*beeb86bdSValentin Clement       region = region->getParentRegion();
305*beeb86bdSValentin Clement     }
306*beeb86bdSValentin Clement     return false;
30747f75930SValentin Clement   }
30847f75930SValentin Clement 
30947f75930SValentin Clement   /// Recursively trace the use of an operation results, calling
310*beeb86bdSValentin Clement   /// collectArrayMentionFrom on the direct and indirect user operands.
31147f75930SValentin Clement   void followUsers(mlir::Operation *op) {
31247f75930SValentin Clement     for (auto userOperand : op->getOperands())
313*beeb86bdSValentin Clement       collectArrayMentionFrom(userOperand);
31447f75930SValentin Clement     // Go through potential converts/coordinate_op.
315*beeb86bdSValentin Clement     for (auto indirectUser : op->getUsers())
31647f75930SValentin Clement       followUsers(indirectUser);
31747f75930SValentin Clement   }
31847f75930SValentin Clement 
31947f75930SValentin Clement   llvm::SmallVectorImpl<mlir::Operation *> &reach;
32047f75930SValentin Clement   llvm::SmallPtrSet<mlir::Value, 16> visited;
32147f75930SValentin Clement   /// Region of the loops nest that produced the array value.
32247f75930SValentin Clement   mlir::Region *loopRegion;
32347f75930SValentin Clement };
32447f75930SValentin Clement } // namespace
32547f75930SValentin Clement 
32647f75930SValentin Clement /// Find all the array operations that access the array value that is loaded by
32747f75930SValentin Clement /// the array load operation, `load`.
328*beeb86bdSValentin Clement void ArrayCopyAnalysis::arrayMentions(
329*beeb86bdSValentin Clement     llvm::SmallVectorImpl<mlir::Operation *> &mentions, ArrayLoadOp load) {
330*beeb86bdSValentin Clement   mentions.clear();
33147f75930SValentin Clement   auto lmIter = loadMapSets.find(load);
332*beeb86bdSValentin Clement   if (lmIter != loadMapSets.end()) {
333*beeb86bdSValentin Clement     for (auto *opnd : lmIter->second) {
334*beeb86bdSValentin Clement       auto *owner = opnd->getOwner();
335*beeb86bdSValentin Clement       if (mlir::isa<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp, ArrayUpdateOp,
336*beeb86bdSValentin Clement                     ArrayModifyOp>(owner))
337*beeb86bdSValentin Clement         mentions.push_back(owner);
338*beeb86bdSValentin Clement     }
339*beeb86bdSValentin Clement     return;
340*beeb86bdSValentin Clement   }
34147f75930SValentin Clement 
34247f75930SValentin Clement   UseSetT visited;
34347f75930SValentin Clement   llvm::SmallVector<mlir::OpOperand *> queue; // uses of ArrayLoad[orig]
34447f75930SValentin Clement 
34547f75930SValentin Clement   auto appendToQueue = [&](mlir::Value val) {
346*beeb86bdSValentin Clement     for (auto &use : val.getUses())
34747f75930SValentin Clement       if (!visited.count(&use)) {
34847f75930SValentin Clement         visited.insert(&use);
34947f75930SValentin Clement         queue.push_back(&use);
35047f75930SValentin Clement       }
35147f75930SValentin Clement   };
35247f75930SValentin Clement 
35347f75930SValentin Clement   // Build the set of uses of `original`.
35447f75930SValentin Clement   // let USES = { uses of original fir.load }
35547f75930SValentin Clement   appendToQueue(load);
35647f75930SValentin Clement 
35747f75930SValentin Clement   // Process the worklist until done.
35847f75930SValentin Clement   while (!queue.empty()) {
35947f75930SValentin Clement     mlir::OpOperand *operand = queue.pop_back_val();
36047f75930SValentin Clement     mlir::Operation *owner = operand->getOwner();
361*beeb86bdSValentin Clement     if (!owner)
362*beeb86bdSValentin Clement       continue;
36347f75930SValentin Clement     auto structuredLoop = [&](auto ro) {
36447f75930SValentin Clement       if (auto blockArg = ro.iterArgToBlockArg(operand->get())) {
36547f75930SValentin Clement         int64_t arg = blockArg.getArgNumber();
366149ad3d5SShraiysh Vaishay         mlir::Value output = ro.getResult(ro.getFinalValue() ? arg : arg - 1);
36747f75930SValentin Clement         appendToQueue(output);
36847f75930SValentin Clement         appendToQueue(blockArg);
36947f75930SValentin Clement       }
37047f75930SValentin Clement     };
37147f75930SValentin Clement     // TODO: this need to be updated to use the control-flow interface.
37247f75930SValentin Clement     auto branchOp = [&](mlir::Block *dest, OperandRange operands) {
37347f75930SValentin Clement       if (operands.empty())
37447f75930SValentin Clement         return;
37547f75930SValentin Clement 
37647f75930SValentin Clement       // Check if this operand is within the range.
37747f75930SValentin Clement       unsigned operandIndex = operand->getOperandNumber();
37847f75930SValentin Clement       unsigned operandsStart = operands.getBeginOperandIndex();
37947f75930SValentin Clement       if (operandIndex < operandsStart ||
38047f75930SValentin Clement           operandIndex >= (operandsStart + operands.size()))
38147f75930SValentin Clement         return;
38247f75930SValentin Clement 
38347f75930SValentin Clement       // Index the successor.
38447f75930SValentin Clement       unsigned argIndex = operandIndex - operandsStart;
38547f75930SValentin Clement       appendToQueue(dest->getArgument(argIndex));
38647f75930SValentin Clement     };
38747f75930SValentin Clement     // Thread uses into structured loop bodies and return value uses.
38847f75930SValentin Clement     if (auto ro = mlir::dyn_cast<DoLoopOp>(owner)) {
38947f75930SValentin Clement       structuredLoop(ro);
39047f75930SValentin Clement     } else if (auto ro = mlir::dyn_cast<IterWhileOp>(owner)) {
39147f75930SValentin Clement       structuredLoop(ro);
39247f75930SValentin Clement     } else if (auto rs = mlir::dyn_cast<ResultOp>(owner)) {
39347f75930SValentin Clement       // Thread any uses of fir.if that return the marked array value.
394*beeb86bdSValentin Clement       mlir::Operation *parent = rs->getParentRegion()->getParentOp();
395*beeb86bdSValentin Clement       if (auto ifOp = mlir::dyn_cast<fir::IfOp>(parent))
39647f75930SValentin Clement         appendToQueue(ifOp.getResult(operand->getOperandNumber()));
39747f75930SValentin Clement     } else if (mlir::isa<ArrayFetchOp>(owner)) {
39847f75930SValentin Clement       // Keep track of array value fetches.
39947f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
40047f75930SValentin Clement                  << "add fetch {" << *owner << "} to array value set\n");
401*beeb86bdSValentin Clement       mentions.push_back(owner);
40247f75930SValentin Clement     } else if (auto update = mlir::dyn_cast<ArrayUpdateOp>(owner)) {
40347f75930SValentin Clement       // Keep track of array value updates and thread the return value uses.
40447f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
40547f75930SValentin Clement                  << "add update {" << *owner << "} to array value set\n");
406*beeb86bdSValentin Clement       mentions.push_back(owner);
40747f75930SValentin Clement       appendToQueue(update.getResult());
40847f75930SValentin Clement     } else if (auto update = mlir::dyn_cast<ArrayModifyOp>(owner)) {
40947f75930SValentin Clement       // Keep track of array value modification and thread the return value
41047f75930SValentin Clement       // uses.
41147f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
41247f75930SValentin Clement                  << "add modify {" << *owner << "} to array value set\n");
413*beeb86bdSValentin Clement       mentions.push_back(owner);
41447f75930SValentin Clement       appendToQueue(update.getResult(1));
415*beeb86bdSValentin Clement     } else if (auto mention = mlir::dyn_cast<ArrayAccessOp>(owner)) {
416*beeb86bdSValentin Clement       mentions.push_back(owner);
417*beeb86bdSValentin Clement     } else if (auto amend = mlir::dyn_cast<ArrayAmendOp>(owner)) {
418*beeb86bdSValentin Clement       mentions.push_back(owner);
419*beeb86bdSValentin Clement       appendToQueue(amend.getResult());
420ace01605SRiver Riddle     } else if (auto br = mlir::dyn_cast<mlir::cf::BranchOp>(owner)) {
4213012f35fSJacques Pienaar       branchOp(br.getDest(), br.getDestOperands());
422ace01605SRiver Riddle     } else if (auto br = mlir::dyn_cast<mlir::cf::CondBranchOp>(owner)) {
42347f75930SValentin Clement       branchOp(br.getTrueDest(), br.getTrueOperands());
42447f75930SValentin Clement       branchOp(br.getFalseDest(), br.getFalseOperands());
42547f75930SValentin Clement     } else if (mlir::isa<ArrayMergeStoreOp>(owner)) {
42647f75930SValentin Clement       // do nothing
42747f75930SValentin Clement     } else {
42847f75930SValentin Clement       llvm::report_fatal_error("array value reached unexpected op");
42947f75930SValentin Clement     }
43047f75930SValentin Clement   }
431*beeb86bdSValentin Clement   loadMapSets.insert({load, visited});
432*beeb86bdSValentin Clement }
433*beeb86bdSValentin Clement 
434*beeb86bdSValentin Clement static bool hasPointerType(mlir::Type type) {
435*beeb86bdSValentin Clement   if (auto boxTy = type.dyn_cast<BoxType>())
436*beeb86bdSValentin Clement     type = boxTy.getEleTy();
437*beeb86bdSValentin Clement   return type.isa<fir::PointerType>();
438*beeb86bdSValentin Clement }
439*beeb86bdSValentin Clement 
440*beeb86bdSValentin Clement // This is a NF performance hack. It makes a simple test that the slices of the
441*beeb86bdSValentin Clement // load, \p ld, and the merge store, \p st, are trivially mutually exclusive.
442*beeb86bdSValentin Clement static bool mutuallyExclusiveSliceRange(ArrayLoadOp ld, ArrayMergeStoreOp st) {
443*beeb86bdSValentin Clement   // If the same array_load, then no further testing is warranted.
444*beeb86bdSValentin Clement   if (ld.getResult() == st.getOriginal())
445*beeb86bdSValentin Clement     return false;
446*beeb86bdSValentin Clement 
447*beeb86bdSValentin Clement   auto getSliceOp = [](mlir::Value val) -> SliceOp {
448*beeb86bdSValentin Clement     if (!val)
449*beeb86bdSValentin Clement       return {};
450*beeb86bdSValentin Clement     auto sliceOp = mlir::dyn_cast_or_null<SliceOp>(val.getDefiningOp());
451*beeb86bdSValentin Clement     if (!sliceOp)
452*beeb86bdSValentin Clement       return {};
453*beeb86bdSValentin Clement     return sliceOp;
454*beeb86bdSValentin Clement   };
455*beeb86bdSValentin Clement 
456*beeb86bdSValentin Clement   auto ldSlice = getSliceOp(ld.getSlice());
457*beeb86bdSValentin Clement   auto stSlice = getSliceOp(st.getSlice());
458*beeb86bdSValentin Clement   if (!ldSlice || !stSlice)
459*beeb86bdSValentin Clement     return false;
460*beeb86bdSValentin Clement 
461*beeb86bdSValentin Clement   // Resign on subobject slices.
462*beeb86bdSValentin Clement   if (!ldSlice.getFields().empty() || !stSlice.getFields().empty() ||
463*beeb86bdSValentin Clement       !ldSlice.getSubstr().empty() || !stSlice.getSubstr().empty())
464*beeb86bdSValentin Clement     return false;
465*beeb86bdSValentin Clement 
466*beeb86bdSValentin Clement   // Crudely test that the two slices do not overlap by looking for the
467*beeb86bdSValentin Clement   // following general condition. If the slices look like (i:j) and (j+1:k) then
468*beeb86bdSValentin Clement   // these ranges do not overlap. The addend must be a constant.
469*beeb86bdSValentin Clement   auto ldTriples = ldSlice.getTriples();
470*beeb86bdSValentin Clement   auto stTriples = stSlice.getTriples();
471*beeb86bdSValentin Clement   const auto size = ldTriples.size();
472*beeb86bdSValentin Clement   if (size != stTriples.size())
473*beeb86bdSValentin Clement     return false;
474*beeb86bdSValentin Clement 
475*beeb86bdSValentin Clement   auto displacedByConstant = [](mlir::Value v1, mlir::Value v2) {
476*beeb86bdSValentin Clement     auto removeConvert = [](mlir::Value v) -> mlir::Operation * {
477*beeb86bdSValentin Clement       auto *op = v.getDefiningOp();
478*beeb86bdSValentin Clement       while (auto conv = mlir::dyn_cast_or_null<ConvertOp>(op))
479*beeb86bdSValentin Clement         op = conv.getValue().getDefiningOp();
480*beeb86bdSValentin Clement       return op;
481*beeb86bdSValentin Clement     };
482*beeb86bdSValentin Clement 
483*beeb86bdSValentin Clement     auto isPositiveConstant = [](mlir::Value v) -> bool {
484*beeb86bdSValentin Clement       if (auto conOp =
485*beeb86bdSValentin Clement               mlir::dyn_cast<mlir::arith::ConstantOp>(v.getDefiningOp()))
486*beeb86bdSValentin Clement         if (auto iattr = conOp.getValue().dyn_cast<mlir::IntegerAttr>())
487*beeb86bdSValentin Clement           return iattr.getInt() > 0;
488*beeb86bdSValentin Clement       return false;
489*beeb86bdSValentin Clement     };
490*beeb86bdSValentin Clement 
491*beeb86bdSValentin Clement     auto *op1 = removeConvert(v1);
492*beeb86bdSValentin Clement     auto *op2 = removeConvert(v2);
493*beeb86bdSValentin Clement     if (!op1 || !op2)
494*beeb86bdSValentin Clement       return false;
495*beeb86bdSValentin Clement     if (auto addi = mlir::dyn_cast<mlir::arith::AddIOp>(op2))
496*beeb86bdSValentin Clement       if ((addi.getLhs().getDefiningOp() == op1 &&
497*beeb86bdSValentin Clement            isPositiveConstant(addi.getRhs())) ||
498*beeb86bdSValentin Clement           (addi.getRhs().getDefiningOp() == op1 &&
499*beeb86bdSValentin Clement            isPositiveConstant(addi.getLhs())))
500*beeb86bdSValentin Clement         return true;
501*beeb86bdSValentin Clement     if (auto subi = mlir::dyn_cast<mlir::arith::SubIOp>(op1))
502*beeb86bdSValentin Clement       if (subi.getLhs().getDefiningOp() == op2 &&
503*beeb86bdSValentin Clement           isPositiveConstant(subi.getRhs()))
504*beeb86bdSValentin Clement         return true;
505*beeb86bdSValentin Clement     return false;
506*beeb86bdSValentin Clement   };
507*beeb86bdSValentin Clement 
508*beeb86bdSValentin Clement   for (std::remove_const_t<decltype(size)> i = 0; i < size; i += 3) {
509*beeb86bdSValentin Clement     // If both are loop invariant, skip to the next triple.
510*beeb86bdSValentin Clement     if (mlir::isa_and_nonnull<fir::UndefOp>(ldTriples[i + 1].getDefiningOp()) &&
511*beeb86bdSValentin Clement         mlir::isa_and_nonnull<fir::UndefOp>(stTriples[i + 1].getDefiningOp())) {
512*beeb86bdSValentin Clement       // Unless either is a vector index, then be conservative.
513*beeb86bdSValentin Clement       if (mlir::isa_and_nonnull<fir::UndefOp>(ldTriples[i].getDefiningOp()) ||
514*beeb86bdSValentin Clement           mlir::isa_and_nonnull<fir::UndefOp>(stTriples[i].getDefiningOp()))
515*beeb86bdSValentin Clement         return false;
516*beeb86bdSValentin Clement       continue;
517*beeb86bdSValentin Clement     }
518*beeb86bdSValentin Clement     // If identical, skip to the next triple.
519*beeb86bdSValentin Clement     if (ldTriples[i] == stTriples[i] && ldTriples[i + 1] == stTriples[i + 1] &&
520*beeb86bdSValentin Clement         ldTriples[i + 2] == stTriples[i + 2])
521*beeb86bdSValentin Clement       continue;
522*beeb86bdSValentin Clement     // If ubound and lbound are the same with a constant offset, skip to the
523*beeb86bdSValentin Clement     // next triple.
524*beeb86bdSValentin Clement     if (displacedByConstant(ldTriples[i + 1], stTriples[i]) ||
525*beeb86bdSValentin Clement         displacedByConstant(stTriples[i + 1], ldTriples[i]))
526*beeb86bdSValentin Clement       continue;
527*beeb86bdSValentin Clement     return false;
528*beeb86bdSValentin Clement   }
529*beeb86bdSValentin Clement   LLVM_DEBUG(llvm::dbgs() << "detected non-overlapping slice ranges on " << ld
530*beeb86bdSValentin Clement                           << " and " << st << ", which is not a conflict\n");
531*beeb86bdSValentin Clement   return true;
53247f75930SValentin Clement }
53347f75930SValentin Clement 
53447f75930SValentin Clement /// Is there a conflict between the array value that was updated and to be
53547f75930SValentin Clement /// stored to `st` and the set of arrays loaded (`reach`) and used to compute
53647f75930SValentin Clement /// the updated value?
53747f75930SValentin Clement static bool conflictOnLoad(llvm::ArrayRef<mlir::Operation *> reach,
53847f75930SValentin Clement                            ArrayMergeStoreOp st) {
53947f75930SValentin Clement   mlir::Value load;
540149ad3d5SShraiysh Vaishay   mlir::Value addr = st.getMemref();
541*beeb86bdSValentin Clement   const bool storeHasPointerType = hasPointerType(addr.getType());
542*beeb86bdSValentin Clement   for (auto *op : reach)
543*beeb86bdSValentin Clement     if (auto ld = mlir::dyn_cast<ArrayLoadOp>(op)) {
544149ad3d5SShraiysh Vaishay       mlir::Type ldTy = ld.getMemref().getType();
545149ad3d5SShraiysh Vaishay       if (ld.getMemref() == addr) {
546*beeb86bdSValentin Clement         if (mutuallyExclusiveSliceRange(ld, st))
547*beeb86bdSValentin Clement           continue;
548149ad3d5SShraiysh Vaishay         if (ld.getResult() != st.getOriginal())
54947f75930SValentin Clement           return true;
550*beeb86bdSValentin Clement         if (load) {
551*beeb86bdSValentin Clement           // TODO: extend this to allow checking if the first `load` and this
552*beeb86bdSValentin Clement           // `ld` are mutually exclusive accesses but not identical.
55347f75930SValentin Clement           return true;
554*beeb86bdSValentin Clement         }
55547f75930SValentin Clement         load = ld;
556*beeb86bdSValentin Clement       } else if ((hasPointerType(ldTy) || storeHasPointerType)) {
557*beeb86bdSValentin Clement         // TODO: Use target attribute to restrict this case further.
558*beeb86bdSValentin Clement         // TODO: Check if types can also allow ruling out some cases. For now,
559*beeb86bdSValentin Clement         // the fact that equivalences is using pointer attribute to enforce
560*beeb86bdSValentin Clement         // aliasing is preventing any attempt to do so, and in general, it may
561*beeb86bdSValentin Clement         // be wrong to use this if any of the types is a complex or a derived
562*beeb86bdSValentin Clement         // for which it is possible to create a pointer to a part with a
563*beeb86bdSValentin Clement         // different type than the whole, although this deserve some more
564*beeb86bdSValentin Clement         // investigation because existing compiler behavior seem to diverge
565*beeb86bdSValentin Clement         // here.
566*beeb86bdSValentin Clement         return true;
56747f75930SValentin Clement       }
56847f75930SValentin Clement     }
56947f75930SValentin Clement   return false;
57047f75930SValentin Clement }
57147f75930SValentin Clement 
572*beeb86bdSValentin Clement /// Is there an access vector conflict on the array being merged into? If the
573*beeb86bdSValentin Clement /// access vectors diverge, then assume that there are potentially overlapping
574*beeb86bdSValentin Clement /// loop-carried references.
575*beeb86bdSValentin Clement static bool conflictOnMerge(llvm::ArrayRef<mlir::Operation *> mentions) {
576*beeb86bdSValentin Clement   if (mentions.size() < 2)
57747f75930SValentin Clement     return false;
57847f75930SValentin Clement   llvm::SmallVector<mlir::Value> indices;
579*beeb86bdSValentin Clement   LLVM_DEBUG(llvm::dbgs() << "check merge conflict on with " << mentions.size()
580*beeb86bdSValentin Clement                           << " mentions on the list\n");
581*beeb86bdSValentin Clement   bool valSeen = false;
582*beeb86bdSValentin Clement   bool refSeen = false;
583*beeb86bdSValentin Clement   for (auto *op : mentions) {
58447f75930SValentin Clement     llvm::SmallVector<mlir::Value> compareVector;
58547f75930SValentin Clement     if (auto u = mlir::dyn_cast<ArrayUpdateOp>(op)) {
586*beeb86bdSValentin Clement       valSeen = true;
58747f75930SValentin Clement       if (indices.empty()) {
588149ad3d5SShraiysh Vaishay         indices = u.getIndices();
58947f75930SValentin Clement         continue;
59047f75930SValentin Clement       }
591149ad3d5SShraiysh Vaishay       compareVector = u.getIndices();
59247f75930SValentin Clement     } else if (auto f = mlir::dyn_cast<ArrayModifyOp>(op)) {
593*beeb86bdSValentin Clement       valSeen = true;
59447f75930SValentin Clement       if (indices.empty()) {
595149ad3d5SShraiysh Vaishay         indices = f.getIndices();
59647f75930SValentin Clement         continue;
59747f75930SValentin Clement       }
598149ad3d5SShraiysh Vaishay       compareVector = f.getIndices();
59947f75930SValentin Clement     } else if (auto f = mlir::dyn_cast<ArrayFetchOp>(op)) {
600*beeb86bdSValentin Clement       valSeen = true;
60147f75930SValentin Clement       if (indices.empty()) {
602149ad3d5SShraiysh Vaishay         indices = f.getIndices();
60347f75930SValentin Clement         continue;
60447f75930SValentin Clement       }
605149ad3d5SShraiysh Vaishay       compareVector = f.getIndices();
606*beeb86bdSValentin Clement     } else if (auto f = mlir::dyn_cast<ArrayAccessOp>(op)) {
607*beeb86bdSValentin Clement       refSeen = true;
608*beeb86bdSValentin Clement       if (indices.empty()) {
609*beeb86bdSValentin Clement         indices = f.getIndices();
610*beeb86bdSValentin Clement         continue;
61147f75930SValentin Clement       }
612*beeb86bdSValentin Clement       compareVector = f.getIndices();
613*beeb86bdSValentin Clement     } else if (mlir::isa<ArrayAmendOp>(op)) {
614*beeb86bdSValentin Clement       refSeen = true;
615*beeb86bdSValentin Clement       continue;
616*beeb86bdSValentin Clement     } else {
617*beeb86bdSValentin Clement       mlir::emitError(op->getLoc(), "unexpected operation in analysis");
618*beeb86bdSValentin Clement     }
619*beeb86bdSValentin Clement     if (compareVector.size() != indices.size() ||
620*beeb86bdSValentin Clement         llvm::any_of(llvm::zip(compareVector, indices), [&](auto pair) {
621*beeb86bdSValentin Clement           return std::get<0>(pair) != std::get<1>(pair);
622*beeb86bdSValentin Clement         }))
62347f75930SValentin Clement       return true;
62447f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "vectors compare equal\n");
62547f75930SValentin Clement   }
626*beeb86bdSValentin Clement   return valSeen && refSeen;
627*beeb86bdSValentin Clement }
628*beeb86bdSValentin Clement 
629*beeb86bdSValentin Clement /// With element-by-reference semantics, an amended array with more than once
630*beeb86bdSValentin Clement /// access to the same loaded array are conservatively considered a conflict.
631*beeb86bdSValentin Clement /// Note: the array copy can still be eliminated in subsequent optimizations.
632*beeb86bdSValentin Clement static bool conflictOnReference(llvm::ArrayRef<mlir::Operation *> mentions) {
633*beeb86bdSValentin Clement   LLVM_DEBUG(llvm::dbgs() << "checking reference semantics " << mentions.size()
634*beeb86bdSValentin Clement                           << '\n');
635*beeb86bdSValentin Clement   if (mentions.size() < 3)
63647f75930SValentin Clement     return false;
637*beeb86bdSValentin Clement   unsigned amendCount = 0;
638*beeb86bdSValentin Clement   unsigned accessCount = 0;
639*beeb86bdSValentin Clement   for (auto *op : mentions) {
640*beeb86bdSValentin Clement     if (mlir::isa<ArrayAmendOp>(op) && ++amendCount > 1) {
641*beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs() << "conflict: multiple amends of array value\n");
642*beeb86bdSValentin Clement       return true;
643*beeb86bdSValentin Clement     }
644*beeb86bdSValentin Clement     if (mlir::isa<ArrayAccessOp>(op) && ++accessCount > 1) {
645*beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs()
646*beeb86bdSValentin Clement                  << "conflict: multiple accesses of array value\n");
647*beeb86bdSValentin Clement       return true;
648*beeb86bdSValentin Clement     }
649*beeb86bdSValentin Clement     if (mlir::isa<ArrayFetchOp, ArrayUpdateOp, ArrayModifyOp>(op)) {
650*beeb86bdSValentin Clement       LLVM_DEBUG(llvm::dbgs()
651*beeb86bdSValentin Clement                  << "conflict: array value has both uses by-value and uses "
652*beeb86bdSValentin Clement                     "by-reference. conservative assumption.\n");
653*beeb86bdSValentin Clement       return true;
654*beeb86bdSValentin Clement     }
655*beeb86bdSValentin Clement   }
656*beeb86bdSValentin Clement   return false;
657*beeb86bdSValentin Clement }
658*beeb86bdSValentin Clement 
659*beeb86bdSValentin Clement static mlir::Operation *
660*beeb86bdSValentin Clement amendingAccess(llvm::ArrayRef<mlir::Operation *> mentions) {
661*beeb86bdSValentin Clement   for (auto *op : mentions)
662*beeb86bdSValentin Clement     if (auto amend = mlir::dyn_cast<ArrayAmendOp>(op))
663*beeb86bdSValentin Clement       return amend.getMemref().getDefiningOp();
664*beeb86bdSValentin Clement   return {};
66547f75930SValentin Clement }
66647f75930SValentin Clement 
66747f75930SValentin Clement // Are either of types of conflicts present?
66847f75930SValentin Clement inline bool conflictDetected(llvm::ArrayRef<mlir::Operation *> reach,
66947f75930SValentin Clement                              llvm::ArrayRef<mlir::Operation *> accesses,
67047f75930SValentin Clement                              ArrayMergeStoreOp st) {
67147f75930SValentin Clement   return conflictOnLoad(reach, st) || conflictOnMerge(accesses);
67247f75930SValentin Clement }
67347f75930SValentin Clement 
674*beeb86bdSValentin Clement // Assume that any call to a function that uses host-associations will be
675*beeb86bdSValentin Clement // modifying the output array.
676*beeb86bdSValentin Clement static bool
677*beeb86bdSValentin Clement conservativeCallConflict(llvm::ArrayRef<mlir::Operation *> reaches) {
678*beeb86bdSValentin Clement   return llvm::any_of(reaches, [](mlir::Operation *op) {
679*beeb86bdSValentin Clement     if (auto call = mlir::dyn_cast<fir::CallOp>(op))
680*beeb86bdSValentin Clement       if (auto callee =
681*beeb86bdSValentin Clement               call.getCallableForCallee().dyn_cast<mlir::SymbolRefAttr>()) {
682*beeb86bdSValentin Clement         auto module = op->getParentOfType<mlir::ModuleOp>();
683*beeb86bdSValentin Clement         return hasHostAssociationArgument(
684*beeb86bdSValentin Clement             module.lookupSymbol<mlir::FuncOp>(callee));
685*beeb86bdSValentin Clement       }
686*beeb86bdSValentin Clement     return false;
687*beeb86bdSValentin Clement   });
688*beeb86bdSValentin Clement }
689*beeb86bdSValentin Clement 
69047f75930SValentin Clement /// Constructor of the array copy analysis.
69147f75930SValentin Clement /// This performs the analysis and saves the intermediate results.
69247f75930SValentin Clement void ArrayCopyAnalysis::construct(mlir::Operation *topLevelOp) {
69347f75930SValentin Clement   topLevelOp->walk([&](Operation *op) {
69447f75930SValentin Clement     if (auto st = mlir::dyn_cast<fir::ArrayMergeStoreOp>(op)) {
695*beeb86bdSValentin Clement       llvm::SmallVector<mlir::Operation *> values;
696149ad3d5SShraiysh Vaishay       ReachCollector::reachingValues(values, st.getSequence());
697*beeb86bdSValentin Clement       bool callConflict = conservativeCallConflict(values);
698*beeb86bdSValentin Clement       llvm::SmallVector<mlir::Operation *> mentions;
699*beeb86bdSValentin Clement       arrayMentions(mentions,
700149ad3d5SShraiysh Vaishay                     mlir::cast<ArrayLoadOp>(st.getOriginal().getDefiningOp()));
701*beeb86bdSValentin Clement       bool conflict = conflictDetected(values, mentions, st);
702*beeb86bdSValentin Clement       bool refConflict = conflictOnReference(mentions);
703*beeb86bdSValentin Clement       if (callConflict || conflict || refConflict) {
70447f75930SValentin Clement         LLVM_DEBUG(llvm::dbgs()
70547f75930SValentin Clement                    << "CONFLICT: copies required for " << st << '\n'
70647f75930SValentin Clement                    << "   adding conflicts on: " << op << " and "
707149ad3d5SShraiysh Vaishay                    << st.getOriginal() << '\n');
70847f75930SValentin Clement         conflicts.insert(op);
709149ad3d5SShraiysh Vaishay         conflicts.insert(st.getOriginal().getDefiningOp());
710*beeb86bdSValentin Clement         if (auto *access = amendingAccess(mentions))
711*beeb86bdSValentin Clement           amendAccesses.insert(access);
71247f75930SValentin Clement       }
713149ad3d5SShraiysh Vaishay       auto *ld = st.getOriginal().getDefiningOp();
71447f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs()
71547f75930SValentin Clement                  << "map: adding {" << *ld << " -> " << st << "}\n");
71647f75930SValentin Clement       useMap.insert({ld, op});
71747f75930SValentin Clement     } else if (auto load = mlir::dyn_cast<ArrayLoadOp>(op)) {
718*beeb86bdSValentin Clement       llvm::SmallVector<mlir::Operation *> mentions;
719*beeb86bdSValentin Clement       arrayMentions(mentions, load);
72047f75930SValentin Clement       LLVM_DEBUG(llvm::dbgs() << "process load: " << load
721*beeb86bdSValentin Clement                               << ", mentions: " << mentions.size() << '\n');
722*beeb86bdSValentin Clement       for (auto *acc : mentions) {
723*beeb86bdSValentin Clement         LLVM_DEBUG(llvm::dbgs() << " mention: " << *acc << '\n');
724*beeb86bdSValentin Clement         if (mlir::isa<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp, ArrayUpdateOp,
725*beeb86bdSValentin Clement                       ArrayModifyOp>(acc)) {
726*beeb86bdSValentin Clement           if (useMap.count(acc)) {
72747f75930SValentin Clement             mlir::emitError(
72847f75930SValentin Clement                 load.getLoc(),
72947f75930SValentin Clement                 "The parallel semantics of multiple array_merge_stores per "
73047f75930SValentin Clement                 "array_load are not supported.");
731*beeb86bdSValentin Clement             continue;
73247f75930SValentin Clement           }
73347f75930SValentin Clement           LLVM_DEBUG(llvm::dbgs()
73447f75930SValentin Clement                      << "map: adding {" << *acc << "} -> {" << load << "}\n");
735*beeb86bdSValentin Clement           useMap.insert({acc, op});
736*beeb86bdSValentin Clement         }
73747f75930SValentin Clement       }
73847f75930SValentin Clement     }
73947f75930SValentin Clement   });
74047f75930SValentin Clement }
74147f75930SValentin Clement 
742*beeb86bdSValentin Clement //===----------------------------------------------------------------------===//
743*beeb86bdSValentin Clement // Conversions for converting out of array value form.
744*beeb86bdSValentin Clement //===----------------------------------------------------------------------===//
745*beeb86bdSValentin Clement 
74647f75930SValentin Clement namespace {
74747f75930SValentin Clement class ArrayLoadConversion : public mlir::OpRewritePattern<ArrayLoadOp> {
74847f75930SValentin Clement public:
74947f75930SValentin Clement   using OpRewritePattern::OpRewritePattern;
75047f75930SValentin Clement 
75147f75930SValentin Clement   mlir::LogicalResult
75247f75930SValentin Clement   matchAndRewrite(ArrayLoadOp load,
75347f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
75447f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "replace load " << load << " with undef.\n");
75547f75930SValentin Clement     rewriter.replaceOpWithNewOp<UndefOp>(load, load.getType());
75647f75930SValentin Clement     return mlir::success();
75747f75930SValentin Clement   }
75847f75930SValentin Clement };
75947f75930SValentin Clement 
76047f75930SValentin Clement class ArrayMergeStoreConversion
76147f75930SValentin Clement     : public mlir::OpRewritePattern<ArrayMergeStoreOp> {
76247f75930SValentin Clement public:
76347f75930SValentin Clement   using OpRewritePattern::OpRewritePattern;
76447f75930SValentin Clement 
76547f75930SValentin Clement   mlir::LogicalResult
76647f75930SValentin Clement   matchAndRewrite(ArrayMergeStoreOp store,
76747f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
76847f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "marking store " << store << " as dead.\n");
76947f75930SValentin Clement     rewriter.eraseOp(store);
77047f75930SValentin Clement     return mlir::success();
77147f75930SValentin Clement   }
77247f75930SValentin Clement };
77347f75930SValentin Clement } // namespace
77447f75930SValentin Clement 
77547f75930SValentin Clement static mlir::Type getEleTy(mlir::Type ty) {
776*beeb86bdSValentin Clement   auto eleTy = unwrapSequenceType(unwrapPassByRefType(ty));
77747f75930SValentin Clement   // FIXME: keep ptr/heap/ref information.
778*beeb86bdSValentin Clement   return ReferenceType::get(eleTy);
77947f75930SValentin Clement }
78047f75930SValentin Clement 
78147f75930SValentin Clement // Extract extents from the ShapeOp/ShapeShiftOp into the result vector.
782*beeb86bdSValentin Clement static bool getAdjustedExtents(mlir::Location loc,
783*beeb86bdSValentin Clement                                mlir::PatternRewriter &rewriter,
784*beeb86bdSValentin Clement                                ArrayLoadOp arrLoad,
785*beeb86bdSValentin Clement                                llvm::SmallVectorImpl<mlir::Value> &result,
78647f75930SValentin Clement                                mlir::Value shape) {
787*beeb86bdSValentin Clement   bool copyUsingSlice = false;
78847f75930SValentin Clement   auto *shapeOp = shape.getDefiningOp();
789*beeb86bdSValentin Clement   if (auto s = mlir::dyn_cast_or_null<ShapeOp>(shapeOp)) {
79047f75930SValentin Clement     auto e = s.getExtents();
79147f75930SValentin Clement     result.insert(result.end(), e.begin(), e.end());
792*beeb86bdSValentin Clement   } else if (auto s = mlir::dyn_cast_or_null<ShapeShiftOp>(shapeOp)) {
79347f75930SValentin Clement     auto e = s.getExtents();
79447f75930SValentin Clement     result.insert(result.end(), e.begin(), e.end());
795*beeb86bdSValentin Clement   } else {
796*beeb86bdSValentin Clement     emitFatalError(loc, "not a fir.shape/fir.shape_shift op");
79747f75930SValentin Clement   }
798*beeb86bdSValentin Clement   auto idxTy = rewriter.getIndexType();
799*beeb86bdSValentin Clement   if (factory::isAssumedSize(result)) {
800*beeb86bdSValentin Clement     // Use slice information to compute the extent of the column.
801*beeb86bdSValentin Clement     auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1);
802*beeb86bdSValentin Clement     mlir::Value size = one;
803*beeb86bdSValentin Clement     if (mlir::Value sliceArg = arrLoad.getSlice()) {
804*beeb86bdSValentin Clement       if (auto sliceOp =
805*beeb86bdSValentin Clement               mlir::dyn_cast_or_null<SliceOp>(sliceArg.getDefiningOp())) {
806*beeb86bdSValentin Clement         auto triples = sliceOp.getTriples();
807*beeb86bdSValentin Clement         const std::size_t tripleSize = triples.size();
808*beeb86bdSValentin Clement         auto module = arrLoad->getParentOfType<mlir::ModuleOp>();
809*beeb86bdSValentin Clement         FirOpBuilder builder(rewriter, getKindMapping(module));
810*beeb86bdSValentin Clement         size = builder.genExtentFromTriplet(loc, triples[tripleSize - 3],
811*beeb86bdSValentin Clement                                             triples[tripleSize - 2],
812*beeb86bdSValentin Clement                                             triples[tripleSize - 1], idxTy);
813*beeb86bdSValentin Clement         copyUsingSlice = true;
814*beeb86bdSValentin Clement       }
815*beeb86bdSValentin Clement     }
816*beeb86bdSValentin Clement     result[result.size() - 1] = size;
817*beeb86bdSValentin Clement   }
818*beeb86bdSValentin Clement   return copyUsingSlice;
81947f75930SValentin Clement }
82047f75930SValentin Clement 
821*beeb86bdSValentin Clement /// Place the extents of the array load, \p arrLoad, into \p result and
822*beeb86bdSValentin Clement /// return a ShapeOp or ShapeShiftOp with the same extents. If \p arrLoad is
823*beeb86bdSValentin Clement /// loading a `!fir.box`, code will be generated to read the extents from the
824*beeb86bdSValentin Clement /// boxed value, and the retunred shape Op will be built with the extents read
825*beeb86bdSValentin Clement /// from the box. Otherwise, the extents will be extracted from the ShapeOp (or
826*beeb86bdSValentin Clement /// ShapeShiftOp) argument of \p arrLoad. \p copyUsingSlice will be set to true
827*beeb86bdSValentin Clement /// if slicing of the output array is to be done in the copy-in/copy-out rather
828*beeb86bdSValentin Clement /// than in the elemental computation step.
829*beeb86bdSValentin Clement static mlir::Value getOrReadExtentsAndShapeOp(
830*beeb86bdSValentin Clement     mlir::Location loc, mlir::PatternRewriter &rewriter, ArrayLoadOp arrLoad,
831*beeb86bdSValentin Clement     llvm::SmallVectorImpl<mlir::Value> &result, bool &copyUsingSlice) {
83247f75930SValentin Clement   assert(result.empty());
833*beeb86bdSValentin Clement   if (arrLoad->hasAttr(fir::getOptionalAttrName()))
834*beeb86bdSValentin Clement     fir::emitFatalError(
835*beeb86bdSValentin Clement         loc, "shapes from array load of OPTIONAL arrays must not be used");
836*beeb86bdSValentin Clement   if (auto boxTy = arrLoad.getMemref().getType().dyn_cast<BoxType>()) {
837*beeb86bdSValentin Clement     auto rank =
838*beeb86bdSValentin Clement         dyn_cast_ptrOrBoxEleTy(boxTy).cast<SequenceType>().getDimension();
83947f75930SValentin Clement     auto idxTy = rewriter.getIndexType();
84047f75930SValentin Clement     for (decltype(rank) dim = 0; dim < rank; ++dim) {
841*beeb86bdSValentin Clement       auto dimVal = rewriter.create<mlir::arith::ConstantIndexOp>(loc, dim);
842*beeb86bdSValentin Clement       auto dimInfo = rewriter.create<BoxDimsOp>(loc, idxTy, idxTy, idxTy,
843*beeb86bdSValentin Clement                                                 arrLoad.getMemref(), dimVal);
84447f75930SValentin Clement       result.emplace_back(dimInfo.getResult(1));
84547f75930SValentin Clement     }
846*beeb86bdSValentin Clement     if (!arrLoad.getShape()) {
847*beeb86bdSValentin Clement       auto shapeType = ShapeType::get(rewriter.getContext(), rank);
848*beeb86bdSValentin Clement       return rewriter.create<ShapeOp>(loc, shapeType, result);
84947f75930SValentin Clement     }
850*beeb86bdSValentin Clement     auto shiftOp = arrLoad.getShape().getDefiningOp<ShiftOp>();
851*beeb86bdSValentin Clement     auto shapeShiftType = ShapeShiftType::get(rewriter.getContext(), rank);
852*beeb86bdSValentin Clement     llvm::SmallVector<mlir::Value> shapeShiftOperands;
853*beeb86bdSValentin Clement     for (auto [lb, extent] : llvm::zip(shiftOp.getOrigins(), result)) {
854*beeb86bdSValentin Clement       shapeShiftOperands.push_back(lb);
855*beeb86bdSValentin Clement       shapeShiftOperands.push_back(extent);
856*beeb86bdSValentin Clement     }
857*beeb86bdSValentin Clement     return rewriter.create<ShapeShiftOp>(loc, shapeShiftType,
858*beeb86bdSValentin Clement                                          shapeShiftOperands);
859*beeb86bdSValentin Clement   }
860*beeb86bdSValentin Clement   copyUsingSlice =
861*beeb86bdSValentin Clement       getAdjustedExtents(loc, rewriter, arrLoad, result, arrLoad.getShape());
862*beeb86bdSValentin Clement   return arrLoad.getShape();
86347f75930SValentin Clement }
86447f75930SValentin Clement 
86547f75930SValentin Clement static mlir::Type toRefType(mlir::Type ty) {
86647f75930SValentin Clement   if (fir::isa_ref_type(ty))
86747f75930SValentin Clement     return ty;
86847f75930SValentin Clement   return fir::ReferenceType::get(ty);
86947f75930SValentin Clement }
87047f75930SValentin Clement 
87147f75930SValentin Clement static mlir::Value
87247f75930SValentin Clement genCoorOp(mlir::PatternRewriter &rewriter, mlir::Location loc, mlir::Type eleTy,
87347f75930SValentin Clement           mlir::Type resTy, mlir::Value alloc, mlir::Value shape,
87447f75930SValentin Clement           mlir::Value slice, mlir::ValueRange indices,
87547f75930SValentin Clement           mlir::ValueRange typeparams, bool skipOrig = false) {
87647f75930SValentin Clement   llvm::SmallVector<mlir::Value> originated;
87747f75930SValentin Clement   if (skipOrig)
87847f75930SValentin Clement     originated.assign(indices.begin(), indices.end());
87947f75930SValentin Clement   else
88047f75930SValentin Clement     originated = fir::factory::originateIndices(loc, rewriter, alloc.getType(),
88147f75930SValentin Clement                                                 shape, indices);
88247f75930SValentin Clement   auto seqTy = fir::dyn_cast_ptrOrBoxEleTy(alloc.getType());
88347f75930SValentin Clement   assert(seqTy && seqTy.isa<fir::SequenceType>());
88447f75930SValentin Clement   const auto dimension = seqTy.cast<fir::SequenceType>().getDimension();
88547f75930SValentin Clement   mlir::Value result = rewriter.create<fir::ArrayCoorOp>(
88647f75930SValentin Clement       loc, eleTy, alloc, shape, slice,
88747f75930SValentin Clement       llvm::ArrayRef<mlir::Value>{originated}.take_front(dimension),
88847f75930SValentin Clement       typeparams);
88947f75930SValentin Clement   if (dimension < originated.size())
89047f75930SValentin Clement     result = rewriter.create<fir::CoordinateOp>(
89147f75930SValentin Clement         loc, resTy, result,
89247f75930SValentin Clement         llvm::ArrayRef<mlir::Value>{originated}.drop_front(dimension));
89347f75930SValentin Clement   return result;
89447f75930SValentin Clement }
89547f75930SValentin Clement 
896*beeb86bdSValentin Clement static mlir::Value getCharacterLen(mlir::Location loc, FirOpBuilder &builder,
897*beeb86bdSValentin Clement                                    ArrayLoadOp load, CharacterType charTy) {
898*beeb86bdSValentin Clement   auto charLenTy = builder.getCharacterLengthType();
899*beeb86bdSValentin Clement   if (charTy.hasDynamicLen()) {
900*beeb86bdSValentin Clement     if (load.getMemref().getType().isa<BoxType>()) {
901*beeb86bdSValentin Clement       // The loaded array is an emboxed value. Get the CHARACTER length from
902*beeb86bdSValentin Clement       // the box value.
903*beeb86bdSValentin Clement       auto eleSzInBytes =
904*beeb86bdSValentin Clement           builder.create<BoxEleSizeOp>(loc, charLenTy, load.getMemref());
905*beeb86bdSValentin Clement       auto kindSize =
906*beeb86bdSValentin Clement           builder.getKindMap().getCharacterBitsize(charTy.getFKind());
907*beeb86bdSValentin Clement       auto kindByteSize =
908*beeb86bdSValentin Clement           builder.createIntegerConstant(loc, charLenTy, kindSize / 8);
909*beeb86bdSValentin Clement       return builder.create<mlir::arith::DivSIOp>(loc, eleSzInBytes,
910*beeb86bdSValentin Clement                                                   kindByteSize);
911*beeb86bdSValentin Clement     }
912*beeb86bdSValentin Clement     // The loaded array is a (set of) unboxed values. If the CHARACTER's
913*beeb86bdSValentin Clement     // length is not a constant, it must be provided as a type parameter to
914*beeb86bdSValentin Clement     // the array_load.
915*beeb86bdSValentin Clement     auto typeparams = load.getTypeparams();
916*beeb86bdSValentin Clement     assert(typeparams.size() > 0 && "expected type parameters on array_load");
917*beeb86bdSValentin Clement     return typeparams.back();
918*beeb86bdSValentin Clement   }
919*beeb86bdSValentin Clement   // The typical case: the length of the CHARACTER is a compile-time
920*beeb86bdSValentin Clement   // constant that is encoded in the type information.
921*beeb86bdSValentin Clement   return builder.createIntegerConstant(loc, charLenTy, charTy.getLen());
922*beeb86bdSValentin Clement }
923*beeb86bdSValentin Clement /// Generate a shallow array copy. This is used for both copy-in and copy-out.
924*beeb86bdSValentin Clement template <bool CopyIn>
925*beeb86bdSValentin Clement void genArrayCopy(mlir::Location loc, mlir::PatternRewriter &rewriter,
926*beeb86bdSValentin Clement                   mlir::Value dst, mlir::Value src, mlir::Value shapeOp,
927*beeb86bdSValentin Clement                   mlir::Value sliceOp, ArrayLoadOp arrLoad) {
928*beeb86bdSValentin Clement   auto insPt = rewriter.saveInsertionPoint();
929*beeb86bdSValentin Clement   llvm::SmallVector<mlir::Value> indices;
930*beeb86bdSValentin Clement   llvm::SmallVector<mlir::Value> extents;
931*beeb86bdSValentin Clement   bool copyUsingSlice =
932*beeb86bdSValentin Clement       getAdjustedExtents(loc, rewriter, arrLoad, extents, shapeOp);
933*beeb86bdSValentin Clement   auto idxTy = rewriter.getIndexType();
934*beeb86bdSValentin Clement   // Build loop nest from column to row.
935*beeb86bdSValentin Clement   for (auto sh : llvm::reverse(extents)) {
936*beeb86bdSValentin Clement     auto ubi = rewriter.create<ConvertOp>(loc, idxTy, sh);
937*beeb86bdSValentin Clement     auto zero = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 0);
938*beeb86bdSValentin Clement     auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1);
939*beeb86bdSValentin Clement     auto ub = rewriter.create<mlir::arith::SubIOp>(loc, idxTy, ubi, one);
940*beeb86bdSValentin Clement     auto loop = rewriter.create<DoLoopOp>(loc, zero, ub, one);
941*beeb86bdSValentin Clement     rewriter.setInsertionPointToStart(loop.getBody());
942*beeb86bdSValentin Clement     indices.push_back(loop.getInductionVar());
943*beeb86bdSValentin Clement   }
944*beeb86bdSValentin Clement   // Reverse the indices so they are in column-major order.
945*beeb86bdSValentin Clement   std::reverse(indices.begin(), indices.end());
946*beeb86bdSValentin Clement   auto typeparams = arrLoad.getTypeparams();
947*beeb86bdSValentin Clement   auto fromAddr = rewriter.create<ArrayCoorOp>(
948*beeb86bdSValentin Clement       loc, getEleTy(src.getType()), src, shapeOp,
949*beeb86bdSValentin Clement       CopyIn && copyUsingSlice ? sliceOp : mlir::Value{},
950*beeb86bdSValentin Clement       factory::originateIndices(loc, rewriter, src.getType(), shapeOp, indices),
951*beeb86bdSValentin Clement       typeparams);
952*beeb86bdSValentin Clement   auto toAddr = rewriter.create<ArrayCoorOp>(
953*beeb86bdSValentin Clement       loc, getEleTy(dst.getType()), dst, shapeOp,
954*beeb86bdSValentin Clement       !CopyIn && copyUsingSlice ? sliceOp : mlir::Value{},
955*beeb86bdSValentin Clement       factory::originateIndices(loc, rewriter, dst.getType(), shapeOp, indices),
956*beeb86bdSValentin Clement       typeparams);
957*beeb86bdSValentin Clement   auto eleTy = unwrapSequenceType(unwrapPassByRefType(dst.getType()));
958*beeb86bdSValentin Clement   auto module = toAddr->getParentOfType<mlir::ModuleOp>();
959*beeb86bdSValentin Clement   FirOpBuilder builder(rewriter, getKindMapping(module));
960*beeb86bdSValentin Clement   // Copy from (to) object to (from) temp copy of same object.
961*beeb86bdSValentin Clement   if (auto charTy = eleTy.dyn_cast<CharacterType>()) {
962*beeb86bdSValentin Clement     auto len = getCharacterLen(loc, builder, arrLoad, charTy);
963*beeb86bdSValentin Clement     CharBoxValue toChar(toAddr, len);
964*beeb86bdSValentin Clement     CharBoxValue fromChar(fromAddr, len);
965*beeb86bdSValentin Clement     factory::genScalarAssignment(builder, loc, toChar, fromChar);
966*beeb86bdSValentin Clement   } else {
967*beeb86bdSValentin Clement     if (hasDynamicSize(eleTy))
968*beeb86bdSValentin Clement       TODO(loc, "copy element of dynamic size");
969*beeb86bdSValentin Clement     factory::genScalarAssignment(builder, loc, toAddr, fromAddr);
970*beeb86bdSValentin Clement   }
971*beeb86bdSValentin Clement   rewriter.restoreInsertionPoint(insPt);
972*beeb86bdSValentin Clement }
973*beeb86bdSValentin Clement 
974*beeb86bdSValentin Clement /// The array load may be either a boxed or unboxed value. If the value is
975*beeb86bdSValentin Clement /// boxed, we read the type parameters from the boxed value.
976*beeb86bdSValentin Clement static llvm::SmallVector<mlir::Value>
977*beeb86bdSValentin Clement genArrayLoadTypeParameters(mlir::Location loc, mlir::PatternRewriter &rewriter,
978*beeb86bdSValentin Clement                            ArrayLoadOp load) {
979*beeb86bdSValentin Clement   if (load.getTypeparams().empty()) {
980*beeb86bdSValentin Clement     auto eleTy =
981*beeb86bdSValentin Clement         unwrapSequenceType(unwrapPassByRefType(load.getMemref().getType()));
982*beeb86bdSValentin Clement     if (hasDynamicSize(eleTy)) {
983*beeb86bdSValentin Clement       if (auto charTy = eleTy.dyn_cast<CharacterType>()) {
984*beeb86bdSValentin Clement         assert(load.getMemref().getType().isa<BoxType>());
985*beeb86bdSValentin Clement         auto module = load->getParentOfType<mlir::ModuleOp>();
986*beeb86bdSValentin Clement         FirOpBuilder builder(rewriter, getKindMapping(module));
987*beeb86bdSValentin Clement         return {getCharacterLen(loc, builder, load, charTy)};
988*beeb86bdSValentin Clement       }
989*beeb86bdSValentin Clement       TODO(loc, "unhandled dynamic type parameters");
990*beeb86bdSValentin Clement     }
991*beeb86bdSValentin Clement     return {};
992*beeb86bdSValentin Clement   }
993*beeb86bdSValentin Clement   return load.getTypeparams();
994*beeb86bdSValentin Clement }
995*beeb86bdSValentin Clement 
996*beeb86bdSValentin Clement static llvm::SmallVector<mlir::Value>
997*beeb86bdSValentin Clement findNonconstantExtents(mlir::Type memrefTy,
998*beeb86bdSValentin Clement                        llvm::ArrayRef<mlir::Value> extents) {
999*beeb86bdSValentin Clement   llvm::SmallVector<mlir::Value> nce;
1000*beeb86bdSValentin Clement   auto arrTy = unwrapPassByRefType(memrefTy);
1001*beeb86bdSValentin Clement   auto seqTy = arrTy.cast<SequenceType>();
1002*beeb86bdSValentin Clement   for (auto [s, x] : llvm::zip(seqTy.getShape(), extents))
1003*beeb86bdSValentin Clement     if (s == SequenceType::getUnknownExtent())
1004*beeb86bdSValentin Clement       nce.emplace_back(x);
1005*beeb86bdSValentin Clement   if (extents.size() > seqTy.getShape().size())
1006*beeb86bdSValentin Clement     for (auto x : extents.drop_front(seqTy.getShape().size()))
1007*beeb86bdSValentin Clement       nce.emplace_back(x);
1008*beeb86bdSValentin Clement   return nce;
1009*beeb86bdSValentin Clement }
1010*beeb86bdSValentin Clement 
101147f75930SValentin Clement namespace {
101247f75930SValentin Clement /// Conversion of fir.array_update and fir.array_modify Ops.
101347f75930SValentin Clement /// If there is a conflict for the update, then we need to perform a
101447f75930SValentin Clement /// copy-in/copy-out to preserve the original values of the array. If there is
101547f75930SValentin Clement /// no conflict, then it is save to eschew making any copies.
101647f75930SValentin Clement template <typename ArrayOp>
101747f75930SValentin Clement class ArrayUpdateConversionBase : public mlir::OpRewritePattern<ArrayOp> {
101847f75930SValentin Clement public:
1019*beeb86bdSValentin Clement   // TODO: Implement copy/swap semantics?
102047f75930SValentin Clement   explicit ArrayUpdateConversionBase(mlir::MLIRContext *ctx,
102147f75930SValentin Clement                                      const ArrayCopyAnalysis &a,
102247f75930SValentin Clement                                      const OperationUseMapT &m)
102347f75930SValentin Clement       : mlir::OpRewritePattern<ArrayOp>{ctx}, analysis{a}, useMap{m} {}
102447f75930SValentin Clement 
1025*beeb86bdSValentin Clement   /// The array_access, \p access, is to be to a cloned copy due to a potential
1026*beeb86bdSValentin Clement   /// conflict. Uses copy-in/copy-out semantics and not copy/swap.
1027*beeb86bdSValentin Clement   mlir::Value referenceToClone(mlir::Location loc,
1028*beeb86bdSValentin Clement                                mlir::PatternRewriter &rewriter,
1029*beeb86bdSValentin Clement                                ArrayOp access) const {
1030*beeb86bdSValentin Clement     LLVM_DEBUG(llvm::dbgs()
1031*beeb86bdSValentin Clement                << "generating copy-in/copy-out loops for " << access << '\n');
1032*beeb86bdSValentin Clement     auto *op = access.getOperation();
1033*beeb86bdSValentin Clement     auto *loadOp = useMap.lookup(op);
1034*beeb86bdSValentin Clement     auto load = mlir::cast<ArrayLoadOp>(loadOp);
1035*beeb86bdSValentin Clement     auto eleTy = access.getType();
1036*beeb86bdSValentin Clement     rewriter.setInsertionPoint(loadOp);
1037*beeb86bdSValentin Clement     // Copy in.
103847f75930SValentin Clement     llvm::SmallVector<mlir::Value> extents;
1039*beeb86bdSValentin Clement     bool copyUsingSlice = false;
1040*beeb86bdSValentin Clement     auto shapeOp = getOrReadExtentsAndShapeOp(loc, rewriter, load, extents,
1041*beeb86bdSValentin Clement                                               copyUsingSlice);
1042*beeb86bdSValentin Clement     llvm::SmallVector<mlir::Value> nonconstantExtents =
1043*beeb86bdSValentin Clement         findNonconstantExtents(load.getMemref().getType(), extents);
1044*beeb86bdSValentin Clement     auto allocmem = rewriter.create<AllocMemOp>(
1045*beeb86bdSValentin Clement         loc, dyn_cast_ptrOrBoxEleTy(load.getMemref().getType()),
1046*beeb86bdSValentin Clement         genArrayLoadTypeParameters(loc, rewriter, load), nonconstantExtents);
1047*beeb86bdSValentin Clement     genArrayCopy</*copyIn=*/true>(load.getLoc(), rewriter, allocmem,
1048*beeb86bdSValentin Clement                                   load.getMemref(), shapeOp, load.getSlice(),
1049*beeb86bdSValentin Clement                                   load);
1050*beeb86bdSValentin Clement     // Generate the reference for the access.
1051*beeb86bdSValentin Clement     rewriter.setInsertionPoint(op);
1052*beeb86bdSValentin Clement     auto coor =
1053*beeb86bdSValentin Clement         genCoorOp(rewriter, loc, getEleTy(load.getType()), eleTy, allocmem,
1054*beeb86bdSValentin Clement                   shapeOp, copyUsingSlice ? mlir::Value{} : load.getSlice(),
1055*beeb86bdSValentin Clement                   access.getIndices(), load.getTypeparams(),
1056*beeb86bdSValentin Clement                   access->hasAttr(factory::attrFortranArrayOffsets()));
1057*beeb86bdSValentin Clement     // Copy out.
1058*beeb86bdSValentin Clement     auto *storeOp = useMap.lookup(loadOp);
1059*beeb86bdSValentin Clement     auto store = mlir::cast<ArrayMergeStoreOp>(storeOp);
1060*beeb86bdSValentin Clement     rewriter.setInsertionPoint(storeOp);
1061*beeb86bdSValentin Clement     // Copy out.
1062*beeb86bdSValentin Clement     genArrayCopy</*copyIn=*/false>(store.getLoc(), rewriter, store.getMemref(),
1063*beeb86bdSValentin Clement                                    allocmem, shapeOp, store.getSlice(), load);
1064*beeb86bdSValentin Clement     rewriter.create<FreeMemOp>(loc, allocmem);
1065*beeb86bdSValentin Clement     return coor;
106647f75930SValentin Clement   }
106747f75930SValentin Clement 
106847f75930SValentin Clement   /// Copy the RHS element into the LHS and insert copy-in/copy-out between a
106947f75930SValentin Clement   /// temp and the LHS if the analysis found potential overlaps between the RHS
1070*beeb86bdSValentin Clement   /// and LHS arrays. The element copy generator must be provided in \p
107147f75930SValentin Clement   /// assignElement. \p update must be the ArrayUpdateOp or the ArrayModifyOp.
107247f75930SValentin Clement   /// Returns the address of the LHS element inside the loop and the LHS
107347f75930SValentin Clement   /// ArrayLoad result.
107447f75930SValentin Clement   std::pair<mlir::Value, mlir::Value>
107547f75930SValentin Clement   materializeAssignment(mlir::Location loc, mlir::PatternRewriter &rewriter,
107647f75930SValentin Clement                         ArrayOp update,
1077*beeb86bdSValentin Clement                         const std::function<void(mlir::Value)> &assignElement,
107847f75930SValentin Clement                         mlir::Type lhsEltRefType) const {
107947f75930SValentin Clement     auto *op = update.getOperation();
1080*beeb86bdSValentin Clement     auto *loadOp = useMap.lookup(op);
108147f75930SValentin Clement     auto load = mlir::cast<ArrayLoadOp>(loadOp);
108247f75930SValentin Clement     LLVM_DEBUG(llvm::outs() << "does " << load << " have a conflict?\n");
108347f75930SValentin Clement     if (analysis.hasPotentialConflict(loadOp)) {
108447f75930SValentin Clement       // If there is a conflict between the arrays, then we copy the lhs array
108547f75930SValentin Clement       // to a temporary, update the temporary, and copy the temporary back to
108647f75930SValentin Clement       // the lhs array. This yields Fortran's copy-in copy-out array semantics.
108747f75930SValentin Clement       LLVM_DEBUG(llvm::outs() << "Yes, conflict was found\n");
108847f75930SValentin Clement       rewriter.setInsertionPoint(loadOp);
108947f75930SValentin Clement       // Copy in.
109047f75930SValentin Clement       llvm::SmallVector<mlir::Value> extents;
1091*beeb86bdSValentin Clement       bool copyUsingSlice = false;
1092*beeb86bdSValentin Clement       auto shapeOp = getOrReadExtentsAndShapeOp(loc, rewriter, load, extents,
1093*beeb86bdSValentin Clement                                                 copyUsingSlice);
1094*beeb86bdSValentin Clement       llvm::SmallVector<mlir::Value> nonconstantExtents =
1095*beeb86bdSValentin Clement           findNonconstantExtents(load.getMemref().getType(), extents);
109647f75930SValentin Clement       auto allocmem = rewriter.create<AllocMemOp>(
1097149ad3d5SShraiysh Vaishay           loc, dyn_cast_ptrOrBoxEleTy(load.getMemref().getType()),
1098*beeb86bdSValentin Clement           genArrayLoadTypeParameters(loc, rewriter, load), nonconstantExtents);
1099*beeb86bdSValentin Clement       genArrayCopy</*copyIn=*/true>(load.getLoc(), rewriter, allocmem,
1100*beeb86bdSValentin Clement                                     load.getMemref(), shapeOp, load.getSlice(),
1101*beeb86bdSValentin Clement                                     load);
110247f75930SValentin Clement       rewriter.setInsertionPoint(op);
1103*beeb86bdSValentin Clement       auto coor = genCoorOp(
110447f75930SValentin Clement           rewriter, loc, getEleTy(load.getType()), lhsEltRefType, allocmem,
1105*beeb86bdSValentin Clement           shapeOp, copyUsingSlice ? mlir::Value{} : load.getSlice(),
1106*beeb86bdSValentin Clement           update.getIndices(), load.getTypeparams(),
1107*beeb86bdSValentin Clement           update->hasAttr(factory::attrFortranArrayOffsets()));
110847f75930SValentin Clement       assignElement(coor);
1109*beeb86bdSValentin Clement       auto *storeOp = useMap.lookup(loadOp);
111047f75930SValentin Clement       auto store = mlir::cast<ArrayMergeStoreOp>(storeOp);
111147f75930SValentin Clement       rewriter.setInsertionPoint(storeOp);
111247f75930SValentin Clement       // Copy out.
1113*beeb86bdSValentin Clement       genArrayCopy</*copyIn=*/false>(store.getLoc(), rewriter,
1114*beeb86bdSValentin Clement                                      store.getMemref(), allocmem, shapeOp,
1115*beeb86bdSValentin Clement                                      store.getSlice(), load);
111647f75930SValentin Clement       rewriter.create<FreeMemOp>(loc, allocmem);
111747f75930SValentin Clement       return {coor, load.getResult()};
111847f75930SValentin Clement     }
111947f75930SValentin Clement     // Otherwise, when there is no conflict (a possible loop-carried
112047f75930SValentin Clement     // dependence), the lhs array can be updated in place.
112147f75930SValentin Clement     LLVM_DEBUG(llvm::outs() << "No, conflict wasn't found\n");
112247f75930SValentin Clement     rewriter.setInsertionPoint(op);
112347f75930SValentin Clement     auto coorTy = getEleTy(load.getType());
1124*beeb86bdSValentin Clement     auto coor = genCoorOp(rewriter, loc, coorTy, lhsEltRefType,
1125*beeb86bdSValentin Clement                           load.getMemref(), load.getShape(), load.getSlice(),
1126*beeb86bdSValentin Clement                           update.getIndices(), load.getTypeparams(),
1127*beeb86bdSValentin Clement                           update->hasAttr(factory::attrFortranArrayOffsets()));
112847f75930SValentin Clement     assignElement(coor);
112947f75930SValentin Clement     return {coor, load.getResult()};
113047f75930SValentin Clement   }
113147f75930SValentin Clement 
1132*beeb86bdSValentin Clement protected:
113347f75930SValentin Clement   const ArrayCopyAnalysis &analysis;
113447f75930SValentin Clement   const OperationUseMapT &useMap;
113547f75930SValentin Clement };
113647f75930SValentin Clement 
113747f75930SValentin Clement class ArrayUpdateConversion : public ArrayUpdateConversionBase<ArrayUpdateOp> {
113847f75930SValentin Clement public:
113947f75930SValentin Clement   explicit ArrayUpdateConversion(mlir::MLIRContext *ctx,
114047f75930SValentin Clement                                  const ArrayCopyAnalysis &a,
114147f75930SValentin Clement                                  const OperationUseMapT &m)
114247f75930SValentin Clement       : ArrayUpdateConversionBase{ctx, a, m} {}
114347f75930SValentin Clement 
114447f75930SValentin Clement   mlir::LogicalResult
114547f75930SValentin Clement   matchAndRewrite(ArrayUpdateOp update,
114647f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
114747f75930SValentin Clement     auto loc = update.getLoc();
114847f75930SValentin Clement     auto assignElement = [&](mlir::Value coor) {
1149*beeb86bdSValentin Clement       auto input = update.getMerge();
1150*beeb86bdSValentin Clement       if (auto inEleTy = dyn_cast_ptrEleTy(input.getType())) {
1151*beeb86bdSValentin Clement         emitFatalError(loc, "array_update on references not supported");
1152*beeb86bdSValentin Clement       } else {
1153*beeb86bdSValentin Clement         rewriter.create<fir::StoreOp>(loc, input, coor);
1154*beeb86bdSValentin Clement       }
115547f75930SValentin Clement     };
1156149ad3d5SShraiysh Vaishay     auto lhsEltRefType = toRefType(update.getMerge().getType());
115747f75930SValentin Clement     auto [_, lhsLoadResult] = materializeAssignment(
115847f75930SValentin Clement         loc, rewriter, update, assignElement, lhsEltRefType);
115947f75930SValentin Clement     update.replaceAllUsesWith(lhsLoadResult);
116047f75930SValentin Clement     rewriter.replaceOp(update, lhsLoadResult);
116147f75930SValentin Clement     return mlir::success();
116247f75930SValentin Clement   }
116347f75930SValentin Clement };
116447f75930SValentin Clement 
116547f75930SValentin Clement class ArrayModifyConversion : public ArrayUpdateConversionBase<ArrayModifyOp> {
116647f75930SValentin Clement public:
116747f75930SValentin Clement   explicit ArrayModifyConversion(mlir::MLIRContext *ctx,
116847f75930SValentin Clement                                  const ArrayCopyAnalysis &a,
116947f75930SValentin Clement                                  const OperationUseMapT &m)
117047f75930SValentin Clement       : ArrayUpdateConversionBase{ctx, a, m} {}
117147f75930SValentin Clement 
117247f75930SValentin Clement   mlir::LogicalResult
117347f75930SValentin Clement   matchAndRewrite(ArrayModifyOp modify,
117447f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
117547f75930SValentin Clement     auto loc = modify.getLoc();
117647f75930SValentin Clement     auto assignElement = [](mlir::Value) {
117747f75930SValentin Clement       // Assignment already materialized by lowering using lhs element address.
117847f75930SValentin Clement     };
117947f75930SValentin Clement     auto lhsEltRefType = modify.getResult(0).getType();
118047f75930SValentin Clement     auto [lhsEltCoor, lhsLoadResult] = materializeAssignment(
118147f75930SValentin Clement         loc, rewriter, modify, assignElement, lhsEltRefType);
118247f75930SValentin Clement     modify.replaceAllUsesWith(mlir::ValueRange{lhsEltCoor, lhsLoadResult});
118347f75930SValentin Clement     rewriter.replaceOp(modify, mlir::ValueRange{lhsEltCoor, lhsLoadResult});
118447f75930SValentin Clement     return mlir::success();
118547f75930SValentin Clement   }
118647f75930SValentin Clement };
118747f75930SValentin Clement 
118847f75930SValentin Clement class ArrayFetchConversion : public mlir::OpRewritePattern<ArrayFetchOp> {
118947f75930SValentin Clement public:
119047f75930SValentin Clement   explicit ArrayFetchConversion(mlir::MLIRContext *ctx,
119147f75930SValentin Clement                                 const OperationUseMapT &m)
119247f75930SValentin Clement       : OpRewritePattern{ctx}, useMap{m} {}
119347f75930SValentin Clement 
119447f75930SValentin Clement   mlir::LogicalResult
119547f75930SValentin Clement   matchAndRewrite(ArrayFetchOp fetch,
119647f75930SValentin Clement                   mlir::PatternRewriter &rewriter) const override {
119747f75930SValentin Clement     auto *op = fetch.getOperation();
119847f75930SValentin Clement     rewriter.setInsertionPoint(op);
119947f75930SValentin Clement     auto load = mlir::cast<ArrayLoadOp>(useMap.lookup(op));
120047f75930SValentin Clement     auto loc = fetch.getLoc();
1201*beeb86bdSValentin Clement     auto coor =
120247f75930SValentin Clement         genCoorOp(rewriter, loc, getEleTy(load.getType()),
1203149ad3d5SShraiysh Vaishay                   toRefType(fetch.getType()), load.getMemref(), load.getShape(),
1204149ad3d5SShraiysh Vaishay                   load.getSlice(), fetch.getIndices(), load.getTypeparams(),
1205*beeb86bdSValentin Clement                   fetch->hasAttr(factory::attrFortranArrayOffsets()));
1206*beeb86bdSValentin Clement     if (isa_ref_type(fetch.getType()))
1207*beeb86bdSValentin Clement       rewriter.replaceOp(fetch, coor);
1208*beeb86bdSValentin Clement     else
120947f75930SValentin Clement       rewriter.replaceOpWithNewOp<fir::LoadOp>(fetch, coor);
121047f75930SValentin Clement     return mlir::success();
121147f75930SValentin Clement   }
121247f75930SValentin Clement 
121347f75930SValentin Clement private:
121447f75930SValentin Clement   const OperationUseMapT &useMap;
121547f75930SValentin Clement };
121647f75930SValentin Clement 
1217*beeb86bdSValentin Clement /// As array_access op is like an array_fetch op, except that it does not imply
1218*beeb86bdSValentin Clement /// a load op. (It operates in the reference domain.)
1219*beeb86bdSValentin Clement class ArrayAccessConversion : public ArrayUpdateConversionBase<ArrayAccessOp> {
1220*beeb86bdSValentin Clement public:
1221*beeb86bdSValentin Clement   explicit ArrayAccessConversion(mlir::MLIRContext *ctx,
1222*beeb86bdSValentin Clement                                  const ArrayCopyAnalysis &a,
1223*beeb86bdSValentin Clement                                  const OperationUseMapT &m)
1224*beeb86bdSValentin Clement       : ArrayUpdateConversionBase{ctx, a, m} {}
1225*beeb86bdSValentin Clement 
1226*beeb86bdSValentin Clement   mlir::LogicalResult
1227*beeb86bdSValentin Clement   matchAndRewrite(ArrayAccessOp access,
1228*beeb86bdSValentin Clement                   mlir::PatternRewriter &rewriter) const override {
1229*beeb86bdSValentin Clement     auto *op = access.getOperation();
1230*beeb86bdSValentin Clement     auto loc = access.getLoc();
1231*beeb86bdSValentin Clement     if (analysis.inAmendAccessSet(op)) {
1232*beeb86bdSValentin Clement       // This array_access is associated with an array_amend and there is a
1233*beeb86bdSValentin Clement       // conflict. Make a copy to store into.
1234*beeb86bdSValentin Clement       auto result = referenceToClone(loc, rewriter, access);
1235*beeb86bdSValentin Clement       access.replaceAllUsesWith(result);
1236*beeb86bdSValentin Clement       rewriter.replaceOp(access, result);
1237*beeb86bdSValentin Clement       return mlir::success();
1238*beeb86bdSValentin Clement     }
1239*beeb86bdSValentin Clement     rewriter.setInsertionPoint(op);
1240*beeb86bdSValentin Clement     auto load = mlir::cast<ArrayLoadOp>(useMap.lookup(op));
1241*beeb86bdSValentin Clement     auto coor = genCoorOp(rewriter, loc, getEleTy(load.getType()),
1242*beeb86bdSValentin Clement                           toRefType(access.getType()), load.getMemref(),
1243*beeb86bdSValentin Clement                           load.getShape(), load.getSlice(), access.getIndices(),
1244*beeb86bdSValentin Clement                           load.getTypeparams(),
1245*beeb86bdSValentin Clement                           access->hasAttr(factory::attrFortranArrayOffsets()));
1246*beeb86bdSValentin Clement     rewriter.replaceOp(access, coor);
1247*beeb86bdSValentin Clement     return mlir::success();
1248*beeb86bdSValentin Clement   }
1249*beeb86bdSValentin Clement };
1250*beeb86bdSValentin Clement 
1251*beeb86bdSValentin Clement /// An array_amend op is a marker to record which array access is being used to
1252*beeb86bdSValentin Clement /// update an array value. After this pass runs, an array_amend has no
1253*beeb86bdSValentin Clement /// semantics. We rewrite these to undefined values here to remove them while
1254*beeb86bdSValentin Clement /// preserving SSA form.
1255*beeb86bdSValentin Clement class ArrayAmendConversion : public mlir::OpRewritePattern<ArrayAmendOp> {
1256*beeb86bdSValentin Clement public:
1257*beeb86bdSValentin Clement   explicit ArrayAmendConversion(mlir::MLIRContext *ctx)
1258*beeb86bdSValentin Clement       : OpRewritePattern{ctx} {}
1259*beeb86bdSValentin Clement 
1260*beeb86bdSValentin Clement   mlir::LogicalResult
1261*beeb86bdSValentin Clement   matchAndRewrite(ArrayAmendOp amend,
1262*beeb86bdSValentin Clement                   mlir::PatternRewriter &rewriter) const override {
1263*beeb86bdSValentin Clement     auto *op = amend.getOperation();
1264*beeb86bdSValentin Clement     rewriter.setInsertionPoint(op);
1265*beeb86bdSValentin Clement     auto loc = amend.getLoc();
1266*beeb86bdSValentin Clement     auto undef = rewriter.create<UndefOp>(loc, amend.getType());
1267*beeb86bdSValentin Clement     rewriter.replaceOp(amend, undef.getResult());
1268*beeb86bdSValentin Clement     return mlir::success();
1269*beeb86bdSValentin Clement   }
1270*beeb86bdSValentin Clement };
1271*beeb86bdSValentin Clement 
127247f75930SValentin Clement class ArrayValueCopyConverter
127347f75930SValentin Clement     : public ArrayValueCopyBase<ArrayValueCopyConverter> {
127447f75930SValentin Clement public:
1275196c4279SRiver Riddle   void runOnOperation() override {
1276196c4279SRiver Riddle     auto func = getOperation();
127747f75930SValentin Clement     LLVM_DEBUG(llvm::dbgs() << "\n\narray-value-copy pass on function '"
127847f75930SValentin Clement                             << func.getName() << "'\n");
127947f75930SValentin Clement     auto *context = &getContext();
128047f75930SValentin Clement 
128147f75930SValentin Clement     // Perform the conflict analysis.
1282*beeb86bdSValentin Clement     const auto &analysis = getAnalysis<ArrayCopyAnalysis>();
128347f75930SValentin Clement     const auto &useMap = analysis.getUseMap();
128447f75930SValentin Clement 
12859f85c198SRiver Riddle     mlir::RewritePatternSet patterns1(context);
128647f75930SValentin Clement     patterns1.insert<ArrayFetchConversion>(context, useMap);
128747f75930SValentin Clement     patterns1.insert<ArrayUpdateConversion>(context, analysis, useMap);
128847f75930SValentin Clement     patterns1.insert<ArrayModifyConversion>(context, analysis, useMap);
1289*beeb86bdSValentin Clement     patterns1.insert<ArrayAccessConversion>(context, analysis, useMap);
1290*beeb86bdSValentin Clement     patterns1.insert<ArrayAmendConversion>(context);
129147f75930SValentin Clement     mlir::ConversionTarget target(*context);
1292*beeb86bdSValentin Clement     target.addLegalDialect<FIROpsDialect, mlir::scf::SCFDialect,
1293*beeb86bdSValentin Clement                            mlir::arith::ArithmeticDialect,
1294*beeb86bdSValentin Clement                            mlir::func::FuncDialect>();
1295*beeb86bdSValentin Clement     target.addIllegalOp<ArrayAccessOp, ArrayAmendOp, ArrayFetchOp,
1296*beeb86bdSValentin Clement                         ArrayUpdateOp, ArrayModifyOp>();
129747f75930SValentin Clement     // Rewrite the array fetch and array update ops.
129847f75930SValentin Clement     if (mlir::failed(
129947f75930SValentin Clement             mlir::applyPartialConversion(func, target, std::move(patterns1)))) {
130047f75930SValentin Clement       mlir::emitError(mlir::UnknownLoc::get(context),
130147f75930SValentin Clement                       "failure in array-value-copy pass, phase 1");
130247f75930SValentin Clement       signalPassFailure();
130347f75930SValentin Clement     }
130447f75930SValentin Clement 
13059f85c198SRiver Riddle     mlir::RewritePatternSet patterns2(context);
130647f75930SValentin Clement     patterns2.insert<ArrayLoadConversion>(context);
130747f75930SValentin Clement     patterns2.insert<ArrayMergeStoreConversion>(context);
130847f75930SValentin Clement     target.addIllegalOp<ArrayLoadOp, ArrayMergeStoreOp>();
130947f75930SValentin Clement     if (mlir::failed(
131047f75930SValentin Clement             mlir::applyPartialConversion(func, target, std::move(patterns2)))) {
131147f75930SValentin Clement       mlir::emitError(mlir::UnknownLoc::get(context),
131247f75930SValentin Clement                       "failure in array-value-copy pass, phase 2");
131347f75930SValentin Clement       signalPassFailure();
131447f75930SValentin Clement     }
131547f75930SValentin Clement   }
131647f75930SValentin Clement };
131747f75930SValentin Clement } // namespace
131847f75930SValentin Clement 
131947f75930SValentin Clement std::unique_ptr<mlir::Pass> fir::createArrayValueCopyPass() {
132047f75930SValentin Clement   return std::make_unique<ArrayValueCopyConverter>();
132147f75930SValentin Clement }
1322