[flang-rt][cuda] Skip scope-exit cleanup when the context has a sticky error (#213184)

A sticky CUDA error (e.g. an illegal memory access in a kernel) leaves
the primary context active but unusable, so CUFDeviceIsActive() reports
it as fine and the compiler-generated scope-exit frees abort a program
that ran to completion: 'cudaFree(p)' failed with
'cudaErrorIllegalAddress'.

Detect this by freeing a null pointer, a no-op that still reports the
sticky error. It runs only once the primary context is known active, so
it cannot lazily create one.
This commit is contained in:
Zhen Wang 2026-07-31 14:38:24 -07:00 committed by GitHub
parent 36187ce08a
commit 5ca2b9313c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 13 additions and 0 deletions

View file

@ -59,6 +59,13 @@ static bool deviceContextTornDown() {
int active{0};
if (getState(device, &flags, &active) == CUDA_SUCCESS) {
tornDown = active == 0;
// A sticky error (e.g. an illegal kernel memory access) leaves the
// primary context active but unusable: later calls all fail, so
// scope-exit frees would abort an otherwise successful program. A
// null free is a no-op that surfaces this without creating a context.
if (!tornDown && cudaFree(nullptr) != cudaSuccess) {
tornDown = true;
}
}
}
} else {

View file

@ -31,13 +31,19 @@ end
program main
integer, allocatable, device :: a(:)
integer, device, pointer :: p(:)
return
end
! The descriptor of a device pointer lives in managed memory and is freed at
! scope exit, under the same device-active guard as the other cleanup.
! CHECK-LABEL: func.func @_QQmain() attributes {fir.bindc_name = "MAIN"}
! CHECK: cuf.alloc !fir.box<!fir.heap<!fir.array<?xi32>>> {bindc_name = "a", data_attr = #cuf.cuda<device>, uniq_name = "_QFEa"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
! CHECK: %[[PTR:.*]]:2 = hlfir.declare %{{.*}} {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<pointer>, uniq_name = "_QFEp"}
! CHECK: %[[ACTIVE:.*]] = fir.call @_FortranACUFDeviceIsActive() {{.*}} : () -> i1
! CHECK-NEXT: fir.if %[[ACTIVE]] {
! CHECK-NEXT: cuf.free %[[PTR]]#0 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?xi32>>>>{{.*}}
! CHECK: cuf.deallocate
! CHECK: cuf.free{{.*}}
! CHECK-NEXT: }