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