From 69ee5a53e58dd8018dc50d728c8d4c1254385009 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Wed, 25 Jan 2023 09:50:19 +0100 Subject: [PATCH] [flang][hlfir] Add hlfir.copy_in and hlfir.copy_out operations These operations implement the optional copy of a non contiguous variable into a temporary before a call, and the copy back from the temporary after the call. Differential Revision: https://reviews.llvm.org/D142342 --- flang/include/flang/Optimizer/Dialect/FIRTypes.td | 1 + flang/include/flang/Optimizer/HLFIR/HLFIROps.td | 68 +++++++++++++++++++++++ flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp | 10 ++++ flang/test/HLFIR/copy-in-out.fir | 27 +++++++++ 4 files changed, 106 insertions(+) create mode 100644 flang/test/HLFIR/copy-in-out.fir diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td index 4dbb611..7569407 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td +++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td @@ -561,6 +561,7 @@ def fir_VoidType : FIR_Type<"Void", "void"> { // Whether a type is a BaseBoxType def IsBaseBoxTypePred : CPred<"$_self.isa<::fir::BaseBoxType>()">; +def fir_BaseBoxType : Type; // Generalized FIR and standard dialect types representing intrinsic types def AnyIntegerLike : TypeConstraint { let assemblyFormat = "$expr attr-dict `:` qualified(type($expr))"; } +def hlfir_CopyInOp : hlfir_Op<"copy_in", []> { + let summary = "copy a variable into a contiguous temporary if it is not contiguous"; + let description = [{ + Copy a variable into a contiguous temporary if the variable is not + an absent optional and is not contiguous at runtime. When a copy is made this + operation returns the temporary as first result, otherwise, it returns the + potentially absent variable storage. The second result indicates if a copy + was made. + + This operation is meant to be used in combination with the hlfir.copy_out + operation that deletes the temporary if it was created and copies the data + back if needed. + This operation allows passing non contiguous arrays to contiguous dummy + arguments, which is possible in Fortran procedure references. + + To deal with the optional case, an extra boolean value can be pass to the + operation. In such cases, the copy-in will only be done if "var_is_present" + is true and, when it is false, the original value will be returned instead. + }]; + + let arguments = (ins fir_BaseBoxType:$var, + Optional:$var_is_present); + + let results = (outs fir_BaseBoxType, I1); + + let assemblyFormat = [{ + $var (`handle_optional` $var_is_present^)? + attr-dict `:` functional-type(operands, results) + }]; + + let builders = [ + OpBuilder<(ins "mlir::Value":$var, "mlir::Value":$var_is_present)> + ]; + + let extraClassDeclaration = [{ + /// Get the resulting copied-in fir.box or fir.class. + mlir::Value getCopiedIn() { + return getResult(0); + } + + /// Get the result indicating if a copy was made. + mlir::Value getWasCopied() { + return getResult(1); + } + }]; +} + +def hlfir_CopyOutOp : hlfir_Op<"copy_out", []> { + let summary = "copy out a variable after a copy in"; + let description = [{ + If the variable was copied in a temporary in the related hlfir.copy_in, + optionally copy back the temporary value to it (that may have been + modified between the hlfir.copy_in and hlfir.copy_out). Then deallocate + the temporary. + The copy back is done if $var is provided and $was_copied is true. + The deallocation of $temp is done if $was_copied is true. + }]; + + let arguments = (ins fir_BaseBoxType:$temp, + I1:$was_copied, + Optional:$var); + + let assemblyFormat = [{ + $temp `,` $was_copied (`to` $var^)? + attr-dict `:` functional-type(operands, results) + }]; +} + #endif // FORTRAN_DIALECT_HLFIR_OPS diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp index ecdc490..56cb542 100644 --- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp +++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp @@ -515,5 +515,15 @@ void hlfir::NullOp::build(mlir::OpBuilder &builder, fir::ReferenceType::get(builder.getNoneType())); } +//===----------------------------------------------------------------------===// +// CopyInOp +//===----------------------------------------------------------------------===// + +void hlfir::CopyInOp::build(mlir::OpBuilder &builder, + mlir::OperationState &odsState, mlir::Value var, + mlir::Value var_is_present) { + return build(builder, odsState, var.getType(), var, var_is_present); +} + #define GET_OP_CLASSES #include "flang/Optimizer/HLFIR/HLFIROps.cpp.inc" diff --git a/flang/test/HLFIR/copy-in-out.fir b/flang/test/HLFIR/copy-in-out.fir new file mode 100644 index 0000000..be24dcc --- /dev/null +++ b/flang/test/HLFIR/copy-in-out.fir @@ -0,0 +1,27 @@ +// Test hlfir.copy_in and hlfir.copy_out operation parse, verify (no errors), +// and unparse. + +// RUN: fir-opt %s | fir-opt | FileCheck %s + +func.func @test_copy_in(%box: !fir.box>, %is_present: i1) { + %0:2 = hlfir.copy_in %box : (!fir.box>) -> (!fir.box>, i1) + %1:2 = hlfir.copy_in %box handle_optional %is_present : (!fir.box>, i1) -> (!fir.box>, i1) + return +} +// CHECK-LABEL: func.func @test_copy_in( +// CHECK-SAME: %[[VAL_0:.*]]: !fir.box>, +// CHECK-SAME: %[[VAL_1:.*]]: i1 +// CHECK: %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_0]] : (!fir.box>) -> (!fir.box>, i1) +// CHECK: %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_0]] handle_optional %[[VAL_1]] : (!fir.box>, i1) -> (!fir.box>, i1) + +func.func @test_copy_out(%box: !fir.box>, %temp: !fir.box>, %was_copied: i1) { + hlfir.copy_out %temp, %was_copied : (!fir.box>, i1) -> () + hlfir.copy_out %temp, %was_copied to %box : (!fir.box>, i1, !fir.box>) -> () + return +} +// CHECK-LABEL: func.func @test_copy_out( +// CHECK-SAME: %[[VAL_0:[^:]*]]: !fir.box>, +// CHECK-SAME: %[[VAL_1:.*]]: !fir.box>, +// CHECK-SAME: %[[VAL_2:.*]]: i1) { +// CHECK: hlfir.copy_out %[[VAL_1]], %[[VAL_2]] : (!fir.box>, i1) -> () +// CHECK: hlfir.copy_out %[[VAL_1]], %[[VAL_2]] to %[[VAL_0]] : (!fir.box>, i1, !fir.box>) -> () -- 2.7.4