}
def hlfir_AsExprOp : hlfir_Op<"as_expr", []> {
- let summary = "Take the value of an array, character or derived expression";
+ let summary = "Take the value of an array, character or derived variable";
let description = [{
- Take the value of an array, character or derived expression.
+ Take the value of an array, character or derived variable.
+ In general, this operation will lead to a copy of the variable
+ in the bufferization pass if it was not transformed.
+
+ However, if it is known that the variable storage will not be used anymore
+ afterwards, the variable storage ownership can be passed to the hlfir.expr
+ by providing the $must_free argument that is a boolean that indicates if
+ the storage must be freed (when it was allocated on the heap).
+ This allows Fortran lowering to build some expression value in memory when
+ there is no adequate hlfir operation, and to promote the result to an
+ hlfir.expr value without paying the price of introducing a copy.
}];
- let arguments = (ins AnyFortranVariable:$var);
+ let arguments = (ins AnyFortranVariable:$var,
+ Optional<I1>:$must_free);
let results = (outs hlfir_ExprType);
+ let extraClassDeclaration = [{
+ // Is this a "move" ?
+ bool isMove() { return getMustFree() != mlir::Value{}; }
+ }];
+
let assemblyFormat = [{
- $var attr-dict `:` functional-type(operands, results)
+ $var (`move` $must_free^)? attr-dict `:` functional-type(operands, results)
}];
- let builders = [OpBuilder<(ins "mlir::Value":$var)>];
+ let builders = [OpBuilder<(ins "mlir::Value":$var, CArg<"mlir::Value", "{}">:$must_free)>];
}
def hlfir_NoReassocOp : hlfir_Op<"no_reassoc", [NoMemoryEffect, SameOperandsAndResultType]> {
#include "mlir/IR/Matchers.h"
#include "mlir/IR/OpImplementation.h"
#include "llvm/ADT/TypeSwitch.h"
-#include <tuple>
#include <optional>
+#include <tuple>
//===----------------------------------------------------------------------===//
// DeclareOp
//===----------------------------------------------------------------------===//
void hlfir::AsExprOp::build(mlir::OpBuilder &builder,
- mlir::OperationState &result, mlir::Value var) {
+ mlir::OperationState &result, mlir::Value var,
+ mlir::Value mustFree) {
hlfir::ExprType::Shape typeShape;
mlir::Type type = getFortranElementOrSequenceType(var.getType());
if (auto seqType = type.dyn_cast<fir::SequenceType>()) {
auto resultType = hlfir::ExprType::get(builder.getContext(), typeShape, type,
/*isPolymorphic: TODO*/ false);
- return build(builder, result, resultType, var);
+ return build(builder, result, resultType, var, mustFree);
}
//===----------------------------------------------------------------------===//
mlir::Location loc = asExpr->getLoc();
auto module = asExpr->getParentOfType<mlir::ModuleOp>();
fir::FirOpBuilder builder(rewriter, fir::getKindMapping(module));
+ if (asExpr.isMove()) {
+ // Move variable storage for the hlfir.expr buffer.
+ mlir::Value bufferizedExpr = packageBufferizedExpr(
+ loc, builder, adaptor.getVar(), adaptor.getMustFree());
+ rewriter.replaceOp(asExpr, bufferizedExpr);
+ return mlir::success();
+ }
+ // Otherwise, create a copy in a new buffer.
hlfir::Entity source = hlfir::Entity{adaptor.getVar()};
auto [temp, cleanup] = createTempFromMold(loc, builder, source);
builder.create<hlfir::AssignOp>(loc, source, temp);
// CHECK: %[[VAL_8:.*]] = fir.undefined tuple<!fir.box<!fir.array<10x?xi32>>, i1>
// CHECK: %[[VAL_9:.*]] = fir.insert_value %[[VAL_8]], %[[VAL_6]], [1 : index] : (tuple<!fir.box<!fir.array<10x?xi32>>, i1>, i1) -> tuple<!fir.box<!fir.array<10x?xi32>>, i1>
// CHECK: %[[VAL_10:.*]] = fir.insert_value %[[VAL_9]], %[[VAL_7]]#0, [0 : index] : (tuple<!fir.box<!fir.array<10x?xi32>>, i1>, !fir.box<!fir.array<10x?xi32>>) -> tuple<!fir.box<!fir.array<10x?xi32>>, i1>
+
+func.func @test_move(%arg0 : !fir.ref<!fir.array<10x20xi32>>, %must_free: i1) {
+ %expr = hlfir.as_expr %arg0 move %must_free: (!fir.ref<!fir.array<10x20xi32>>, i1) -> !hlfir.expr<10x20xi32>
+ return
+}
+// CHECK-LABEL: func.func @test_move(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<10x20xi32>>,
+// CHECK-SAME: %[[VAL_1:.*]]: i1) {
+// CHECK: %[[VAL_2:.*]] = fir.undefined tuple<!fir.ref<!fir.array<10x20xi32>>, i1>
+// CHECK: %[[VAL_3:.*]] = fir.insert_value %[[VAL_2]], %[[VAL_1]], [1 : index] : (tuple<!fir.ref<!fir.array<10x20xi32>>, i1>, i1) -> tuple<!fir.ref<!fir.array<10x20xi32>>, i1>
+// CHECK: %[[VAL_4:.*]] = fir.insert_value %[[VAL_3]], %[[VAL_0]], [0 : index] : (tuple<!fir.ref<!fir.array<10x20xi32>>, i1>, !fir.ref<!fir.array<10x20xi32>>) -> tuple<!fir.ref<!fir.array<10x20xi32>>, i1>
// CHECK-LABEL: func.func @array_expr_2(
// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<10xi32>>) {
// CHECK: hlfir.as_expr %[[VAL_0]] : (!fir.ref<!fir.array<10xi32>>) -> !hlfir.expr<10xi32>
+
+func.func @array_expr_move(%arg0: !fir.ref<!fir.array<10xi32>>, %must_free: i1) {
+ %0 = hlfir.as_expr %arg0 move %must_free : (!fir.ref<!fir.array<10xi32>>, i1) -> !hlfir.expr<10xi32>
+ return
+}
+// CHECK-LABEL: func.func @array_expr_move(
+// CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<10xi32>>,
+// CHECK-SAME: %[[VAL_1:.*]]: i1) {
+// CHECK: hlfir.as_expr %[[VAL_0]] move %[[VAL_1]] : (!fir.ref<!fir.array<10xi32>>, i1) -> !hlfir.expr<10xi32>