From fdf7994244cfc6dc5d7f8252d7ba7c701eb4e9e0 Mon Sep 17 00:00:00 2001 From: David Bayer Date: Mon, 27 Oct 2025 15:46:09 +0100 Subject: [PATCH 1/4] Implement `cudax::demangle` --- .../cuda/experimental/__binutils/demangle.cuh | 101 ++++++++++++++++++ cudax/include/cuda/experimental/binutils.cuh | 26 +++++ cudax/test/CMakeLists.txt | 24 ++++- cudax/test/binutils/demangle.pass.cpp | 65 +++++++++++ 4 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 cudax/include/cuda/experimental/__binutils/demangle.cuh create mode 100644 cudax/include/cuda/experimental/binutils.cuh create mode 100644 cudax/test/binutils/demangle.pass.cpp diff --git a/cudax/include/cuda/experimental/__binutils/demangle.cuh b/cudax/include/cuda/experimental/__binutils/demangle.cuh new file mode 100644 index 00000000000..65baba68021 --- /dev/null +++ b/cudax/include/cuda/experimental/__binutils/demangle.cuh @@ -0,0 +1,101 @@ +//===----------------------------------------------------------------------===// +// +// Part of CUDA Experimental in CUDA C++ Core Libraries, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +#ifndef _CUDAX___BINUTILS_DEMANGLE_CUH +#define _CUDAX___BINUTILS_DEMANGLE_CUH + +#include + +#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) +# pragma GCC system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) +# pragma clang system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) +# pragma system_header +#endif // no system header + +#if _CCCL_HAS_CTK() && !_CCCL_COMPILER(NVRTC) + +# include +# include +# include +# include +# include +# include + +# include + +# if _CCCL_HAS_INCLUDE() +# include +# endif // _CCCL_HAS_INCLUDE() + +# include + +namespace cuda::experimental +{ + +// todo: make this function take cuda::std::cstring_view once P3655 is merged to C++29 and implemented in libcu++ + +//! @brief Demangles a CUDA C++ mangled name. +//! +//! @param __name The mangled name to demangle. +//! +//! @return A `std::string` containing the demangled name. +//! +//! @throws std::bad_alloc if memory allocation fails. +//! @throws std::runtime_error if the passed \c __name is not a valid mangled symbol. +template +[[nodiscard]] _CCCL_HOST_API ::std::string demangle(::cuda::std::string_view __name) +{ +# if !_CCCL_HAS_INCLUDE() + static_assert(__always_false_v<_Dummy>, "cuda::demangle requires the `cuxxfilt` package from the CUDA Toolkit."); +# else // ^^^ no cuxxfilt ^^^ / vvv has cuxxfilt vvv + // input must be zero-terminated, so we convert string_view to std::string + ::std::string __name_in{__name.begin(), __name.end()}; + + int __status{}; + char* __dname = ::__cu_demangle(__name_in.c_str(), nullptr, nullptr, &__status); + + try + { + switch (__status) + { + case 0: { + ::std::string __ret{__dname}; + ::cuda::std::free(__dname); + return __ret; + } + case -1: + ::cuda::std::__throw_bad_alloc(); + case -2: + ::cuda::std::__throw_runtime_error("invalid mangled name passed to cuda::demangle function"); + case -3: + _CCCL_ASSERT(false, "cccl internal error - invalid argument passed to __cu_demangle"); + [[fallthrough]]; + default: + _CCCL_UNREACHABLE(); + } + } + catch (...) + { + // If an exception is thrown, free the allocated memory and rethrow the exception + ::cuda::std::free(__dname); + throw; + } +# endif // ^^^ has cuxxfilt ^^^ +} + +} // namespace cuda::experimental + +# include + +#endif // _CCCL_HAS_CTK() && !_CCCL_COMPILER(NVRTC) + +#endif // _CUDAX___BINUTILS_DEMANGLE_CUH diff --git a/cudax/include/cuda/experimental/binutils.cuh b/cudax/include/cuda/experimental/binutils.cuh new file mode 100644 index 00000000000..a9efa88951f --- /dev/null +++ b/cudax/include/cuda/experimental/binutils.cuh @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// Part of CUDA Experimental in CUDA C++ Core Libraries, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +#ifndef _CUDAX_BINUTILS +#define _CUDAX_BINUTILS + +#include + +#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC) +# pragma GCC system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG) +# pragma clang system_header +#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC) +# pragma system_header +#endif // no system header + +#include + +#endif // _CUDAX_BINUTILS diff --git a/cudax/test/CMakeLists.txt b/cudax/test/CMakeLists.txt index 9550ff588b3..b1d887efc12 100644 --- a/cudax/test/CMakeLists.txt +++ b/cudax/test/CMakeLists.txt @@ -1,6 +1,13 @@ cccl_get_c2h() find_package(cudax) # already found, bring in version info. +find_package(CUDAToolkit) + +# Find the cu++filt library. +find_library(cudax_cufilt_lib "cufilt" PATHS "${CUDAToolkit_LIBRARY_DIR}" NO_DEFAULT_PATH) +if (NOT cudax_cufilt_lib) + message(FATAL_ERROR "cudax: cu++filt library (libcufilt.a) not found.") +endif() if (cudax_ENABLE_CUFILE) find_package(CUDAToolkit REQUIRED) @@ -29,7 +36,7 @@ function(cudax_add_catch2_test target_name_var test_name cn_target) # ARGN=test target_link_libraries(${test_target} PRIVATE ${cn_target} cccl.c2h.main - cudart + CUDA::cudart ) target_compile_options(${test_target} PRIVATE "-DLIBCUDACXX_ENABLE_EXPERIMENTAL_MEMORY_RESOURCE" @@ -165,6 +172,21 @@ foreach(cn_target IN LISTS cudax_TARGETS) algorithm/copy.cu ) + # On windows, cu++filt library is compiled with static runtime which causes problems when linking it with cccl.c2h.main + # which is compiled with dynamic runtime. So, we do everything by hand. The test is implemented in lit style, so we + # we avoid linking with cccl.c2h.main + set(test_target ${config_prefix}.test.binutils_demangle) + add_executable(${test_target} binutils/demangle.pass.cpp) + target_compile_options(${test_target} PRIVATE "$<$:/MT>") + target_link_libraries(${test_target} PRIVATE + ${cn_target} + CUDA::cudart_static + ${cudax_cufilt_lib} + ) + cudax_clone_target_properties(${test_target} ${cn_target}) + add_dependencies(${config_meta_target} ${test_target}) + add_test(NAME ${test_target} COMMAND "$") + if (cudax_ENABLE_CUFILE) cudax_add_catch2_test(cufile_driver_target cufile_driver ${cn_target} cufile/driver.cu diff --git a/cudax/test/binutils/demangle.pass.cpp b/cudax/test/binutils/demangle.pass.cpp new file mode 100644 index 00000000000..1d84c8e7041 --- /dev/null +++ b/cudax/test/binutils/demangle.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of CUDA Experimental in CUDA C++ Core Libraries, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +// This test uses assert(...) for checking results +#undef NDEBUG + +#include +#include +#include + +#include + +#include +#include + +namespace cudax = cuda::experimental; + +bool test() +{ + // 1. Test signature + static_assert(cuda::std::is_same_v); + static_assert(!noexcept(cudax::demangle(cuda::std::string_view{}))); + + // 2. Test positive case + { + constexpr auto real_mangled_name = "_ZN8clstmp01I5cls01E13clstmp01_mf01Ev"; + const auto demangled = cudax::demangle(real_mangled_name); + assert(demangled == "clstmp01::clstmp01_mf01()"); + } + + // 3. Test error case + { +#if _CCCL_HAS_EXCEPTIONS() + constexpr auto fake_mangled_name = "B@d_iDentiFier"; + try + { + auto demangled = cudax::demangle(fake_mangled_name); + assert(false); + } + catch (const std::runtime_error&) + { + assert(true); + } + catch (...) + { + assert(false); + } +#endif // _CCCL_HAS_EXCEPTIONS() + } + + return true; +} + +int main(int, char**) +{ + NV_IF_TARGET(NV_IS_HOST, (test();)) + return 0; +} From 5c07102ae4fa7a79a14182d9924b1b31e9b582d4 Mon Sep 17 00:00:00 2001 From: David Bayer Date: Mon, 27 Oct 2025 17:18:39 +0100 Subject: [PATCH 2/4] Add missing `nvdecode.h` tests --- .../cuda/experimental/__binutils/demangle.cuh | 16 +++---- cudax/test/CMakeLists.txt | 27 ++--------- cudax/test/binutils/CMakeLists.txt | 47 +++++++++++++++++++ .../binutils/demangle_no_nvdecode.fail.cpp | 37 +++++++++++++++ .../binutils/demangle_no_nvdecode.pass.cpp | 20 ++++++++ 5 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 cudax/test/binutils/CMakeLists.txt create mode 100644 cudax/test/binutils/demangle_no_nvdecode.fail.cpp create mode 100644 cudax/test/binutils/demangle_no_nvdecode.pass.cpp diff --git a/cudax/include/cuda/experimental/__binutils/demangle.cuh b/cudax/include/cuda/experimental/__binutils/demangle.cuh index 65baba68021..8e22baf4547 100644 --- a/cudax/include/cuda/experimental/__binutils/demangle.cuh +++ b/cudax/include/cuda/experimental/__binutils/demangle.cuh @@ -21,7 +21,7 @@ # pragma system_header #endif // no system header -#if _CCCL_HAS_CTK() && !_CCCL_COMPILER(NVRTC) +#if !_CCCL_COMPILER(NVRTC) # include # include @@ -32,12 +32,11 @@ # include -# if _CCCL_HAS_INCLUDE() -# include -# endif // _CCCL_HAS_INCLUDE() - # include +// Forward declaration of __cu_demangle function from (cuxxfilt package from CUDA Toolkit) +extern "C" char* __cu_demangle(const char* id, char* output_buffer, ::cuda::std::size_t* length, int* status); + namespace cuda::experimental { @@ -52,10 +51,11 @@ namespace cuda::experimental //! @throws std::bad_alloc if memory allocation fails. //! @throws std::runtime_error if the passed \c __name is not a valid mangled symbol. template -[[nodiscard]] _CCCL_HOST_API ::std::string demangle(::cuda::std::string_view __name) +[[nodiscard]] _CCCL_HOST_API ::std::string demangle([[maybe_unused]] ::cuda::std::string_view __name) { # if !_CCCL_HAS_INCLUDE() - static_assert(__always_false_v<_Dummy>, "cuda::demangle requires the `cuxxfilt` package from the CUDA Toolkit."); + static_assert(::cuda::std::__always_false_v<_Dummy>, + "cuda::demangle requires the `cuxxfilt` package from the CUDA Toolkit."); # else // ^^^ no cuxxfilt ^^^ / vvv has cuxxfilt vvv // input must be zero-terminated, so we convert string_view to std::string ::std::string __name_in{__name.begin(), __name.end()}; @@ -96,6 +96,6 @@ template # include -#endif // _CCCL_HAS_CTK() && !_CCCL_COMPILER(NVRTC) +#endif // !_CCCL_COMPILER(NVRTC) #endif // _CUDAX___BINUTILS_DEMANGLE_CUH diff --git a/cudax/test/CMakeLists.txt b/cudax/test/CMakeLists.txt index b1d887efc12..8e9e3bba9cb 100644 --- a/cudax/test/CMakeLists.txt +++ b/cudax/test/CMakeLists.txt @@ -1,13 +1,7 @@ cccl_get_c2h() find_package(cudax) # already found, bring in version info. -find_package(CUDAToolkit) - -# Find the cu++filt library. -find_library(cudax_cufilt_lib "cufilt" PATHS "${CUDAToolkit_LIBRARY_DIR}" NO_DEFAULT_PATH) -if (NOT cudax_cufilt_lib) - message(FATAL_ERROR "cudax: cu++filt library (libcufilt.a) not found.") -endif() +find_package(CUDAToolkit REQUIRED) if (cudax_ENABLE_CUFILE) find_package(CUDAToolkit REQUIRED) @@ -172,21 +166,6 @@ foreach(cn_target IN LISTS cudax_TARGETS) algorithm/copy.cu ) - # On windows, cu++filt library is compiled with static runtime which causes problems when linking it with cccl.c2h.main - # which is compiled with dynamic runtime. So, we do everything by hand. The test is implemented in lit style, so we - # we avoid linking with cccl.c2h.main - set(test_target ${config_prefix}.test.binutils_demangle) - add_executable(${test_target} binutils/demangle.pass.cpp) - target_compile_options(${test_target} PRIVATE "$<$:/MT>") - target_link_libraries(${test_target} PRIVATE - ${cn_target} - CUDA::cudart_static - ${cudax_cufilt_lib} - ) - cudax_clone_target_properties(${test_target} ${cn_target}) - add_dependencies(${config_meta_target} ${test_target}) - add_test(NAME ${test_target} COMMAND "$") - if (cudax_ENABLE_CUFILE) cudax_add_catch2_test(cufile_driver_target cufile_driver ${cn_target} cufile/driver.cu @@ -199,6 +178,10 @@ foreach(cn_target IN LISTS cudax_TARGETS) endif() endforeach() +# On windows, cu++filt library is compiled with static runtime which causes problems when linking it with cccl.c2h.main +# which is compiled with dynamic runtime. So, we do everything by hand in binutils directory. +add_subdirectory(binutils) + # FIXME: Enable MSVC if (cudax_ENABLE_CUDASTF AND NOT "MSVC" STREQUAL "${CMAKE_CXX_COMPILER_ID}") diff --git a/cudax/test/binutils/CMakeLists.txt b/cudax/test/binutils/CMakeLists.txt new file mode 100644 index 00000000000..1be21f2def3 --- /dev/null +++ b/cudax/test/binutils/CMakeLists.txt @@ -0,0 +1,47 @@ +find_package(cudax REQUIRED) +find_package(CUDAToolkit REQUIRED) + +# Find the cu++filt library. +find_library(cudax_cufilt_lib "cufilt" PATHS "${CUDAToolkit_LIBRARY_DIR}" NO_DEFAULT_PATH) +if (NOT cudax_cufilt_lib) + message(FATAL_ERROR "cudax: cu++filt library (libcufilt.a) not found.") +endif() + +foreach(cn_target IN LISTS cudax_TARGETS) + cudax_get_target_property(config_prefix ${cn_target} PREFIX) + set(config_meta_target ${config_prefix}.tests) + + # 1. positive case (pass) + set(test_target ${config_prefix}.test.binutils_demangle) + add_executable(${test_target} demangle.pass.cpp) + target_compile_options(${test_target} PRIVATE "$<$:/MT>") + target_link_libraries(${test_target} PRIVATE + ${cn_target} + CUDA::cudart_static + ${cudax_cufilt_lib} + ) + cudax_clone_target_properties(${test_target} ${cn_target}) + add_dependencies(${config_meta_target} ${test_target}) + add_test(NAME ${test_target} COMMAND "$") + + # 2. missing include but the function is not used (pass) + set(test_target ${config_prefix}.test.binutils_demangle_no_nvdecode_pass) + add_executable(${test_target} demangle_no_nvdecode.pass.cpp) + target_compile_options(${test_target} PRIVATE "$<$:/MT>") + target_link_libraries(${test_target} PRIVATE ${cn_target}) + cudax_clone_target_properties(${test_target} ${cn_target}) + add_dependencies(${config_meta_target} ${test_target}) + add_test(NAME ${test_target} COMMAND "$") + + # 3. missing and the function is used (fail) + set(test_target ${config_prefix}.test.binutils_demangle_no_nvdecode_fail) + add_test(NAME ${test_target} + COMMAND ${CMAKE_CTEST_COMMAND} + --build-and-test + ${CMAKE_CURRENT_LIST_DIR}/binutils_demangle_no_nvdecode_fail + ${CMAKE_CURRENT_BINARY_DIR}/binutils_demangle_no_nvdecode_fail + --build-generator ${CMAKE_GENERATOR} + --test-command ${CMAKE_CTEST_COMMAND} + ) + set_tests_properties(${test_target} PROPERTIES WILL_FAIL true) +endforeach() diff --git a/cudax/test/binutils/demangle_no_nvdecode.fail.cpp b/cudax/test/binutils/demangle_no_nvdecode.fail.cpp new file mode 100644 index 00000000000..9b48d711020 --- /dev/null +++ b/cudax/test/binutils/demangle_no_nvdecode.fail.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// Part of CUDA Experimental in CUDA C++ Core Libraries, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +// This test uses assert(...) for checking results +#undef NDEBUG + +#include + +#include + +#if _CCCL_HAS_INCLUDE() +# error "This test requires not to be findable in PATH." +#endif + +namespace cudax = cuda::experimental; + +bool test() +{ + constexpr auto real_mangled_name = "_ZN8clstmp01I5cls01E13clstmp01_mf01Ev"; + const auto demangled = cudax::demangle(real_mangled_name); + assert(demangled == "clstmp01::clstmp01_mf01()"); + + return true; +} + +int main(int, char**) +{ + NV_IF_TARGET(NV_IS_HOST, (test();)) + return 0; +} diff --git a/cudax/test/binutils/demangle_no_nvdecode.pass.cpp b/cudax/test/binutils/demangle_no_nvdecode.pass.cpp new file mode 100644 index 00000000000..50f968c641b --- /dev/null +++ b/cudax/test/binutils/demangle_no_nvdecode.pass.cpp @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// Part of CUDA Experimental in CUDA C++ Core Libraries, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. +// +//===----------------------------------------------------------------------===// + +#include + +#if _CCCL_HAS_INCLUDE() +# error "This test requires not to be findable in PATH." +#endif + +int main(int, char**) +{ + return 0; +} From 041a5a055aefb5dc15442b471fda757f80a39ae6 Mon Sep 17 00:00:00 2001 From: David Bayer Date: Mon, 27 Oct 2025 17:25:01 +0100 Subject: [PATCH 3/4] fix missing return --- cudax/include/cuda/experimental/__binutils/demangle.cuh | 1 + 1 file changed, 1 insertion(+) diff --git a/cudax/include/cuda/experimental/__binutils/demangle.cuh b/cudax/include/cuda/experimental/__binutils/demangle.cuh index 8e22baf4547..c2916151651 100644 --- a/cudax/include/cuda/experimental/__binutils/demangle.cuh +++ b/cudax/include/cuda/experimental/__binutils/demangle.cuh @@ -56,6 +56,7 @@ template # if !_CCCL_HAS_INCLUDE() static_assert(::cuda::std::__always_false_v<_Dummy>, "cuda::demangle requires the `cuxxfilt` package from the CUDA Toolkit."); + return {}; # else // ^^^ no cuxxfilt ^^^ / vvv has cuxxfilt vvv // input must be zero-terminated, so we convert string_view to std::string ::std::string __name_in{__name.begin(), __name.end()}; From 2910290aa5aa228dde094a3e318a7fa472f1229f Mon Sep 17 00:00:00 2001 From: David Bayer Date: Mon, 27 Oct 2025 17:29:02 +0100 Subject: [PATCH 4/4] fix cmake --- cudax/test/binutils/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cudax/test/binutils/CMakeLists.txt b/cudax/test/binutils/CMakeLists.txt index 1be21f2def3..453656e7d50 100644 --- a/cudax/test/binutils/CMakeLists.txt +++ b/cudax/test/binutils/CMakeLists.txt @@ -10,6 +10,7 @@ endif() foreach(cn_target IN LISTS cudax_TARGETS) cudax_get_target_property(config_prefix ${cn_target} PREFIX) set(config_meta_target ${config_prefix}.tests) + cudax_get_target_property(config_dialect ${cn_target} DIALECT) # 1. positive case (pass) set(test_target ${config_prefix}.test.binutils_demangle) @@ -20,6 +21,7 @@ foreach(cn_target IN LISTS cudax_TARGETS) CUDA::cudart_static ${cudax_cufilt_lib} ) + cccl_configure_target(${test_target} DIALECT ${config_dialect}) cudax_clone_target_properties(${test_target} ${cn_target}) add_dependencies(${config_meta_target} ${test_target}) add_test(NAME ${test_target} COMMAND "$") @@ -29,6 +31,7 @@ foreach(cn_target IN LISTS cudax_TARGETS) add_executable(${test_target} demangle_no_nvdecode.pass.cpp) target_compile_options(${test_target} PRIVATE "$<$:/MT>") target_link_libraries(${test_target} PRIVATE ${cn_target}) + cccl_configure_target(${test_target} DIALECT ${config_dialect}) cudax_clone_target_properties(${test_target} ${cn_target}) add_dependencies(${config_meta_target} ${test_target}) add_test(NAME ${test_target} COMMAND "$")