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 ®ion, 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 ®ion) { 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 ®ion) { 3554f30a63cSJean Perier return llvm::any_of( 356*a59f7124SjeanPerier effects, [®ion](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