[flang-commits] [flang] [flang] Create temporaries for array sections passed to IGNORE_TKR dummy args (PR #147419)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Wed Jul 9 10:51:43 PDT 2025


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

>From 2477076ba81ec61e8bbff36fb624e8bb76c27380 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Fri, 4 Jul 2025 16:26:17 -0400
Subject: [PATCH 1/7] [flang] Contiguity check on IGNORE_TKR argument

If argument is marked as IGNORE_TKR, enable contiguity check on it,
unless IGNORE_TKR explicitly specifies to ignore contiguity.
---
 flang/lib/Lower/CallInterface.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 72431a9cfacc4..abe43dc9cb2b7 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -1426,6 +1426,13 @@ bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous()
           &characteristics->u);
   if (!dummy)
     return false;
+  if (dummy->ignoreTKR.test(common::IgnoreTKR::Contiguous))
+    return false;
+
+  // TODO: should this check ignore "device" or "managed"?
+  if (dummy->ignoreTKR.any())
+    return true;
+
   const auto &shapeAttrs = dummy->type.attrs();
   using ShapeAttrs = Fortran::evaluate::characteristics::TypeAndShape::Attr;
   if (shapeAttrs.test(ShapeAttrs::AssumedRank) ||

>From 50dd216ff0487709d52944006e35e6102015cef0 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 7 Jul 2025 10:20:38 -0400
Subject: [PATCH 2/7] [flang] Add -pedantic check for passing non-VOLATILE
 array section to VOLATILE dummy arg

Add the check, which partially addresses https://github.com/llvm/llvm-project/issues/137369

Implement HasTriplet().
---
 flang/include/flang/Evaluate/tools.h |  3 +++
 flang/lib/Evaluate/tools.cpp         | 19 +++++++++++++++++++
 flang/lib/Semantics/definable.cpp    |  3 +++
 3 files changed, 25 insertions(+)

diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index cad1b634f8924..0604049fd6a6d 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1123,6 +1123,9 @@ extern template semantics::UnorderedSymbolSet CollectCudaSymbols(
 // Predicate: does a variable contain a vector-valued subscript (not a triplet)?
 bool HasVectorSubscript(const Expr<SomeType> &);
 
+// Predicate: does a variable contain a triplet?
+bool HasTriplet(const Expr<SomeType> &);
+
 // Predicate: does an expression contain constant?
 bool HasConstant(const Expr<SomeType> &);
 
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index fcacdb93d662b..50a201dfcaced 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1173,6 +1173,25 @@ bool HasVectorSubscript(const Expr<SomeType> &expr) {
   return HasVectorSubscriptHelper{}(expr);
 }
 
+// HasTriplet()
+struct HasTripletHelper
+    : public AnyTraverse<HasTripletHelper, bool,
+          /*TraverseAssocEntityDetails=*/false> {
+  using Base = AnyTraverse<HasTripletHelper, bool, false>;
+  HasTripletHelper() : Base{*this} {}
+  using Base::operator();
+  bool operator()(const Subscript &ss) const {
+    return std::holds_alternative<Triplet>(ss.u);
+  }
+  bool operator()(const ProcedureRef &) const {
+    return false; // don't descend into function call arguments
+  }
+};
+
+bool HasTriplet(const Expr<SomeType> &expr) {
+  return HasTripletHelper{}(expr);
+}
+
 // HasConstant()
 struct HasConstantHelper : public AnyTraverse<HasConstantHelper, bool,
                                /*TraverseAssocEntityDetails=*/false> {
diff --git a/flang/lib/Semantics/definable.cpp b/flang/lib/Semantics/definable.cpp
index 08cb268b318ae..b2d2330887a53 100644
--- a/flang/lib/Semantics/definable.cpp
+++ b/flang/lib/Semantics/definable.cpp
@@ -371,6 +371,9 @@ std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
         return parser::Message{at,
             "Variable '%s' has a vector subscript"_err_en_US, expr.AsFortran()};
       }
+    } else if (evaluate::HasTriplet(expr)) {
+      return parser::Message{at,
+          "Variable '%s' has array section"_err_en_US, expr.AsFortran()};
     }
     if (FindPureProcedureContaining(scope) &&
         evaluate::ExtractCoarrayRef(expr)) {

>From 28cb65fbc9ee572dfffac698f0853e3bf5932871 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 7 Jul 2025 13:29:46 -0400
Subject: [PATCH 3/7] Undid change to definable.cpp

---
 flang/lib/Semantics/definable.cpp | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/flang/lib/Semantics/definable.cpp b/flang/lib/Semantics/definable.cpp
index b2d2330887a53..08cb268b318ae 100644
--- a/flang/lib/Semantics/definable.cpp
+++ b/flang/lib/Semantics/definable.cpp
@@ -371,9 +371,6 @@ std::optional<parser::Message> WhyNotDefinable(parser::CharBlock at,
         return parser::Message{at,
             "Variable '%s' has a vector subscript"_err_en_US, expr.AsFortran()};
       }
-    } else if (evaluate::HasTriplet(expr)) {
-      return parser::Message{at,
-          "Variable '%s' has array section"_err_en_US, expr.AsFortran()};
     }
     if (FindPureProcedureContaining(scope) &&
         evaluate::ExtractCoarrayRef(expr)) {

>From a176d2d1a6aab8c3cc3c4ceba907462970d86565 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 7 Jul 2025 16:19:47 -0400
Subject: [PATCH 4/7] The current implementation seems to work, but I will
 likely move some code around

---
 flang/include/flang/Lower/CallInterface.h | 2 +-
 flang/lib/Lower/CallInterface.cpp         | 5 +++--
 flang/lib/Lower/ConvertCall.cpp           | 9 ++++++++-
 3 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/flang/include/flang/Lower/CallInterface.h b/flang/include/flang/Lower/CallInterface.h
index 72bc9dd890a94..f2c8db47c60b1 100644
--- a/flang/include/flang/Lower/CallInterface.h
+++ b/flang/include/flang/Lower/CallInterface.h
@@ -170,7 +170,7 @@ class CallInterface {
     /// Is the argument INTENT(OUT)
     bool isIntentOut() const;
     /// Does the argument have the CONTIGUOUS attribute or have explicit shape?
-    bool mustBeMadeContiguous() const;
+    bool mustBeMadeContiguous(const bool argHasTriplet = false) const;
     /// Does the dummy argument have the VALUE attribute?
     bool hasValueAttribute() const;
     /// Does the dummy argument have the ALLOCATABLE attribute?
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index abe43dc9cb2b7..2ffdd985e4f72 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -1416,8 +1416,9 @@ bool Fortran::lower::CallInterface<T>::PassedEntity::isIntentOut() const {
     return true;
   return characteristics->GetIntent() == Fortran::common::Intent::Out;
 }
+
 template <typename T>
-bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous()
+bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous(const bool argHasTriplet)
     const {
   if (!characteristics)
     return true;
@@ -1430,7 +1431,7 @@ bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous()
     return false;
 
   // TODO: should this check ignore "device" or "managed"?
-  if (dummy->ignoreTKR.any())
+  if (dummy->ignoreTKR.any() && argHasTriplet)
     return true;
 
   const auto &shapeAttrs = dummy->type.attrs();
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 6ed15df0de754..91e54b15fb869 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1255,11 +1255,18 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       passingPolymorphicToNonPolymorphic &&
       (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
 
+  // Helper function to make it easier to unwrap and use expression
+  auto argHasTriplet = [](const Fortran::evaluate::ActualArgument &arg) -> bool {
+    if (const auto *expr = arg.UnwrapExpr())
+      return HasTriplet(*expr);
+    return false;
+  };
+
   // The simple contiguity of the actual is "lost" when passing a polymorphic
   // to a non polymorphic entity because the dummy dynamic type matters for
   // the contiguity.
   const bool mustDoCopyInOut =
-      actual.isArray() && arg.mustBeMadeContiguous() &&
+      actual.isArray() && arg.mustBeMadeContiguous(argHasTriplet(*arg.entity)) &&
       (passingPolymorphicToNonPolymorphic ||
        !isSimplyContiguous(*arg.entity, foldingContext));
 

>From 96daba4399d66cd16667592cae4e0aff5e332f63 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 7 Jul 2025 18:55:10 -0400
Subject: [PATCH 5/7] clang-format

---
 flang/lib/Evaluate/tools.cpp      | 9 +++------
 flang/lib/Lower/CallInterface.cpp | 4 ++--
 flang/lib/Lower/ConvertCall.cpp   | 6 ++++--
 3 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 50a201dfcaced..1c43e475905d2 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -1174,9 +1174,8 @@ bool HasVectorSubscript(const Expr<SomeType> &expr) {
 }
 
 // HasTriplet()
-struct HasTripletHelper
-    : public AnyTraverse<HasTripletHelper, bool,
-          /*TraverseAssocEntityDetails=*/false> {
+struct HasTripletHelper : public AnyTraverse<HasTripletHelper, bool,
+                              /*TraverseAssocEntityDetails=*/false> {
   using Base = AnyTraverse<HasTripletHelper, bool, false>;
   HasTripletHelper() : Base{*this} {}
   using Base::operator();
@@ -1188,9 +1187,7 @@ struct HasTripletHelper
   }
 };
 
-bool HasTriplet(const Expr<SomeType> &expr) {
-  return HasTripletHelper{}(expr);
-}
+bool HasTriplet(const Expr<SomeType> &expr) { return HasTripletHelper{}(expr); }
 
 // HasConstant()
 struct HasConstantHelper : public AnyTraverse<HasConstantHelper, bool,
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 2ffdd985e4f72..26bb1acfeea1b 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -1418,8 +1418,8 @@ bool Fortran::lower::CallInterface<T>::PassedEntity::isIntentOut() const {
 }
 
 template <typename T>
-bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous(const bool argHasTriplet)
-    const {
+bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous(
+    const bool argHasTriplet) const {
   if (!characteristics)
     return true;
   const auto *dummy =
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 91e54b15fb869..0da869adc5efa 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1256,7 +1256,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
 
   // Helper function to make it easier to unwrap and use expression
-  auto argHasTriplet = [](const Fortran::evaluate::ActualArgument &arg) -> bool {
+  auto argHasTriplet =
+      [](const Fortran::evaluate::ActualArgument &arg) -> bool {
     if (const auto *expr = arg.UnwrapExpr())
       return HasTriplet(*expr);
     return false;
@@ -1266,7 +1267,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
   // to a non polymorphic entity because the dummy dynamic type matters for
   // the contiguity.
   const bool mustDoCopyInOut =
-      actual.isArray() && arg.mustBeMadeContiguous(argHasTriplet(*arg.entity)) &&
+      actual.isArray() &&
+      arg.mustBeMadeContiguous(argHasTriplet(*arg.entity)) &&
       (passingPolymorphicToNonPolymorphic ||
        !isSimplyContiguous(*arg.entity, foldingContext));
 

>From aace0c2a95b6d8af56deb8f20f0aae92d64f3c36 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 7 Jul 2025 19:19:01 -0400
Subject: [PATCH 6/7] Some reformatting and comments

---
 flang/lib/Lower/CallInterface.cpp | 2 ++
 flang/lib/Lower/ConvertCall.cpp   | 8 ++++----
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 26bb1acfeea1b..80f8eb969797a 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -1417,6 +1417,8 @@ bool Fortran::lower::CallInterface<T>::PassedEntity::isIntentOut() const {
   return characteristics->GetIntent() == Fortran::common::Intent::Out;
 }
 
+/// Returning "true" from this function is a prerequisite for running
+/// contiguity check on the actual argument.
 template <typename T>
 bool Fortran::lower::CallInterface<T>::PassedEntity::mustBeMadeContiguous(
     const bool argHasTriplet) const {
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 0da869adc5efa..34f35e4fd4fb4 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1256,19 +1256,19 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
 
   // Helper function to make it easier to unwrap and use expression
-  auto argHasTriplet =
-      [](const Fortran::evaluate::ActualArgument &arg) -> bool {
+  auto argHasTriplet = [](const Fortran::evaluate::ActualArgument &arg) -> bool {
     if (const auto *expr = arg.UnwrapExpr())
       return HasTriplet(*expr);
     return false;
   };
 
+  const bool actualHasTriplet = argHasTriplet(*arg.entity);
+
   // The simple contiguity of the actual is "lost" when passing a polymorphic
   // to a non polymorphic entity because the dummy dynamic type matters for
   // the contiguity.
   const bool mustDoCopyInOut =
-      actual.isArray() &&
-      arg.mustBeMadeContiguous(argHasTriplet(*arg.entity)) &&
+      actual.isArray() && arg.mustBeMadeContiguous(actualHasTriplet) &&
       (passingPolymorphicToNonPolymorphic ||
        !isSimplyContiguous(*arg.entity, foldingContext));
 

>From 5fdfb455a4618a2bfb3b219dd51acab3da791d13 Mon Sep 17 00:00:00 2001
From: Eugene Epshteyn <eepshteyn at nvidia.com>
Date: Mon, 7 Jul 2025 19:20:01 -0400
Subject: [PATCH 7/7] clang-format

---
 flang/lib/Lower/ConvertCall.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 34f35e4fd4fb4..4d7d142c6b49d 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1256,7 +1256,8 @@ static PreparedDummyArgument preparePresentUserCallActualArgument(
       (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType));
 
   // Helper function to make it easier to unwrap and use expression
-  auto argHasTriplet = [](const Fortran::evaluate::ActualArgument &arg) -> bool {
+  auto argHasTriplet =
+      [](const Fortran::evaluate::ActualArgument &arg) -> bool {
     if (const auto *expr = arg.UnwrapExpr())
       return HasTriplet(*expr);
     return false;



More information about the flang-commits mailing list