From 5ca2b9313c4f33385aa60c554cb36f76bef67e55 Mon Sep 17 00:00:00 2001 From: Zhen Wang Date: Fri, 31 Jul 2026 14:38:24 -0700 Subject: [PATCH] [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. --- flang-rt/lib/cuda/allocator.cpp | 7 +++++++ flang/test/Lower/CUDA/cuda-return01.cuf | 6 ++++++ 2 files changed, 13 insertions(+) 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: }