diff options
Diffstat (limited to 'flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp')
| -rw-r--r-- | flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp | 185 |
1 files changed, 78 insertions, 107 deletions
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp index bf3cf861e46f..0cfefc2d23ec 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/OptimizedBufferization.cpp @@ -87,6 +87,13 @@ private: /// determines if the transformation can be applied to this elemental static std::optional<MatchInfo> findMatch(hlfir::ElementalOp elemental); + /// Returns the array indices for the given hlfir.designate. + /// It recognizes the computations used to transform the one-based indices + /// into the array's lb-based indices, and returns the one-based indices + /// in these cases. + static llvm::SmallVector<mlir::Value> + getDesignatorIndices(hlfir::DesignateOp designate); + public: using mlir::OpRewritePattern<hlfir::ElementalOp>::OpRewritePattern; @@ -430,6 +437,73 @@ bool ArraySectionAnalyzer::isLess(mlir::Value v1, mlir::Value v2) { return false; } +llvm::SmallVector<mlir::Value> +ElementalAssignBufferization::getDesignatorIndices( + hlfir::DesignateOp designate) { + mlir::Value memref = designate.getMemref(); + + // If the object is a box, then the indices may be adjusted + // according to the box's lower bound(s). Scan through + // the computations to try to find the one-based indices. + if (mlir::isa<fir::BaseBoxType>(memref.getType())) { + // Look for the following pattern: + // %13 = fir.load %12 : !fir.ref<!fir.box<...> + // %14:3 = fir.box_dims %13, %c0 : (!fir.box<...>, index) -> ... + // %17 = arith.subi %14#0, %c1 : index + // %18 = arith.addi %arg2, %17 : index + // %19 = hlfir.designate %13 (%18) : (!fir.box<...>, index) -> ... + // + // %arg2 is a one-based index. + + auto isNormalizedLb = [memref](mlir::Value v, unsigned dim) { + // Return true, if v and dim are such that: + // %14:3 = fir.box_dims %13, %dim : (!fir.box<...>, index) -> ... + // %17 = arith.subi %14#0, %c1 : index + // %19 = hlfir.designate %13 (...) : (!fir.box<...>, index) -> ... + if (auto subOp = + mlir::dyn_cast_or_null<mlir::arith::SubIOp>(v.getDefiningOp())) { + auto cst = fir::getIntIfConstant(subOp.getRhs()); + if (!cst || *cst != 1) + return false; + if (auto dimsOp = mlir::dyn_cast_or_null<fir::BoxDimsOp>( + subOp.getLhs().getDefiningOp())) { + if (memref != dimsOp.getVal() || + dimsOp.getResult(0) != subOp.getLhs()) + return false; + auto dimsOpDim = fir::getIntIfConstant(dimsOp.getDim()); + return dimsOpDim && dimsOpDim == dim; + } + } + return false; + }; + + llvm::SmallVector<mlir::Value> newIndices; + for (auto index : llvm::enumerate(designate.getIndices())) { + if (auto addOp = mlir::dyn_cast_or_null<mlir::arith::AddIOp>( + index.value().getDefiningOp())) { + for (unsigned opNum = 0; opNum < 2; ++opNum) + if (isNormalizedLb(addOp->getOperand(opNum), index.index())) { + newIndices.push_back(addOp->getOperand((opNum + 1) % 2)); + break; + } + + // If new one-based index was not added, exit early. + if (newIndices.size() <= index.index()) + break; + } + } + + // If any of the indices is not adjusted to the array's lb, + // then return the original designator indices. + if (newIndices.size() != designate.getIndices().size()) + return designate.getIndices(); + + return newIndices; + } + + return designate.getIndices(); +} + std::optional<ElementalAssignBufferization::MatchInfo> ElementalAssignBufferization::findMatch(hlfir::ElementalOp elemental) { mlir::Operation::user_range users = elemental->getUsers(); @@ -557,7 +631,7 @@ ElementalAssignBufferization::findMatch(hlfir::ElementalOp elemental) { << " at " << elemental.getLoc() << "\n"); return std::nullopt; } - auto indices = designate.getIndices(); + auto indices = getDesignatorIndices(designate); auto elementalIndices = elemental.getIndices(); if (indices.size() == elementalIndices.size() && std::equal(indices.begin(), indices.end(), elementalIndices.begin(), @@ -698,108 +772,6 @@ llvm::LogicalResult BroadcastAssignBufferization::matchAndRewrite( return mlir::success(); } -/// Expand hlfir.assign of array RHS to array LHS into a loop nest -/// of element-by-element assignments: -/// hlfir.assign %4 to %5 : !fir.ref<!fir.array<3x3xf32>>, -/// !fir.ref<!fir.array<3x3xf32>> -/// into: -/// fir.do_loop %arg1 = %c1 to %c3 step %c1 unordered { -/// fir.do_loop %arg2 = %c1 to %c3 step %c1 unordered { -/// %6 = hlfir.designate %4 (%arg2, %arg1) : -/// (!fir.ref<!fir.array<3x3xf32>>, index, index) -> !fir.ref<f32> -/// %7 = fir.load %6 : !fir.ref<f32> -/// %8 = hlfir.designate %5 (%arg2, %arg1) : -/// (!fir.ref<!fir.array<3x3xf32>>, index, index) -> !fir.ref<f32> -/// hlfir.assign %7 to %8 : f32, !fir.ref<f32> -/// } -/// } -/// -/// The transformation is correct only when LHS and RHS do not alias. -/// This transformation does not support runtime checking for -/// non-conforming LHS/RHS arrays' shapes currently. -class VariableAssignBufferization - : public mlir::OpRewritePattern<hlfir::AssignOp> { -private: -public: - using mlir::OpRewritePattern<hlfir::AssignOp>::OpRewritePattern; - - llvm::LogicalResult - matchAndRewrite(hlfir::AssignOp assign, - mlir::PatternRewriter &rewriter) const override; -}; - -llvm::LogicalResult VariableAssignBufferization::matchAndRewrite( - hlfir::AssignOp assign, mlir::PatternRewriter &rewriter) const { - if (assign.isAllocatableAssignment()) - return rewriter.notifyMatchFailure(assign, "AssignOp may imply allocation"); - - hlfir::Entity rhs{assign.getRhs()}; - - // To avoid conflicts with ElementalAssignBufferization pattern, we avoid - // matching RHS when it is an `ExprType` defined by an `ElementalOp`; which is - // among the main criteria matched by ElementalAssignBufferization. - if (mlir::isa<hlfir::ExprType>(rhs.getType()) && - mlir::isa<hlfir::ElementalOp>(rhs.getDefiningOp())) - return rewriter.notifyMatchFailure( - assign, "RHS is an ExprType defined by ElementalOp"); - - if (!rhs.isArray()) - return rewriter.notifyMatchFailure(assign, - "AssignOp's RHS is not an array"); - - mlir::Type rhsEleTy = rhs.getFortranElementType(); - if (!fir::isa_trivial(rhsEleTy)) - return rewriter.notifyMatchFailure( - assign, "AssignOp's RHS data type is not trivial"); - - hlfir::Entity lhs{assign.getLhs()}; - if (!lhs.isArray()) - return rewriter.notifyMatchFailure(assign, - "AssignOp's LHS is not an array"); - - mlir::Type lhsEleTy = lhs.getFortranElementType(); - if (!fir::isa_trivial(lhsEleTy)) - return rewriter.notifyMatchFailure( - assign, "AssignOp's LHS data type is not trivial"); - - if (lhsEleTy != rhsEleTy) - return rewriter.notifyMatchFailure(assign, - "RHS/LHS element types mismatch"); - - fir::AliasAnalysis aliasAnalysis; - mlir::AliasResult aliasRes = aliasAnalysis.alias(lhs, rhs); - // TODO: use areIdenticalOrDisjointSlices() to check if - // we can still do the expansion. - if (!aliasRes.isNo()) { - LLVM_DEBUG(llvm::dbgs() << "VariableAssignBufferization:\n" - << "\tLHS: " << lhs << "\n" - << "\tRHS: " << rhs << "\n" - << "\tALIAS: " << aliasRes << "\n"); - return rewriter.notifyMatchFailure(assign, "RHS/LHS may alias"); - } - - mlir::Location loc = assign->getLoc(); - fir::FirOpBuilder builder(rewriter, assign.getOperation()); - builder.setInsertionPoint(assign); - rhs = hlfir::derefPointersAndAllocatables(loc, builder, rhs); - lhs = hlfir::derefPointersAndAllocatables(loc, builder, lhs); - mlir::Value shape = hlfir::genShape(loc, builder, lhs); - llvm::SmallVector<mlir::Value> extents = - hlfir::getIndexExtents(loc, builder, shape); - hlfir::LoopNest loopNest = - hlfir::genLoopNest(loc, builder, extents, /*isUnordered=*/true, - flangomp::shouldUseWorkshareLowering(assign)); - builder.setInsertionPointToStart(loopNest.body); - auto rhsArrayElement = - hlfir::getElementAt(loc, builder, rhs, loopNest.oneBasedIndices); - rhsArrayElement = hlfir::loadTrivialScalar(loc, builder, rhsArrayElement); - auto lhsArrayElement = - hlfir::getElementAt(loc, builder, lhs, loopNest.oneBasedIndices); - builder.create<hlfir::AssignOp>(loc, rhsArrayElement, lhsArrayElement); - rewriter.eraseOp(assign); - return mlir::success(); -} - using GenBodyFn = std::function<mlir::Value(fir::FirOpBuilder &, mlir::Location, mlir::Value, const llvm::SmallVectorImpl<mlir::Value> &)>; @@ -1206,9 +1178,9 @@ public: loc, resultArr, builder.createBool(loc, false)); // Check all the users - the destroy is no longer required, and any assign - // can use resultArr directly so that VariableAssignBufferization in this - // pass can optimize the results. Other operations are replaces with an - // AsExpr for the temporary resultArr. + // can use resultArr directly so that InlineHLFIRAssign pass + // can optimize the results. Other operations are replaced with an AsExpr + // for the temporary resultArr. llvm::SmallVector<hlfir::DestroyOp> destroys; llvm::SmallVector<hlfir::AssignOp> assigns; for (auto user : mloc->getUsers()) { @@ -1356,7 +1328,6 @@ public: // This requires small code reordering in ElementalAssignBufferization. patterns.insert<ElementalAssignBufferization>(context); patterns.insert<BroadcastAssignBufferization>(context); - patterns.insert<VariableAssignBufferization>(context); patterns.insert<EvaluateIntoMemoryAssignBufferization>(context); patterns.insert<ReductionConversion<hlfir::CountOp>>(context); patterns.insert<ReductionConversion<hlfir::AnyOp>>(context); |
