From 5d43a6bd4fb27015e6917eb471bc725fe31b9ac2 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Thu, 28 Nov 2024 08:25:05 -0800 Subject: [PATCH] [flang][hlfir] add hlfir.eval_in_mem operation --- .../flang/Optimizer/Builder/HLFIRTools.h | 19 ++++ .../include/flang/Optimizer/HLFIR/HLFIROps.td | 59 ++++++++++ flang/lib/Optimizer/Builder/HLFIRTools.cpp | 47 ++++++++ flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 76 ++++++++++--- .../HLFIR/Transforms/BufferizeHLFIR.cpp | 33 +++++- flang/test/HLFIR/eval_in_mem-codegen.fir | 107 ++++++++++++++++++ flang/test/HLFIR/eval_in_mem.fir | 99 ++++++++++++++++ flang/test/HLFIR/invalid.fir | 34 ++++++ 8 files changed, 454 insertions(+), 20 deletions(-) create mode 100644 flang/test/HLFIR/eval_in_mem-codegen.fir create mode 100644 flang/test/HLFIR/eval_in_mem.fir diff --git a/flang/include/flang/Optimizer/Builder/HLFIRTools.h b/flang/include/flang/Optimizer/Builder/HLFIRTools.h index f073f494b3fb2..efbd9e4f50d43 100644 --- a/flang/include/flang/Optimizer/Builder/HLFIRTools.h +++ b/flang/include/flang/Optimizer/Builder/HLFIRTools.h @@ -33,6 +33,7 @@ class AssociateOp; class ElementalOp; class ElementalOpInterface; class ElementalAddrOp; +class EvaluateInMemoryOp; class YieldElementOp; /// Is this a Fortran variable for which the defining op carrying the Fortran @@ -398,6 +399,24 @@ mlir::Value inlineElementalOp( mlir::IRMapping &mapper, const std::function &mustRecursivelyInline); +/// Create a new temporary with the shape and parameters of the provided +/// hlfir.eval_in_mem operation and clone the body of the hlfir.eval_in_mem +/// operating on this new temporary. returns the temporary and whether the +/// temporary is heap or stack allocated. +std::pair +computeEvaluateOpInNewTemp(mlir::Location, fir::FirOpBuilder &, + hlfir::EvaluateInMemoryOp evalInMem, + mlir::Value shape, mlir::ValueRange typeParams); + +// Clone the body of the hlfir.eval_in_mem operating on this the provided +// storage. The provided storage must be a contiguous "raw" memory reference +// (not a fir.box) big enough to hold the value computed by hlfir.eval_in_mem. +// No runtime check is inserted by this utility to enforce that. It is also +// usually invalid to provide some storage that is already addressed directly +// or indirectly inside the hlfir.eval_in_mem body. +void computeEvaluateOpIn(mlir::Location, fir::FirOpBuilder &, + hlfir::EvaluateInMemoryOp, mlir::Value storage); + std::pair> convertToValue(mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity entity); diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td index 1ab8793f72652..a9826543f48b6 100644 --- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td +++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td @@ -1755,4 +1755,63 @@ def hlfir_CharExtremumOp : hlfir_Op<"char_extremum", let hasVerifier = 1; } +def hlfir_EvaluateInMemoryOp : hlfir_Op<"eval_in_mem", [AttrSizedOperandSegments, + RecursiveMemoryEffects, RecursivelySpeculatable, + SingleBlockImplicitTerminator<"fir::FirEndOp">]> { + let summary = "Wrap an in-memory implementation that computes expression value"; + let description = [{ + Returns a Fortran expression value for which the computation is + implemented inside the region operating on the block argument which + is a raw memory reference corresponding to the expression type. + + The shape and type parameters of the expressions are operands of the + operations. + + The memory cannot escape the region, and it is not described how it is + allocated. This facilitates later elision of the temporary storage for the + expression evaluation if it can be evaluated in some other storage (like a + left-hand side variable). + + Example: + + A function returning an array can be represented as: + ``` + %1 = fir.shape %c10 : (index) -> !fir.shape<1> + %2 = hlfir.eval_in_mem shape %1 : (!fir.shape<1>) -> !hlfir.expr<10xf32> { + ^bb0(%arg0: !fir.ref>): + %3 = fir.call @_QParray_func() fastmath : () -> !fir.array<10xf32> + fir.save_result %3 to %arg0(%1) : !fir.array<10xf32>, !fir.ref>, !fir.shape<1> + } + ``` + }]; + + let arguments = (ins + Optional:$shape, + Variadic:$typeparams + ); + + let results = (outs hlfir_ExprType); + let regions = (region SizedRegion<1>:$body); + + let assemblyFormat = [{ + (`shape` $shape^)? (`typeparams` $typeparams^)? + attr-dict `:` functional-type(operands, results) + $body}]; + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<(ins "mlir::Type":$result_type, "mlir::Value":$shape, + CArg<"mlir::ValueRange", "{}">:$typeparams)> + ]; + + let extraClassDeclaration = [{ + // Return block argument representing the memory where the expression + // is evaluated. + mlir::Value getMemory() {return getBody().getArgument(0);} + }]; + + let hasVerifier = 1; +} + + #endif // FORTRAN_DIALECT_HLFIR_OPS diff --git a/flang/lib/Optimizer/Builder/HLFIRTools.cpp b/flang/lib/Optimizer/Builder/HLFIRTools.cpp index 7425ccf7fc0e3..1bd950f2445ee 100644 --- a/flang/lib/Optimizer/Builder/HLFIRTools.cpp +++ b/flang/lib/Optimizer/Builder/HLFIRTools.cpp @@ -535,6 +535,8 @@ static mlir::Value tryRetrievingShapeOrShift(hlfir::Entity entity) { if (mlir::isa(entity.getType())) { if (auto elemental = entity.getDefiningOp()) return elemental.getShape(); + if (auto evalInMem = entity.getDefiningOp()) + return evalInMem.getShape(); return mlir::Value{}; } if (auto varIface = entity.getIfVariableInterface()) @@ -642,6 +644,11 @@ void hlfir::genLengthParameters(mlir::Location loc, fir::FirOpBuilder &builder, result.append(elemental.getTypeparams().begin(), elemental.getTypeparams().end()); return; + } else if (auto evalInMem = + expr.getDefiningOp()) { + result.append(evalInMem.getTypeparams().begin(), + evalInMem.getTypeparams().end()); + return; } else if (auto apply = expr.getDefiningOp()) { result.append(apply.getTypeparams().begin(), apply.getTypeparams().end()); return; @@ -1313,3 +1320,43 @@ hlfir::genTypeAndKindConvert(mlir::Location loc, fir::FirOpBuilder &builder, }; return {hlfir::Entity{convertedRhs}, cleanup}; } + +std::pair hlfir::computeEvaluateOpInNewTemp( + mlir::Location loc, fir::FirOpBuilder &builder, + hlfir::EvaluateInMemoryOp evalInMem, mlir::Value shape, + mlir::ValueRange typeParams) { + llvm::StringRef tmpName{".tmp.expr_result"}; + llvm::SmallVector extents = + hlfir::getIndexExtents(loc, builder, shape); + mlir::Type baseType = + hlfir::getFortranElementOrSequenceType(evalInMem.getType()); + bool heapAllocated = fir::hasDynamicSize(baseType); + // Note: temporaries are stack allocated here when possible (do not require + // stack save/restore) because flang has always stack allocated function + // results. + mlir::Value temp = heapAllocated + ? builder.createHeapTemporary(loc, baseType, tmpName, + extents, typeParams) + : builder.createTemporary(loc, baseType, tmpName, + extents, typeParams); + mlir::Value innerMemory = evalInMem.getMemory(); + temp = builder.createConvert(loc, innerMemory.getType(), temp); + auto declareOp = builder.create( + loc, temp, tmpName, shape, typeParams, + /*dummy_scope=*/nullptr, fir::FortranVariableFlagsAttr{}); + computeEvaluateOpIn(loc, builder, evalInMem, declareOp.getOriginalBase()); + return {hlfir::Entity{declareOp.getBase()}, /*heapAllocated=*/heapAllocated}; +} + +void hlfir::computeEvaluateOpIn(mlir::Location loc, fir::FirOpBuilder &builder, + hlfir::EvaluateInMemoryOp evalInMem, + mlir::Value storage) { + mlir::Value innerMemory = evalInMem.getMemory(); + mlir::Value storageCast = + builder.createConvert(loc, innerMemory.getType(), storage); + mlir::IRMapping mapper; + mapper.map(innerMemory, storageCast); + for (auto &op : evalInMem.getBody().front().without_terminator()) + builder.clone(op, mapper); + return; +} diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp index b593383ff2848..8751988244648 100644 --- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -333,6 +333,25 @@ static void printDesignatorComplexPart(mlir::OpAsmPrinter &p, p << "real"; } } +template +static llvm::LogicalResult verifyTypeparams(Op &op, mlir::Type elementType, + unsigned numLenParam) { + if (mlir::isa(elementType)) { + if (numLenParam != 1) + return op.emitOpError("must be provided one length parameter when the " + "result is a character"); + } else if (fir::isRecordWithTypeParameters(elementType)) { + if (numLenParam != + mlir::cast(elementType).getNumLenParams()) + return op.emitOpError("must be provided the same number of length " + "parameters as in the result derived type"); + } else if (numLenParam != 0) { + return op.emitOpError( + "must not be provided length parameters if the result " + "type does not have length parameters"); + } + return mlir::success(); +} llvm::LogicalResult hlfir::DesignateOp::verify() { mlir::Type memrefType = getMemref().getType(); @@ -462,20 +481,10 @@ llvm::LogicalResult hlfir::DesignateOp::verify() { return emitOpError("shape must be a fir.shape or fir.shapeshift with " "the rank of the result"); } - auto numLenParam = getTypeparams().size(); - if (mlir::isa(outputElementType)) { - if (numLenParam != 1) - return emitOpError("must be provided one length parameter when the " - "result is a character"); - } else if (fir::isRecordWithTypeParameters(outputElementType)) { - if (numLenParam != - mlir::cast(outputElementType).getNumLenParams()) - return emitOpError("must be provided the same number of length " - "parameters as in the result derived type"); - } else if (numLenParam != 0) { - return emitOpError("must not be provided length parameters if the result " - "type does not have length parameters"); - } + if (auto res = + verifyTypeparams(*this, outputElementType, getTypeparams().size()); + failed(res)) + return res; } return mlir::success(); } @@ -1989,6 +1998,45 @@ hlfir::GetLengthOp::canonicalize(GetLengthOp getLength, return mlir::success(); } +//===----------------------------------------------------------------------===// +// EvaluateInMemoryOp +//===----------------------------------------------------------------------===// + +void hlfir::EvaluateInMemoryOp::build(mlir::OpBuilder &builder, + mlir::OperationState &odsState, + mlir::Type resultType, mlir::Value shape, + mlir::ValueRange typeparams) { + odsState.addTypes(resultType); + if (shape) + odsState.addOperands(shape); + odsState.addOperands(typeparams); + odsState.addAttribute( + getOperandSegmentSizeAttr(), + builder.getDenseI32ArrayAttr( + {shape ? 1 : 0, static_cast(typeparams.size())})); + mlir::Region *bodyRegion = odsState.addRegion(); + bodyRegion->push_back(new mlir::Block{}); + mlir::Type memType = fir::ReferenceType::get( + hlfir::getFortranElementOrSequenceType(resultType)); + bodyRegion->front().addArgument(memType, odsState.location); + EvaluateInMemoryOp::ensureTerminator(*bodyRegion, builder, odsState.location); +} + +llvm::LogicalResult hlfir::EvaluateInMemoryOp::verify() { + unsigned shapeRank = 0; + if (mlir::Value shape = getShape()) + if (auto shapeTy = mlir::dyn_cast(shape.getType())) + shapeRank = shapeTy.getRank(); + auto exprType = mlir::cast(getResult().getType()); + if (shapeRank != exprType.getRank()) + return emitOpError("`shape` rank must match the result rank"); + mlir::Type elementType = exprType.getElementType(); + if (auto res = verifyTypeparams(*this, elementType, getTypeparams().size()); + failed(res)) + return res; + return mlir::success(); +} + #include "flang/Optimizer/HLFIR/HLFIROpInterfaces.cpp.inc" #define GET_OP_CLASSES #include "flang/Optimizer/HLFIR/HLFIREnums.cpp.inc" diff --git a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp index 1848dbe2c7a2c..347f0a5630777 100644 --- a/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp +++ b/flang/lib/Optimizer/HLFIR/Transforms/BufferizeHLFIR.cpp @@ -905,6 +905,26 @@ struct CharExtremumOpConversion } }; +struct EvaluateInMemoryOpConversion + : public mlir::OpConversionPattern { + using mlir::OpConversionPattern< + hlfir::EvaluateInMemoryOp>::OpConversionPattern; + explicit EvaluateInMemoryOpConversion(mlir::MLIRContext *ctx) + : mlir::OpConversionPattern{ctx} {} + llvm::LogicalResult + matchAndRewrite(hlfir::EvaluateInMemoryOp evalInMemOp, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + mlir::Location loc = evalInMemOp->getLoc(); + fir::FirOpBuilder builder(rewriter, evalInMemOp.getOperation()); + auto [temp, isHeapAlloc] = hlfir::computeEvaluateOpInNewTemp( + loc, builder, evalInMemOp, adaptor.getShape(), adaptor.getTypeparams()); + mlir::Value bufferizedExpr = + packageBufferizedExpr(loc, builder, temp, isHeapAlloc); + rewriter.replaceOp(evalInMemOp, bufferizedExpr); + return mlir::success(); + } +}; + class BufferizeHLFIR : public hlfir::impl::BufferizeHLFIRBase { public: void runOnOperation() override { @@ -918,12 +938,13 @@ class BufferizeHLFIR : public hlfir::impl::BufferizeHLFIRBase { auto module = this->getOperation(); auto *context = &getContext(); mlir::RewritePatternSet patterns(context); - patterns.insert(context); + patterns + .insert(context); mlir::ConversionTarget target(*context); // Note that YieldElementOp is not marked as an illegal operation. // It must be erased by its parent converter and there is no explicit diff --git a/flang/test/HLFIR/eval_in_mem-codegen.fir b/flang/test/HLFIR/eval_in_mem-codegen.fir new file mode 100644 index 0000000000000..26a989832ca92 --- /dev/null +++ b/flang/test/HLFIR/eval_in_mem-codegen.fir @@ -0,0 +1,107 @@ +// Test hlfir.eval_in_mem default code generation. + +// RUN: fir-opt %s --bufferize-hlfir -o - | FileCheck %s + +func.func @_QPtest() { + %c10 = arith.constant 10 : index + %0 = fir.address_of(@_QFtestEx) : !fir.ref> + %1 = fir.shape %c10 : (index) -> !fir.shape<1> + %2 = hlfir.eval_in_mem shape %1 : (!fir.shape<1>) -> !hlfir.expr<10xf32> { + ^bb0(%arg0: !fir.ref>): + %3 = fir.call @_QParray_func() fastmath : () -> !fir.array<10xf32> + fir.save_result %3 to %arg0(%1) : !fir.array<10xf32>, !fir.ref>, !fir.shape<1> + } + hlfir.assign %2 to %0 : !hlfir.expr<10xf32>, !fir.ref> + hlfir.destroy %2 : !hlfir.expr<10xf32> + return +} +fir.global internal @_QFtestEx : !fir.array<10xf32> +func.func private @_QParray_func() -> !fir.array<10xf32> + + +func.func @_QPtest_char() { + %c10 = arith.constant 10 : index + %c5 = arith.constant 5 : index + %0 = fir.address_of(@_QFtest_charEx) : !fir.ref>> + %1 = fir.shape %c10 : (index) -> !fir.shape<1> + %2 = hlfir.eval_in_mem shape %1 typeparams %c5 : (!fir.shape<1>, index) -> !hlfir.expr<10x!fir.char<1,5>> { + ^bb0(%arg0: !fir.ref>>): + %3 = fir.call @_QPchar_array_func() fastmath : () -> !fir.array<10x!fir.char<1,5>> + fir.save_result %3 to %arg0(%1) typeparams %c5 : !fir.array<10x!fir.char<1,5>>, !fir.ref>>, !fir.shape<1>, index + } + hlfir.assign %2 to %0 : !hlfir.expr<10x!fir.char<1,5>>, !fir.ref>> + hlfir.destroy %2 : !hlfir.expr<10x!fir.char<1,5>> + return +} + +fir.global internal @_QFtest_charEx : !fir.array<10x!fir.char<1,5>> +func.func private @_QPchar_array_func() -> !fir.array<10x!fir.char<1,5>> + +func.func @test_dynamic(%arg0: !fir.box>, %arg1: index) { + %0 = fir.shape %arg1 : (index) -> !fir.shape<1> + %1 = hlfir.eval_in_mem shape %0 : (!fir.shape<1>) -> !hlfir.expr { + ^bb0(%arg2: !fir.ref>): + %2 = fir.call @_QPdyn_array_func(%arg1) : (index) -> !fir.array + fir.save_result %2 to %arg2(%0) : !fir.array, !fir.ref>, !fir.shape<1> + } + hlfir.assign %1 to %arg0 : !hlfir.expr, !fir.box> + hlfir.destroy %1 : !hlfir.expr + return +} +func.func private @_QPdyn_array_func(index) -> !fir.array + +// CHECK-LABEL: func.func @_QPtest() { +// CHECK: %[[VAL_0:.*]] = fir.alloca !fir.array<10xf32> {bindc_name = ".tmp.expr_result"} +// CHECK: %[[VAL_1:.*]] = arith.constant 10 : index +// CHECK: %[[VAL_2:.*]] = fir.address_of(@_QFtestEx) : !fir.ref> +// CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[VAL_0]](%[[VAL_3]]) {uniq_name = ".tmp.expr_result"} : (!fir.ref>, !fir.shape<1>) -> (!fir.ref>, !fir.ref>) +// CHECK: %[[VAL_5:.*]] = fir.call @_QParray_func() fastmath : () -> !fir.array<10xf32> +// CHECK: fir.save_result %[[VAL_5]] to %[[VAL_4]]#1(%[[VAL_3]]) : !fir.array<10xf32>, !fir.ref>, !fir.shape<1> +// CHECK: %[[VAL_6:.*]] = arith.constant false +// CHECK: %[[VAL_7:.*]] = fir.undefined tuple>, i1> +// CHECK: %[[VAL_8:.*]] = fir.insert_value %[[VAL_7]], %[[VAL_6]], [1 : index] : (tuple>, i1>, i1) -> tuple>, i1> +// CHECK: %[[VAL_9:.*]] = fir.insert_value %[[VAL_8]], %[[VAL_4]]#0, [0 : index] : (tuple>, i1>, !fir.ref>) -> tuple>, i1> +// CHECK: hlfir.assign %[[VAL_4]]#0 to %[[VAL_2]] : !fir.ref>, !fir.ref> +// CHECK: return +// CHECK: } +// CHECK: fir.global internal @_QFtestEx : !fir.array<10xf32> +// CHECK: func.func private @_QParray_func() -> !fir.array<10xf32> + +// CHECK-LABEL: func.func @_QPtest_char() { +// CHECK: %[[VAL_0:.*]] = fir.alloca !fir.array<10x!fir.char<1,5>> {bindc_name = ".tmp.expr_result"} +// CHECK: %[[VAL_1:.*]] = arith.constant 10 : index +// CHECK: %[[VAL_2:.*]] = arith.constant 5 : index +// CHECK: %[[VAL_3:.*]] = fir.address_of(@_QFtest_charEx) : !fir.ref>> +// CHECK: %[[VAL_4:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_0]](%[[VAL_4]]) typeparams %[[VAL_2]] {uniq_name = ".tmp.expr_result"} : (!fir.ref>>, !fir.shape<1>, index) -> (!fir.ref>>, !fir.ref>>) +// CHECK: %[[VAL_6:.*]] = fir.call @_QPchar_array_func() fastmath : () -> !fir.array<10x!fir.char<1,5>> +// CHECK: fir.save_result %[[VAL_6]] to %[[VAL_5]]#1(%[[VAL_4]]) typeparams %[[VAL_2]] : !fir.array<10x!fir.char<1,5>>, !fir.ref>>, !fir.shape<1>, index +// CHECK: %[[VAL_7:.*]] = arith.constant false +// CHECK: %[[VAL_8:.*]] = fir.undefined tuple>>, i1> +// CHECK: %[[VAL_9:.*]] = fir.insert_value %[[VAL_8]], %[[VAL_7]], [1 : index] : (tuple>>, i1>, i1) -> tuple>>, i1> +// CHECK: %[[VAL_10:.*]] = fir.insert_value %[[VAL_9]], %[[VAL_5]]#0, [0 : index] : (tuple>>, i1>, !fir.ref>>) -> tuple>>, i1> +// CHECK: hlfir.assign %[[VAL_5]]#0 to %[[VAL_3]] : !fir.ref>>, !fir.ref>> +// CHECK: return +// CHECK: } +// CHECK: fir.global internal @_QFtest_charEx : !fir.array<10x!fir.char<1,5>> +// CHECK: func.func private @_QPchar_array_func() -> !fir.array<10x!fir.char<1,5>> + +// CHECK-LABEL: func.func @test_dynamic( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>, +// CHECK-SAME: %[[VAL_1:.*]]: index) { +// CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_3:.*]] = fir.allocmem !fir.array, %[[VAL_1]] {bindc_name = ".tmp.expr_result", uniq_name = ""} +// CHECK: %[[VAL_4:.*]] = fir.convert %[[VAL_3]] : (!fir.heap>) -> !fir.ref> +// CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[VAL_4]](%[[VAL_2]]) {uniq_name = ".tmp.expr_result"} : (!fir.ref>, !fir.shape<1>) -> (!fir.box>, !fir.ref>) +// CHECK: %[[VAL_6:.*]] = fir.call @_QPdyn_array_func(%[[VAL_1]]) : (index) -> !fir.array +// CHECK: fir.save_result %[[VAL_6]] to %[[VAL_5]]#1(%[[VAL_2]]) : !fir.array, !fir.ref>, !fir.shape<1> +// CHECK: %[[VAL_7:.*]] = arith.constant true +// CHECK: %[[VAL_8:.*]] = fir.undefined tuple>, i1> +// CHECK: %[[VAL_9:.*]] = fir.insert_value %[[VAL_8]], %[[VAL_7]], [1 : index] : (tuple>, i1>, i1) -> tuple>, i1> +// CHECK: %[[VAL_10:.*]] = fir.insert_value %[[VAL_9]], %[[VAL_5]]#0, [0 : index] : (tuple>, i1>, !fir.box>) -> tuple>, i1> +// CHECK: hlfir.assign %[[VAL_5]]#0 to %[[VAL_0]] : !fir.box>, !fir.box> +// CHECK: %[[VAL_11:.*]] = fir.box_addr %[[VAL_5]]#0 : (!fir.box>) -> !fir.heap> +// CHECK: fir.freemem %[[VAL_11]] : !fir.heap> +// CHECK: return +// CHECK: } diff --git a/flang/test/HLFIR/eval_in_mem.fir b/flang/test/HLFIR/eval_in_mem.fir new file mode 100644 index 0000000000000..34e48ed5be545 --- /dev/null +++ b/flang/test/HLFIR/eval_in_mem.fir @@ -0,0 +1,99 @@ +// Test hlfir.eval_in_mem operation parse, verify (no errors), and unparse. + +// RUN: fir-opt %s | fir-opt | FileCheck %s + +func.func @_QPtest() { + %c10 = arith.constant 10 : index + %0 = fir.address_of(@_QFtestEx) : !fir.ref> + %1 = fir.shape %c10 : (index) -> !fir.shape<1> + %2 = hlfir.eval_in_mem shape %1 : (!fir.shape<1>) -> !hlfir.expr<10xf32> { + ^bb0(%arg0: !fir.ref>): + %3 = fir.call @_QParray_func() fastmath : () -> !fir.array<10xf32> + fir.save_result %3 to %arg0(%1) : !fir.array<10xf32>, !fir.ref>, !fir.shape<1> + } + hlfir.assign %2 to %0 : !hlfir.expr<10xf32>, !fir.ref> + hlfir.destroy %2 : !hlfir.expr<10xf32> + return +} +fir.global internal @_QFtestEx : !fir.array<10xf32> +func.func private @_QParray_func() -> !fir.array<10xf32> + + +func.func @_QPtest_char() { + %c10 = arith.constant 10 : index + %c5 = arith.constant 5 : index + %0 = fir.address_of(@_QFtest_charEx) : !fir.ref>> + %1 = fir.shape %c10 : (index) -> !fir.shape<1> + %2 = hlfir.eval_in_mem shape %1 typeparams %c5 : (!fir.shape<1>, index) -> !hlfir.expr<10x!fir.char<1,5>> { + ^bb0(%arg0: !fir.ref>>): + %3 = fir.call @_QPchar_array_func() fastmath : () -> !fir.array<10x!fir.char<1,5>> + fir.save_result %3 to %arg0(%1) typeparams %c5 : !fir.array<10x!fir.char<1,5>>, !fir.ref>>, !fir.shape<1>, index + } + hlfir.assign %2 to %0 : !hlfir.expr<10x!fir.char<1,5>>, !fir.ref>> + hlfir.destroy %2 : !hlfir.expr<10x!fir.char<1,5>> + return +} + +fir.global internal @_QFtest_charEx : !fir.array<10x!fir.char<1,5>> +func.func private @_QPchar_array_func() -> !fir.array<10x!fir.char<1,5>> + +func.func @test_dynamic(%arg0: !fir.box>, %arg1: index) { + %0 = fir.shape %arg1 : (index) -> !fir.shape<1> + %1 = hlfir.eval_in_mem shape %0 : (!fir.shape<1>) -> !hlfir.expr { + ^bb0(%arg2: !fir.ref>): + %2 = fir.call @_QPdyn_array_func(%arg1) : (index) -> !fir.array + fir.save_result %2 to %arg2(%0) : !fir.array, !fir.ref>, !fir.shape<1> + } + hlfir.assign %1 to %arg0 : !hlfir.expr, !fir.box> + hlfir.destroy %1 : !hlfir.expr + return +} +func.func private @_QPdyn_array_func(index) -> !fir.array + +// CHECK-LABEL: func.func @_QPtest() { +// CHECK: %[[VAL_0:.*]] = arith.constant 10 : index +// CHECK: %[[VAL_1:.*]] = fir.address_of(@_QFtestEx) : !fir.ref> +// CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_0]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_3:.*]] = hlfir.eval_in_mem shape %[[VAL_2]] : (!fir.shape<1>) -> !hlfir.expr<10xf32> { +// CHECK: ^bb0(%[[VAL_4:.*]]: !fir.ref>): +// CHECK: %[[VAL_5:.*]] = fir.call @_QParray_func() fastmath : () -> !fir.array<10xf32> +// CHECK: fir.save_result %[[VAL_5]] to %[[VAL_4]](%[[VAL_2]]) : !fir.array<10xf32>, !fir.ref>, !fir.shape<1> +// CHECK: } +// CHECK: hlfir.assign %[[VAL_3]] to %[[VAL_1]] : !hlfir.expr<10xf32>, !fir.ref> +// CHECK: hlfir.destroy %[[VAL_3]] : !hlfir.expr<10xf32> +// CHECK: return +// CHECK: } +// CHECK: fir.global internal @_QFtestEx : !fir.array<10xf32> +// CHECK: func.func private @_QParray_func() -> !fir.array<10xf32> + +// CHECK-LABEL: func.func @_QPtest_char() { +// CHECK: %[[VAL_0:.*]] = arith.constant 10 : index +// CHECK: %[[VAL_1:.*]] = arith.constant 5 : index +// CHECK: %[[VAL_2:.*]] = fir.address_of(@_QFtest_charEx) : !fir.ref>> +// CHECK: %[[VAL_3:.*]] = fir.shape %[[VAL_0]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_4:.*]] = hlfir.eval_in_mem shape %[[VAL_3]] typeparams %[[VAL_1]] : (!fir.shape<1>, index) -> !hlfir.expr<10x!fir.char<1,5>> { +// CHECK: ^bb0(%[[VAL_5:.*]]: !fir.ref>>): +// CHECK: %[[VAL_6:.*]] = fir.call @_QPchar_array_func() fastmath : () -> !fir.array<10x!fir.char<1,5>> +// CHECK: fir.save_result %[[VAL_6]] to %[[VAL_5]](%[[VAL_3]]) typeparams %[[VAL_1]] : !fir.array<10x!fir.char<1,5>>, !fir.ref>>, !fir.shape<1>, index +// CHECK: } +// CHECK: hlfir.assign %[[VAL_4]] to %[[VAL_2]] : !hlfir.expr<10x!fir.char<1,5>>, !fir.ref>> +// CHECK: hlfir.destroy %[[VAL_4]] : !hlfir.expr<10x!fir.char<1,5>> +// CHECK: return +// CHECK: } +// CHECK: fir.global internal @_QFtest_charEx : !fir.array<10x!fir.char<1,5>> +// CHECK: func.func private @_QPchar_array_func() -> !fir.array<10x!fir.char<1,5>> + +// CHECK-LABEL: func.func @test_dynamic( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>, +// CHECK-SAME: %[[VAL_1:.*]]: index) { +// CHECK: %[[VAL_2:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1> +// CHECK: %[[VAL_3:.*]] = hlfir.eval_in_mem shape %[[VAL_2]] : (!fir.shape<1>) -> !hlfir.expr { +// CHECK: ^bb0(%[[VAL_4:.*]]: !fir.ref>): +// CHECK: %[[VAL_5:.*]] = fir.call @_QPdyn_array_func(%[[VAL_1]]) : (index) -> !fir.array +// CHECK: fir.save_result %[[VAL_5]] to %[[VAL_4]](%[[VAL_2]]) : !fir.array, !fir.ref>, !fir.shape<1> +// CHECK: } +// CHECK: hlfir.assign %[[VAL_3]] to %[[VAL_0]] : !hlfir.expr, !fir.box> +// CHECK: hlfir.destroy %[[VAL_3]] : !hlfir.expr +// CHECK: return +// CHECK: } +// CHECK: func.func private @_QPdyn_array_func(index) -> !fir.array diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir index c390dddcf3f38..5c5db7aac0697 100644 --- a/flang/test/HLFIR/invalid.fir +++ b/flang/test/HLFIR/invalid.fir @@ -1314,3 +1314,37 @@ func.func @end_associate_with_alloc_comp(%var: !hlfir.expr>}>>>, i1 return } + +// ----- + +func.func @bad_eval_in_mem_1() { + %c10 = arith.constant 10 : index + %1 = fir.shape %c10 : (index) -> !fir.shape<1> +// expected-error@+1 {{'hlfir.eval_in_mem' op result #0 must be The type of an array, character, or derived type Fortran expression, but got '!fir.array<10xf32>'}} + %2 = hlfir.eval_in_mem shape %1 : (!fir.shape<1>) -> !fir.array<10xf32> { + ^bb0(%arg0: !fir.ref>): + } + return +} + +// ----- + +func.func @bad_eval_in_mem_2() { + %c10 = arith.constant 10 : index + %1 = fir.shape %c10, %c10 : (index, index) -> !fir.shape<2> + // expected-error@+1 {{'hlfir.eval_in_mem' op `shape` rank must match the result rank}} + %2 = hlfir.eval_in_mem shape %1 : (!fir.shape<2>) -> !hlfir.expr<10xf32> { + ^bb0(%arg0: !fir.ref>): + } + return +} + +// ----- + +func.func @bad_eval_in_mem_3() { + // expected-error@+1 {{'hlfir.eval_in_mem' op must be provided one length parameter when the result is a character}} + %1 = hlfir.eval_in_mem : () -> !hlfir.expr> { + ^bb0(%arg0: !fir.ref>): + } + return +}