[flang-commits] [flang] [flang][cuda] Lower c_devptr value arguments in bind(c) like c_ptr (PR #199316)

Valentin Clement バレンタイン クレメン via flang-commits flang-commits at lists.llvm.org
Fri May 22 21:06:46 PDT 2026


https://github.com/clementval created https://github.com/llvm/llvm-project/pull/199316

Treat `type(c_devptr), value` arguments in BIND(C) interfaces like `type(c_ptr), value` by passing the nested raw address value instead of the outer derived type ABI. This keeps call signatures consistent for CUDA Fortran generic specifics that share a C binding label and avoids argument misclassification at the x86_64 register/stack boundary.

>From dbbeaa6c41b0e9b57ba9b34c3ae319b95e762e2a Mon Sep 17 00:00:00 2001
From: Valentin Clement <clementval at gmail.com>
Date: Fri, 22 May 2026 21:04:34 -0700
Subject: [PATCH] [flang][cuda] Lower c_devptr value arguments in bind(c) like
 c_ptr

---
 flang/lib/Lower/CallInterface.cpp     |  8 +++++--
 flang/lib/Lower/ConvertCall.cpp       | 18 ++++++++-------
 flang/lib/Lower/ConvertVariable.cpp   | 33 +++++++++++++++------------
 flang/test/HLFIR/c_devptr_byvalue.cuf | 22 ++++++++++++++++++
 4 files changed, 57 insertions(+), 24 deletions(-)
 create mode 100644 flang/test/HLFIR/c_devptr_byvalue.cuf

diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index e9059581c690a..99ed29973f8ce 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -1202,16 +1202,20 @@ class Fortran::lower::CallInterfaceImpl {
       Property prop = Property::BaseAddress;
       if (isValueAttr) {
         bool isBuiltinCptrType = fir::isa_builtin_cptr_type(type);
+        bool isBuiltinCdevptrType = fir::isa_builtin_cdevptr_type(type);
         if (isBindC || (!mlir::isa<fir::SequenceType>(type) &&
                         !obj.attrs.test(Attrs::Optional) &&
                         (dynamicType.category() !=
                              Fortran::common::TypeCategory::Derived ||
-                         isBuiltinCptrType))) {
+                         isBuiltinCptrType || isBuiltinCdevptrType))) {
           passBy = PassEntityBy::Value;
           prop = Property::Value;
-          if (isBuiltinCptrType) {
+          if (isBuiltinCptrType || isBuiltinCdevptrType) {
             auto recTy = mlir::dyn_cast<fir::RecordType>(type);
             mlir::Type fieldTy = recTy.getTypeList()[0].second;
+            if (isBuiltinCdevptrType)
+              fieldTy =
+                  mlir::cast<fir::RecordType>(fieldTy).getTypeList()[0].second;
             passType = fir::ReferenceType::get(fieldTy);
           } else {
             passType = type;
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index e6c89122bde23..6f67312616e9b 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -79,15 +79,15 @@ static fir::ExtendedValue toExtendedValue(mlir::Location loc, mlir::Value base,
   return base;
 }
 
-/// Lower a type(C_PTR/C_FUNPTR) argument with VALUE attribute into a
+/// Lower a type(C_PTR/C_FUNPTR/C_DEVPTR) argument with VALUE attribute into a
 /// reference. A C pointer can correspond to a Fortran dummy argument of type
 /// C_PTR with the VALUE attribute. (see 18.3.6 note 3).
 static mlir::Value genRecordCPtrValueArg(fir::FirOpBuilder &builder,
                                          mlir::Location loc, mlir::Value rec,
-                                         mlir::Type ty) {
-  mlir::Value cAddr = fir::factory::genCPtrOrCFunptrAddr(builder, loc, rec, ty);
-  mlir::Value cVal = fir::LoadOp::create(builder, loc, cAddr);
-  return builder.createConvert(loc, cAddr.getType(), cVal);
+                                         mlir::Type) {
+  mlir::Value cVal = fir::factory::genCPtrOrCFunptrValue(builder, loc, rec);
+  return builder.createConvert(loc, fir::ReferenceType::get(cVal.getType()),
+                               cVal);
 }
 
 // Find the argument that corresponds to the host associations.
@@ -610,7 +610,8 @@ Fortran::lower::genCallOpAndResult(
       }
     } else {
       mlir::Type fromTy = fir::unwrapRefType(fst.getType());
-      if (fir::isa_builtin_cptr_type(fromTy) &&
+      if ((fir::isa_builtin_cptr_type(fromTy) ||
+           fir::isa_builtin_cdevptr_type(fromTy)) &&
           Fortran::lower::isCPtrArgByValueType(snd)) {
         cast = genRecordCPtrValueArg(builder, loc, fst, fromTy);
       } else if (fir::isa_derived(snd) && !fir::isa_derived(fst.getType())) {
@@ -1751,8 +1752,9 @@ void prepareUserCallArguments(
       hlfir::Entity value = hlfir::loadTrivialScalar(loc, builder, actual);
 
       mlir::Type eleTy = value.getFortranElementType();
-      if (fir::isa_builtin_cptr_type(eleTy)) {
-        // Pass-by-value argument of type(C_PTR/C_FUNPTR).
+      if (fir::isa_builtin_cptr_type(eleTy) ||
+          fir::isa_builtin_cdevptr_type(eleTy)) {
+        // Pass-by-value argument of type(C_PTR/C_FUNPTR/C_DEVPTR).
         // Load the __address component and pass it by value.
         if (value.isValue()) {
           auto associate = hlfir::genAssociateExpr(loc, builder, value, eleTy,
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 4e7b091d26186..0a44221f32484 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -588,15 +588,14 @@ fir::GlobalOp Fortran::lower::defineGlobal(
     if (details && details->init()) {
       auto sym{*details->init()};
       if (sym) // Has a procedure target.
-        createGlobalInitialization(
-            builder, global, [&](fir::FirOpBuilder &b) {
-              Fortran::lower::StatementContext stmtCtx(
-                  /*cleanupProhibited=*/true);
-              auto box{Fortran::lower::convertProcedureDesignatorInitialTarget(
-                  converter, loc, *sym)};
-              auto castTo{builder.createConvert(loc, symTy, box)};
-              fir::HasValueOp::create(b, loc, castTo);
-            });
+        createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
+          Fortran::lower::StatementContext stmtCtx(
+              /*cleanupProhibited=*/true);
+          auto box{Fortran::lower::convertProcedureDesignatorInitialTarget(
+              converter, loc, *sym)};
+          auto castTo{builder.createConvert(loc, symTy, box)};
+          fir::HasValueOp::create(b, loc, castTo);
+        });
       else { // Has NULL() target.
         createGlobalInitialization(builder, global, [&](fir::FirOpBuilder &b) {
           auto box{fir::factory::createNullBoxProc(b, loc, symTy)};
@@ -2506,17 +2505,23 @@ void Fortran::lower::mapSymbolAttributes(
   if (!addr) {
     if (arg) {
       mlir::Type argType = arg.getType();
+      mlir::Type symType = converter.genType(sym);
       const bool isCptrByVal = Fortran::semantics::IsBuiltinCPtr(sym) &&
                                Fortran::lower::isCPtrArgByValueType(argType);
-      if (isCptrByVal || !fir::conformsWithPassByRef(argType)) {
+      const bool isCdevptrByVal = fir::isa_builtin_cdevptr_type(symType) &&
+                                  Fortran::lower::isCPtrArgByValueType(argType);
+      if (isCptrByVal || isCdevptrByVal ||
+          !fir::conformsWithPassByRef(argType)) {
         // Dummy argument passed in register. Place the value in memory at that
         // point since lowering expect symbols to be mapped to memory addresses.
-        mlir::Type symType = converter.genType(sym);
         addr = fir::AllocaOp::create(builder, loc, symType);
-        if (isCptrByVal) {
-          // Place the void* address into the CPTR address component.
+        if (isCptrByVal || isCdevptrByVal) {
+          // Place the void* address into the pointer address component.
           mlir::Value addrComponent =
-              fir::factory::genCPtrOrCFunptrAddr(builder, loc, addr, symType);
+              isCdevptrByVal
+                  ? fir::factory::genCDevPtrAddr(builder, loc, addr, symType)
+                  : fir::factory::genCPtrOrCFunptrAddr(builder, loc, addr,
+                                                       symType);
           builder.createStoreWithConvert(loc, arg, addrComponent);
         } else {
           builder.createStoreWithConvert(loc, arg, addr);
diff --git a/flang/test/HLFIR/c_devptr_byvalue.cuf b/flang/test/HLFIR/c_devptr_byvalue.cuf
new file mode 100644
index 0000000000000..46229df4610a9
--- /dev/null
+++ b/flang/test/HLFIR/c_devptr_byvalue.cuf
@@ -0,0 +1,22 @@
+! RUN: bbc -emit-hlfir -fcuda %s -o - | FileCheck %s
+
+! CHECK-LABEL:   func.func @_QPtest_c_devptr(
+! CHECK-SAME:                                %[[ARG0:.*]]: !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>> {fir.bindc_name = "cdevptr"}) {
+! CHECK:           %[[DSCOPE:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK:           %[[DECL:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %[[DSCOPE]] arg {{[0-9]+}} {uniq_name = "_QFtest_c_devptrEcdevptr"} : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.dscope) -> (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>, !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>)
+! CHECK:           %[[CPTR:.*]] = fir.coordinate_of %[[DECL]]#0, cptr : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_devptr{cptr:!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>}>>) -> !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>
+! CHECK:           %[[ADDR:.*]] = fir.coordinate_of %[[CPTR]], __address : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_ptr{__address:i64}>>) -> !fir.ref<i64>
+! CHECK:           %[[VAL:.*]] = fir.load %[[ADDR]] : !fir.ref<i64>
+! CHECK:           %[[ARG:.*]] = fir.convert %[[VAL]] : (i64) -> !fir.ref<i64>
+! CHECK:           fir.call @get_expected_f(%[[ARG]]) proc_attrs<bind_c> fastmath<contract> : (!fir.ref<i64>) -> ()
+subroutine test_c_devptr(cdevptr)
+  use __fortran_builtins, only : c_devptr => __builtin_c_devptr
+  interface
+     subroutine get_expected_f(src) bind(c)
+       use __fortran_builtins, only : c_devptr => __builtin_c_devptr
+       type(c_devptr), value :: src
+     end subroutine get_expected_f
+  end interface
+  type(c_devptr) :: cdevptr
+  call get_expected_f(cdevptr)
+end



More information about the flang-commits mailing list