[flang-commits] [flang] [flang][DRAFT-DEV][DO NOT REVIEW] Copy-in/Copy-out determination (PR #151426)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Thu Jul 31 19:20:34 PDT 2025


https://github.com/eugeneepshteyn updated https://github.com/llvm/llvm-project/pull/151426

>From 54c0158dcce9b5ebb6a3658298dedcda72b41f52 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 17:08:06 -0400
Subject: [PATCH 1/9] [flang][DRAFT] Copy-in/Copy-out determination

Plumbing/API for copy-in/copy-out
---
 flang/include/flang/Evaluate/call.h | 19 +++++++++++++++++--
 flang/lib/Evaluate/call.cpp         | 16 ++++++++++++++++
 2 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 2a5929b873d74..ac11527e4ecaa 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -52,7 +52,7 @@ using SymbolRef = common::Reference<const Symbol>;
 
 class ActualArgument {
 public:
-  ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef);
+  ENUM_CLASS(Attr, PassedObject, PercentVal, PercentRef, CopyIn, CopyOut);
   using Attrs = common::EnumSet<Attr, Attr_enumSize>;
 
   // Dummy arguments that are TYPE(*) can be forwarded as actual arguments.
@@ -131,7 +131,6 @@ class ActualArgument {
     return *this;
   }
 
-  bool Matches(const characteristics::DummyArgument &) const;
   common::Intent dummyIntent() const { return dummyIntent_; }
   ActualArgument &set_dummyIntent(common::Intent intent) {
     dummyIntent_ = intent;
@@ -161,6 +160,20 @@ class ActualArgument {
     return *this;
   }
 
+  // This actual argument may need copy-in before the procedure call
+  bool mayNeedCopyIn() const { return attrs_.test(Attr::CopyIn); };
+  ActualArgument &set_mayNeedCopyIn() {
+    attrs_ = attrs_ + Attr::CopyIn;
+    return *this;
+  }
+
+  // This actual argument may need copy-out after the procedure call
+  bool mayNeedCopyOut() const { return attrs_.test(Attr::CopyOut); };
+  ActualArgument &set_mayNeedCopyOut() {
+    attrs_ = attrs_ + Attr::CopyOut;
+    return *this;
+  }
+
 private:
   // Subtlety: There is a distinction that must be maintained here between an
   // actual argument expression that is a variable and one that is not,
@@ -272,6 +285,8 @@ class ProcedureRef {
   bool operator==(const ProcedureRef &) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
+  void DetermineCopyInOut();
+
 protected:
   ProcedureDesignator proc_;
   ActualArguments arguments_;
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index f77df92a7597a..1e582a516a694 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -13,6 +13,7 @@
 #include "flang/Evaluate/expression.h"
 #include "flang/Evaluate/tools.h"
 #include "flang/Semantics/symbol.h"
+#include "flang/Semantics/semantics.h"
 #include "flang/Support/Fortran.h"
 
 namespace Fortran::evaluate {
@@ -247,4 +248,19 @@ ProcedureRef::~ProcedureRef() {}
 
 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 
+void ProcedureRef::DetermineCopyInOut() {
+  if (!proc().GetSymbol()) {
+    return;
+  }
+  // Get folding context of the call site owner
+  FoldingContext &fc{proc_.GetSymbol()->owner().context().foldingContext()};
+  auto procInfo{characteristics::Procedure::Characterize(
+      proc(), fc, /*emitError=*/false)};
+  if (!procInfo) {
+    return;
+  }
+  // TODO: at this point have dummy arguments as procInfo->dummyArguments
+  // and have actual arguments via arguments_
+}
+
 } // namespace Fortran::evaluate

>From 808fb20c3e4d6dc80b7e3793fb28b4f7854b07f8 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 17:44:37 -0400
Subject: [PATCH 2/9] Call DetermineCopyInOut() from lowering

---
 flang/include/flang/Lower/CallInterface.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index 72bc9dd890a94..eca697d474c47 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -284,6 +284,9 @@ class CallerInterface : public CallInterface<CallerInterface> {
   CallerInterface(const Fortran::evaluate::ProcedureRef &p,
                   Fortran::lower::AbstractConverter &c)
       : CallInterface{c}, procRef{p} {
+    // Ensure that procRef gathers necessary information to determine the
+    // need for copy-in and copy-out
+    const_cast<Fortran::evaluate::ProcedureRef &>(procRef).DetermineCopyInOut();
     declare();
     mapPassedEntities();
     actualInputs.resize(getNumFIRArguments());

>From 28bc5bd95f83f131c4a905f69e402fc97a1b7e9d Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 18:20:23 -0400
Subject: [PATCH 3/9] clang-format

---
 flang/lib/Evaluate/call.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 1e582a516a694..0cfad4cfae17e 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -12,8 +12,8 @@
 #include "flang/Evaluate/check-expression.h"
 #include "flang/Evaluate/expression.h"
 #include "flang/Evaluate/tools.h"
-#include "flang/Semantics/symbol.h"
 #include "flang/Semantics/semantics.h"
+#include "flang/Semantics/symbol.h"
 #include "flang/Support/Fortran.h"
 
 namespace Fortran::evaluate {

>From f44b9459f6e2c0f60d6f66d00ec37d383befe021 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 19:54:01 -0400
Subject: [PATCH 4/9] DetermineCopyInOut() is now called from ProcedureRef
 constructor

---
 flang/include/flang/Evaluate/call.h       | 8 ++++++--
 flang/include/flang/Lower/CallInterface.h | 3 ---
 flang/lib/Evaluate/call.cpp               | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index ac11527e4ecaa..56338901b22bb 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -248,7 +248,11 @@ class ProcedureRef {
   ProcedureRef(ProcedureDesignator &&p, ActualArguments &&a,
       bool hasAlternateReturns = false)
       : proc_{std::move(p)}, arguments_{std::move(a)},
-        hasAlternateReturns_{hasAlternateReturns} {}
+        hasAlternateReturns_{hasAlternateReturns} {
+    // Gathers necessary information to determine the need for copy-in and
+    // copy-out
+    DetermineCopyInOut();
+  }
   ~ProcedureRef();
   static void Deleter(ProcedureRef *);
 
@@ -285,9 +289,9 @@ class ProcedureRef {
   bool operator==(const ProcedureRef &) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
+protected:
   void DetermineCopyInOut();
 
-protected:
   ProcedureDesignator proc_;
   ActualArguments arguments_;
   Chevrons chevrons_;
diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index eca697d474c47..72bc9dd890a94 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -284,9 +284,6 @@ class CallerInterface : public CallInterface<CallerInterface> {
   CallerInterface(const Fortran::evaluate::ProcedureRef &p,
                   Fortran::lower::AbstractConverter &c)
       : CallInterface{c}, procRef{p} {
-    // Ensure that procRef gathers necessary information to determine the
-    // need for copy-in and copy-out
-    const_cast<Fortran::evaluate::ProcedureRef &>(procRef).DetermineCopyInOut();
     declare();
     mapPassedEntities();
     actualInputs.resize(getNumFIRArguments());
diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 0cfad4cfae17e..c558335c16545 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -255,7 +255,7 @@ void ProcedureRef::DetermineCopyInOut() {
   // Get folding context of the call site owner
   FoldingContext &fc{proc_.GetSymbol()->owner().context().foldingContext()};
   auto procInfo{characteristics::Procedure::Characterize(
-      proc(), fc, /*emitError=*/false)};
+      proc_, fc, /*emitError=*/true)};
   if (!procInfo) {
     return;
   }

>From ffd65635da52fb2788056113b9fa3bdf2d430436 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 19:57:40 -0400
Subject: [PATCH 5/9] clang-format

---
 flang/lib/Evaluate/call.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index c558335c16545..5a94071d9bf20 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -254,8 +254,8 @@ void ProcedureRef::DetermineCopyInOut() {
   }
   // Get folding context of the call site owner
   FoldingContext &fc{proc_.GetSymbol()->owner().context().foldingContext()};
-  auto procInfo{characteristics::Procedure::Characterize(
-      proc_, fc, /*emitError=*/true)};
+  auto procInfo{
+      characteristics::Procedure::Characterize(proc_, fc, /*emitError=*/true)};
   if (!procInfo) {
     return;
   }

>From fb3a93c6da7f34110734f606e795533b83ff1562 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Wed, 30 Jul 2025 22:39:44 -0400
Subject: [PATCH 6/9] Minor tweak

---
 flang/lib/Evaluate/call.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 5a94071d9bf20..605931d5d6dfb 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -249,7 +249,7 @@ ProcedureRef::~ProcedureRef() {}
 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 
 void ProcedureRef::DetermineCopyInOut() {
-  if (!proc().GetSymbol()) {
+  if (!proc_.GetSymbol()) {
     return;
   }
   // Get folding context of the call site owner

>From 527b2d7646ca11a4fa9921695b4efd360a9c5b54 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 31 Jul 2025 13:32:10 -0400
Subject: [PATCH 7/9] DetermineCopyInOut() is now called at ProcedureRef
 instantiation

---
 flang/include/flang/Evaluate/call.h | 5 +----
 flang/lib/Semantics/expression.cpp  | 1 +
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 56338901b22bb..3d4f791380fba 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -249,9 +249,6 @@ class ProcedureRef {
       bool hasAlternateReturns = false)
       : proc_{std::move(p)}, arguments_{std::move(a)},
         hasAlternateReturns_{hasAlternateReturns} {
-    // Gathers necessary information to determine the need for copy-in and
-    // copy-out
-    DetermineCopyInOut();
   }
   ~ProcedureRef();
   static void Deleter(ProcedureRef *);
@@ -289,9 +286,9 @@ class ProcedureRef {
   bool operator==(const ProcedureRef &) const;
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
-protected:
   void DetermineCopyInOut();
 
+protected:
   ProcedureDesignator proc_;
   ActualArguments arguments_;
   Chevrons chevrons_;
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index 92dbe0e5da11c..29917a570ddc5 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -3455,6 +3455,7 @@ void ExpressionAnalyzer::Analyze(const parser::CallStmt &callStmt) {
                 HasAlternateReturns(callee->arguments)},
             ProcedureRef::Deleter);
         DEREF(callStmt.typedCall.get()).set_chevrons(std::move(*chevrons));
+        DEREF(callStmt.typedCall.get()).DetermineCopyInOut();
         return;
       }
     }

>From 9c1755b248f4fe7b38547af5ee74563cfdcd6753 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 31 Jul 2025 22:01:17 -0400
Subject: [PATCH 8/9] Very rough beginnings of argument handling in
 DetermineCopyInOut()

---
 flang/lib/Evaluate/call.cpp | 61 +++++++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 605931d5d6dfb..6f604d2aed28b 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -248,6 +248,23 @@ ProcedureRef::~ProcedureRef() {}
 
 void ProcedureRef::Deleter(ProcedureRef *p) { delete p; }
 
+// We don't know the dummy argument info (e.g., procedure with implicit
+// interface
+static void DetermineCopyInOutArgument(
+    const characteristics::Procedure &procInfo, ActualArgument &actual) {
+
+  // Only check that actual argument is contiguous
+  // For non-contiguous, do copy-in
+}
+
+static void DetermineCopyInOutArgument(
+    const characteristics::Procedure &procInfo,
+    ActualArgument &actual, characteristics::DummyArgument &dummy) {
+
+  // TODO: assert? procInfo.HasExplicitInterface()
+
+}
+
 void ProcedureRef::DetermineCopyInOut() {
   if (!proc_.GetSymbol()) {
     return;
@@ -261,6 +278,50 @@ void ProcedureRef::DetermineCopyInOut() {
   }
   // TODO: at this point have dummy arguments as procInfo->dummyArguments
   // and have actual arguments via arguments_
+
+  // TODO: implicitly declared procedure may not have any information about
+  // its dummy args. Handle this case.
+
+  // Don't change anything about actual or dummy arguments, except for
+  // computing copy-in/copy-out information. If detect something wrong with
+  // the arguments, stop processing and let semantic analysis generate the
+  // error messages.
+  size_t index{0};
+  std::set<std::string> processedKeywords;
+  bool seenKeyword{false};
+  for (auto &actual : arguments_) {
+    if (!actual) {
+      continue;
+    }
+    if (index >= procInfo->dummyArguments.size()) {
+      // More actual arguments than dummy arguments. Semantic analysis will
+      // deal with the error.
+      return;
+    }
+    if (actual->keyword()) {
+      seenKeyword = true;
+      auto actualName = actual->keyword()->ToString();
+      if (processedKeywords.find(actualName) != processedKeywords.end()) {
+        // Actual arguments with duplicate keywords. Semantic analysis will
+        // deal with the error.
+        return;
+      }
+      else {
+        processedKeywords.insert(actualName);
+
+      }
+    }
+    else if (seenKeyword) {
+      // Non-keyword actual argument after have seen at least one keyword
+      // actual argument. Semantic analysis will deal with the error.
+      return;
+    }
+    else {
+      // Positional argument processing
+    }
+
+    ++index;
+  }
 }
 
 } // namespace Fortran::evaluate

>From 3198e9059218f6a1ecccd9e609e6eb9e425281e0 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Thu, 31 Jul 2025 22:20:18 -0400
Subject: [PATCH 9/9] More args handing in DetermineCopyInOut()

---
 flang/lib/Evaluate/call.cpp | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Evaluate/call.cpp b/flang/lib/Evaluate/call.cpp
index 6f604d2aed28b..be5db758e9717 100644
--- a/flang/lib/Evaluate/call.cpp
+++ b/flang/lib/Evaluate/call.cpp
@@ -276,12 +276,15 @@ void ProcedureRef::DetermineCopyInOut() {
   if (!procInfo) {
     return;
   }
-  // TODO: at this point have dummy arguments as procInfo->dummyArguments
-  // and have actual arguments via arguments_
-
-  // TODO: implicitly declared procedure may not have any information about
-  // its dummy args. Handle this case.
-
+  if (!procInfo->HasExplicitInterface()) {
+    for (auto &actual : arguments_) {
+      if (!actual) {
+        continue;
+      }
+      DetermineCopyInOutArgument(*procInfo, *actual);
+    }
+    return;
+  }
   // Don't change anything about actual or dummy arguments, except for
   // computing copy-in/copy-out information. If detect something wrong with
   // the arguments, stop processing and let semantic analysis generate the
@@ -308,7 +311,13 @@ void ProcedureRef::DetermineCopyInOut() {
       }
       else {
         processedKeywords.insert(actualName);
-
+        if (auto it = std::find_if(procInfo->dummyArguments.begin(),
+            procInfo->dummyArguments.end(),
+            [&](const characteristics::DummyArgument &dummy) {
+              return dummy.name == actualName;
+            }); it != procInfo->dummyArguments.end()) {
+          DetermineCopyInOutArgument(*procInfo, *actual, *it);
+        }
       }
     }
     else if (seenKeyword) {
@@ -318,6 +327,8 @@ void ProcedureRef::DetermineCopyInOut() {
     }
     else {
       // Positional argument processing
+      DetermineCopyInOutArgument(*procInfo, *actual,
+          procInfo->dummyArguments[index]);
     }
 
     ++index;



More information about the flang-commits mailing list