diff --git a/cudax/include/cuda/experimental/__binutils/demangle.cuh b/cudax/include/cuda/experimental/__binutils/demangle.cuh new file mode 100644 index 00000000000..c2916151651 --- /dev/null +++ b/cudax/include/cuda/experimental/__binutils/demangle.cuh @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// 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_COMPILER(NVRTC) + +# include +# include +# include +# include +# include +# include + +# 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 +{ + +// 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([[maybe_unused]] ::cuda::std::string_view __name) +{ +# 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()}; + + 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_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..8e9e3bba9cb 100644 --- a/cudax/test/CMakeLists.txt +++ b/cudax/test/CMakeLists.txt @@ -1,6 +1,7 @@ cccl_get_c2h() find_package(cudax) # already found, bring in version info. +find_package(CUDAToolkit REQUIRED) if (cudax_ENABLE_CUFILE) find_package(CUDAToolkit REQUIRED) @@ -29,7 +30,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" @@ -177,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..453656e7d50 --- /dev/null +++ b/cudax/test/binutils/CMakeLists.txt @@ -0,0 +1,50 @@ +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) + cudax_get_target_property(config_dialect ${cn_target} DIALECT) + + # 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} + ) + 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 "$") + + # 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}) + 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 "$") + + # 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.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; +} 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; +}