[flang][hlfir] Add hlfir.copy_in and hlfir.copy_out operations
authorJean Perier <jperier@nvidia.com>
Wed, 25 Jan 2023 08:50:19 +0000 (09:50 +0100)
committerJean Perier <jperier@nvidia.com>
Wed, 25 Jan 2023 08:50:52 +0000 (09:50 +0100)
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
flang/include/flang/Optimizer/HLFIR/HLFIROps.td
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
flang/test/HLFIR/copy-in-out.fir [new file with mode: 0644]

index 4dbb611..7569407 100644 (file)
@@ -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<IsBaseBoxTypePred, "fir.box or fir.class type">;
 
 // Generalized FIR and standard dialect types representing intrinsic types
 def AnyIntegerLike : TypeConstraint<Or<[SignlessIntegerLike.predicate,
index 82bfb2a..3228685 100644 (file)
@@ -523,4 +523,72 @@ def hlfir_DestroyOp : hlfir_Op<"destroy", []> {
   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<I1>:$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<fir_BaseBoxType>:$var);
+
+  let assemblyFormat = [{
+    $temp `,` $was_copied (`to` $var^)?
+    attr-dict `:` functional-type(operands, results)
+  }];
+}
+
 #endif // FORTRAN_DIALECT_HLFIR_OPS
index ecdc490..56cb542 100644 (file)
@@ -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 (file)
index 0000000..be24dcc
--- /dev/null
@@ -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<!fir.array<?xf64>>, %is_present: i1) {
+  %0:2 = hlfir.copy_in %box : (!fir.box<!fir.array<?xf64>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+  %1:2 = hlfir.copy_in %box handle_optional %is_present : (!fir.box<!fir.array<?xf64>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1)
+  return
+}
+// CHECK-LABEL:   func.func @test_copy_in(
+// CHECK-SAME:    %[[VAL_0:.*]]: !fir.box<!fir.array<?xf64>>,
+// CHECK-SAME:    %[[VAL_1:.*]]: i1
+// CHECK:  %[[VAL_2:.*]]:2 = hlfir.copy_in %[[VAL_0]]  : (!fir.box<!fir.array<?xf64>>) -> (!fir.box<!fir.array<?xf64>>, i1)
+// CHECK:  %[[VAL_3:.*]]:2 = hlfir.copy_in %[[VAL_0]] handle_optional %[[VAL_1]] : (!fir.box<!fir.array<?xf64>>, i1) -> (!fir.box<!fir.array<?xf64>>, i1)
+
+func.func @test_copy_out(%box: !fir.box<!fir.array<?xf64>>, %temp: !fir.box<!fir.array<?xf64>>, %was_copied: i1) {
+  hlfir.copy_out %temp, %was_copied : (!fir.box<!fir.array<?xf64>>, i1) -> ()
+  hlfir.copy_out %temp, %was_copied to %box : (!fir.box<!fir.array<?xf64>>, i1, !fir.box<!fir.array<?xf64>>) -> ()
+  return
+}
+// CHECK-LABEL:   func.func @test_copy_out(
+// CHECK-SAME:    %[[VAL_0:[^:]*]]: !fir.box<!fir.array<?xf64>>,
+// CHECK-SAME:    %[[VAL_1:.*]]: !fir.box<!fir.array<?xf64>>,
+// CHECK-SAME:    %[[VAL_2:.*]]: i1) {
+// CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_2]] : (!fir.box<!fir.array<?xf64>>, i1) -> ()
+// CHECK:  hlfir.copy_out %[[VAL_1]], %[[VAL_2]] to %[[VAL_0]] : (!fir.box<!fir.array<?xf64>>, i1, !fir.box<!fir.array<?xf64>>) -> ()