[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