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 ®ion : 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 ©UsingSlice) {
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