diff --git a/.github/actions/fetch_ctk/action.yml b/.github/actions/fetch_ctk/action.yml index 5850b4c78..669943296 100644 --- a/.github/actions/fetch_ctk/action.yml +++ b/.github/actions/fetch_ctk/action.yml @@ -123,4 +123,4 @@ runs: echo "CUDA_PATH=${CUDA_PATH}" >> $GITHUB_ENV echo "CUDA_HOME=${CUDA_PATH}" >> $GITHUB_ENV echo "${CUDA_PATH}/bin" >> $GITHUB_PATH - echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}:${CUDA_PATH}/lib" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}:${CUDA_PATH}/lib:${CUDA_PATH}/nvvm/lib64" >> $GITHUB_ENV diff --git a/cuda_bindings/cuda/bindings/_internal/nvvm.pxd b/cuda_bindings/cuda/bindings/_internal/nvvm.pxd new file mode 100644 index 000000000..0feebf251 --- /dev/null +++ b/cuda_bindings/cuda/bindings/_internal/nvvm.pxd @@ -0,0 +1,25 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. + +from ..cynvvm cimport * + + +############################################################################### +# Wrapper functions +############################################################################### + +cdef nvvmResult _nvvmVersion(int* major, int* minor) except* nogil +cdef nvvmResult _nvvmIRVersion(int* majorIR, int* minorIR, int* majorDbg, int* minorDbg) except* nogil +cdef nvvmResult _nvvmCreateProgram(nvvmProgram* prog) except* nogil +cdef nvvmResult _nvvmDestroyProgram(nvvmProgram* prog) except* nogil +cdef nvvmResult _nvvmAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil +cdef nvvmResult _nvvmLazyAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil +cdef nvvmResult _nvvmCompileProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil +cdef nvvmResult _nvvmVerifyProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil +cdef nvvmResult _nvvmGetCompiledResultSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil +cdef nvvmResult _nvvmGetCompiledResult(nvvmProgram prog, char* buffer) except* nogil +cdef nvvmResult _nvvmGetProgramLogSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil +cdef nvvmResult _nvvmGetProgramLog(nvvmProgram prog, char* buffer) except* nogil diff --git a/cuda_bindings/cuda/bindings/_internal/nvvm_linux.pyx b/cuda_bindings/cuda/bindings/_internal/nvvm_linux.pyx new file mode 100644 index 000000000..e21218772 --- /dev/null +++ b/cuda_bindings/cuda/bindings/_internal/nvvm_linux.pyx @@ -0,0 +1,360 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. + +from libc.stdint cimport intptr_t + +from .utils cimport get_nvvm_dso_version_suffix + +from .utils import FunctionNotFoundError, NotSupportedError + +############################################################################### +# Extern +############################################################################### + +cdef extern from "" nogil: + void* dlopen(const char*, int) + char* dlerror() + void* dlsym(void*, const char*) + int dlclose(void*) + + enum: + RTLD_LAZY + RTLD_NOW + RTLD_GLOBAL + RTLD_LOCAL + + const void* RTLD_DEFAULT 'RTLD_DEFAULT' + + +############################################################################### +# Wrapper init +############################################################################### + +cdef bint __py_nvvm_init = False +cdef void* __cuDriverGetVersion = NULL + +cdef void* __nvvmVersion = NULL +cdef void* __nvvmIRVersion = NULL +cdef void* __nvvmCreateProgram = NULL +cdef void* __nvvmDestroyProgram = NULL +cdef void* __nvvmAddModuleToProgram = NULL +cdef void* __nvvmLazyAddModuleToProgram = NULL +cdef void* __nvvmCompileProgram = NULL +cdef void* __nvvmVerifyProgram = NULL +cdef void* __nvvmGetCompiledResultSize = NULL +cdef void* __nvvmGetCompiledResult = NULL +cdef void* __nvvmGetProgramLogSize = NULL +cdef void* __nvvmGetProgramLog = NULL + + +cdef void* load_library(const int driver_ver) except* with gil: + cdef void* handle + for suffix in get_nvvm_dso_version_suffix(driver_ver): + so_name = "libnvvm.so" + (f".{suffix}" if suffix else suffix) + handle = dlopen(so_name.encode(), RTLD_NOW | RTLD_GLOBAL) + if handle != NULL: + break + else: + err_msg = dlerror() + raise RuntimeError(f'Failed to dlopen libnvvm ({err_msg.decode()})') + return handle + + +cdef int _check_or_init_nvvm() except -1 nogil: + global __py_nvvm_init + if __py_nvvm_init: + return 0 + + # Load driver to check version + cdef void* handle = NULL + handle = dlopen('libcuda.so.1', RTLD_NOW | RTLD_GLOBAL) + if handle == NULL: + with gil: + err_msg = dlerror() + raise NotSupportedError(f'CUDA driver is not found ({err_msg.decode()})') + global __cuDriverGetVersion + if __cuDriverGetVersion == NULL: + __cuDriverGetVersion = dlsym(handle, "cuDriverGetVersion") + if __cuDriverGetVersion == NULL: + with gil: + raise RuntimeError('something went wrong') + cdef int err, driver_ver + err = (__cuDriverGetVersion)(&driver_ver) + if err != 0: + with gil: + raise RuntimeError('something went wrong') + #dlclose(handle) + handle = NULL + + # Load function + global __nvvmVersion + __nvvmVersion = dlsym(RTLD_DEFAULT, 'nvvmVersion') + if __nvvmVersion == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmVersion = dlsym(handle, 'nvvmVersion') + + global __nvvmIRVersion + __nvvmIRVersion = dlsym(RTLD_DEFAULT, 'nvvmIRVersion') + if __nvvmIRVersion == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmIRVersion = dlsym(handle, 'nvvmIRVersion') + + global __nvvmCreateProgram + __nvvmCreateProgram = dlsym(RTLD_DEFAULT, 'nvvmCreateProgram') + if __nvvmCreateProgram == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmCreateProgram = dlsym(handle, 'nvvmCreateProgram') + + global __nvvmDestroyProgram + __nvvmDestroyProgram = dlsym(RTLD_DEFAULT, 'nvvmDestroyProgram') + if __nvvmDestroyProgram == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmDestroyProgram = dlsym(handle, 'nvvmDestroyProgram') + + global __nvvmAddModuleToProgram + __nvvmAddModuleToProgram = dlsym(RTLD_DEFAULT, 'nvvmAddModuleToProgram') + if __nvvmAddModuleToProgram == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmAddModuleToProgram = dlsym(handle, 'nvvmAddModuleToProgram') + + global __nvvmLazyAddModuleToProgram + __nvvmLazyAddModuleToProgram = dlsym(RTLD_DEFAULT, 'nvvmLazyAddModuleToProgram') + if __nvvmLazyAddModuleToProgram == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmLazyAddModuleToProgram = dlsym(handle, 'nvvmLazyAddModuleToProgram') + + global __nvvmCompileProgram + __nvvmCompileProgram = dlsym(RTLD_DEFAULT, 'nvvmCompileProgram') + if __nvvmCompileProgram == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmCompileProgram = dlsym(handle, 'nvvmCompileProgram') + + global __nvvmVerifyProgram + __nvvmVerifyProgram = dlsym(RTLD_DEFAULT, 'nvvmVerifyProgram') + if __nvvmVerifyProgram == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmVerifyProgram = dlsym(handle, 'nvvmVerifyProgram') + + global __nvvmGetCompiledResultSize + __nvvmGetCompiledResultSize = dlsym(RTLD_DEFAULT, 'nvvmGetCompiledResultSize') + if __nvvmGetCompiledResultSize == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmGetCompiledResultSize = dlsym(handle, 'nvvmGetCompiledResultSize') + + global __nvvmGetCompiledResult + __nvvmGetCompiledResult = dlsym(RTLD_DEFAULT, 'nvvmGetCompiledResult') + if __nvvmGetCompiledResult == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmGetCompiledResult = dlsym(handle, 'nvvmGetCompiledResult') + + global __nvvmGetProgramLogSize + __nvvmGetProgramLogSize = dlsym(RTLD_DEFAULT, 'nvvmGetProgramLogSize') + if __nvvmGetProgramLogSize == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmGetProgramLogSize = dlsym(handle, 'nvvmGetProgramLogSize') + + global __nvvmGetProgramLog + __nvvmGetProgramLog = dlsym(RTLD_DEFAULT, 'nvvmGetProgramLog') + if __nvvmGetProgramLog == NULL: + if handle == NULL: + handle = load_library(driver_ver) + __nvvmGetProgramLog = dlsym(handle, 'nvvmGetProgramLog') + + __py_nvvm_init = True + return 0 + + +cdef dict func_ptrs = None + + +cpdef dict _inspect_function_pointers(): + global func_ptrs + if func_ptrs is not None: + return func_ptrs + + _check_or_init_nvvm() + cdef dict data = {} + + global __nvvmVersion + data["__nvvmVersion"] = __nvvmVersion + + global __nvvmIRVersion + data["__nvvmIRVersion"] = __nvvmIRVersion + + global __nvvmCreateProgram + data["__nvvmCreateProgram"] = __nvvmCreateProgram + + global __nvvmDestroyProgram + data["__nvvmDestroyProgram"] = __nvvmDestroyProgram + + global __nvvmAddModuleToProgram + data["__nvvmAddModuleToProgram"] = __nvvmAddModuleToProgram + + global __nvvmLazyAddModuleToProgram + data["__nvvmLazyAddModuleToProgram"] = __nvvmLazyAddModuleToProgram + + global __nvvmCompileProgram + data["__nvvmCompileProgram"] = __nvvmCompileProgram + + global __nvvmVerifyProgram + data["__nvvmVerifyProgram"] = __nvvmVerifyProgram + + global __nvvmGetCompiledResultSize + data["__nvvmGetCompiledResultSize"] = __nvvmGetCompiledResultSize + + global __nvvmGetCompiledResult + data["__nvvmGetCompiledResult"] = __nvvmGetCompiledResult + + global __nvvmGetProgramLogSize + data["__nvvmGetProgramLogSize"] = __nvvmGetProgramLogSize + + global __nvvmGetProgramLog + data["__nvvmGetProgramLog"] = __nvvmGetProgramLog + + func_ptrs = data + return data + + +cpdef _inspect_function_pointer(str name): + global func_ptrs + if func_ptrs is None: + func_ptrs = _inspect_function_pointers() + return func_ptrs[name] + + +############################################################################### +# Wrapper functions +############################################################################### + +cdef nvvmResult _nvvmVersion(int* major, int* minor) except* nogil: + global __nvvmVersion + _check_or_init_nvvm() + if __nvvmVersion == NULL: + with gil: + raise FunctionNotFoundError("function nvvmVersion is not found") + return (__nvvmVersion)( + major, minor) + + +cdef nvvmResult _nvvmIRVersion(int* majorIR, int* minorIR, int* majorDbg, int* minorDbg) except* nogil: + global __nvvmIRVersion + _check_or_init_nvvm() + if __nvvmIRVersion == NULL: + with gil: + raise FunctionNotFoundError("function nvvmIRVersion is not found") + return (__nvvmIRVersion)( + majorIR, minorIR, majorDbg, minorDbg) + + +cdef nvvmResult _nvvmCreateProgram(nvvmProgram* prog) except* nogil: + global __nvvmCreateProgram + _check_or_init_nvvm() + if __nvvmCreateProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmCreateProgram is not found") + return (__nvvmCreateProgram)( + prog) + + +cdef nvvmResult _nvvmDestroyProgram(nvvmProgram* prog) except* nogil: + global __nvvmDestroyProgram + _check_or_init_nvvm() + if __nvvmDestroyProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmDestroyProgram is not found") + return (__nvvmDestroyProgram)( + prog) + + +cdef nvvmResult _nvvmAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil: + global __nvvmAddModuleToProgram + _check_or_init_nvvm() + if __nvvmAddModuleToProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmAddModuleToProgram is not found") + return (__nvvmAddModuleToProgram)( + prog, buffer, size, name) + + +cdef nvvmResult _nvvmLazyAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil: + global __nvvmLazyAddModuleToProgram + _check_or_init_nvvm() + if __nvvmLazyAddModuleToProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmLazyAddModuleToProgram is not found") + return (__nvvmLazyAddModuleToProgram)( + prog, buffer, size, name) + + +cdef nvvmResult _nvvmCompileProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil: + global __nvvmCompileProgram + _check_or_init_nvvm() + if __nvvmCompileProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmCompileProgram is not found") + return (__nvvmCompileProgram)( + prog, numOptions, options) + + +cdef nvvmResult _nvvmVerifyProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil: + global __nvvmVerifyProgram + _check_or_init_nvvm() + if __nvvmVerifyProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmVerifyProgram is not found") + return (__nvvmVerifyProgram)( + prog, numOptions, options) + + +cdef nvvmResult _nvvmGetCompiledResultSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil: + global __nvvmGetCompiledResultSize + _check_or_init_nvvm() + if __nvvmGetCompiledResultSize == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetCompiledResultSize is not found") + return (__nvvmGetCompiledResultSize)( + prog, bufferSizeRet) + + +cdef nvvmResult _nvvmGetCompiledResult(nvvmProgram prog, char* buffer) except* nogil: + global __nvvmGetCompiledResult + _check_or_init_nvvm() + if __nvvmGetCompiledResult == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetCompiledResult is not found") + return (__nvvmGetCompiledResult)( + prog, buffer) + + +cdef nvvmResult _nvvmGetProgramLogSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil: + global __nvvmGetProgramLogSize + _check_or_init_nvvm() + if __nvvmGetProgramLogSize == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetProgramLogSize is not found") + return (__nvvmGetProgramLogSize)( + prog, bufferSizeRet) + + +cdef nvvmResult _nvvmGetProgramLog(nvvmProgram prog, char* buffer) except* nogil: + global __nvvmGetProgramLog + _check_or_init_nvvm() + if __nvvmGetProgramLog == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetProgramLog is not found") + return (__nvvmGetProgramLog)( + prog, buffer) diff --git a/cuda_bindings/cuda/bindings/_internal/nvvm_windows.pyx b/cuda_bindings/cuda/bindings/_internal/nvvm_windows.pyx new file mode 100644 index 000000000..b8e679547 --- /dev/null +++ b/cuda_bindings/cuda/bindings/_internal/nvvm_windows.pyx @@ -0,0 +1,373 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. + +from libc.stdint cimport intptr_t + +from .utils cimport get_nvvm_dso_version_suffix + +from .utils import FunctionNotFoundError, NotSupportedError + +import os +import site + +import win32api + + +############################################################################### +# Wrapper init +############################################################################### + +LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 +LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000 +LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100 +cdef bint __py_nvvm_init = False +cdef void* __cuDriverGetVersion = NULL + +cdef void* __nvvmVersion = NULL +cdef void* __nvvmIRVersion = NULL +cdef void* __nvvmCreateProgram = NULL +cdef void* __nvvmDestroyProgram = NULL +cdef void* __nvvmAddModuleToProgram = NULL +cdef void* __nvvmLazyAddModuleToProgram = NULL +cdef void* __nvvmCompileProgram = NULL +cdef void* __nvvmVerifyProgram = NULL +cdef void* __nvvmGetCompiledResultSize = NULL +cdef void* __nvvmGetCompiledResult = NULL +cdef void* __nvvmGetProgramLogSize = NULL +cdef void* __nvvmGetProgramLog = NULL + + +cdef inline list get_site_packages(): + return [site.getusersitepackages()] + site.getsitepackages() + + +cdef load_library(const int driver_ver): + handle = 0 + + for suffix in get_nvvm_dso_version_suffix(driver_ver): + if len(suffix) == 0: + continue + dll_name = "nvvm64_40_0" + + # First check if the DLL has been loaded by 3rd parties + try: + handle = win32api.GetModuleHandle(dll_name) + except: + pass + else: + break + + # Next, check if DLLs are installed via pip + for sp in get_site_packages(): + mod_path = os.path.join(sp, "nvidia", "cuda_nvcc", "nvvm", "bin") + if not os.path.isdir(mod_path): + continue + os.add_dll_directory(mod_path) + try: + handle = win32api.LoadLibraryEx( + # Note: LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR needs an abs path... + os.path.join(mod_path, dll_name), + 0, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR) + except: + pass + else: + break + + # Finally, try default search + try: + handle = win32api.LoadLibrary(dll_name) + except: + pass + else: + break + else: + raise RuntimeError('Failed to load nvvm') + + assert handle != 0 + return handle + + +cdef int _check_or_init_nvvm() except -1 nogil: + global __py_nvvm_init + if __py_nvvm_init: + return 0 + + cdef int err, driver_ver + with gil: + # Load driver to check version + try: + handle = win32api.LoadLibraryEx("nvcuda.dll", 0, LOAD_LIBRARY_SEARCH_SYSTEM32) + except Exception as e: + raise NotSupportedError(f'CUDA driver is not found ({e})') + global __cuDriverGetVersion + if __cuDriverGetVersion == NULL: + __cuDriverGetVersion = win32api.GetProcAddress(handle, 'cuDriverGetVersion') + if __cuDriverGetVersion == NULL: + raise RuntimeError('something went wrong') + err = (__cuDriverGetVersion)(&driver_ver) + if err != 0: + raise RuntimeError('something went wrong') + + # Load library + handle = load_library(driver_ver) + + # Load function + global __nvvmVersion + try: + __nvvmVersion = win32api.GetProcAddress(handle, 'nvvmVersion') + except: + pass + + global __nvvmIRVersion + try: + __nvvmIRVersion = win32api.GetProcAddress(handle, 'nvvmIRVersion') + except: + pass + + global __nvvmCreateProgram + try: + __nvvmCreateProgram = win32api.GetProcAddress(handle, 'nvvmCreateProgram') + except: + pass + + global __nvvmDestroyProgram + try: + __nvvmDestroyProgram = win32api.GetProcAddress(handle, 'nvvmDestroyProgram') + except: + pass + + global __nvvmAddModuleToProgram + try: + __nvvmAddModuleToProgram = win32api.GetProcAddress(handle, 'nvvmAddModuleToProgram') + except: + pass + + global __nvvmLazyAddModuleToProgram + try: + __nvvmLazyAddModuleToProgram = win32api.GetProcAddress(handle, 'nvvmLazyAddModuleToProgram') + except: + pass + + global __nvvmCompileProgram + try: + __nvvmCompileProgram = win32api.GetProcAddress(handle, 'nvvmCompileProgram') + except: + pass + + global __nvvmVerifyProgram + try: + __nvvmVerifyProgram = win32api.GetProcAddress(handle, 'nvvmVerifyProgram') + except: + pass + + global __nvvmGetCompiledResultSize + try: + __nvvmGetCompiledResultSize = win32api.GetProcAddress(handle, 'nvvmGetCompiledResultSize') + except: + pass + + global __nvvmGetCompiledResult + try: + __nvvmGetCompiledResult = win32api.GetProcAddress(handle, 'nvvmGetCompiledResult') + except: + pass + + global __nvvmGetProgramLogSize + try: + __nvvmGetProgramLogSize = win32api.GetProcAddress(handle, 'nvvmGetProgramLogSize') + except: + pass + + global __nvvmGetProgramLog + try: + __nvvmGetProgramLog = win32api.GetProcAddress(handle, 'nvvmGetProgramLog') + except: + pass + + __py_nvvm_init = True + return 0 + + +cdef dict func_ptrs = None + + +cpdef dict _inspect_function_pointers(): + global func_ptrs + if func_ptrs is not None: + return func_ptrs + + _check_or_init_nvvm() + cdef dict data = {} + + global __nvvmVersion + data["__nvvmVersion"] = __nvvmVersion + + global __nvvmIRVersion + data["__nvvmIRVersion"] = __nvvmIRVersion + + global __nvvmCreateProgram + data["__nvvmCreateProgram"] = __nvvmCreateProgram + + global __nvvmDestroyProgram + data["__nvvmDestroyProgram"] = __nvvmDestroyProgram + + global __nvvmAddModuleToProgram + data["__nvvmAddModuleToProgram"] = __nvvmAddModuleToProgram + + global __nvvmLazyAddModuleToProgram + data["__nvvmLazyAddModuleToProgram"] = __nvvmLazyAddModuleToProgram + + global __nvvmCompileProgram + data["__nvvmCompileProgram"] = __nvvmCompileProgram + + global __nvvmVerifyProgram + data["__nvvmVerifyProgram"] = __nvvmVerifyProgram + + global __nvvmGetCompiledResultSize + data["__nvvmGetCompiledResultSize"] = __nvvmGetCompiledResultSize + + global __nvvmGetCompiledResult + data["__nvvmGetCompiledResult"] = __nvvmGetCompiledResult + + global __nvvmGetProgramLogSize + data["__nvvmGetProgramLogSize"] = __nvvmGetProgramLogSize + + global __nvvmGetProgramLog + data["__nvvmGetProgramLog"] = __nvvmGetProgramLog + + func_ptrs = data + return data + + +cpdef _inspect_function_pointer(str name): + global func_ptrs + if func_ptrs is None: + func_ptrs = _inspect_function_pointers() + return func_ptrs[name] + + +############################################################################### +# Wrapper functions +############################################################################### + +cdef nvvmResult _nvvmVersion(int* major, int* minor) except* nogil: + global __nvvmVersion + _check_or_init_nvvm() + if __nvvmVersion == NULL: + with gil: + raise FunctionNotFoundError("function nvvmVersion is not found") + return (__nvvmVersion)( + major, minor) + + +cdef nvvmResult _nvvmIRVersion(int* majorIR, int* minorIR, int* majorDbg, int* minorDbg) except* nogil: + global __nvvmIRVersion + _check_or_init_nvvm() + if __nvvmIRVersion == NULL: + with gil: + raise FunctionNotFoundError("function nvvmIRVersion is not found") + return (__nvvmIRVersion)( + majorIR, minorIR, majorDbg, minorDbg) + + +cdef nvvmResult _nvvmCreateProgram(nvvmProgram* prog) except* nogil: + global __nvvmCreateProgram + _check_or_init_nvvm() + if __nvvmCreateProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmCreateProgram is not found") + return (__nvvmCreateProgram)( + prog) + + +cdef nvvmResult _nvvmDestroyProgram(nvvmProgram* prog) except* nogil: + global __nvvmDestroyProgram + _check_or_init_nvvm() + if __nvvmDestroyProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmDestroyProgram is not found") + return (__nvvmDestroyProgram)( + prog) + + +cdef nvvmResult _nvvmAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil: + global __nvvmAddModuleToProgram + _check_or_init_nvvm() + if __nvvmAddModuleToProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmAddModuleToProgram is not found") + return (__nvvmAddModuleToProgram)( + prog, buffer, size, name) + + +cdef nvvmResult _nvvmLazyAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil: + global __nvvmLazyAddModuleToProgram + _check_or_init_nvvm() + if __nvvmLazyAddModuleToProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmLazyAddModuleToProgram is not found") + return (__nvvmLazyAddModuleToProgram)( + prog, buffer, size, name) + + +cdef nvvmResult _nvvmCompileProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil: + global __nvvmCompileProgram + _check_or_init_nvvm() + if __nvvmCompileProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmCompileProgram is not found") + return (__nvvmCompileProgram)( + prog, numOptions, options) + + +cdef nvvmResult _nvvmVerifyProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil: + global __nvvmVerifyProgram + _check_or_init_nvvm() + if __nvvmVerifyProgram == NULL: + with gil: + raise FunctionNotFoundError("function nvvmVerifyProgram is not found") + return (__nvvmVerifyProgram)( + prog, numOptions, options) + + +cdef nvvmResult _nvvmGetCompiledResultSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil: + global __nvvmGetCompiledResultSize + _check_or_init_nvvm() + if __nvvmGetCompiledResultSize == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetCompiledResultSize is not found") + return (__nvvmGetCompiledResultSize)( + prog, bufferSizeRet) + + +cdef nvvmResult _nvvmGetCompiledResult(nvvmProgram prog, char* buffer) except* nogil: + global __nvvmGetCompiledResult + _check_or_init_nvvm() + if __nvvmGetCompiledResult == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetCompiledResult is not found") + return (__nvvmGetCompiledResult)( + prog, buffer) + + +cdef nvvmResult _nvvmGetProgramLogSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil: + global __nvvmGetProgramLogSize + _check_or_init_nvvm() + if __nvvmGetProgramLogSize == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetProgramLogSize is not found") + return (__nvvmGetProgramLogSize)( + prog, bufferSizeRet) + + +cdef nvvmResult _nvvmGetProgramLog(nvvmProgram prog, char* buffer) except* nogil: + global __nvvmGetProgramLog + _check_or_init_nvvm() + if __nvvmGetProgramLog == NULL: + with gil: + raise FunctionNotFoundError("function nvvmGetProgramLog is not found") + return (__nvvmGetProgramLog)( + prog, buffer) diff --git a/cuda_bindings/cuda/bindings/_internal/utils.pxd b/cuda_bindings/cuda/bindings/_internal/utils.pxd index d629179dc..cac7846ff 100644 --- a/cuda_bindings/cuda/bindings/_internal/utils.pxd +++ b/cuda_bindings/cuda/bindings/_internal/utils.pxd @@ -145,6 +145,8 @@ ctypedef fused ResT: int32_t int64_t char + float + double ctypedef fused PtrT: @@ -165,3 +167,4 @@ cdef bint is_nested_sequence(data) cdef void* get_buffer_pointer(buf, Py_ssize_t size, readonly=*) except* cdef tuple get_nvjitlink_dso_version_suffix(int driver_ver) +cdef tuple get_nvvm_dso_version_suffix(int driver_ver) diff --git a/cuda_bindings/cuda/bindings/_internal/utils.pyx b/cuda_bindings/cuda/bindings/_internal/utils.pyx index 55945ec96..0a693c052 100644 --- a/cuda_bindings/cuda/bindings/_internal/utils.pyx +++ b/cuda_bindings/cuda/bindings/_internal/utils.pyx @@ -132,5 +132,12 @@ class NotSupportedError(RuntimeError): pass cdef tuple get_nvjitlink_dso_version_suffix(int driver_ver): if 12000 <= driver_ver < 13000: return ('12', '') - else: - raise NotSupportedError('only CUDA 12 driver is supported') + raise NotSupportedError(f'CUDA driver version {driver_ver} is not supported') + + +cdef tuple get_nvvm_dso_version_suffix(int driver_ver): + if 11000 <= driver_ver < 11020: + return ('3', '') + if 11020 <= driver_ver < 13000: + return ('4', '') + raise NotSupportedError(f'CUDA driver version {driver_ver} is not supported') diff --git a/cuda_bindings/cuda/bindings/cynvvm.pxd b/cuda_bindings/cuda/bindings/cynvvm.pxd new file mode 100644 index 000000000..fa27d99bb --- /dev/null +++ b/cuda_bindings/cuda/bindings/cynvvm.pxd @@ -0,0 +1,46 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. + + +############################################################################### +# Types (structs, enums, ...) +############################################################################### + +# enums +ctypedef enum nvvmResult "nvvmResult": + NVVM_SUCCESS "NVVM_SUCCESS" = 0 + NVVM_ERROR_OUT_OF_MEMORY "NVVM_ERROR_OUT_OF_MEMORY" = 1 + NVVM_ERROR_PROGRAM_CREATION_FAILURE "NVVM_ERROR_PROGRAM_CREATION_FAILURE" = 2 + NVVM_ERROR_IR_VERSION_MISMATCH "NVVM_ERROR_IR_VERSION_MISMATCH" = 3 + NVVM_ERROR_INVALID_INPUT "NVVM_ERROR_INVALID_INPUT" = 4 + NVVM_ERROR_INVALID_PROGRAM "NVVM_ERROR_INVALID_PROGRAM" = 5 + NVVM_ERROR_INVALID_IR "NVVM_ERROR_INVALID_IR" = 6 + NVVM_ERROR_INVALID_OPTION "NVVM_ERROR_INVALID_OPTION" = 7 + NVVM_ERROR_NO_MODULE_IN_PROGRAM "NVVM_ERROR_NO_MODULE_IN_PROGRAM" = 8 + NVVM_ERROR_COMPILATION "NVVM_ERROR_COMPILATION" = 9 + NVVM_ERROR_CANCELLED "NVVM_ERROR_CANCELLED" = 10 + + +# types +ctypedef void* nvvmProgram 'nvvmProgram' + + +############################################################################### +# Functions +############################################################################### + +cdef nvvmResult nvvmVersion(int* major, int* minor) except* nogil +cdef nvvmResult nvvmIRVersion(int* majorIR, int* minorIR, int* majorDbg, int* minorDbg) except* nogil +cdef nvvmResult nvvmCreateProgram(nvvmProgram* prog) except* nogil +cdef nvvmResult nvvmDestroyProgram(nvvmProgram* prog) except* nogil +cdef nvvmResult nvvmAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil +cdef nvvmResult nvvmLazyAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil +cdef nvvmResult nvvmCompileProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil +cdef nvvmResult nvvmVerifyProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil +cdef nvvmResult nvvmGetCompiledResultSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil +cdef nvvmResult nvvmGetCompiledResult(nvvmProgram prog, char* buffer) except* nogil +cdef nvvmResult nvvmGetProgramLogSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil +cdef nvvmResult nvvmGetProgramLog(nvvmProgram prog, char* buffer) except* nogil diff --git a/cuda_bindings/cuda/bindings/cynvvm.pyx b/cuda_bindings/cuda/bindings/cynvvm.pyx new file mode 100644 index 000000000..1812998e1 --- /dev/null +++ b/cuda_bindings/cuda/bindings/cynvvm.pyx @@ -0,0 +1,59 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. + +from ._internal cimport nvvm as _nvvm + + +############################################################################### +# Wrapper functions +############################################################################### + +cdef nvvmResult nvvmVersion(int* major, int* minor) except* nogil: + return _nvvm._nvvmVersion(major, minor) + + +cdef nvvmResult nvvmIRVersion(int* majorIR, int* minorIR, int* majorDbg, int* minorDbg) except* nogil: + return _nvvm._nvvmIRVersion(majorIR, minorIR, majorDbg, minorDbg) + + +cdef nvvmResult nvvmCreateProgram(nvvmProgram* prog) except* nogil: + return _nvvm._nvvmCreateProgram(prog) + + +cdef nvvmResult nvvmDestroyProgram(nvvmProgram* prog) except* nogil: + return _nvvm._nvvmDestroyProgram(prog) + + +cdef nvvmResult nvvmAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil: + return _nvvm._nvvmAddModuleToProgram(prog, buffer, size, name) + + +cdef nvvmResult nvvmLazyAddModuleToProgram(nvvmProgram prog, const char* buffer, size_t size, const char* name) except* nogil: + return _nvvm._nvvmLazyAddModuleToProgram(prog, buffer, size, name) + + +cdef nvvmResult nvvmCompileProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil: + return _nvvm._nvvmCompileProgram(prog, numOptions, options) + + +cdef nvvmResult nvvmVerifyProgram(nvvmProgram prog, int numOptions, const char** options) except* nogil: + return _nvvm._nvvmVerifyProgram(prog, numOptions, options) + + +cdef nvvmResult nvvmGetCompiledResultSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil: + return _nvvm._nvvmGetCompiledResultSize(prog, bufferSizeRet) + + +cdef nvvmResult nvvmGetCompiledResult(nvvmProgram prog, char* buffer) except* nogil: + return _nvvm._nvvmGetCompiledResult(prog, buffer) + + +cdef nvvmResult nvvmGetProgramLogSize(nvvmProgram prog, size_t* bufferSizeRet) except* nogil: + return _nvvm._nvvmGetProgramLogSize(prog, bufferSizeRet) + + +cdef nvvmResult nvvmGetProgramLog(nvvmProgram prog, char* buffer) except* nogil: + return _nvvm._nvvmGetProgramLog(prog, buffer) diff --git a/cuda_bindings/cuda/bindings/nvvm.pxd b/cuda_bindings/cuda/bindings/nvvm.pxd new file mode 100644 index 000000000..dc8b2eea1 --- /dev/null +++ b/cuda_bindings/cuda/bindings/nvvm.pxd @@ -0,0 +1,40 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. + +from libc.stdint cimport intptr_t + +from .cynvvm cimport * + + +############################################################################### +# Types +############################################################################### + +ctypedef nvvmProgram Program + + +############################################################################### +# Enum +############################################################################### + +ctypedef nvvmResult _Result + + +############################################################################### +# Functions +############################################################################### + +cpdef tuple version() +cpdef tuple ir_version() +cpdef intptr_t create_program() except? 0 +cpdef add_module_to_program(intptr_t prog, buffer, size_t size, name) +cpdef lazy_add_module_to_program(intptr_t prog, buffer, size_t size, name) +cpdef compile_program(intptr_t prog, int num_options, options) +cpdef verify_program(intptr_t prog, int num_options, options) +cpdef size_t get_compiled_result_size(intptr_t prog) except? 0 +cpdef get_compiled_result(intptr_t prog, buffer) +cpdef size_t get_program_log_size(intptr_t prog) except? 0 +cpdef get_program_log(intptr_t prog, buffer) diff --git a/cuda_bindings/cuda/bindings/nvvm.pyx b/cuda_bindings/cuda/bindings/nvvm.pyx new file mode 100644 index 000000000..2a334994c --- /dev/null +++ b/cuda_bindings/cuda/bindings/nvvm.pyx @@ -0,0 +1,284 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE +# +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. + +cimport cython # NOQA + +from ._internal.utils cimport (get_buffer_pointer, get_nested_resource_ptr, + nested_resource) + +from enum import IntEnum as _IntEnum + + +############################################################################### +# Enum +############################################################################### + +class Result(_IntEnum): + """See `nvvmResult`.""" + SUCCESS = NVVM_SUCCESS + ERROR_OUT_OF_MEMORY = NVVM_ERROR_OUT_OF_MEMORY + ERROR_PROGRAM_CREATION_FAILURE = NVVM_ERROR_PROGRAM_CREATION_FAILURE + ERROR_IR_VERSION_MISMATCH = NVVM_ERROR_IR_VERSION_MISMATCH + ERROR_INVALID_INPUT = NVVM_ERROR_INVALID_INPUT + ERROR_INVALID_PROGRAM = NVVM_ERROR_INVALID_PROGRAM + ERROR_INVALID_IR = NVVM_ERROR_INVALID_IR + ERROR_INVALID_OPTION = NVVM_ERROR_INVALID_OPTION + ERROR_NO_MODULE_IN_PROGRAM = NVVM_ERROR_NO_MODULE_IN_PROGRAM + ERROR_COMPILATION = NVVM_ERROR_COMPILATION + ERROR_CANCELLED = NVVM_ERROR_CANCELLED + + +############################################################################### +# Error handling +############################################################################### + +class nvvmError(Exception): + + def __init__(self, status): + self.status = status + s = Result(status) + cdef str err = f"{s.name} ({s.value})" + super(nvvmError, self).__init__(err) + + def __reduce__(self): + return (type(self), (self.status,)) + + +@cython.profile(False) +cdef int check_status(int status) except 1 nogil: + if status != 0: + with gil: + raise nvvmError(status) + return status + + +############################################################################### +# Wrapper functions +############################################################################### + +cpdef destroy_program(intptr_t prog): + """Destroy a program. + + Args: + prog (intptr_t): nvvm prog. + + .. seealso:: `nvvmDestroyProgram` + """ + cdef Program p = prog + with nogil: + status = nvvmDestroyProgram(&p) + check_status(status) + + +cpdef tuple version(): + """Get the NVVM version. + + Returns: + A 2-tuple containing: + + - int: NVVM major version number. + - int: NVVM minor version number. + + .. seealso:: `nvvmVersion` + """ + cdef int major + cdef int minor + with nogil: + status = nvvmVersion(&major, &minor) + check_status(status) + return (major, minor) + + +cpdef tuple ir_version(): + """Get the NVVM IR version. + + Returns: + A 4-tuple containing: + + - int: NVVM IR major version number. + - int: NVVM IR minor version number. + - int: NVVM IR debug metadata major version number. + - int: NVVM IR debug metadata minor version number. + + .. seealso:: `nvvmIRVersion` + """ + cdef int major_ir + cdef int minor_ir + cdef int major_dbg + cdef int minor_dbg + with nogil: + status = nvvmIRVersion(&major_ir, &minor_ir, &major_dbg, &minor_dbg) + check_status(status) + return (major_ir, minor_ir, major_dbg, minor_dbg) + + +cpdef intptr_t create_program() except? 0: + """Create a program, and set the value of its handle to ``*prog``. + + Returns: + intptr_t: NVVM program. + + .. seealso:: `nvvmCreateProgram` + """ + cdef Program prog + with nogil: + status = nvvmCreateProgram(&prog) + check_status(status) + return prog + + +cpdef add_module_to_program(intptr_t prog, buffer, size_t size, name): + """Add a module level NVVM IR to a program. + + Args: + prog (intptr_t): NVVM program. + buffer (bytes): NVVM IR module in the bitcode or text representation. + size (size_t): Size of the NVVM IR module. + name (str): Name of the NVVM IR module. If NULL, "" is used as the name. + + .. seealso:: `nvvmAddModuleToProgram` + """ + cdef void* _buffer_ = get_buffer_pointer(buffer, size, readonly=True) + if not isinstance(name, str): + raise TypeError("name must be a Python str") + cdef bytes _temp_name_ = (name).encode() + cdef char* _name_ = _temp_name_ + with nogil: + status = nvvmAddModuleToProgram(prog, _buffer_, size, _name_) + check_status(status) + + +cpdef lazy_add_module_to_program(intptr_t prog, buffer, size_t size, name): + """Add a module level NVVM IR to a program. + + Args: + prog (intptr_t): NVVM program. + buffer (bytes): NVVM IR module in the bitcode representation. + size (size_t): Size of the NVVM IR module. + name (str): Name of the NVVM IR module. If NULL, "" is used as the name. + + .. seealso:: `nvvmLazyAddModuleToProgram` + """ + cdef void* _buffer_ = get_buffer_pointer(buffer, size, readonly=True) + if not isinstance(name, str): + raise TypeError("name must be a Python str") + cdef bytes _temp_name_ = (name).encode() + cdef char* _name_ = _temp_name_ + with nogil: + status = nvvmLazyAddModuleToProgram(prog, _buffer_, size, _name_) + check_status(status) + + +cpdef compile_program(intptr_t prog, int num_options, options): + """Compile the NVVM program. + + Args: + prog (intptr_t): NVVM program. + num_options (int): Number of compiler ``options`` passed. + options (object): Compiler options in the form of C string array. It can be: + + - an :class:`int` as the pointer address to the nested sequence, or + - a Python sequence of :class:`int`\s, each of which is a pointer address + to a valid sequence of 'char', or + - a nested Python sequence of ``str``. + + + .. seealso:: `nvvmCompileProgram` + """ + cdef nested_resource[ char ] _options_ + get_nested_resource_ptr[char](_options_, options, NULL) + with nogil: + status = nvvmCompileProgram(prog, num_options, (_options_.ptrs.data())) + check_status(status) + + +cpdef verify_program(intptr_t prog, int num_options, options): + """Verify the NVVM program. + + Args: + prog (intptr_t): NVVM program. + num_options (int): Number of compiler ``options`` passed. + options (object): Compiler options in the form of C string array. It can be: + + - an :class:`int` as the pointer address to the nested sequence, or + - a Python sequence of :class:`int`\s, each of which is a pointer address + to a valid sequence of 'char', or + - a nested Python sequence of ``str``. + + + .. seealso:: `nvvmVerifyProgram` + """ + cdef nested_resource[ char ] _options_ + get_nested_resource_ptr[char](_options_, options, NULL) + with nogil: + status = nvvmVerifyProgram(prog, num_options, (_options_.ptrs.data())) + check_status(status) + + +cpdef size_t get_compiled_result_size(intptr_t prog) except? 0: + """Get the size of the compiled result. + + Args: + prog (intptr_t): NVVM program. + + Returns: + size_t: Size of the compiled result (including the trailing NULL). + + .. seealso:: `nvvmGetCompiledResultSize` + """ + cdef size_t buffer_size_ret + with nogil: + status = nvvmGetCompiledResultSize(prog, &buffer_size_ret) + check_status(status) + return buffer_size_ret + + +cpdef get_compiled_result(intptr_t prog, buffer): + """Get the compiled result. + + Args: + prog (intptr_t): NVVM program. + buffer (bytes): Compiled result. + + .. seealso:: `nvvmGetCompiledResult` + """ + cdef void* _buffer_ = get_buffer_pointer(buffer, -1, readonly=False) + with nogil: + status = nvvmGetCompiledResult(prog, _buffer_) + check_status(status) + + +cpdef size_t get_program_log_size(intptr_t prog) except? 0: + """Get the Size of Compiler/Verifier Message. + + Args: + prog (intptr_t): NVVM program. + + Returns: + size_t: Size of the compilation/verification log (including the trailing NULL). + + .. seealso:: `nvvmGetProgramLogSize` + """ + cdef size_t buffer_size_ret + with nogil: + status = nvvmGetProgramLogSize(prog, &buffer_size_ret) + check_status(status) + return buffer_size_ret + + +cpdef get_program_log(intptr_t prog, buffer): + """Get the Compiler/Verifier Message. + + Args: + prog (intptr_t): NVVM program. + buffer (bytes): Compilation/Verification log. + + .. seealso:: `nvvmGetProgramLog` + """ + cdef void* _buffer_ = get_buffer_pointer(buffer, -1, readonly=False) + with nogil: + status = nvvmGetProgramLog(prog, _buffer_) + check_status(status) diff --git a/cuda_bindings/pyproject.toml b/cuda_bindings/pyproject.toml index 7965448b0..8921cc5a2 100644 --- a/cuda_bindings/pyproject.toml +++ b/cuda_bindings/pyproject.toml @@ -37,8 +37,9 @@ dependencies = [ [project.optional-dependencies] all = [ + "nvidia-cuda-nvcc-cu12", "nvidia-cuda-nvrtc-cu12", - "nvidia-nvjitlink-cu12>=12.3" + "nvidia-nvjitlink-cu12>=12.3", ] [project.urls] diff --git a/cuda_bindings/setup.py b/cuda_bindings/setup.py index af9e474c9..887f30ac2 100644 --- a/cuda_bindings/setup.py +++ b/cuda_bindings/setup.py @@ -314,6 +314,7 @@ def do_cythonize(extensions): ["cuda/*.pyx"], # internal files used by generated bindings ["cuda/bindings/_internal/nvjitlink.pyx"], + ["cuda/bindings/_internal/nvvm.pyx"], ["cuda/bindings/_internal/utils.pyx"], ] @@ -350,6 +351,14 @@ def build_extension(self, ext): ldflag = "-Wl,--disable-new-dtags,-rpath,$ORIGIN/../../../nvidia/cuda_nvrtc/lib" elif ext.name == "cuda.bindings._internal.nvjitlink": ldflag = "-Wl,--disable-new-dtags,-rpath,$ORIGIN/../../../nvidia/nvjitlink/lib" + elif ext.name == "cuda.bindings._internal.nvvm": + # from /site-packages/cuda/bindings/_internal/ + # to /site-packages/nvidia/cuda_nvcc/nvvm/lib64/ + rel1 = "$ORIGIN/../../../nvidia/cuda_nvcc/nvvm/lib64" + # from /lib/python3.*/site-packages/cuda/bindings/_internal/ + # to /lib/nvvm/lib64/ + rel2 = "$ORIGIN/../../../../../../nvvm/lib64" + ldflag = f"-Wl,--disable-new-dtags,-rpath,{rel1},-rpath,{rel2}" else: ldflag = None diff --git a/cuda_bindings/tests/test_nvvm.py b/cuda_bindings/tests/test_nvvm.py new file mode 100644 index 000000000..4bf0a3ceb --- /dev/null +++ b/cuda_bindings/tests/test_nvvm.py @@ -0,0 +1,193 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + +import base64 +import re +from contextlib import contextmanager + +import pytest + +from cuda.bindings import nvvm + +MINIMAL_NVVMIR_TXT = b"""\ +target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64" + +target triple = "nvptx64-nvidia-cuda" + +define void @kernel() { +entry: + ret void +} + +!nvvm.annotations = !{!0} +!0 = !{void ()* @kernel, !"kernel", i32 1} + +!nvvmir.version = !{!1} +!1 = !{i32 2, i32 0, i32 3, i32 1} +""" # noqa: E501 + +# Equivalent to MINIMAL_NVVMIR_TXT +MINIMAL_NVVMIR_BITCODE = base64.b64decode(""" +QkPA3jUUAAAFAAAAYgwwJElZvmbu034tRAEyBQAAAAAhDAAAJAEAAAsCIQACAAAAEwAAAAeBI5FB +yARJBhAyOZIBhAwlBQgZHgSLYoAMRQJCkgtCZBAyFDgIGEsKMjKISJAUIENGiKUAGTJC5EgOkJEh +xFBBUYGM4YPligQZRgaJIAAACwAAADIiyAggZIUEkyGkhASTIeOEoZAUEkyGjAuEZEwQFCMAJQBl +IGCOAAwAAAAAEyZ3sAd4oAd8sAM6aAN3sId0IId0CIc2GId6IIdw2OAS5dAG8KAHdkAHemAHdKAH +dkAHbZAOcaAHeKAHeNAG6YAHeoAHeoAHbZAOcWAHehAHdqAHcWAHbZAOcyAHejAHcqAHcyAHbZAO +dkAHemAHdKAHdkAHbZAOcSAHeKAHcSAHeKAHcSAHeNAG5jAHcqAHcyAHejAHctAG5mAHdKAHdkAH +emAHdNAG9hAHdqAHcWAHehAHdtAG9jAHcqAHcyAHejAHctAG9mAHdKAHdkAHemAHdNAG9hAHcoAH +ehAHcoAHehAHcoAHbeAOcWAHejAHcqAHdkAHGiEMGTFIgzDA8jdVxSCRvyxDIsAIAAAAAAAAAAAA +AEBig0BRlAAAgCwQBgAAADIemAwZEUyQjAkmR8YEQ2IJFMEIQBkAALEYAABtAAAAMwiAHMThHGYU +AT2IQziEw4xCgAd5eAdzmHEM5gAP7RAO9IAOMwxCHsLBHc6hHGYwBT2IQziEgxvMAz3IQz2MAz3M +eIx0cAd7CAd5SIdwcAd6cAN2eIdwIIcZzBEO7JAO4TAPbjAP4/AO8FAOMxDEHd4hHNghHcJhHmYw +iTu8gzvQQzm0Azy8gzyEAzvM8BR2YAd7aAc3aIdyaAc3gIdwkIdwYAd2KAd2+AV2eId3gIdfCIdx +GIdymId5mIEs7vAO7uAO9cAO7DADYsihHOShHMyhHOShHNxhHMohHMSBHcphBtaQQznIQzmYQznI +Qzm4wziUQziIAzuUwy+8gzz8gjvUAzuwwwzHaYdwWIdycIN0aAd4YId0GId0oIcZzlMP7gAP8lAO +5JAO40AP4SAO7FAOMyAoHdzBHsJBHtIhHNyBHtzgHOThHeoBHmYYUTiwQzqcgzvMUCR2YAd7aAc3 +YId3eAd4mFFM9JAP8FAOMx5qHsphHOghHd7BHX4BHuShHMwhHfBhBlSFgzjMwzuwQz3QQzn8wjzk +QzuIwzuww4zFCod5mId3GId0CAd6KAdyAAAAAHkgAAAeAAAAYh5IIEOIDBk5GSSQkUDGyMhoIlAI +FDKeGBkhR8iQUQwIBQAABgAAAGtlcm5lbAAAIwgCMIJABCMIhDCCQAwjCAQxwyAEwwwEURiDjAQm +KCE3O7s2lzA3tze6MLq0N7e5UQIjHTc7u7Y0ORe7Mrm5tDe3UYIDAAAAqRgAAAsAAAALCnIoh3eA +B3pYcJhDPbjDOLBDOdDDguYcxqEN6EEewsEd5iEd6CEd3sEdANEQAAAGAAAAB8w8pIM7nAM7lAM9 +oIM8lEM4kMMBAAAAYSAAAAYAAAATBAGGAwEAAAIAAAAHUBDNFGEAAAAAAABxIAAAAwAAADIOECKE +AKACAAAAAAAAAABlDAAAHQAAABIDlOgAAAAAAAAAAAYAAAAFAAAARAAAAAEAAABQAAAAAAAAAFAA +AAABAAAAaAAAAAAAAAALAAAAEwAAAB4AAAARAAAALwAAAAAAAAAAAAAAAQAAAAAAAAAAAAAABgAA +AAAAAAAGAAAA/////wAkAAAAAAAAXQwAAA8AAAASA5RvAAAAAGtlcm5lbDUuMC4xbnZwdHg2NC1u +dmlkaWEtY3VkYW1pbmltYWxfbnZ2bWlyLmxsAAAAAAA= +""") +# To regenerate, pull and start a docker container: +# docker pull centos/llvm-toolset-7-centos7 +# docker run -it centos/llvm-toolset-7-centos7 /bin/bash +# In the docker container, copy MINIMAL_NVVMIR_TXT to a file with name minimal_nvvmir.ll +# Then run: +# llvm-as minimal_nvvmir.ll -o minimal_nvvmir.bc +# Save this to encode.py: +# import base64, sys, textwrap +# bitcode = open(sys.argv[1], "rb").read() +# encoded_bitcode = base64.b64encode(bitcode).decode("ascii") +# wrapped_base64 = "\n".join(textwrap.wrap(encoded_bitcode, width=76)) +# print(wrapped_base64) +# Then run: +# python encode.py minimal_nvvmir.bc + + +@pytest.fixture(params=["txt", "bitcode"]) +def minimal_nvvmir(request): + return MINIMAL_NVVMIR_TXT if request.param == "txt" else MINIMAL_NVVMIR_BITCODE + + +@pytest.fixture(params=[nvvm.compile_program, nvvm.verify_program]) +def compile_or_verify(request): + return request.param + + +def match_exact(s): + return "^" + re.escape(s) + "$" + + +@contextmanager +def nvvm_program() -> int: + prog: int = nvvm.create_program() + try: + yield prog + finally: + nvvm.destroy_program(prog) + + +def get_program_log(prog): + buffer = bytearray(nvvm.get_program_log_size(prog)) + nvvm.get_program_log(prog, buffer) + return buffer.decode(errors="backslashreplace") + + +def test_nvvm_version(): + ver = nvvm.version() + assert len(ver) == 2 + assert ver >= (2, 0) + + +def test_nvvm_ir_version(): + ver = nvvm.ir_version() + assert len(ver) == 4 + assert ver >= (2, 0, 3, 1) + + +def test_create_and_destroy(): + with nvvm_program() as prog: + assert isinstance(prog, int) + assert prog != 0 + + +@pytest.mark.parametrize("add_fn", [nvvm.add_module_to_program, nvvm.lazy_add_module_to_program]) +def test_add_module_to_program_fail(add_fn): + with nvvm_program() as prog, pytest.raises(ValueError): + # Passing a C NULL pointer generates "ERROR_INVALID_INPUT (4)", + # but that is not possible through our Python bindings. + # The ValueError originates from the cython bindings code. + add_fn(prog, None, 0, "FileNameHere.ll") + + +def test_c_or_v_program_fail_no_module(compile_or_verify): + with nvvm_program() as prog, pytest.raises(nvvm.nvvmError, match=match_exact("ERROR_NO_MODULE_IN_PROGRAM (8)")): + compile_or_verify(prog, 0, []) + + +def test_c_or_v_program_fail_invalid_ir(compile_or_verify): + expected_error = "ERROR_COMPILATION (9)" if compile_or_verify is nvvm.compile_program else "ERROR_INVALID_IR (6)" + nvvm_ll = b"This is not NVVM IR" + with nvvm_program() as prog: + nvvm.add_module_to_program(prog, nvvm_ll, len(nvvm_ll), "FileNameHere.ll") + with pytest.raises(nvvm.nvvmError, match=match_exact(expected_error)): + compile_or_verify(prog, 0, []) + assert get_program_log(prog) == "FileNameHere.ll (1, 0): parse expected top-level entity\x00" + + +def test_c_or_v_program_fail_bad_option(minimal_nvvmir, compile_or_verify): + with nvvm_program() as prog: + nvvm.add_module_to_program(prog, minimal_nvvmir, len(minimal_nvvmir), "FileNameHere.ll") + with pytest.raises(nvvm.nvvmError, match=match_exact("ERROR_INVALID_OPTION (7)")): + compile_or_verify(prog, 1, ["BadOption"]) + assert get_program_log(prog) == "libnvvm : error: BadOption is an unsupported option\x00" + + +@pytest.mark.parametrize( + ("get_size", "get_buffer"), + [ + (nvvm.get_compiled_result_size, nvvm.get_compiled_result), + (nvvm.get_program_log_size, nvvm.get_program_log), + ], +) +def test_get_buffer_empty(get_size, get_buffer): + with nvvm_program() as prog: + buffer_size = get_size(prog) + assert buffer_size == 1 + buffer = bytearray(buffer_size) + get_buffer(prog, buffer) + assert buffer == b"\x00" + + +@pytest.mark.parametrize("options", [[], ["-opt=0"], ["-opt=3", "-g"]]) +def test_compile_program_with_minimal_nnvm_ir(minimal_nvvmir, options): + with nvvm_program() as prog: + nvvm.add_module_to_program(prog, minimal_nvvmir, len(minimal_nvvmir), "FileNameHere.ll") + try: + nvvm.compile_program(prog, len(options), options) + except nvvm.nvvmError as e: + raise RuntimeError(get_program_log(prog)) from e + else: + log_size = nvvm.get_program_log_size(prog) + assert log_size == 1 + buffer = bytearray(log_size) + nvvm.get_program_log(prog, buffer) + assert buffer == b"\x00" + result_size = nvvm.get_compiled_result_size(prog) + buffer = bytearray(result_size) + nvvm.get_compiled_result(prog, buffer) + assert ".visible .entry kernel()" in buffer.decode() + + +@pytest.mark.parametrize("options", [[], ["-opt=0"], ["-opt=3", "-g"]]) +def test_verify_program_with_minimal_nnvm_ir(minimal_nvvmir, options): + with nvvm_program() as prog: + nvvm.add_module_to_program(prog, minimal_nvvmir, len(minimal_nvvmir), "FileNameHere.ll") + nvvm.verify_program(prog, len(options), options)