From 9c7e203be3e553eca95e0b8fc1fa89cbe43eec69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Clement=20=28=E3=83=90=E3=83=AC=E3=83=B3?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=B3=20=E3=82=AF=E3=83=AC=E3=83=A1?= =?UTF-8?q?=E3=83=B3=29?= Date: Tue, 17 Mar 2026 10:09:15 -0700 Subject: [PATCH] [flang] Fix ignore_tkr(c) passing descriptor instead of base address for non-descriptor dummies (#186894) When ignore_tkr(c) is set and the actual argument is an allocatable or pointer (stored as a descriptor), the lowering code was unconditionally returning the descriptor pointer as-is, regardless of whether the dummy argument expects a descriptor. For bind(c) interfaces with assumed-size dummies (e.g., cuFFT), the dummy expects a raw pointer, not a descriptor. Passing the descriptor caused the C function to receive the wrong address, leading to silent data corruption and invalid descriptor crashes at deallocation. The fix adds a check that the early return for ignore_tkr(c) only applies when the dummy type is itself a descriptor type. When the dummy expects a base address, the normal path is taken, which correctly extracts the base address from the descriptor via fir.box_addr. --- flang/docs/Directives.md | 19 +++++---- flang/lib/Lower/ConvertCall.cpp | 2 +- .../Lower/HLFIR/ignore-tkr-c-base-addr.f90 | 40 +++++++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 flang/test/Lower/HLFIR/ignore-tkr-c-base-addr.f90 diff --git a/flang/docs/Directives.md b/flang/docs/Directives.md index 7080e5c5cf54..07ba5c25a1a7 100644 --- a/flang/docs/Directives.md +++ b/flang/docs/Directives.md @@ -18,15 +18,18 @@ A list of non-standard directives supported by Flang directive allow actual arguments that would otherwise be diagnosed as incompatible in type (T), kind (K), rank (R), CUDA device (D), or managed (M) status. The letter (A) is a shorthand for (TKRDM), and is the default when no - letters appear. The letter (C) checks for contiguity, for example allowing an - element of an assumed-shape array to be passed as a dummy argument. It also - specifies that dummy arguments passed by descriptor should not have their - descriptor copied or reboxed, allowing the original descriptor to be passed + letters appear. The letter (C) checks for contiguity, for example allowing + an element of an assumed-shape array to be passed as a dummy argument. When + the dummy argument is passed by descriptor, (C) specifies that the descriptor + should not be copied or reboxed, allowing the original descriptor to be passed directly even if attributes like ALLOCATABLE or POINTER don't match exactly. - The letter (P) ignores pointer and allocatable matching, so that one can pass an - allocatable array to routine with pointer array argument and vice versa. For - example, if one wanted to call a "set all bytes to zero" utility that could - be applied to arrays of any type or rank: + When the dummy argument is not passed by descriptor (e.g., an assumed-size + array in a BIND(C) interface), the base address is extracted from the actual + argument's descriptor and passed as a raw pointer. + The letter (P) ignores pointer and allocatable matching, so that one can pass + an allocatable array to routine with pointer array argument and vice versa. + For example, if one wanted to call a "set all bytes to zero" utility that + could be applied to arrays of any type or rank: ``` interface subroutine clear(arr,bytes) diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp index d72f74b440c5..ae9d1733d053 100644 --- a/flang/lib/Lower/ConvertCall.cpp +++ b/flang/lib/Lower/ConvertCall.cpp @@ -1349,7 +1349,7 @@ static PreparedDummyArgument preparePresentUserCallActualArgument( hlfir::Entity actual = preparedActual.getActual(loc, builder); if (arg.testTKR(Fortran::common::IgnoreTKR::Contiguous) && - actual.isBoxAddress()) { + actual.isBoxAddress() && fir::isBoxAddressOrValue(dummyType)) { // With ignore_tkr(c), pointer to a descriptor should be passed as is return PreparedDummyArgument{actual, /*cleanups=*/{}}; } diff --git a/flang/test/Lower/HLFIR/ignore-tkr-c-base-addr.f90 b/flang/test/Lower/HLFIR/ignore-tkr-c-base-addr.f90 new file mode 100644 index 000000000000..e04e8d1a2140 --- /dev/null +++ b/flang/test/Lower/HLFIR/ignore-tkr-c-base-addr.f90 @@ -0,0 +1,40 @@ +! RUN: bbc -emit-hlfir -o - %s | FileCheck %s + +! Test that ignore_tkr(c) with a non-descriptor dummy (assumed-size) extracts +! the base address from allocatable/pointer actual arguments instead of passing +! the descriptor. This pattern is used by CUDA library interfaces like cuFFT. + +module m_ignore_tkr_c_base_addr + interface + subroutine pass_assumed_size(a) + !dir$ ignore_tkr(c) a + real :: a(*) + end subroutine + end interface +contains + ! CHECK-LABEL: func.func @_QMm_ignore_tkr_c_base_addrPtest_allocatable( + ! CHECK-SAME: %[[ARR:.*]]: !fir.ref>>> + subroutine test_allocatable(arr) + real, allocatable :: arr(:) + ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARR]] + ! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0 : !fir.ref>>> + ! CHECK: %[[ADDR:.*]] = fir.box_addr %[[LOAD]] : (!fir.box>>) -> !fir.heap> + ! CHECK: %[[CONV:.*]] = fir.convert %[[ADDR]] : (!fir.heap>) -> !fir.ref> + ! CHECK: fir.call @_QPpass_assumed_size(%[[CONV]]) {{.*}} : (!fir.ref>) -> () + call pass_assumed_size(arr) + end subroutine + + ! CHECK-LABEL: func.func @_QMm_ignore_tkr_c_base_addrPtest_pointer( + ! CHECK-SAME: %[[ARR:.*]]: !fir.ref>>> + subroutine test_pointer(arr) + real, pointer :: arr(:) + ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARR]] + ! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0 : !fir.ref>>> + ! CHECK: %[[ADDR:.*]] = fir.box_addr %[[LOAD]] : (!fir.box>>) -> !fir.ptr> + ! CHECK: %[[CONV:.*]] = fir.convert %[[ADDR]] : (!fir.ptr>) -> !fir.ref> + ! CHECK: fir.call @_QPpass_assumed_size(%[[CONV]]) {{.*}} : (!fir.ref>) -> () + call pass_assumed_size(arr) + end subroutine + + ! CHECK: func.func private @_QPpass_assumed_size(!fir.ref>) +end module