//===- PropagateFortranVariableAttributes.cpp -----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file /// This file defines a pass that propagates FortranVariableFlagsAttr /// attributes through HLFIR. For example, it can set contiguous attribute /// on hlfir.designate that produces a contiguous slice of a contiguous /// Fortran array. This pass can be applied multiple times to expose /// more Fortran attributes, e.g. after inlining and constant propagation. //===----------------------------------------------------------------------===// #include "flang/Optimizer/Builder/HLFIRTools.h" #include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "flang/Optimizer/HLFIR/HLFIRDialect.h" #include "flang/Optimizer/HLFIR/HLFIROps.h" #include "flang/Optimizer/HLFIR/Passes.h" #include "llvm/ADT/TypeSwitch.h" namespace hlfir { #define GEN_PASS_DEF_PROPAGATEFORTRANVARIABLEATTRIBUTES #include "flang/Optimizer/HLFIR/Passes.h.inc" } // namespace hlfir #define DEBUG_TYPE "propagate-fortran-attrs" namespace { class PropagateFortranVariableAttributes : public hlfir::impl::PropagateFortranVariableAttributesBase< PropagateFortranVariableAttributes> { public: using PropagateFortranVariableAttributesBase< PropagateFortranVariableAttributes>:: PropagateFortranVariableAttributesBase; void runOnOperation() override; }; class Propagator { public: void process(mlir::Operation *op); private: static bool isContiguous(mlir::Operation *op) { // Treat data allocations as contiguous, so that we can propagate // the continuity from them. Allocations of fir.box must not be treated // as contiguous. if (mlir::isa(op) && !mlir::isa( fir::unwrapRefType(op->getResult(0).getType()))) return true; auto varOp = mlir::dyn_cast(op); if (!varOp) return false; return hlfir::Entity{varOp}.isSimplyContiguous(); } static void setContiguousAttr(fir::FortranVariableOpInterface op); }; } // namespace void Propagator::setContiguousAttr(fir::FortranVariableOpInterface op) { LLVM_DEBUG(llvm::dbgs() << "Setting continuity for:\n" << op << "\n"); fir::FortranVariableFlagsEnum attrs = op.getFortranAttrs().value_or(fir::FortranVariableFlagsEnum::None); attrs = attrs | fir::FortranVariableFlagsEnum::contiguous; op.setFortranAttrs(attrs); } void Propagator::process(mlir::Operation *op) { if (!isContiguous(op)) return; llvm::SmallVector workList{op}; while (!workList.empty()) { mlir::Operation *current = workList.pop_back_val(); LLVM_DEBUG(llvm::dbgs() << "Propagating continuity from operation:\n" << *current << "\n"); for (mlir::OpOperand &use : current->getUses()) { mlir::Operation *useOp = use.getOwner(); if (auto varOp = mlir::dyn_cast(useOp)) { // If the user is not currently contiguous, set the contiguous // attribute and skip it. The propagation will pick it up later. mlir::Value memref; mlir::TypeSwitch(useOp) .Case( [&](auto op) { memref = op.getMemref(); }) .Default([&](auto op) {}); if (memref == use.get() && !isContiguous(varOp)) { // Make additional checks for hlfir.designate. if (auto designateOp = mlir::dyn_cast(useOp)) if (!hlfir::designatePreservesContinuity(designateOp)) continue; setContiguousAttr(varOp); } continue; } mlir::TypeSwitch(useOp) .Case( [&](fir::ConvertOp op) { workList.push_back(op.getOperation()); }) .Case([&](fir::EmboxOp op) { if (op.getMemref() == use.get()) workList.push_back(op.getOperation()); }) .Case([&](fir::ReboxOp op) { if (op.getBox() == use.get() && fir::reboxPreservesContinuity(op)) workList.push_back(op.getOperation()); }); } } } void PropagateFortranVariableAttributes::runOnOperation() { mlir::Operation *rootOp = getOperation(); mlir::MLIRContext *context = &getContext(); mlir::RewritePatternSet patterns(context); Propagator propagator; rootOp->walk([&](mlir::Operation *op) { propagator.process(op); return mlir::WalkResult::advance(); }); }