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