[flang-commits] [flang] [flang][cuda] Assign a scalar character function result on the host (PR #217076)

via flang-commits flang-commits at lists.llvm.org
Tue Aug 18 09:46:19 PDT 2026


https://github.com/khaki3 created https://github.com/llvm/llvm-project/pull/217076

A managed function result makes an assignment a cuf.data_transfer:

    function f() result(r)
      character(len=:), allocatable :: r   ! managed under -gpu=mem:managed
    end function
    character(len=256) :: c
    c = f()

The rank-0 result r is lowered to a !fir.boxchar<1>, an {address, length}
value rather than a reference, so the non-descriptor transfer path has no
address to take:

    'fir.convert' op invalid type conversion '!fir.boxchar<1>' / '!fir.llvm_ptr<i8>'

A fixed-length result does have an address, but the copy is sized by the
character kind instead of the length: one character copied, c left unpadded.

Managed and unified results are host accessible, so assign a scalar character
result on the host and get the padding rules. Array results pass by
descriptor and are unaffected.

>From 027f6e9a567dc09be1657c91ee9a518ed2547a54 Mon Sep 17 00:00:00 2001
From: Kazuaki Matsumura <kmatsumura at nvidia.com>
Date: Tue, 18 Aug 2026 07:24:03 -0700
Subject: [PATCH] [flang][cuda] Assign a scalar character function result on
 the host

Example:
```fortran
function c2f_string(cstr) result(r)
  character(len=:), allocatable :: r     ! managed under -gpu=mem:managed
end function
...
character(len=256) :: lookup_filename
lookup_filename = c2f_string(c_filename)
```

The managed function result turns this assignment into a cuf.data_transfer.
A scalar character is lowered to a !fir.boxchar<1>, an {address, length} value
rather than a descriptor in memory, so the non-descriptor transfer path has no
address to take and cuf-convert fails with "'fir.convert' op invalid type
conversion '!fir.boxchar<1>' / '!fir.llvm_ptr<i8>'". A fixed-length result does
provide an address, but the byte count is the character element size, so only
one character is copied and the destination is never padded.

Fix: keep a scalar character result a host assignment. Character array results
are unaffected: they are passed as a descriptor, so the runtime handles the
length adjustment.
---
 flang/lib/Lower/Bridge.cpp                    | 10 ++++++++-
 flang/test/Lower/CUDA/cuda-managed-assign.cuf | 21 +++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index bff6b51e50e18..d6d7b1838df58 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -5284,11 +5284,19 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   // attribute. Such a result may be produced by an asynchronous kernel, so
   // consuming it in an assignment must be a synchronizing data transfer rather
   // than a plain host assignment. A whole-allocatable left-hand side is
-  // excluded: it has reallocation semantics and is performed on the host.
+  // excluded: it has reallocation semantics and is performed on the host, and
+  // so is a scalar character result: it carries its length outside of a
+  // descriptor, so a transfer would copy raw bytes instead of padding or
+  // truncating the destination.
   bool
   isCUDAFunctionResultTransfer(const Fortran::evaluate::Assignment &assign) {
     if (Fortran::evaluate::IsAllocatableDesignator(assign.lhs))
       return false;
+    if (assign.rhs.Rank() == 0)
+      if (std::optional<Fortran::evaluate::DynamicType> type{
+              assign.rhs.GetType()})
+        if (type->category() == Fortran::common::TypeCategory::Character)
+          return false;
     const Fortran::evaluate::ProcedureRef *procRef =
         Fortran::evaluate::UnwrapProcedureRef(assign.rhs);
     if (!procRef)
diff --git a/flang/test/Lower/CUDA/cuda-managed-assign.cuf b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
index 694555a173cf6..09bfcd5296c89 100644
--- a/flang/test/Lower/CUDA/cuda-managed-assign.cuf
+++ b/flang/test/Lower/CUDA/cuda-managed-assign.cuf
@@ -14,6 +14,15 @@ contains
     allocate(r(n))
     r = 7
   end function
+  function frchar(n) result(r)
+    integer, value :: n
+    character(len=:), allocatable, managed :: r
+    allocate(character(len=n) :: r)
+  end function
+  function frcharfix() result(r)
+    character(len=4), allocatable, managed :: r
+    allocate(r)
+  end function
 end module
 
 ! Assignments that are data transfers.
@@ -310,3 +319,15 @@ end subroutine
 
 ! CHECK-LABEL: func.func @_QPmanaged_component_attr_section_assign()
 ! CHECK-NOT: cuf.data_transfer
+
+! A scalar character result carries its length outside of a descriptor, so a
+! transfer would copy raw bytes instead of padding or truncating the destination.
+subroutine managed_char_func_result()
+  use mfr
+  character(len=16) :: h
+  h = frchar(4)
+  h = frcharfix()
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmanaged_char_func_result()
+! CHECK-NOT: cuf.data_transfer



More information about the flang-commits mailing list