[flang] Fix ignore_tkr(c) passing descriptor instead of base address for non-descriptor dummies#186894
[flang] Fix ignore_tkr(c) passing descriptor instead of base address for non-descriptor dummies#186894clementval wants to merge 3 commits intollvm:mainfrom
Conversation
…for non-descriptor dummies
|
@llvm/pr-subscribers-flang-fir-hlfir Author: Valentin Clement (バレンタイン クレメン) (clementval) ChangesWhen 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. Full diff: https://github.com/llvm/llvm-project/pull/186894.diff 2 Files Affected:
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index d72f74b440c53..ae9d1733d053d 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 0000000000000..fc9905a49c83b
--- /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) bind(c, name="pass_assumed_size")
+ !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<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+ subroutine test_allocatable(arr)
+ real, allocatable :: arr(:)
+ ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARR]]
+ ! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xf32>>>>
+ ! CHECK: %[[ADDR:.*]] = fir.box_addr %[[LOAD]] : (!fir.box<!fir.heap<!fir.array<?xf32>>>) -> !fir.heap<!fir.array<?xf32>>
+ ! CHECK: %[[CONV:.*]] = fir.convert %[[ADDR]] : (!fir.heap<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
+ ! CHECK: fir.call @pass_assumed_size(%[[CONV]]) {{.*}} : (!fir.ref<!fir.array<?xf32>>) -> ()
+ call pass_assumed_size(arr)
+ end subroutine
+
+ ! CHECK-LABEL: func.func @_QMm_ignore_tkr_c_base_addrPtest_pointer(
+ ! CHECK-SAME: %[[ARR:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
+ subroutine test_pointer(arr)
+ real, pointer :: arr(:)
+ ! CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARR]]
+ ! CHECK: %[[LOAD:.*]] = fir.load %[[DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xf32>>>>
+ ! CHECK: %[[ADDR:.*]] = fir.box_addr %[[LOAD]] : (!fir.box<!fir.ptr<!fir.array<?xf32>>>) -> !fir.ptr<!fir.array<?xf32>>
+ ! CHECK: %[[CONV:.*]] = fir.convert %[[ADDR]] : (!fir.ptr<!fir.array<?xf32>>) -> !fir.ref<!fir.array<?xf32>>
+ ! CHECK: fir.call @pass_assumed_size(%[[CONV]]) {{.*}} : (!fir.ref<!fir.array<?xf32>>) -> ()
+ call pass_assumed_size(arr)
+ end subroutine
+
+ ! CHECK: func.func private @pass_assumed_size(!fir.ref<!fir.array<?xf32>>)
+end module
|
vzakhari
left a comment
There was a problem hiding this comment.
LGTM, but please wait for Eugene's review.
|
You would also need to modify https://github.com/llvm/llvm-project/blob/main/flang/docs/Directives.md to describe the behavior change. |
eugeneepshteyn
left a comment
There was a problem hiding this comment.
Requesting a test tweak to remove bind(c) and documentation update, otherwise LGTM.
Done |
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.