| 1 | //===- PropagateFortranVariableAttributes.cpp -----------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | /// \file |
| 9 | /// This file defines a pass that propagates FortranVariableFlagsAttr |
| 10 | /// attributes through HLFIR. For example, it can set contiguous attribute |
| 11 | /// on hlfir.designate that produces a contiguous slice of a contiguous |
| 12 | /// Fortran array. This pass can be applied multiple times to expose |
| 13 | /// more Fortran attributes, e.g. after inlining and constant propagation. |
| 14 | //===----------------------------------------------------------------------===// |
| 15 | |
| 16 | #include "flang/Optimizer/Builder/HLFIRTools.h" |
| 17 | #include "flang/Optimizer/Dialect/FIROpsSupport.h" |
| 18 | #include "flang/Optimizer/HLFIR/HLFIRDialect.h" |
| 19 | #include "flang/Optimizer/HLFIR/HLFIROps.h" |
| 20 | #include "flang/Optimizer/HLFIR/Passes.h" |
| 21 | #include "llvm/ADT/TypeSwitch.h" |
| 22 | |
| 23 | namespace hlfir { |
| 24 | #define GEN_PASS_DEF_PROPAGATEFORTRANVARIABLEATTRIBUTES |
| 25 | #include "flang/Optimizer/HLFIR/Passes.h.inc" |
| 26 | } // namespace hlfir |
| 27 | |
| 28 | #define DEBUG_TYPE "propagate-fortran-attrs" |
| 29 | |
| 30 | namespace { |
| 31 | class PropagateFortranVariableAttributes |
| 32 | : public hlfir::impl::PropagateFortranVariableAttributesBase< |
| 33 | PropagateFortranVariableAttributes> { |
| 34 | public: |
| 35 | using PropagateFortranVariableAttributesBase< |
| 36 | PropagateFortranVariableAttributes>:: |
| 37 | PropagateFortranVariableAttributesBase; |
| 38 | void runOnOperation() override; |
| 39 | }; |
| 40 | |
| 41 | class Propagator { |
| 42 | public: |
| 43 | void process(mlir::Operation *op); |
| 44 | |
| 45 | private: |
| 46 | static bool isContiguous(mlir::Operation *op) { |
| 47 | // Treat data allocations as contiguous, so that we can propagate |
| 48 | // the continuity from them. Allocations of fir.box must not be treated |
| 49 | // as contiguous. |
| 50 | if (mlir::isa<fir::AllocaOp, fir::AllocMemOp>(op) && |
| 51 | !mlir::isa<fir::BaseBoxType>( |
| 52 | fir::unwrapRefType(op->getResult(0).getType()))) |
| 53 | return true; |
| 54 | auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(op); |
| 55 | if (!varOp) |
| 56 | return false; |
| 57 | return hlfir::Entity{varOp}.isSimplyContiguous(); |
| 58 | } |
| 59 | |
| 60 | static void setContiguousAttr(fir::FortranVariableOpInterface op); |
| 61 | }; |
| 62 | } // namespace |
| 63 | |
| 64 | void Propagator::setContiguousAttr(fir::FortranVariableOpInterface op) { |
| 65 | LLVM_DEBUG(llvm::dbgs() << "Setting continuity for:\n" << op << "\n" ); |
| 66 | fir::FortranVariableFlagsEnum attrs = |
| 67 | op.getFortranAttrs().value_or(fir::FortranVariableFlagsEnum::None); |
| 68 | attrs = attrs | fir::FortranVariableFlagsEnum::contiguous; |
| 69 | op.setFortranAttrs(attrs); |
| 70 | } |
| 71 | |
| 72 | void Propagator::process(mlir::Operation *op) { |
| 73 | if (!isContiguous(op)) |
| 74 | return; |
| 75 | llvm::SmallVector<mlir::Operation *> workList{op}; |
| 76 | while (!workList.empty()) { |
| 77 | mlir::Operation *current = workList.pop_back_val(); |
| 78 | LLVM_DEBUG(llvm::dbgs() << "Propagating continuity from operation:\n" |
| 79 | << *current << "\n" ); |
| 80 | |
| 81 | for (mlir::OpOperand &use : current->getUses()) { |
| 82 | mlir::Operation *useOp = use.getOwner(); |
| 83 | if (auto varOp = mlir::dyn_cast<fir::FortranVariableOpInterface>(useOp)) { |
| 84 | // If the user is not currently contiguous, set the contiguous |
| 85 | // attribute and skip it. The propagation will pick it up later. |
| 86 | mlir::Value memref; |
| 87 | mlir::TypeSwitch<mlir::Operation *, void>(useOp) |
| 88 | .Case<hlfir::DeclareOp, hlfir::DesignateOp>( |
| 89 | [&](auto op) { memref = op.getMemref(); }) |
| 90 | .Default([&](auto op) {}); |
| 91 | |
| 92 | if (memref == use.get() && !isContiguous(varOp)) { |
| 93 | // Make additional checks for hlfir.designate. |
| 94 | if (auto designateOp = mlir::dyn_cast<hlfir::DesignateOp>(useOp)) |
| 95 | if (!hlfir::designatePreservesContinuity(designateOp)) |
| 96 | continue; |
| 97 | |
| 98 | setContiguousAttr(varOp); |
| 99 | } |
| 100 | continue; |
| 101 | } |
| 102 | mlir::TypeSwitch<mlir::Operation *, void>(useOp) |
| 103 | .Case( |
| 104 | [&](fir::ConvertOp op) { workList.push_back(op.getOperation()); }) |
| 105 | .Case([&](fir::EmboxOp op) { |
| 106 | if (op.getMemref() == use.get()) |
| 107 | workList.push_back(op.getOperation()); |
| 108 | }) |
| 109 | .Case([&](fir::ReboxOp op) { |
| 110 | if (op.getBox() == use.get() && fir::reboxPreservesContinuity(op)) |
| 111 | workList.push_back(op.getOperation()); |
| 112 | }); |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | void PropagateFortranVariableAttributes::runOnOperation() { |
| 118 | mlir::Operation *rootOp = getOperation(); |
| 119 | mlir::MLIRContext *context = &getContext(); |
| 120 | mlir::RewritePatternSet patterns(context); |
| 121 | Propagator propagator; |
| 122 | rootOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) { |
| 123 | propagator.process(op); |
| 124 | return mlir::WalkResult::advance(); |
| 125 | }); |
| 126 | } |
| 127 | |