diff --git a/flang/include/flang/Optimizer/HLFIR/Passes.h b/flang/include/flang/Optimizer/HLFIR/Passes.h --- a/flang/include/flang/Optimizer/HLFIR/Passes.h +++ b/flang/include/flang/Optimizer/HLFIR/Passes.h @@ -22,6 +22,7 @@ #include "flang/Optimizer/HLFIR/Passes.h.inc" std::unique_ptr createConvertHLFIRtoFIRPass(); +std::unique_ptr createBufferizeHLFIRPass(); #define GEN_PASS_REGISTRATION #include "flang/Optimizer/HLFIR/Passes.h.inc" diff --git a/flang/include/flang/Optimizer/HLFIR/Passes.td b/flang/include/flang/Optimizer/HLFIR/Passes.td --- a/flang/include/flang/Optimizer/HLFIR/Passes.td +++ b/flang/include/flang/Optimizer/HLFIR/Passes.td @@ -15,4 +15,9 @@ let constructor = "hlfir::createConvertHLFIRtoFIRPass()"; } +def BufferizeHLFIR : Pass<"bufferize-hlfir", "::mlir::ModuleOp"> { + let summary = "Convert HLFIR operations operating on hlfir.expr into operations on memory"; + let constructor = "hlfir::createBufferizeHLFIRPass()"; +} + #endif //FORTRAN_DIALECT_HLFIR_PASSES diff --git a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp @@ -0,0 +1,114 @@ +//===- BufferizeHLFIR.cpp - Bufferize HLFIR ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// This file defines a pass that bufferize hlfir.expr. It translates operations +// producing or consuming hlfir.expr into operations operating on memory. +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Builder/Character.h" +#include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Builder/HLFIRTools.h" +#include "flang/Optimizer/Builder/MutableBox.h" +#include "flang/Optimizer/Builder/Runtime/Assign.h" +#include "flang/Optimizer/Builder/Todo.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Optimizer/HLFIR/HLFIROps.h" +#include "flang/Optimizer/HLFIR/Passes.h" +#include "flang/Optimizer/Support/FIRContext.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Pass/PassManager.h" +#include "mlir/Transforms/DialectConversion.h" + +namespace hlfir { +#define GEN_PASS_DEF_BUFFERIZEHLFIR +#include "flang/Optimizer/HLFIR/Passes.h.inc" +} // namespace hlfir + +namespace { + +struct AssignOpConversion : public mlir::OpConversionPattern { + using mlir::OpConversionPattern::OpConversionPattern; + explicit AssignOpConversion(mlir::MLIRContext *ctx) + : mlir::OpConversionPattern{ctx} {} + mlir::LogicalResult + matchAndRewrite(hlfir::AssignOp assign, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp( + assign, adaptor.getOperands()[0], adaptor.getOperands()[1]); + return mlir::success(); + } +}; + +struct ConcatOpConversion : public mlir::OpConversionPattern { + using mlir::OpConversionPattern::OpConversionPattern; + explicit ConcatOpConversion(mlir::MLIRContext *ctx) + : mlir::OpConversionPattern{ctx} {} + mlir::LogicalResult + matchAndRewrite(hlfir::ConcatOp concat, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + mlir::Location loc = concat->getLoc(); + auto module = concat->getParentOfType(); + fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module)); + assert(adaptor.getStrings().size() >= 2 && + "must have at least two strings operands"); + if (adaptor.getStrings().size() > 2) + TODO(loc, "codegen of optimized chained concatenation of more than two " + "strings"); + hlfir::Entity lhs{adaptor.getStrings()[0]}; + hlfir::Entity rhs{adaptor.getStrings()[1]}; + auto [lhsExv, c1] = hlfir::translateToExtendedValue(loc, builder, lhs); + auto [rhsExv, c2] = hlfir::translateToExtendedValue(loc, builder, rhs); + assert(!c1 && !c2 && "expected variables"); + fir::ExtendedValue res = + fir::factory::CharacterExprHelper{builder, loc}.createConcatenate( + *lhsExv.getCharBox(), *rhsExv.getCharBox()); + auto hlfirTempRes = hlfir::genDeclare(loc, builder, res, "tmp", + fir::FortranVariableFlagsAttr{}); + rewriter.replaceOp(concat, hlfirTempRes); + return mlir::success(); + } +}; + +class BufferizeHLFIR : public hlfir::impl::BufferizeHLFIRBase { +public: + void runOnOperation() override { + // TODO: make this a pass operating on FuncOp. The issue is that + // FirOpBuilder helpers may generate new FuncOp because of runtime/llvm + // intrinsics calls creation. This may create race conflict if the pass is + // scheduleed on FuncOp. A solution could be to provide an optional mutex + // when building a FirOpBuilder and locking around FuncOp and GlobalOp + // creation, but this needs a bit more thinking, so at this point the pass + // is scheduled on the moduleOp. + auto module = this->getOperation(); + auto *context = &getContext(); + mlir::RewritePatternSet patterns(context); + patterns.insert(context); + mlir::ConversionTarget target(*context); + target.markUnknownOpDynamicallyLegal([](mlir::Operation *op) { + return llvm::all_of( + op->getResultTypes(), + [](mlir::Type ty) { return !ty.isa(); }) && + llvm::all_of(op->getOperandTypes(), [](mlir::Type ty) { + return !ty.isa(); + }); + }); + if (mlir::failed( + mlir::applyFullConversion(module, target, std::move(patterns)))) { + mlir::emitError(mlir::UnknownLoc::get(context), + "failure in HLFIR bufferization pass"); + signalPassFailure(); + } + } +}; +} // namespace + +std::unique_ptr hlfir::createBufferizeHLFIRPass() { + return std::make_unique(); +} diff --git a/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt b/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt --- a/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt +++ b/flang/lib/Optimizer/HLFIR/Transforms/CMakeLists.txt @@ -1,6 +1,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_flang_library(HLFIRTransforms + BufferizeHLFIR.cpp ConvertToFIR.cpp DEPENDS diff --git a/flang/test/HLFIR/concat-bufferization.fir b/flang/test/HLFIR/concat-bufferization.fir new file mode 100644 --- /dev/null +++ b/flang/test/HLFIR/concat-bufferization.fir @@ -0,0 +1,127 @@ +// Test hlfir.concat operation lowering to operations operating on memory. + +// RUN: fir-opt %s -bufferize-hlfir | FileCheck %s + + +func.func @concat(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>, %arg2: !fir.boxchar<1>) { + %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %1:2 = hlfir.declare %0#0 typeparams %0#1 {uniq_name = "c1"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %3:2 = hlfir.declare %2#0 typeparams %2#1 {uniq_name = "c2"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + %4:2 = fir.unboxchar %arg2 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %5:2 = hlfir.declare %4#0 typeparams %4#1 {uniq_name = "c3"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + %6 = arith.addi %2#1, %4#1 : index + %7 = hlfir.concat %3#0, %5#0 len %6 : (!fir.boxchar<1>, !fir.boxchar<1>, index) -> !hlfir.expr> + hlfir.assign %7 to %1#0 : !hlfir.expr>, !fir.boxchar<1> + return +} +// CHECK-LABEL: func.func @concat( +// CHECK-SAME: %[[VAL_0:[^:]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:[^:]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_2:[^:]*]]: !fir.boxchar<1>) { +// CHECK: %[[VAL_3:.*]]:2 = fir.unboxchar %[[VAL_0]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +// CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_3]]#0 typeparams %[[VAL_3]]#1 {uniq_name = "c1"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_5:.*]]:2 = fir.unboxchar %[[VAL_1]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +// CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[VAL_5]]#0 typeparams %[[VAL_5]]#1 {uniq_name = "c2"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_7:.*]]:2 = fir.unboxchar %[[VAL_2]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +// CHECK: %[[VAL_8:.*]]:2 = hlfir.declare %[[VAL_7]]#0 typeparams %[[VAL_7]]#1 {uniq_name = "c3"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_9:.*]] = arith.addi %[[VAL_5]]#1, %[[VAL_7]]#1 : index +// CHECK: %[[VAL_10:.*]] = arith.addi %[[VAL_5]]#1, %[[VAL_7]]#1 : index +// CHECK: %[[VAL_11:.*]] = fir.alloca !fir.char<1,?>(%[[VAL_10]] : index) {bindc_name = ".chrtmp"} +// CHECK: %[[VAL_12:.*]] = arith.constant 1 : i64 +// CHECK: %[[VAL_13:.*]] = fir.convert %[[VAL_5]]#1 : (index) -> i64 +// CHECK: %[[VAL_14:.*]] = arith.muli %[[VAL_12]], %[[VAL_13]] : i64 +// CHECK: %[[VAL_15:.*]] = arith.constant false +// CHECK: %[[VAL_16:.*]] = fir.convert %[[VAL_11]] : (!fir.ref>) -> !fir.ref +// CHECK: %[[VAL_17:.*]] = fir.convert %[[VAL_6]]#1 : (!fir.ref>) -> !fir.ref +// CHECK: fir.call @llvm.memmove.p0.p0.i64(%[[VAL_16]], %[[VAL_17]], %[[VAL_14]], %[[VAL_15]]) : (!fir.ref, !fir.ref, i64, i1) -> () +// CHECK: %[[VAL_18:.*]] = arith.constant 1 : index +// CHECK: %[[VAL_19:.*]] = arith.subi %[[VAL_10]], %[[VAL_18]] : index +// CHECK: fir.do_loop %[[VAL_20:.*]] = %[[VAL_5]]#1 to %[[VAL_19]] step %[[VAL_18]] { +// CHECK: %[[VAL_21:.*]] = arith.subi %[[VAL_20]], %[[VAL_5]]#1 : index +// CHECK: %[[VAL_22:.*]] = fir.convert %[[VAL_8]]#1 : (!fir.ref>) -> !fir.ref>> +// CHECK: %[[VAL_23:.*]] = fir.coordinate_of %[[VAL_22]], %[[VAL_21]] : (!fir.ref>>, index) -> !fir.ref> +// CHECK: %[[VAL_24:.*]] = fir.load %[[VAL_23]] : !fir.ref> +// CHECK: %[[VAL_25:.*]] = fir.convert %[[VAL_11]] : (!fir.ref>) -> !fir.ref>> +// CHECK: %[[VAL_26:.*]] = fir.coordinate_of %[[VAL_25]], %[[VAL_20]] : (!fir.ref>>, index) -> !fir.ref> +// CHECK: fir.store %[[VAL_24]] to %[[VAL_26]] : !fir.ref> +// CHECK: } +// CHECK: %[[VAL_27:.*]]:2 = hlfir.declare %[[VAL_11]] typeparams %[[VAL_10]] {uniq_name = "tmp"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: hlfir.assign %[[VAL_27]]#0 to %[[VAL_4]]#0 : !fir.boxchar<1>, !fir.boxchar<1> + + +func.func @concat_chained(%arg0: !fir.boxchar<1>, %arg1: !fir.boxchar<1>, %arg2: !fir.boxchar<1>, %arg3: !fir.boxchar<1>) { + %0:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %1:2 = hlfir.declare %0#0 typeparams %0#1 {uniq_name = "c1"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + %2:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %3:2 = hlfir.declare %2#0 typeparams %2#1 {uniq_name = "c2"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + %4:2 = fir.unboxchar %arg2 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %5:2 = hlfir.declare %4#0 typeparams %4#1 {uniq_name = "c3"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + %6:2 = fir.unboxchar %arg3 : (!fir.boxchar<1>) -> (!fir.ref>, index) + %7:2 = hlfir.declare %6#0 typeparams %6#1 {uniq_name = "c4"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) + %8 = arith.addi %2#1, %4#1 : index + %9 = hlfir.concat %3#0, %5#0 len %8 : (!fir.boxchar<1>, !fir.boxchar<1>, index) -> !hlfir.expr> + %10 = arith.addi %8, %6#1 : index + %11 = hlfir.concat %9, %7#0 len %10 : (!hlfir.expr>, !fir.boxchar<1>, index) -> !hlfir.expr> + hlfir.assign %11 to %1#0 : !hlfir.expr>, !fir.boxchar<1> + return +} +// CHECK-LABEL: func.func @concat_chained( +// CHECK-SAME: %[[VAL_0:[^:]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_1:[^:]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_2:[^:]*]]: !fir.boxchar<1>, +// CHECK-SAME: %[[VAL_3:[^:]*]]: !fir.boxchar<1>) { +// CHECK: %[[VAL_4:.*]]:2 = fir.unboxchar %[[VAL_0]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +// CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_4]]#0 typeparams %[[VAL_4]]#1 {uniq_name = "c1"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_6:.*]]:2 = fir.unboxchar %[[VAL_1]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +// CHECK: %[[VAL_7:.*]]:2 = hlfir.declare %[[VAL_6]]#0 typeparams %[[VAL_6]]#1 {uniq_name = "c2"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_8:.*]]:2 = fir.unboxchar %[[VAL_2]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +// CHECK: %[[VAL_9:.*]]:2 = hlfir.declare %[[VAL_8]]#0 typeparams %[[VAL_8]]#1 {uniq_name = "c3"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_10:.*]]:2 = fir.unboxchar %[[VAL_3]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +// CHECK: %[[VAL_11:.*]]:2 = hlfir.declare %[[VAL_10]]#0 typeparams %[[VAL_10]]#1 {uniq_name = "c4"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_12:.*]] = arith.addi %[[VAL_6]]#1, %[[VAL_8]]#1 : index +// CHECK: %[[VAL_13:.*]] = arith.addi %[[VAL_6]]#1, %[[VAL_8]]#1 : index +// CHECK: %[[VAL_14:.*]] = fir.alloca !fir.char<1,?>(%[[VAL_13]] : index) {bindc_name = ".chrtmp"} +// CHECK: %[[VAL_15:.*]] = arith.constant 1 : i64 +// CHECK: %[[VAL_16:.*]] = fir.convert %[[VAL_6]]#1 : (index) -> i64 +// CHECK: %[[VAL_17:.*]] = arith.muli %[[VAL_15]], %[[VAL_16]] : i64 +// CHECK: %[[VAL_18:.*]] = arith.constant false +// CHECK: %[[VAL_19:.*]] = fir.convert %[[VAL_14]] : (!fir.ref>) -> !fir.ref +// CHECK: %[[VAL_20:.*]] = fir.convert %[[VAL_7]]#1 : (!fir.ref>) -> !fir.ref +// CHECK: fir.call @llvm.memmove.p0.p0.i64(%[[VAL_19]], %[[VAL_20]], %[[VAL_17]], %[[VAL_18]]) : (!fir.ref, !fir.ref, i64, i1) -> () +// CHECK: %[[VAL_21:.*]] = arith.constant 1 : index +// CHECK: %[[VAL_22:.*]] = arith.subi %[[VAL_13]], %[[VAL_21]] : index +// CHECK: fir.do_loop %[[VAL_23:.*]] = %[[VAL_6]]#1 to %[[VAL_22]] step %[[VAL_21]] { +// CHECK: %[[VAL_24:.*]] = arith.subi %[[VAL_23]], %[[VAL_6]]#1 : index +// CHECK: %[[VAL_25:.*]] = fir.convert %[[VAL_9]]#1 : (!fir.ref>) -> !fir.ref>> +// CHECK: %[[VAL_26:.*]] = fir.coordinate_of %[[VAL_25]], %[[VAL_24]] : (!fir.ref>>, index) -> !fir.ref> +// CHECK: %[[VAL_27:.*]] = fir.load %[[VAL_26]] : !fir.ref> +// CHECK: %[[VAL_28:.*]] = fir.convert %[[VAL_14]] : (!fir.ref>) -> !fir.ref>> +// CHECK: %[[VAL_29:.*]] = fir.coordinate_of %[[VAL_28]], %[[VAL_23]] : (!fir.ref>>, index) -> !fir.ref> +// CHECK: fir.store %[[VAL_27]] to %[[VAL_29]] : !fir.ref> +// CHECK: } +// CHECK: %[[VAL_30:.*]]:2 = hlfir.declare %[[VAL_14]] typeparams %[[VAL_13]] {uniq_name = "tmp"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: %[[VAL_31:.*]] = arith.addi %[[VAL_12]], %[[VAL_10]]#1 : index +// CHECK: %[[VAL_32:.*]] = arith.addi %[[VAL_13]], %[[VAL_10]]#1 : index +// CHECK: %[[VAL_33:.*]] = fir.alloca !fir.char<1,?>(%[[VAL_32]] : index) {bindc_name = ".chrtmp"} +// CHECK: %[[VAL_34:.*]] = arith.constant 1 : i64 +// CHECK: %[[VAL_35:.*]] = fir.convert %[[VAL_13]] : (index) -> i64 +// CHECK: %[[VAL_36:.*]] = arith.muli %[[VAL_34]], %[[VAL_35]] : i64 +// CHECK: %[[VAL_37:.*]] = arith.constant false +// CHECK: %[[VAL_38:.*]] = fir.convert %[[VAL_33]] : (!fir.ref>) -> !fir.ref +// CHECK: %[[VAL_39:.*]] = fir.convert %[[VAL_30]]#1 : (!fir.ref>) -> !fir.ref +// CHECK: fir.call @llvm.memmove.p0.p0.i64(%[[VAL_38]], %[[VAL_39]], %[[VAL_36]], %[[VAL_37]]) : (!fir.ref, !fir.ref, i64, i1) -> () +// CHECK: %[[VAL_40:.*]] = arith.constant 1 : index +// CHECK: %[[VAL_41:.*]] = arith.subi %[[VAL_32]], %[[VAL_40]] : index +// CHECK: fir.do_loop %[[VAL_42:.*]] = %[[VAL_13]] to %[[VAL_41]] step %[[VAL_40]] { +// CHECK: %[[VAL_43:.*]] = arith.subi %[[VAL_42]], %[[VAL_13]] : index +// CHECK: %[[VAL_44:.*]] = fir.convert %[[VAL_11]]#1 : (!fir.ref>) -> !fir.ref>> +// CHECK: %[[VAL_45:.*]] = fir.coordinate_of %[[VAL_44]], %[[VAL_43]] : (!fir.ref>>, index) -> !fir.ref> +// CHECK: %[[VAL_46:.*]] = fir.load %[[VAL_45]] : !fir.ref> +// CHECK: %[[VAL_47:.*]] = fir.convert %[[VAL_33]] : (!fir.ref>) -> !fir.ref>> +// CHECK: %[[VAL_48:.*]] = fir.coordinate_of %[[VAL_47]], %[[VAL_42]] : (!fir.ref>>, index) -> !fir.ref> +// CHECK: fir.store %[[VAL_46]] to %[[VAL_48]] : !fir.ref> +// CHECK: } +// CHECK: %[[VAL_49:.*]]:2 = hlfir.declare %[[VAL_33]] typeparams %[[VAL_32]] {uniq_name = "tmp"} : (!fir.ref>, index) -> (!fir.boxchar<1>, !fir.ref>) +// CHECK: hlfir.assign %[[VAL_49]]#0 to %[[VAL_5]]#0 : !fir.boxchar<1>, !fir.boxchar<1> +