diff --git a/flang-rt/lib/cuda/allocator.cpp b/flang-rt/lib/cuda/allocator.cpp index 4161caa7cb02..d8287d6f696e 100644 --- a/flang-rt/lib/cuda/allocator.cpp +++ b/flang-rt/lib/cuda/allocator.cpp @@ -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 { diff --git a/flang/test/Lower/CUDA/cuda-return01.cuf b/flang/test/Lower/CUDA/cuda-return01.cuf index 6e5d02c3b396..9ea5ce153808 100644 --- a/flang/test/Lower/CUDA/cuda-return01.cuf +++ b/flang/test/Lower/CUDA/cuda-return01.cuf @@ -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>> {bindc_name = "a", data_attr = #cuf.cuda, uniq_name = "_QFEa"} -> !fir.ref>>> +! CHECK: %[[PTR:.*]]:2 = hlfir.declare %{{.*}} {data_attr = #cuf.cuda, fortran_attrs = #fir.var_attrs, uniq_name = "_QFEp"} ! CHECK: %[[ACTIVE:.*]] = fir.call @_FortranACUFDeviceIsActive() {{.*}} : () -> i1 ! CHECK-NEXT: fir.if %[[ACTIVE]] { +! CHECK-NEXT: cuf.free %[[PTR]]#0 : !fir.ref>>>{{.*}} ! CHECK: cuf.deallocate ! CHECK: cuf.free{{.*}} ! CHECK-NEXT: }