Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions cuda_core/cuda/core/_cpp/resource_handles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ decltype(&cuLibraryLoadData) p_cuLibraryLoadData = nullptr;
decltype(&cuLibraryUnload) p_cuLibraryUnload = nullptr;
decltype(&cuLibraryGetKernel) p_cuLibraryGetKernel = nullptr;

// NVRTC function pointers
decltype(&nvrtcDestroyProgram) p_nvrtcDestroyProgram = nullptr;

// NVVM function pointers (may be null if NVVM is not available)
NvvmDestroyProgramFn p_nvvmDestroyProgram = nullptr;

// ============================================================================
// GIL management helpers
// ============================================================================
Expand Down Expand Up @@ -764,4 +770,64 @@ KernelHandle create_kernel_handle_ref(CUkernel kernel, const LibraryHandle& h_li
return KernelHandle(box, &box->resource);
}

// ============================================================================
// NVRTC Program Handles
// ============================================================================

namespace {
struct NvrtcProgramBox {
nvrtcProgram resource;
};
} // namespace

NvrtcProgramHandle create_nvrtc_program_handle(nvrtcProgram prog) {
auto box = std::shared_ptr<NvrtcProgramBox>(
new NvrtcProgramBox{prog},
[](NvrtcProgramBox* b) {
// Note: nvrtcDestroyProgram takes nvrtcProgram* and nulls it,
// but we're deleting the box anyway so nulling is harmless.
// Errors are ignored (standard destructor practice).
p_nvrtcDestroyProgram(&b->resource);
delete b;
}
);
return NvrtcProgramHandle(box, &box->resource);
}

NvrtcProgramHandle create_nvrtc_program_handle_ref(nvrtcProgram prog) {
auto box = std::make_shared<NvrtcProgramBox>(NvrtcProgramBox{prog});
return NvrtcProgramHandle(box, &box->resource);
}

// ============================================================================
// NVVM Program Handles
// ============================================================================

namespace {
struct NvvmProgramBox {
nvvmProgram resource;
};
} // namespace

NvvmProgramHandle create_nvvm_program_handle(nvvmProgram prog) {
auto box = std::shared_ptr<NvvmProgramBox>(
new NvvmProgramBox{prog},
[](NvvmProgramBox* b) {
// Note: nvvmDestroyProgram takes nvvmProgram* and nulls it,
// but we're deleting the box anyway so nulling is harmless.
// If NVVM is not available, the function pointer is null.
if (p_nvvmDestroyProgram) {
p_nvvmDestroyProgram(&b->resource);
}
delete b;
}
);
return NvvmProgramHandle(box, &box->resource);
}

NvvmProgramHandle create_nvvm_program_handle_ref(nvvmProgram prog) {
auto box = std::make_shared<NvvmProgramBox>(NvvmProgramBox{prog});
return NvvmProgramHandle(box, &box->resource);
}

} // namespace cuda_core
101 changes: 91 additions & 10 deletions cuda_core/cuda/core/_cpp/resource_handles.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@

#include <Python.h>
#include <cuda.h>
#include <nvrtc.h>
#include <cstdint>
#include <memory>

// Forward declaration for NVVM - avoids nvvm.h dependency
// Use void* to match cuda.bindings.cynvvm's typedef
using nvvmProgram = void*;

namespace cuda_core {

// ============================================================================
Expand Down Expand Up @@ -67,6 +72,28 @@ extern decltype(&cuLibraryLoadData) p_cuLibraryLoadData;
extern decltype(&cuLibraryUnload) p_cuLibraryUnload;
extern decltype(&cuLibraryGetKernel) p_cuLibraryGetKernel;

// ============================================================================
// NVRTC function pointers
//
// These are populated by _resource_handles.pyx at module import time using
// function pointers extracted from cuda.bindings.cynvrtc.__pyx_capi__.
// ============================================================================

extern decltype(&nvrtcDestroyProgram) p_nvrtcDestroyProgram;

// ============================================================================
// NVVM function pointers
//
// These are populated by _resource_handles.pyx at module import time using
// function pointers extracted from cuda.bindings.cynvvm.__pyx_capi__.
// Note: May be null if NVVM is not available at runtime.
// ============================================================================

// Function pointer type for nvvmDestroyProgram (avoids nvvm.h dependency)
// Signature: nvvmResult nvvmDestroyProgram(nvvmProgram *prog)
using NvvmDestroyProgramFn = int (*)(nvvmProgram*);
extern NvvmDestroyProgramFn p_nvvmDestroyProgram;

// ============================================================================
// Handle type aliases - expose only the raw CUDA resource
// ============================================================================
Expand All @@ -77,6 +104,8 @@ using EventHandle = std::shared_ptr<const CUevent>;
using MemoryPoolHandle = std::shared_ptr<const CUmemoryPool>;
using LibraryHandle = std::shared_ptr<const CUlibrary>;
using KernelHandle = std::shared_ptr<const CUkernel>;
using NvrtcProgramHandle = std::shared_ptr<const nvrtcProgram>;
using NvvmProgramHandle = std::shared_ptr<const nvvmProgram>;

// ============================================================================
// Context handle functions
Expand Down Expand Up @@ -260,6 +289,33 @@ KernelHandle create_kernel_handle(const LibraryHandle& h_library, const char* na
// Use for borrowed kernels. The library handle keeps the library alive.
KernelHandle create_kernel_handle_ref(CUkernel kernel, const LibraryHandle& h_library);

// ============================================================================
// NVRTC Program handle functions
// ============================================================================

// Create an owning NVRTC program handle.
// When the last reference is released, nvrtcDestroyProgram is called.
// Use this to wrap a program created via nvrtcCreateProgram.
NvrtcProgramHandle create_nvrtc_program_handle(nvrtcProgram prog);

// Create a non-owning NVRTC program handle (references existing program).
// The program will NOT be destroyed when the handle is released.
NvrtcProgramHandle create_nvrtc_program_handle_ref(nvrtcProgram prog);

// ============================================================================
// NVVM Program handle functions
// ============================================================================

// Create an owning NVVM program handle.
// When the last reference is released, nvvmDestroyProgram is called.
// Use this to wrap a program created via nvvmCreateProgram.
// Note: If NVVM is not available (p_nvvmDestroyProgram is null), the deleter is a no-op.
NvvmProgramHandle create_nvvm_program_handle(nvvmProgram prog);

// Create a non-owning NVVM program handle (references existing program).
// The program will NOT be destroyed when the handle is released.
NvvmProgramHandle create_nvvm_program_handle_ref(nvvmProgram prog);

// ============================================================================
// Overloaded helper functions to extract raw resources from handles
// ============================================================================
Expand Down Expand Up @@ -293,6 +349,14 @@ inline CUkernel as_cu(const KernelHandle& h) noexcept {
return h ? *h : nullptr;
}

inline nvrtcProgram as_cu(const NvrtcProgramHandle& h) noexcept {
return h ? *h : nullptr;
}

inline nvvmProgram as_cu(const NvvmProgramHandle& h) noexcept {
return h ? *h : nullptr;
}

// as_intptr() - extract handle as intptr_t for Python interop
// Using signed intptr_t per C standard convention and issue #1342
inline std::intptr_t as_intptr(const ContextHandle& h) noexcept {
Expand Down Expand Up @@ -323,11 +387,19 @@ inline std::intptr_t as_intptr(const KernelHandle& h) noexcept {
return reinterpret_cast<std::intptr_t>(as_cu(h));
}

// as_py() - convert handle to Python driver wrapper object (returns new reference)
inline std::intptr_t as_intptr(const NvrtcProgramHandle& h) noexcept {
return reinterpret_cast<std::intptr_t>(as_cu(h));
}

inline std::intptr_t as_intptr(const NvvmProgramHandle& h) noexcept {
return reinterpret_cast<std::intptr_t>(as_cu(h));
}

// as_py() - convert handle to Python wrapper object (returns new reference)
namespace detail {
// n.b. class lookup is not cached to avoid deadlock hazard, see DESIGN.md
inline PyObject* make_py(const char* class_name, std::intptr_t value) noexcept {
PyObject* mod = PyImport_ImportModule("cuda.bindings.driver");
inline PyObject* make_py(const char* module_name, const char* class_name, std::intptr_t value) noexcept {
PyObject* mod = PyImport_ImportModule(module_name);
if (!mod) return nullptr;
PyObject* cls = PyObject_GetAttrString(mod, class_name);
Py_DECREF(mod);
Expand All @@ -339,31 +411,40 @@ inline PyObject* make_py(const char* class_name, std::intptr_t value) noexcept {
} // namespace detail

inline PyObject* as_py(const ContextHandle& h) noexcept {
return detail::make_py("CUcontext", as_intptr(h));
return detail::make_py("cuda.bindings.driver", "CUcontext", as_intptr(h));
}

inline PyObject* as_py(const StreamHandle& h) noexcept {
return detail::make_py("CUstream", as_intptr(h));
return detail::make_py("cuda.bindings.driver", "CUstream", as_intptr(h));
}

inline PyObject* as_py(const EventHandle& h) noexcept {
return detail::make_py("CUevent", as_intptr(h));
return detail::make_py("cuda.bindings.driver", "CUevent", as_intptr(h));
}

inline PyObject* as_py(const MemoryPoolHandle& h) noexcept {
return detail::make_py("CUmemoryPool", as_intptr(h));
return detail::make_py("cuda.bindings.driver", "CUmemoryPool", as_intptr(h));
}

inline PyObject* as_py(const DevicePtrHandle& h) noexcept {
return detail::make_py("CUdeviceptr", as_intptr(h));
return detail::make_py("cuda.bindings.driver", "CUdeviceptr", as_intptr(h));
}

inline PyObject* as_py(const LibraryHandle& h) noexcept {
return detail::make_py("CUlibrary", as_intptr(h));
return detail::make_py("cuda.bindings.driver", "CUlibrary", as_intptr(h));
}

inline PyObject* as_py(const KernelHandle& h) noexcept {
return detail::make_py("CUkernel", as_intptr(h));
return detail::make_py("cuda.bindings.driver", "CUkernel", as_intptr(h));
}

inline PyObject* as_py(const NvrtcProgramHandle& h) noexcept {
return detail::make_py("cuda.bindings.nvrtc", "nvrtcProgram", as_intptr(h));
}

inline PyObject* as_py(const NvvmProgramHandle& h) noexcept {
// NVVM bindings use raw integers, not wrapper classes
return PyLong_FromSsize_t(as_intptr(h));
}

} // namespace cuda_core
15 changes: 15 additions & 0 deletions cuda_core/cuda/core/_program.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

from ._resource_handles cimport NvrtcProgramHandle, NvvmProgramHandle


cdef class Program:
cdef:
NvrtcProgramHandle _h_nvrtc
NvvmProgramHandle _h_nvvm
str _backend
object _linker # Linker
object _options # ProgramOptions
object __weakref__
Loading
Loading