[flang-commits] [flang] [flang][cuda] Emit error when a device actual argument is used in host intrinsic (PR #172914)
Valentin Clement バレンタイン クレメン via flang-commits
flang-commits at lists.llvm.org
Thu Dec 18 14:36:58 PST 2025
https://github.com/clementval updated https://github.com/llvm/llvm-project/pull/172914
>From 69f26bd62a183969642c95389e65bb93f90578c6 Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Thu, 18 Dec 2025 11:35:56 -0800
Subject: [PATCH 1/2] [flang][cuda] Emit error when a device actual argument is
used in host intrinsic
---
flang/include/flang/Semantics/scope.h | 2 +-
flang/include/flang/Semantics/tools.h | 1 +
flang/lib/Semantics/check-call.cpp | 31 ++++++++++++++++++++
flang/lib/Semantics/resolve-names.cpp | 11 +++++++
flang/lib/Semantics/tools.cpp | 8 +++++
flang/test/Lower/CUDA/cuda-data-transfer.cuf | 12 +++++++-
flang/test/Semantics/cuf23.cuf | 7 +++++
7 files changed, 70 insertions(+), 2 deletions(-)
diff --git a/flang/include/flang/Semantics/scope.h b/flang/include/flang/Semantics/scope.h
index ecffdb468bf6c..25806fafc689a 100644
--- a/flang/include/flang/Semantics/scope.h
+++ b/flang/include/flang/Semantics/scope.h
@@ -61,7 +61,7 @@ class Scope {
public:
ENUM_CLASS(Kind, Global, IntrinsicModules, Module, MainProgram, Subprogram,
BlockData, DerivedType, BlockConstruct, Forall, OtherConstruct,
- OpenACCConstruct, ImpliedDos, OtherClause)
+ OpenACCConstruct, ImpliedDos, OtherClause, CUFKernelDoConstruct)
using ImportKind = common::ImportKind;
// Create the Global scope -- the root of the scope tree
diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h
index 1c3477013b559..1ea40bbb43e61 100644
--- a/flang/include/flang/Semantics/tools.h
+++ b/flang/include/flang/Semantics/tools.h
@@ -46,6 +46,7 @@ const Scope *FindModuleOrSubmoduleContaining(const Scope &);
const Scope *FindModuleFileContaining(const Scope &);
const Scope *FindPureProcedureContaining(const Scope &);
const Scope *FindOpenACCConstructContaining(const Scope *);
+const Scope *FindCUFKernelDoConstructContaining(const Scope *);
const Symbol *FindInterface(const Symbol &);
const Symbol *FindSubprogram(const Symbol &);
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 022b4289b4e7c..2bf1d41e4e449 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -1145,6 +1145,37 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
dummyName, toStr(dummyDataAttr), toStr(actualDataAttr));
}
}
+ // Emit an error message if an actual argument passed to a host intrinsic is
+ // on the device.
+ if (intrinsic && !FindCUDADeviceContext(scope) &&
+ !FindOpenACCConstructContaining(scope) &&
+ !FindCUFKernelDoConstructContaining(scope)) {
+ if (intrinsic->name != "__builtin_c_f_pointer" &&
+ intrinsic->name != "__builtin_c_loc") {
+ std::optional<common::CUDADataAttr> actualDataAttr;
+ if (const auto *actualObject{actualLastSymbol
+ ? actualLastSymbol->detailsIf<ObjectEntityDetails>()
+ : nullptr}) {
+ actualDataAttr = actualObject->cudaDataAttr();
+ }
+ if (actualDataAttr && *actualDataAttr == common::CUDADataAttr::Device) {
+ // Allocatable or pointer with device attribute have their descriptor in
+ // managed memory. It is allowed to pass them to some inquiry
+ // intrinsics.
+ if (!actualLastSymbol || !IsAllocatableOrPointer(*actualLastSymbol) ||
+ (IsAllocatableOrPointer(*actualLastSymbol) &&
+ intrinsic->name != "size" && intrinsic->name != "lbound" &&
+ intrinsic->name != "ubound" && intrinsic->name != "shape" &&
+ intrinsic->name != "allocated" &&
+ intrinsic->name != "associated" && intrinsic->name != "kind" &&
+ intrinsic->name != "present")) {
+ messages.Say(
+ "Actual argument %s associated with host intrinsic %s is on the device"_err_en_US,
+ actualLastSymbol->name(), intrinsic->name);
+ }
+ }
+ }
+ }
// Warning for breaking F'2023 change with character allocatables
if (intrinsic && dummy.intent != common::Intent::In) {
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 345a0e4e8ecce..a7c5d5c3ca317 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -1081,6 +1081,8 @@ class DeclarationVisitor : public ArraySpecVisitor,
bool Pre(const parser::SaveStmt &);
bool Pre(const parser::BasedPointer &);
void Post(const parser::BasedPointer &);
+ bool Pre(const parser::CUFKernelDoConstruct &);
+ void Post(const parser::CUFKernelDoConstruct &);
void PointerInitialization(
const parser::Name &, const parser::InitialDataTarget &);
@@ -9257,6 +9259,15 @@ void DeclarationVisitor::LegacyDataInitialization(const parser::Name &name,
}
}
+bool DeclarationVisitor::Pre(const parser::CUFKernelDoConstruct &x) {
+ PushScope(Scope::Kind::CUFKernelDoConstruct, nullptr);
+ return true;
+}
+
+void DeclarationVisitor::Post(const parser::CUFKernelDoConstruct &x) {
+ PopScope();
+}
+
void ResolveNamesVisitor::HandleCall(
Symbol::Flag procFlag, const parser::Call &call) {
common::visit(
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index cf1e5e7d44565..c1d7c288d27f7 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -122,6 +122,14 @@ const Scope *FindOpenACCConstructContaining(const Scope *scope) {
: nullptr;
}
+const Scope *FindCUFKernelDoConstructContaining(const Scope *scope) {
+ return scope ? FindScopeContaining(*scope,
+ [](const Scope &s) {
+ return s.kind() == Scope::Kind::CUFKernelDoConstruct;
+ })
+ : nullptr;
+}
+
// 7.5.2.4 "same derived type" test -- rely on IsTkCompatibleWith() and its
// infrastructure to detect and handle comparisons on distinct (but "same")
// sequence/bind(C) derived types
diff --git a/flang/test/Lower/CUDA/cuda-data-transfer.cuf b/flang/test/Lower/CUDA/cuda-data-transfer.cuf
index 015947430b07c..e0c23a1d5df8f 100644
--- a/flang/test/Lower/CUDA/cuda-data-transfer.cuf
+++ b/flang/test/Lower/CUDA/cuda-data-transfer.cuf
@@ -22,6 +22,15 @@ module mod1
end function
end interface
+ interface
+ function somefunction(d, n)
+ integer :: n
+ real(8), device :: d(n,n)
+ real(8) :: somefunction(n)
+ end function
+ end interface
+
+
contains
function dev1(a)
integer, device :: a(:)
@@ -447,9 +456,10 @@ end subroutine
! CHECK: cuf.data_transfer
subroutine sub23(n)
+ use mod1
integer :: n
real(8), device :: d(n,n), x(n)
- x = sum(d,dim=2) ! Was triggering Unsupported CUDA data transfer
+ x = somefunction(d,2) ! Was triggering Unsupported CUDA data transfer
end subroutine
! CHECK-LABEL: func.func @_QPsub23
diff --git a/flang/test/Semantics/cuf23.cuf b/flang/test/Semantics/cuf23.cuf
index 8c03c18d9b0db..73f13d1d46a17 100644
--- a/flang/test/Semantics/cuf23.cuf
+++ b/flang/test/Semantics/cuf23.cuf
@@ -53,3 +53,10 @@ attributes(global) subroutine global_with_block()
print*, a ! ok
end block
end subroutine
+
+subroutine intrinsic_error(n)
+ integer :: n
+ real(8), device :: d(n,n), x(n)
+!ERROR: Actual argument d associated with host intrinsic sum is on the device
+ x = sum(d,dim=2)
+end subroutine
>From 687ec5b00ef0154c01001c5933e21a7b462e65bd Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Thu, 18 Dec 2025 14:36:45 -0800
Subject: [PATCH 2/2] Use StringSet for the list of intrinsics
---
flang/lib/Semantics/check-call.cpp | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 2bf1d41e4e449..2d2fefb5e6167 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -18,6 +18,7 @@
#include "flang/Parser/message.h"
#include "flang/Semantics/scope.h"
#include "flang/Semantics/tools.h"
+#include "llvm/ADT/StringSet.h"
#include <map>
#include <string>
@@ -340,6 +341,15 @@ static bool DefersSameTypeParameters(
return true;
}
+// List of intrinsics that are skipped when checking for device actual
+// arguments.
+static const llvm::StringSet<> cudaSkippedIntrinsics = {"__builtin_c_f_pointer",
+ "__builtin_c_loc"};
+// List of intrinsics that can have a device actual argument if it is an
+// allocatable or pointer.
+static const llvm::StringSet<> cudaAllowedIntrinsics = {"size", "lbound",
+ "ubound", "shape", "allocated", "associated", "kind", "present"};
+
static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
const std::string &dummyName, evaluate::Expr<evaluate::SomeType> &actual,
characteristics::TypeAndShape &actualType, bool isElemental,
@@ -1150,8 +1160,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
if (intrinsic && !FindCUDADeviceContext(scope) &&
!FindOpenACCConstructContaining(scope) &&
!FindCUFKernelDoConstructContaining(scope)) {
- if (intrinsic->name != "__builtin_c_f_pointer" &&
- intrinsic->name != "__builtin_c_loc") {
+ if (!cudaSkippedIntrinsics.contains(intrinsic->name)) {
std::optional<common::CUDADataAttr> actualDataAttr;
if (const auto *actualObject{actualLastSymbol
? actualLastSymbol->detailsIf<ObjectEntityDetails>()
@@ -1164,11 +1173,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
// intrinsics.
if (!actualLastSymbol || !IsAllocatableOrPointer(*actualLastSymbol) ||
(IsAllocatableOrPointer(*actualLastSymbol) &&
- intrinsic->name != "size" && intrinsic->name != "lbound" &&
- intrinsic->name != "ubound" && intrinsic->name != "shape" &&
- intrinsic->name != "allocated" &&
- intrinsic->name != "associated" && intrinsic->name != "kind" &&
- intrinsic->name != "present")) {
+ !cudaAllowedIntrinsics.contains(intrinsic->name))) {
messages.Say(
"Actual argument %s associated with host intrinsic %s is on the device"_err_en_US,
actualLastSymbol->name(), intrinsic->name);
More information about the flang-commits
mailing list