xref: /llvm-project/flang/lib/Optimizer/HLFIR/Transforms/ScheduleOrderedAssignments.cpp (revision a59f7124349fc42e6aa8031796f310bb883d95de)
14f30a63cSJean Perier //===- ScheduleOrderedAssignments.cpp -- Ordered Assignment Scheduling ----===//
24f30a63cSJean Perier //
34f30a63cSJean Perier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44f30a63cSJean Perier // See https://llvm.org/LICENSE.txt for license information.
54f30a63cSJean Perier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
64f30a63cSJean Perier //
74f30a63cSJean Perier //===----------------------------------------------------------------------===//
84f30a63cSJean Perier 
94f30a63cSJean Perier #include "ScheduleOrderedAssignments.h"
104f30a63cSJean Perier #include "flang/Optimizer/Analysis/AliasAnalysis.h"
114f30a63cSJean Perier #include "flang/Optimizer/Builder/FIRBuilder.h"
124f30a63cSJean Perier #include "flang/Optimizer/Builder/Todo.h"
134f30a63cSJean Perier #include "flang/Optimizer/Dialect/Support/FIRContext.h"
144f30a63cSJean Perier #include "llvm/ADT/SmallSet.h"
154f30a63cSJean Perier #include "llvm/Support/Debug.h"
164f30a63cSJean Perier 
174f30a63cSJean Perier #define DEBUG_TYPE "flang-ordered-assignment"
184f30a63cSJean Perier 
194f30a63cSJean Perier //===----------------------------------------------------------------------===//
204f30a63cSJean Perier // Scheduling logging utilities for debug and test
214f30a63cSJean Perier //===----------------------------------------------------------------------===//
224f30a63cSJean Perier 
234f30a63cSJean Perier /// Log RAW or WAW conflict.
244f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logConflict(llvm::raw_ostream &os,
254f30a63cSJean Perier                                               mlir::Value writtenOrReadVarA,
264f30a63cSJean Perier                                               mlir::Value writtenVarB);
274f30a63cSJean Perier /// Log when an expression evaluation must be saved.
284f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logSaveEvaluation(llvm::raw_ostream &os,
294f30a63cSJean Perier                                                     unsigned runid,
304f30a63cSJean Perier                                                     mlir::Region &yieldRegion,
314f30a63cSJean Perier                                                     bool anyWrite);
324f30a63cSJean Perier /// Log when an assignment is scheduled.
334f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logAssignmentEvaluation(
344f30a63cSJean Perier     llvm::raw_ostream &os, unsigned runid, hlfir::RegionAssignOp assign);
354f30a63cSJean Perier /// Log when starting to schedule an order assignment tree.
364f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logStartScheduling(
374f30a63cSJean Perier     llvm::raw_ostream &os, hlfir::OrderedAssignmentTreeOpInterface root);
384f30a63cSJean Perier /// Log op if effect value is not known.
394f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logIfUnkownEffectValue(
404f30a63cSJean Perier     llvm::raw_ostream &os, mlir::MemoryEffects::EffectInstance effect,
414f30a63cSJean Perier     mlir::Operation &op);
424f30a63cSJean Perier 
434f30a63cSJean Perier //===----------------------------------------------------------------------===//
444f30a63cSJean Perier // Scheduling Implementation
454f30a63cSJean Perier //===----------------------------------------------------------------------===//
464f30a63cSJean Perier 
474f30a63cSJean Perier namespace {
484f30a63cSJean Perier /// Structure that is in charge of building the schedule. For each
494f30a63cSJean Perier /// hlfir.region_assign inside an ordered assignment tree, it is walked through
504f30a63cSJean Perier /// the parent operations and their "leaf" regions (that contain expression
514f30a63cSJean Perier /// evaluations). The Scheduler analyze the memory effects of these regions
524f30a63cSJean Perier /// against the effect of the current assignment, and if any conflict is found,
534f30a63cSJean Perier /// it will create an action to save the value computed by the region before the
544f30a63cSJean Perier /// assignment evaluation.
554f30a63cSJean Perier class Scheduler {
564f30a63cSJean Perier public:
574f30a63cSJean Perier   Scheduler(bool tryFusingAssignments)
584f30a63cSJean Perier       : tryFusingAssignments{tryFusingAssignments} {}
594f30a63cSJean Perier 
604f30a63cSJean Perier   /// Start scheduling an assignment. Gather the write side effect from the
614f30a63cSJean Perier   /// assignment.
624f30a63cSJean Perier   void startSchedulingAssignment(hlfir::RegionAssignOp assign,
634f30a63cSJean Perier                                  bool leafRegionsMayOnlyRead);
644f30a63cSJean Perier 
654f30a63cSJean Perier   /// Start analysing a set of evaluation regions that can be evaluated in
664f30a63cSJean Perier   /// any order between themselves according to Fortran rules (like the controls
674f30a63cSJean Perier   /// of forall). The point of this is to avoid adding the side effects of
684f30a63cSJean Perier   /// independent evaluations to a run that would save only one of the control.
694f30a63cSJean Perier   void startIndependentEvaluationGroup() {
704f30a63cSJean Perier     assert(independentEvaluationEffects.empty() &&
714f30a63cSJean Perier            "previous group was not finished");
724f30a63cSJean Perier   };
734f30a63cSJean Perier 
744f30a63cSJean Perier   /// Analyze the memory effects of a region containing an expression
754f30a63cSJean Perier   /// evaluation. If any conflict is found with the current assignment, or if
764f30a63cSJean Perier   /// the expression has write effects (which is possible outside of forall),
774f30a63cSJean Perier   /// create an action in the schedule to save the value in the schedule before
784f30a63cSJean Perier   /// evaluating the current assignment. For expression with write effect,
794f30a63cSJean Perier   /// saving them ensures they are evaluated only once. A region whose value
804f30a63cSJean Perier   /// was saved in a previous run is considered to have no side effects with the
814f30a63cSJean Perier   /// current assignment: the saved value will be used.
824f30a63cSJean Perier   void saveEvaluationIfConflict(mlir::Region &yieldRegion,
834f30a63cSJean Perier                                 bool leafRegionsMayOnlyRead,
847095a86fSSlava Zakharin                                 bool yieldIsImplicitRead = true,
857095a86fSSlava Zakharin                                 bool evaluationsMayConflict = false);
864f30a63cSJean Perier 
874f30a63cSJean Perier   /// Finish evaluating a group of independent regions. The current independent
884f30a63cSJean Perier   /// regions effects are added to the "parent" effect list since evaluating the
894f30a63cSJean Perier   /// next analyzed region would require evaluating the current independent
904f30a63cSJean Perier   /// regions.
914f30a63cSJean Perier   void finishIndependentEvaluationGroup() {
924f30a63cSJean Perier     parentEvaluationEffects.append(independentEvaluationEffects.begin(),
934f30a63cSJean Perier                                    independentEvaluationEffects.end());
944f30a63cSJean Perier     independentEvaluationEffects.clear();
954f30a63cSJean Perier   }
964f30a63cSJean Perier 
974f30a63cSJean Perier   /// After all the dependent evaluation regions have been analyzed, create the
984f30a63cSJean Perier   /// action to evaluate the assignment that was being analyzed.
994f30a63cSJean Perier   void finishSchedulingAssignment(hlfir::RegionAssignOp assign);
1004f30a63cSJean Perier 
1014f30a63cSJean Perier   /// Once all the assignments have been analyzed and scheduled, return the
1024f30a63cSJean Perier   /// schedule. The scheduler object should not be used after this call.
1034f30a63cSJean Perier   hlfir::Schedule moveSchedule() { return std::move(schedule); }
1044f30a63cSJean Perier 
1054f30a63cSJean Perier private:
1064f30a63cSJean Perier   /// Save a conflicting region that is evaluating an expression that is
1074f30a63cSJean Perier   /// controlling or masking the current assignment, or is evaluating the
1084f30a63cSJean Perier   /// RHS/LHS.
1094f30a63cSJean Perier   void
1104f30a63cSJean Perier   saveEvaluation(mlir::Region &yieldRegion,
1114f30a63cSJean Perier                  llvm::ArrayRef<mlir::MemoryEffects::EffectInstance> effects,
1124f30a63cSJean Perier                  bool anyWrite);
1134f30a63cSJean Perier 
1144f30a63cSJean Perier   /// Can the current assignment be schedule with the previous run. This is
1154f30a63cSJean Perier   /// only possible if the assignment and all of its dependencies have no side
1164f30a63cSJean Perier   /// effects conflicting with the previous run.
1174f30a63cSJean Perier   bool canFuseAssignmentWithPreviousRun();
1184f30a63cSJean Perier 
1194f30a63cSJean Perier   /// Memory effects of the assignments being lowered.
1204f30a63cSJean Perier   llvm::SmallVector<mlir::MemoryEffects::EffectInstance> assignEffects;
1217095a86fSSlava Zakharin   /// Memory effects of the evaluations implied by the assignments
1227095a86fSSlava Zakharin   /// being lowered. They do not include the implicit writes
1237095a86fSSlava Zakharin   /// to the LHS of the assignments.
1247095a86fSSlava Zakharin   llvm::SmallVector<mlir::MemoryEffects::EffectInstance> assignEvaluateEffects;
1254f30a63cSJean Perier   /// Memory effects of the unsaved evaluation region that are controlling or
1264f30a63cSJean Perier   /// masking the current assignments.
1274f30a63cSJean Perier   llvm::SmallVector<mlir::MemoryEffects::EffectInstance>
1284f30a63cSJean Perier       parentEvaluationEffects;
1294f30a63cSJean Perier   /// Same as parentEvaluationEffects, but for the current "leaf group" being
1304f30a63cSJean Perier   /// analyzed scheduled.
1314f30a63cSJean Perier   llvm::SmallVector<mlir::MemoryEffects::EffectInstance>
1324f30a63cSJean Perier       independentEvaluationEffects;
1334f30a63cSJean Perier 
1344f30a63cSJean Perier   /// Were any region saved for the current assignment?
1354f30a63cSJean Perier   bool savedAnyRegionForCurrentAssignment = false;
1364f30a63cSJean Perier 
1374f30a63cSJean Perier   // Schedule being built.
1384f30a63cSJean Perier   hlfir::Schedule schedule;
1394f30a63cSJean Perier   /// Leaf regions that have been saved so far.
1404f30a63cSJean Perier   llvm::SmallSet<mlir::Region *, 16> savedRegions;
1414f30a63cSJean Perier   /// Is schedule.back() a schedule that is only saving region with read
1424f30a63cSJean Perier   /// effects?
1434f30a63cSJean Perier   bool currentRunIsReadOnly = false;
1444f30a63cSJean Perier 
1454f30a63cSJean Perier   /// Option to tell if the scheduler should try fusing to assignments in the
1464f30a63cSJean Perier   /// same loops.
1474f30a63cSJean Perier   const bool tryFusingAssignments;
1484f30a63cSJean Perier };
1494f30a63cSJean Perier } // namespace
1504f30a63cSJean Perier 
1514f30a63cSJean Perier //===----------------------------------------------------------------------===//
1524f30a63cSJean Perier // Scheduling Implementation : gathering memory effects of nodes.
1534f30a63cSJean Perier //===----------------------------------------------------------------------===//
1544f30a63cSJean Perier 
1554f30a63cSJean Perier /// Is \p var the result of a ForallIndexOp?
1564f30a63cSJean Perier /// Read effects to forall index can be ignored since forall
1574f30a63cSJean Perier /// indices cannot be assigned to.
1584f30a63cSJean Perier static bool isForallIndex(mlir::Value var) {
1594f30a63cSJean Perier   return var &&
1604f30a63cSJean Perier          mlir::isa_and_nonnull<hlfir::ForallIndexOp>(var.getDefiningOp());
1614f30a63cSJean Perier }
1624f30a63cSJean Perier 
1634f30a63cSJean Perier /// Gather the memory effects of the operations contained in a region.
1644f30a63cSJean Perier /// \p mayOnlyRead can be given to exclude some potential write effects that
1654f30a63cSJean Perier /// cannot affect the current scheduling problem because it is known that the
1664f30a63cSJean Perier /// regions are evaluating pure expressions from a Fortran point of view. It is
1674f30a63cSJean Perier /// useful because low level IR in the region may contain operation that lacks
1684f30a63cSJean Perier /// side effect interface, or that are writing temporary variables that may be
1694f30a63cSJean Perier /// hard to identify as such (one would have to prove the write is "local" to
1704f30a63cSJean Perier /// the region even when the alloca may be outside of the region).
1714f30a63cSJean Perier static void gatherMemoryEffects(
1724f30a63cSJean Perier     mlir::Region &region, bool mayOnlyRead,
1734f30a63cSJean Perier     llvm::SmallVectorImpl<mlir::MemoryEffects::EffectInstance> &effects) {
1744f30a63cSJean Perier   /// This analysis is a simple walk of all the operations of the region that is
1754f30a63cSJean Perier   /// evaluating and yielding a value. This is a lot simpler and safer than
1764f30a63cSJean Perier   /// trying to walk back the SSA DAG from the yielded value. But if desired,
1774f30a63cSJean Perier   /// this could be changed.
1784f30a63cSJean Perier   for (mlir::Operation &op : region.getOps()) {
1794f30a63cSJean Perier     if (op.hasTrait<mlir::OpTrait::HasRecursiveMemoryEffects>()) {
1804f30a63cSJean Perier       for (mlir::Region &subRegion : op.getRegions())
1814f30a63cSJean Perier         gatherMemoryEffects(subRegion, mayOnlyRead, effects);
1824f30a63cSJean Perier       // In MLIR, RecursiveMemoryEffects can be combined with
1834f30a63cSJean Perier       // MemoryEffectOpInterface to describe extra effects on top of the
1844f30a63cSJean Perier       // effects of the nested operations.  However, the presence of
1854f30a63cSJean Perier       // RecursiveMemoryEffects and the absence of MemoryEffectOpInterface
1864f30a63cSJean Perier       // implies the operation has no other memory effects than the one of its
1874f30a63cSJean Perier       // nested operations.
1884f30a63cSJean Perier       if (!mlir::isa<mlir::MemoryEffectOpInterface>(op))
1894f30a63cSJean Perier         continue;
1904f30a63cSJean Perier     }
1914f30a63cSJean Perier     mlir::MemoryEffectOpInterface interface =
1924f30a63cSJean Perier         mlir::dyn_cast<mlir::MemoryEffectOpInterface>(op);
1934f30a63cSJean Perier     if (!interface) {
1944f30a63cSJean Perier       LLVM_DEBUG(llvm::dbgs() << "unknown effect: " << op << "\n";);
1954f30a63cSJean Perier       // There is no generic way to know what this operation is reading/writing
1964f30a63cSJean Perier       // to. Assume the worst. No need to continue analyzing the code any
1974f30a63cSJean Perier       // further.
1984f30a63cSJean Perier       effects.emplace_back(mlir::MemoryEffects::Read::get());
1994f30a63cSJean Perier       if (!mayOnlyRead)
2004f30a63cSJean Perier         effects.emplace_back(mlir::MemoryEffects::Write::get());
2014f30a63cSJean Perier       return;
2024f30a63cSJean Perier     }
2034f30a63cSJean Perier     // Collect read/write effects. Alloc/Free effects do not matter, they
2044f30a63cSJean Perier     // are either local to the evaluation region and can be repeated, or, if
2054f30a63cSJean Perier     // they are allocatable/pointer allocation/deallocation, they are conveyed
2064f30a63cSJean Perier     // via the write that is updating the descriptor/allocatable (and there
2074f30a63cSJean Perier     // cannot be any indirect allocatable/pointer allocation/deallocation if
2084f30a63cSJean Perier     // mayOnlyRead is set). When mayOnlyRead is set, local write effects are
2094f30a63cSJean Perier     // also ignored.
2104f30a63cSJean Perier     llvm::SmallVector<mlir::MemoryEffects::EffectInstance> opEffects;
2114f30a63cSJean Perier     interface.getEffects(opEffects);
2124f30a63cSJean Perier     for (auto &effect : opEffects)
2134f30a63cSJean Perier       if (!isForallIndex(effect.getValue())) {
2144f30a63cSJean Perier         if (mlir::isa<mlir::MemoryEffects::Read>(effect.getEffect())) {
2154f30a63cSJean Perier           LLVM_DEBUG(logIfUnkownEffectValue(llvm::dbgs(), effect, op););
2164f30a63cSJean Perier           effects.push_back(effect);
2174f30a63cSJean Perier         } else if (!mayOnlyRead &&
2184f30a63cSJean Perier                    mlir::isa<mlir::MemoryEffects::Write>(effect.getEffect())) {
2194f30a63cSJean Perier           LLVM_DEBUG(logIfUnkownEffectValue(llvm::dbgs(), effect, op););
2204f30a63cSJean Perier           effects.push_back(effect);
2214f30a63cSJean Perier         }
2224f30a63cSJean Perier       }
2234f30a63cSJean Perier   }
2244f30a63cSJean Perier }
2254f30a63cSJean Perier 
2264f30a63cSJean Perier /// Return the entity yielded by a region, or a null value if the region
2274f30a63cSJean Perier /// is not terminated by a yield.
2282c1ae801Sdonald chen static mlir::OpOperand *getYieldedEntity(mlir::Region &region) {
2294f30a63cSJean Perier   if (region.empty() || region.back().empty())
2304f30a63cSJean Perier     return nullptr;
2314f30a63cSJean Perier   if (auto yield = mlir::dyn_cast<hlfir::YieldOp>(region.back().back()))
2322c1ae801Sdonald chen     return &yield.getEntityMutable();
2336c14e849SJean Perier   if (auto elementalAddr =
2346c14e849SJean Perier           mlir::dyn_cast<hlfir::ElementalAddrOp>(region.back().back()))
2352c1ae801Sdonald chen     return &elementalAddr.getYieldOp().getEntityMutable();
2364f30a63cSJean Perier   return nullptr;
2374f30a63cSJean Perier }
2384f30a63cSJean Perier 
2394f30a63cSJean Perier /// Gather the effect of an assignment. This is the implicit write to the LHS
2404f30a63cSJean Perier /// of an assignment. This also includes the effects of the user defined
2414f30a63cSJean Perier /// assignment, if any, but this does not include the effects of evaluating the
2424f30a63cSJean Perier /// RHS and LHS, which occur before the assignment effects in Fortran.
2434f30a63cSJean Perier static void gatherAssignEffects(
2444f30a63cSJean Perier     hlfir::RegionAssignOp regionAssign,
2454f30a63cSJean Perier     bool userDefAssignmentMayOnlyWriteToAssignedVariable,
2464f30a63cSJean Perier     llvm::SmallVectorImpl<mlir::MemoryEffects::EffectInstance> &assignEffects) {
2472c1ae801Sdonald chen   mlir::OpOperand *assignedVar = getYieldedEntity(regionAssign.getLhsRegion());
2486c14e849SJean Perier   assert(assignedVar && "lhs cannot be an empty region");
2494f30a63cSJean Perier   assignEffects.emplace_back(mlir::MemoryEffects::Write::get(), assignedVar);
2504f30a63cSJean Perier 
25192311347SJean Perier   if (!regionAssign.getUserDefinedAssignment().empty()) {
25292311347SJean Perier     // The write effect on the INTENT(OUT) LHS argument is already taken
25392311347SJean Perier     // into account above.
25492311347SJean Perier     // This side effects are "defensive" and could be improved.
25592311347SJean Perier     // On top of the passed RHS argument, user defined assignments (even when
25692311347SJean Perier     // pure) may also read host/used/common variable. Impure user defined
25792311347SJean Perier     // assignments may write to host/used/common variables not passed via
25892311347SJean Perier     // arguments. For now, simply assume the worst. Once fir.call side effects
25992311347SJean Perier     // analysis is improved, it would best to let the call side effects be used
26092311347SJean Perier     // directly.
26192311347SJean Perier     if (userDefAssignmentMayOnlyWriteToAssignedVariable)
26292311347SJean Perier       assignEffects.emplace_back(mlir::MemoryEffects::Read::get());
26392311347SJean Perier     else
26492311347SJean Perier       assignEffects.emplace_back(mlir::MemoryEffects::Write::get());
26592311347SJean Perier   }
2664f30a63cSJean Perier }
2674f30a63cSJean Perier 
2687095a86fSSlava Zakharin /// Gather the effects of evaluations implied by the given assignment.
2697095a86fSSlava Zakharin /// These are the effects of operations from LHS and RHS.
2707095a86fSSlava Zakharin static void gatherAssignEvaluationEffects(
2717095a86fSSlava Zakharin     hlfir::RegionAssignOp regionAssign,
2727095a86fSSlava Zakharin     bool userDefAssignmentMayOnlyWriteToAssignedVariable,
2737095a86fSSlava Zakharin     llvm::SmallVectorImpl<mlir::MemoryEffects::EffectInstance> &assignEffects) {
2747095a86fSSlava Zakharin   gatherMemoryEffects(regionAssign.getLhsRegion(),
2757095a86fSSlava Zakharin                       userDefAssignmentMayOnlyWriteToAssignedVariable,
2767095a86fSSlava Zakharin                       assignEffects);
2777095a86fSSlava Zakharin   gatherMemoryEffects(regionAssign.getRhsRegion(),
2787095a86fSSlava Zakharin                       userDefAssignmentMayOnlyWriteToAssignedVariable,
2797095a86fSSlava Zakharin                       assignEffects);
2807095a86fSSlava Zakharin }
2817095a86fSSlava Zakharin 
2824f30a63cSJean Perier //===----------------------------------------------------------------------===//
2834f30a63cSJean Perier // Scheduling Implementation : finding conflicting memory effects.
2844f30a63cSJean Perier //===----------------------------------------------------------------------===//
2854f30a63cSJean Perier 
2864f30a63cSJean Perier /// Follow addressing and declare like operation to the storage source.
2874f30a63cSJean Perier /// This allows using FIR alias analysis that otherwise does not know
2884f30a63cSJean Perier /// about those operations. This is correct, but ignoring the designate
2894f30a63cSJean Perier /// and declare info may yield false positive regarding aliasing (e.g,
2904f30a63cSJean Perier /// if it could be proved that the variable are different sub-part of
2914f30a63cSJean Perier /// an array).
2924f30a63cSJean Perier static mlir::Value getStorageSource(mlir::Value var) {
2934f30a63cSJean Perier   // TODO: define some kind of View interface for Fortran in FIR,
2944f30a63cSJean Perier   // and use it in the FIR alias analysis.
2954f30a63cSJean Perier   mlir::Value source = var;
2964f30a63cSJean Perier   while (auto *op = source.getDefiningOp()) {
2974f30a63cSJean Perier     if (auto designate = mlir::dyn_cast<hlfir::DesignateOp>(op)) {
2984f30a63cSJean Perier       source = designate.getMemref();
2994f30a63cSJean Perier     } else if (auto declare = mlir::dyn_cast<hlfir::DeclareOp>(op)) {
3004f30a63cSJean Perier       source = declare.getMemref();
3014f30a63cSJean Perier     } else {
3024f30a63cSJean Perier       break;
3034f30a63cSJean Perier     }
3044f30a63cSJean Perier   }
3054f30a63cSJean Perier   return source;
3064f30a63cSJean Perier }
3074f30a63cSJean Perier 
3084f30a63cSJean Perier /// Could there be any read or write in effectsA on a variable written to in
3094f30a63cSJean Perier /// effectsB?
3104f30a63cSJean Perier static bool
3114f30a63cSJean Perier anyRAWorWAW(llvm::ArrayRef<mlir::MemoryEffects::EffectInstance> effectsA,
3124f30a63cSJean Perier             llvm::ArrayRef<mlir::MemoryEffects::EffectInstance> effectsB,
3134f30a63cSJean Perier             fir::AliasAnalysis &aliasAnalysis) {
3144f30a63cSJean Perier   for (const auto &effectB : effectsB)
3154f30a63cSJean Perier     if (mlir::isa<mlir::MemoryEffects::Write>(effectB.getEffect())) {
3164f30a63cSJean Perier       mlir::Value writtenVarB = effectB.getValue();
3174f30a63cSJean Perier       if (writtenVarB)
3184f30a63cSJean Perier         writtenVarB = getStorageSource(writtenVarB);
3194f30a63cSJean Perier       for (const auto &effectA : effectsA)
3204f30a63cSJean Perier         if (mlir::isa<mlir::MemoryEffects::Write, mlir::MemoryEffects::Read>(
3214f30a63cSJean Perier                 effectA.getEffect())) {
3224f30a63cSJean Perier           mlir::Value writtenOrReadVarA = effectA.getValue();
3234f30a63cSJean Perier           if (!writtenVarB || !writtenOrReadVarA) {
3244f30a63cSJean Perier             LLVM_DEBUG(
3254f30a63cSJean Perier                 logConflict(llvm::dbgs(), writtenOrReadVarA, writtenVarB););
3264f30a63cSJean Perier             return true; // unknown conflict.
3274f30a63cSJean Perier           }
3284f30a63cSJean Perier           writtenOrReadVarA = getStorageSource(writtenOrReadVarA);
3294f30a63cSJean Perier           if (!aliasAnalysis.alias(writtenOrReadVarA, writtenVarB).isNo()) {
3304f30a63cSJean Perier             LLVM_DEBUG(
3314f30a63cSJean Perier                 logConflict(llvm::dbgs(), writtenOrReadVarA, writtenVarB););
3324f30a63cSJean Perier             return true;
3334f30a63cSJean Perier           }
3344f30a63cSJean Perier         }
3354f30a63cSJean Perier     }
3364f30a63cSJean Perier   return false;
3374f30a63cSJean Perier }
3384f30a63cSJean Perier 
3394f30a63cSJean Perier /// Could there be any read or write in effectsA on a variable written to in
3404f30a63cSJean Perier /// effectsB, or any read in effectsB on a variable written to in effectsA?
3414f30a63cSJean Perier static bool
3424f30a63cSJean Perier conflict(llvm::ArrayRef<mlir::MemoryEffects::EffectInstance> effectsA,
3434f30a63cSJean Perier          llvm::ArrayRef<mlir::MemoryEffects::EffectInstance> effectsB) {
3444f30a63cSJean Perier   fir::AliasAnalysis aliasAnalysis;
3454f30a63cSJean Perier   // (RAW || WAW) || (WAR || WAW).
3464f30a63cSJean Perier   return anyRAWorWAW(effectsA, effectsB, aliasAnalysis) ||
3474f30a63cSJean Perier          anyRAWorWAW(effectsB, effectsA, aliasAnalysis);
3484f30a63cSJean Perier }
3494f30a63cSJean Perier 
350*a59f7124SjeanPerier /// Could there be any write effects in "effects" affecting memory storages
351*a59f7124SjeanPerier /// that are not local to the current region.
3524f30a63cSJean Perier static bool
353*a59f7124SjeanPerier anyNonLocalWrite(llvm::ArrayRef<mlir::MemoryEffects::EffectInstance> effects,
354*a59f7124SjeanPerier                  mlir::Region &region) {
3554f30a63cSJean Perier   return llvm::any_of(
356*a59f7124SjeanPerier       effects, [&region](const mlir::MemoryEffects::EffectInstance &effect) {
357*a59f7124SjeanPerier         if (mlir::isa<mlir::MemoryEffects::Write>(effect.getEffect())) {
358*a59f7124SjeanPerier           if (mlir::Value v = effect.getValue()) {
359*a59f7124SjeanPerier             v = getStorageSource(v);
360*a59f7124SjeanPerier             if (v.getDefiningOp<fir::AllocaOp>() ||
361*a59f7124SjeanPerier                 v.getDefiningOp<fir::AllocMemOp>())
362*a59f7124SjeanPerier               return !region.isAncestor(v.getParentRegion());
363*a59f7124SjeanPerier           }
364*a59f7124SjeanPerier           return true;
365*a59f7124SjeanPerier         }
366*a59f7124SjeanPerier         return false;
3674f30a63cSJean Perier       });
3684f30a63cSJean Perier }
3694f30a63cSJean Perier 
3704f30a63cSJean Perier //===----------------------------------------------------------------------===//
3714f30a63cSJean Perier // Scheduling Implementation : Scheduler class implementation
3724f30a63cSJean Perier //===----------------------------------------------------------------------===//
3734f30a63cSJean Perier 
3744f30a63cSJean Perier void Scheduler::startSchedulingAssignment(hlfir::RegionAssignOp assign,
3754f30a63cSJean Perier                                           bool leafRegionsMayOnlyRead) {
3764f30a63cSJean Perier   gatherAssignEffects(assign, leafRegionsMayOnlyRead, assignEffects);
3777095a86fSSlava Zakharin   // Unconditionally collect effects of the evaluations of LHS and RHS
3787095a86fSSlava Zakharin   // in case they need to be analyzed for any parent that might be
3797095a86fSSlava Zakharin   // affected by conflicts of these evaluations.
3807095a86fSSlava Zakharin   // This collection migth be skipped, if there are no such parents,
3817095a86fSSlava Zakharin   // but for the time being we run it always.
3827095a86fSSlava Zakharin   gatherAssignEvaluationEffects(assign, leafRegionsMayOnlyRead,
3837095a86fSSlava Zakharin                                 assignEvaluateEffects);
3844f30a63cSJean Perier }
3854f30a63cSJean Perier 
3864f30a63cSJean Perier void Scheduler::saveEvaluationIfConflict(mlir::Region &yieldRegion,
3874f30a63cSJean Perier                                          bool leafRegionsMayOnlyRead,
3887095a86fSSlava Zakharin                                          bool yieldIsImplicitRead,
3897095a86fSSlava Zakharin                                          bool evaluationsMayConflict) {
3904f30a63cSJean Perier   // If the region evaluation was previously executed and saved, the saved
3914f30a63cSJean Perier   // value will be used when evaluating the current assignment and this has
3924f30a63cSJean Perier   // no effects in the current assignment evaluation.
3934f30a63cSJean Perier   if (savedRegions.contains(&yieldRegion))
3944f30a63cSJean Perier     return;
3954f30a63cSJean Perier   llvm::SmallVector<mlir::MemoryEffects::EffectInstance> effects;
3964f30a63cSJean Perier   gatherMemoryEffects(yieldRegion, leafRegionsMayOnlyRead, effects);
3974f30a63cSJean Perier   // Yield has no effect as such, but in the context of order assignments.
3984f30a63cSJean Perier   // The order assignments will usually read the yielded entity (except for
3994f30a63cSJean Perier   // the yielded assignments LHS that is only read if this is an assignment
4004f30a63cSJean Perier   // with a finalizer, or a user defined assignment where the LHS is
4014f30a63cSJean Perier   // intent(inout)).
4024f30a63cSJean Perier   if (yieldIsImplicitRead) {
4032c1ae801Sdonald chen     mlir::OpOperand *entity = getYieldedEntity(yieldRegion);
4042c1ae801Sdonald chen     if (entity && hlfir::isFortranVariableType(entity->get().getType()))
4054f30a63cSJean Perier       effects.emplace_back(mlir::MemoryEffects::Read::get(), entity);
4064f30a63cSJean Perier   }
407*a59f7124SjeanPerier   if (!leafRegionsMayOnlyRead && anyNonLocalWrite(effects, yieldRegion)) {
408*a59f7124SjeanPerier     // Region with write effect must be executed only once (unless all writes
409*a59f7124SjeanPerier     // affect storages allocated inside the region): save it the first time it
410*a59f7124SjeanPerier     // is encountered.
411*a59f7124SjeanPerier     LLVM_DEBUG(llvm::dbgs()
412*a59f7124SjeanPerier                    << "saving eval because write effect prevents re-evaluation"
413*a59f7124SjeanPerier                    << "\n";);
4144f30a63cSJean Perier     saveEvaluation(yieldRegion, effects, /*anyWrite=*/true);
4154f30a63cSJean Perier   } else if (conflict(effects, assignEffects)) {
4164f30a63cSJean Perier     // Region that conflicts with the current assignments must be fully
4174f30a63cSJean Perier     // evaluated and saved before doing the assignment (Note that it may
4184f30a63cSJean Perier     // have already have been evaluated without saving it before, but this
4194f30a63cSJean Perier     // implies that it never conflicted with a prior assignment, so its value
4204f30a63cSJean Perier     // should be the same.)
4214f30a63cSJean Perier     saveEvaluation(yieldRegion, effects, /*anyWrite=*/false);
4227095a86fSSlava Zakharin   } else if (evaluationsMayConflict &&
4237095a86fSSlava Zakharin              conflict(effects, assignEvaluateEffects)) {
4247095a86fSSlava Zakharin     // If evaluations of the assignment may conflict with the yield
4257095a86fSSlava Zakharin     // evaluations, we have to save yield evaluation.
4267095a86fSSlava Zakharin     // For example, a WHERE mask might be written by the masked assignment
4277095a86fSSlava Zakharin     // evaluations, and it has to be saved in this case:
4287095a86fSSlava Zakharin     //   where (mask) r = f() ! function f modifies mask
429*a59f7124SjeanPerier     saveEvaluation(yieldRegion, effects,
430*a59f7124SjeanPerier                    anyNonLocalWrite(effects, yieldRegion));
4314f30a63cSJean Perier   } else {
4324f30a63cSJean Perier     // Can be executed while doing the assignment.
4334f30a63cSJean Perier     independentEvaluationEffects.append(effects.begin(), effects.end());
4344f30a63cSJean Perier   }
4354f30a63cSJean Perier }
4364f30a63cSJean Perier 
4374f30a63cSJean Perier void Scheduler::saveEvaluation(
4384f30a63cSJean Perier     mlir::Region &yieldRegion,
4394f30a63cSJean Perier     llvm::ArrayRef<mlir::MemoryEffects::EffectInstance> effects,
4404f30a63cSJean Perier     bool anyWrite) {
4414f30a63cSJean Perier   savedAnyRegionForCurrentAssignment = true;
4424f30a63cSJean Perier   if (anyWrite) {
4434f30a63cSJean Perier     // Create a new run just for regions with side effect. Further analysis
4444f30a63cSJean Perier     // could try to prove the effects do not conflict with the previous
4454f30a63cSJean Perier     // schedule.
4464f30a63cSJean Perier     schedule.emplace_back(hlfir::Run{});
4474f30a63cSJean Perier     currentRunIsReadOnly = false;
4484f30a63cSJean Perier   } else if (!currentRunIsReadOnly) {
4494f30a63cSJean Perier     // For now, do not try to fuse an evaluation with a previous
4504f30a63cSJean Perier     // run that contains any write effects. One could try to prove
4514f30a63cSJean Perier     // that "effects" do not conflict with the current run assignments.
4524f30a63cSJean Perier     schedule.emplace_back(hlfir::Run{});
4534f30a63cSJean Perier     currentRunIsReadOnly = true;
4544f30a63cSJean Perier   }
4554f30a63cSJean Perier   // Otherwise, save the yielded entity in the current run, that already
4564f30a63cSJean Perier   // saving other read only entities.
4574f30a63cSJean Perier   schedule.back().actions.emplace_back(hlfir::SaveEntity{&yieldRegion});
4584f30a63cSJean Perier   // The run to save the yielded entity will need to evaluate all the unsaved
4594f30a63cSJean Perier   // parent control or masks. Note that these effects may already be in the
4604f30a63cSJean Perier   // current run memoryEffects, but it is just easier always add them, even if
4614f30a63cSJean Perier   // this may add them again.
4624f30a63cSJean Perier   schedule.back().memoryEffects.append(parentEvaluationEffects.begin(),
4634f30a63cSJean Perier                                        parentEvaluationEffects.end());
4644f30a63cSJean Perier   schedule.back().memoryEffects.append(effects.begin(), effects.end());
4654f30a63cSJean Perier   savedRegions.insert(&yieldRegion);
4664f30a63cSJean Perier   LLVM_DEBUG(
4674f30a63cSJean Perier       logSaveEvaluation(llvm::dbgs(), schedule.size(), yieldRegion, anyWrite););
4684f30a63cSJean Perier }
4694f30a63cSJean Perier 
4704f30a63cSJean Perier bool Scheduler::canFuseAssignmentWithPreviousRun() {
4714f30a63cSJean Perier   // If a region was saved for the current assignment, the previous
4724f30a63cSJean Perier   // run is already known to conflict. Skip the analysis.
4734f30a63cSJean Perier   if (savedAnyRegionForCurrentAssignment || schedule.empty())
4744f30a63cSJean Perier     return false;
4754f30a63cSJean Perier   auto &previousRunEffects = schedule.back().memoryEffects;
4764f30a63cSJean Perier   return !conflict(previousRunEffects, assignEffects) &&
4774f30a63cSJean Perier          !conflict(previousRunEffects, parentEvaluationEffects) &&
4784f30a63cSJean Perier          !conflict(previousRunEffects, independentEvaluationEffects);
4794f30a63cSJean Perier }
4804f30a63cSJean Perier 
4814f30a63cSJean Perier void Scheduler::finishSchedulingAssignment(hlfir::RegionAssignOp assign) {
4824f30a63cSJean Perier   // For now, always schedule each assignment in its own run. They could
4834f30a63cSJean Perier   // be done as part of previous assignment runs if it is proven they have
4844f30a63cSJean Perier   // no conflicting effects.
4854f30a63cSJean Perier   currentRunIsReadOnly = false;
4864f30a63cSJean Perier   if (!tryFusingAssignments || !canFuseAssignmentWithPreviousRun())
4874f30a63cSJean Perier     schedule.emplace_back(hlfir::Run{});
4884f30a63cSJean Perier   schedule.back().actions.emplace_back(assign);
4894f30a63cSJean Perier   // TODO: when fusing, it would probably be best to filter the
4904f30a63cSJean Perier   // parentEvaluationEffects that already in the previous run effects (since
4914f30a63cSJean Perier   // assignments may share the same parents), otherwise, this can make the
4924f30a63cSJean Perier   // conflict() calls more and more expensive.
4934f30a63cSJean Perier   schedule.back().memoryEffects.append(parentEvaluationEffects.begin(),
4944f30a63cSJean Perier                                        parentEvaluationEffects.end());
4954f30a63cSJean Perier   schedule.back().memoryEffects.append(assignEffects.begin(),
4964f30a63cSJean Perier                                        assignEffects.end());
4974f30a63cSJean Perier   assignEffects.clear();
4987095a86fSSlava Zakharin   assignEvaluateEffects.clear();
4994f30a63cSJean Perier   parentEvaluationEffects.clear();
5004f30a63cSJean Perier   independentEvaluationEffects.clear();
5014f30a63cSJean Perier   savedAnyRegionForCurrentAssignment = false;
5024f30a63cSJean Perier   LLVM_DEBUG(logAssignmentEvaluation(llvm::dbgs(), schedule.size(), assign));
5034f30a63cSJean Perier }
5044f30a63cSJean Perier 
5054f30a63cSJean Perier //===----------------------------------------------------------------------===//
5064f30a63cSJean Perier // Scheduling Implementation : driving the Scheduler in the assignment tree.
5074f30a63cSJean Perier //===----------------------------------------------------------------------===//
5084f30a63cSJean Perier 
5094f30a63cSJean Perier /// Gather the hlfir.region_assign nested directly and indirectly inside root in
5104f30a63cSJean Perier /// execution order.
5114f30a63cSJean Perier static void
5124f30a63cSJean Perier gatherAssignments(hlfir::OrderedAssignmentTreeOpInterface root,
5134f30a63cSJean Perier                   llvm::SmallVector<hlfir::RegionAssignOp> &assignments) {
5144f30a63cSJean Perier   llvm::SmallVector<mlir::Operation *> nodeStack{root.getOperation()};
5154f30a63cSJean Perier   while (!nodeStack.empty()) {
5164f30a63cSJean Perier     mlir::Operation *node = nodeStack.pop_back_val();
5174f30a63cSJean Perier     if (auto regionAssign = mlir::dyn_cast<hlfir::RegionAssignOp>(node)) {
5184f30a63cSJean Perier       assignments.push_back(regionAssign);
5194f30a63cSJean Perier       continue;
5204f30a63cSJean Perier     }
5214f30a63cSJean Perier     auto nodeIface =
5224f30a63cSJean Perier         mlir::dyn_cast<hlfir::OrderedAssignmentTreeOpInterface>(node);
5234f30a63cSJean Perier     if (nodeIface)
5244f30a63cSJean Perier       if (mlir::Block *block = nodeIface.getSubTreeBlock())
5254f30a63cSJean Perier         for (mlir::Operation &op : llvm::reverse(block->getOperations()))
5264f30a63cSJean Perier           nodeStack.push_back(&op);
5274f30a63cSJean Perier   }
5284f30a63cSJean Perier }
5294f30a63cSJean Perier 
5304f30a63cSJean Perier /// Gather the parents of (not included) \p node in reverse execution order.
5314f30a63cSJean Perier static void gatherParents(
5324f30a63cSJean Perier     hlfir::OrderedAssignmentTreeOpInterface node,
5334f30a63cSJean Perier     llvm::SmallVectorImpl<hlfir::OrderedAssignmentTreeOpInterface> &parents) {
5344f30a63cSJean Perier   while (node) {
5354f30a63cSJean Perier     auto parent =
5364f30a63cSJean Perier         mlir::dyn_cast_or_null<hlfir::OrderedAssignmentTreeOpInterface>(
5374f30a63cSJean Perier             node->getParentOp());
5384f30a63cSJean Perier     if (parent && parent.getSubTreeRegion() == node->getParentRegion()) {
5394f30a63cSJean Perier       parents.push_back(parent);
5404f30a63cSJean Perier       node = parent;
5414f30a63cSJean Perier     } else {
5424f30a63cSJean Perier       break;
5434f30a63cSJean Perier     }
5444f30a63cSJean Perier   }
5454f30a63cSJean Perier }
5464f30a63cSJean Perier 
5474f30a63cSJean Perier // Build the list of the parent nodes for this assignment. The list is built
5484f30a63cSJean Perier // from the closest parent until the ordered assignment tree root (this is the
5494f30a63cSJean Perier // revere of their execution order).
5504f30a63cSJean Perier static void gatherAssignmentParents(
5514f30a63cSJean Perier     hlfir::RegionAssignOp assign,
5524f30a63cSJean Perier     llvm::SmallVectorImpl<hlfir::OrderedAssignmentTreeOpInterface> &parents) {
5534f30a63cSJean Perier   gatherParents(mlir::cast<hlfir::OrderedAssignmentTreeOpInterface>(
5544f30a63cSJean Perier                     assign.getOperation()),
5554f30a63cSJean Perier                 parents);
5564f30a63cSJean Perier }
5574f30a63cSJean Perier 
5584f30a63cSJean Perier hlfir::Schedule
5594f30a63cSJean Perier hlfir::buildEvaluationSchedule(hlfir::OrderedAssignmentTreeOpInterface root,
5604f30a63cSJean Perier                                bool tryFusingAssignments) {
5614f30a63cSJean Perier   LLVM_DEBUG(logStartScheduling(llvm::dbgs(), root););
5624f30a63cSJean Perier   // The expressions inside an hlfir.forall must be pure (with the Fortran
5634f30a63cSJean Perier   // definition of pure). This is not a commitment that there are no operation
5644f30a63cSJean Perier   // with write effect in the regions: entities local to the region may still
5654f30a63cSJean Perier   // be written to (e.g., a temporary accumulator implementing SUM). This is
5664f30a63cSJean Perier   // a commitment that no write effect will affect the scheduling problem, and
5674f30a63cSJean Perier   // that all write effect caught by MLIR analysis can be ignored for the
5684f30a63cSJean Perier   // current problem.
5694f30a63cSJean Perier   const bool leafRegionsMayOnlyRead =
5704f30a63cSJean Perier       mlir::isa<hlfir::ForallOp>(root.getOperation());
5714f30a63cSJean Perier 
5724f30a63cSJean Perier   // Loop through the assignments and schedule them.
5734f30a63cSJean Perier   Scheduler scheduler(tryFusingAssignments);
5744f30a63cSJean Perier   llvm::SmallVector<hlfir::RegionAssignOp> assignments;
5754f30a63cSJean Perier   gatherAssignments(root, assignments);
5764f30a63cSJean Perier   for (hlfir::RegionAssignOp assign : assignments) {
5774f30a63cSJean Perier     scheduler.startSchedulingAssignment(assign, leafRegionsMayOnlyRead);
5784f30a63cSJean Perier     // Go through the list of parents (not including the current
5794f30a63cSJean Perier     // hlfir.region_assign) in Fortran execution order so that any parent leaf
5804f30a63cSJean Perier     // region that must be saved is saved in order.
5814f30a63cSJean Perier     llvm::SmallVector<hlfir::OrderedAssignmentTreeOpInterface> parents;
5824f30a63cSJean Perier     gatherAssignmentParents(assign, parents);
5834f30a63cSJean Perier     for (hlfir::OrderedAssignmentTreeOpInterface parent :
5844f30a63cSJean Perier          llvm::reverse(parents)) {
5854f30a63cSJean Perier       scheduler.startIndependentEvaluationGroup();
5864f30a63cSJean Perier       llvm::SmallVector<mlir::Region *, 4> yieldRegions;
5874f30a63cSJean Perier       parent.getLeafRegions(yieldRegions);
5887095a86fSSlava Zakharin       // TODO: is this really limited to WHERE/ELSEWHERE?
5897095a86fSSlava Zakharin       bool evaluationsMayConflict = mlir::isa<hlfir::WhereOp>(parent) ||
5907095a86fSSlava Zakharin                                     mlir::isa<hlfir::ElseWhereOp>(parent);
5914f30a63cSJean Perier       for (mlir::Region *yieldRegion : yieldRegions)
5927095a86fSSlava Zakharin         scheduler.saveEvaluationIfConflict(*yieldRegion, leafRegionsMayOnlyRead,
5937095a86fSSlava Zakharin                                            /*yieldIsImplicitRead=*/true,
5947095a86fSSlava Zakharin                                            evaluationsMayConflict);
5954f30a63cSJean Perier       scheduler.finishIndependentEvaluationGroup();
5964f30a63cSJean Perier     }
5974f30a63cSJean Perier     // Look for conflicts between the RHS/LHS evaluation and the assignments.
5984f30a63cSJean Perier     // The LHS yield has no implicit read effect on the produced variable (the
5994f30a63cSJean Perier     // variable is not read before the assignment).
6004f30a63cSJean Perier     scheduler.startIndependentEvaluationGroup();
6014f30a63cSJean Perier     scheduler.saveEvaluationIfConflict(assign.getRhsRegion(),
6024f30a63cSJean Perier                                        leafRegionsMayOnlyRead);
603e52a6d77SJean Perier     // There is no point to save the LHS outside of Forall and assignment to a
604e52a6d77SJean Perier     // vector subscripted LHS because the LHS is already fully evaluated and
605e52a6d77SJean Perier     // saved in the resulting SSA address value (that may be a descriptor or
606e52a6d77SJean Perier     // descriptor address).
607e52a6d77SJean Perier     if (mlir::isa<hlfir::ForallOp>(root.getOperation()) ||
608e52a6d77SJean Perier         mlir::isa<hlfir::ElementalAddrOp>(assign.getLhsRegion().back().back()))
6094f30a63cSJean Perier       scheduler.saveEvaluationIfConflict(assign.getLhsRegion(),
6104f30a63cSJean Perier                                          leafRegionsMayOnlyRead,
6114f30a63cSJean Perier                                          /*yieldIsImplicitRead=*/false);
6124f30a63cSJean Perier     scheduler.finishIndependentEvaluationGroup();
6134f30a63cSJean Perier     scheduler.finishSchedulingAssignment(assign);
6144f30a63cSJean Perier   }
6154f30a63cSJean Perier   return scheduler.moveSchedule();
6164f30a63cSJean Perier }
6174f30a63cSJean Perier 
6184f30a63cSJean Perier mlir::Value hlfir::SaveEntity::getSavedValue() {
6192c1ae801Sdonald chen   mlir::OpOperand *saved = getYieldedEntity(*yieldRegion);
6204f30a63cSJean Perier   assert(saved && "SaveEntity must contain region terminated by YieldOp");
6212c1ae801Sdonald chen   return saved->get();
6224f30a63cSJean Perier }
6234f30a63cSJean Perier 
6244f30a63cSJean Perier //===----------------------------------------------------------------------===//
6254f30a63cSJean Perier // Debug and test logging implementation
6264f30a63cSJean Perier //===----------------------------------------------------------------------===//
6274f30a63cSJean Perier 
6284f30a63cSJean Perier static llvm::raw_ostream &printRegionId(llvm::raw_ostream &os,
6294f30a63cSJean Perier                                         mlir::Region &yieldRegion) {
6304f30a63cSJean Perier   mlir::Operation *parent = yieldRegion.getParentOp();
6314f30a63cSJean Perier   if (auto forall = mlir::dyn_cast<hlfir::ForallOp>(parent)) {
6324f30a63cSJean Perier     if (&forall.getLbRegion() == &yieldRegion)
6334f30a63cSJean Perier       os << "lb";
6344f30a63cSJean Perier     else if (&forall.getUbRegion() == &yieldRegion)
6354f30a63cSJean Perier       os << "ub";
6364f30a63cSJean Perier     else if (&forall.getStepRegion() == &yieldRegion)
6374f30a63cSJean Perier       os << "step";
6384f30a63cSJean Perier   } else if (auto assign = mlir::dyn_cast<hlfir::ForallMaskOp>(parent)) {
6394f30a63cSJean Perier     if (&assign.getMaskRegion() == &yieldRegion)
6404f30a63cSJean Perier       os << "mask";
6414f30a63cSJean Perier   } else if (auto assign = mlir::dyn_cast<hlfir::RegionAssignOp>(parent)) {
6424f30a63cSJean Perier     if (&assign.getRhsRegion() == &yieldRegion)
6434f30a63cSJean Perier       os << "rhs";
6444f30a63cSJean Perier     else if (&assign.getLhsRegion() == &yieldRegion)
6454f30a63cSJean Perier       os << "lhs";
646222a8a1bSJean Perier   } else if (auto where = mlir::dyn_cast<hlfir::WhereOp>(parent)) {
647222a8a1bSJean Perier     if (&where.getMaskRegion() == &yieldRegion)
648222a8a1bSJean Perier       os << "mask";
649222a8a1bSJean Perier   } else if (auto elseWhereOp = mlir::dyn_cast<hlfir::ElseWhereOp>(parent)) {
650222a8a1bSJean Perier     if (&elseWhereOp.getMaskRegion() == &yieldRegion)
651222a8a1bSJean Perier       os << "mask";
6524f30a63cSJean Perier   } else {
6534f30a63cSJean Perier     os << "unknown";
6544f30a63cSJean Perier   }
6554f30a63cSJean Perier   return os;
6564f30a63cSJean Perier }
6574f30a63cSJean Perier 
6584f30a63cSJean Perier static llvm::raw_ostream &
6594f30a63cSJean Perier printNodeIndexInBody(llvm::raw_ostream &os,
6604f30a63cSJean Perier                      hlfir::OrderedAssignmentTreeOpInterface node,
6614f30a63cSJean Perier                      hlfir::OrderedAssignmentTreeOpInterface parent) {
6624f30a63cSJean Perier   if (!parent || !parent.getSubTreeRegion())
6634f30a63cSJean Perier     return os;
6644f30a63cSJean Perier   mlir::Operation *nodeOp = node.getOperation();
6654f30a63cSJean Perier   unsigned index = 1;
6664f30a63cSJean Perier   for (mlir::Operation &op : parent.getSubTreeRegion()->getOps())
6674f30a63cSJean Perier     if (nodeOp == &op) {
6684f30a63cSJean Perier       return os << index;
6694f30a63cSJean Perier     } else if (nodeOp->getName() == op.getName()) {
6704f30a63cSJean Perier       ++index;
6714f30a63cSJean Perier     }
6724f30a63cSJean Perier   return os;
6734f30a63cSJean Perier }
6744f30a63cSJean Perier 
6754f30a63cSJean Perier static llvm::raw_ostream &printNodePath(llvm::raw_ostream &os,
6764f30a63cSJean Perier                                         mlir::Operation *op) {
6774f30a63cSJean Perier   auto node =
6784f30a63cSJean Perier       mlir::dyn_cast_or_null<hlfir::OrderedAssignmentTreeOpInterface>(op);
6794f30a63cSJean Perier   if (!node) {
6804f30a63cSJean Perier     os << "unknown node";
6814f30a63cSJean Perier     return os;
6824f30a63cSJean Perier   }
6834f30a63cSJean Perier   llvm::SmallVector<hlfir::OrderedAssignmentTreeOpInterface> parents;
6844f30a63cSJean Perier   gatherParents(node, parents);
6854f30a63cSJean Perier   hlfir::OrderedAssignmentTreeOpInterface previousParent;
6864f30a63cSJean Perier   for (auto parent : llvm::reverse(parents)) {
6874f30a63cSJean Perier     os << parent->getName().stripDialect();
6884f30a63cSJean Perier     printNodeIndexInBody(os, parent, previousParent) << "/";
6894f30a63cSJean Perier     previousParent = parent;
6904f30a63cSJean Perier   }
6914f30a63cSJean Perier   os << node->getName().stripDialect();
6924f30a63cSJean Perier   return printNodeIndexInBody(os, node, previousParent);
6934f30a63cSJean Perier }
6944f30a63cSJean Perier 
6954f30a63cSJean Perier static llvm::raw_ostream &printRegionPath(llvm::raw_ostream &os,
6964f30a63cSJean Perier                                           mlir::Region &yieldRegion) {
6974f30a63cSJean Perier   printNodePath(os, yieldRegion.getParentOp()) << "/";
6984f30a63cSJean Perier   return printRegionId(os, yieldRegion);
6994f30a63cSJean Perier }
7004f30a63cSJean Perier 
7014f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logSaveEvaluation(llvm::raw_ostream &os,
7024f30a63cSJean Perier                                                     unsigned runid,
7034f30a63cSJean Perier                                                     mlir::Region &yieldRegion,
7044f30a63cSJean Perier                                                     bool anyWrite) {
7054f30a63cSJean Perier   os << "run " << runid << " save  " << (anyWrite ? "(w)" : "  ") << ": ";
7064f30a63cSJean Perier   printRegionPath(os, yieldRegion) << "\n";
7074f30a63cSJean Perier }
7084f30a63cSJean Perier 
7094f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logAssignmentEvaluation(
7104f30a63cSJean Perier     llvm::raw_ostream &os, unsigned runid, hlfir::RegionAssignOp assign) {
7114f30a63cSJean Perier   os << "run " << runid << " evaluate: ";
7124f30a63cSJean Perier   printNodePath(os, assign.getOperation()) << "\n";
7134f30a63cSJean Perier }
7144f30a63cSJean Perier 
7154f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logConflict(llvm::raw_ostream &os,
7164f30a63cSJean Perier                                               mlir::Value writtenOrReadVarA,
7174f30a63cSJean Perier                                               mlir::Value writtenVarB) {
7184f30a63cSJean Perier   auto printIfValue = [&](mlir::Value var) -> llvm::raw_ostream & {
7194f30a63cSJean Perier     if (!var)
7204f30a63cSJean Perier       return os << "<unknown>";
7214f30a63cSJean Perier     return os << var;
7224f30a63cSJean Perier   };
7234f30a63cSJean Perier   os << "conflict: R/W: ";
7244f30a63cSJean Perier   printIfValue(writtenOrReadVarA) << " W:";
7254f30a63cSJean Perier   printIfValue(writtenVarB) << "\n";
7264f30a63cSJean Perier }
7274f30a63cSJean Perier 
7284f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logStartScheduling(
7294f30a63cSJean Perier     llvm::raw_ostream &os, hlfir::OrderedAssignmentTreeOpInterface root) {
7304f30a63cSJean Perier   os << "------------ scheduling ";
7314f30a63cSJean Perier   printNodePath(os, root.getOperation());
7324f30a63cSJean Perier   if (auto funcOp = root->getParentOfType<mlir::func::FuncOp>())
7334f30a63cSJean Perier     os << " in " << funcOp.getSymName() << " ";
7344f30a63cSJean Perier   os << "------------\n";
7354f30a63cSJean Perier }
7364f30a63cSJean Perier 
7374f30a63cSJean Perier static void LLVM_ATTRIBUTE_UNUSED logIfUnkownEffectValue(
7384f30a63cSJean Perier     llvm::raw_ostream &os, mlir::MemoryEffects::EffectInstance effect,
7394f30a63cSJean Perier     mlir::Operation &op) {
7404f30a63cSJean Perier   if (effect.getValue() != nullptr)
7414f30a63cSJean Perier     return;
7424f30a63cSJean Perier   os << "unknown effected value (";
7434f30a63cSJean Perier   os << (mlir::isa<mlir::MemoryEffects::Read>(effect.getEffect()) ? "R" : "W");
7444f30a63cSJean Perier   os << "): " << op << "\n";
7454f30a63cSJean Perier }
746