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
102 changes: 102 additions & 0 deletions cudax/include/cuda/experimental/__binutils/demangle.cuh
Original file line number Diff line number Diff line change
@@ -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 <cuda/std/detail/__config>

#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 <cuda/std/__cstddef/types.h>
# include <cuda/std/__exception/throw_error.h>
# include <cuda/std/__new/bad_alloc.h>
# include <cuda/std/__type_traits/always_false.h>
# include <cuda/std/cstdlib>
# include <cuda/std/string_view>

# include <string>

# include <cuda/std/__cccl/prologue.h>

// Forward declaration of __cu_demangle function from <nv_decode.h> (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 <class _Dummy = void>
[[nodiscard]] _CCCL_HOST_API ::std::string demangle([[maybe_unused]] ::cuda::std::string_view __name)
{
# if !_CCCL_HAS_INCLUDE(<nv_decode.h>)
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 <cuda/std/__cccl/epilogue.h>

#endif // !_CCCL_COMPILER(NVRTC)

#endif // _CUDAX___BINUTILS_DEMANGLE_CUH
26 changes: 26 additions & 0 deletions cudax/include/cuda/experimental/binutils.cuh
Original file line number Diff line number Diff line change
@@ -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 <cuda/std/detail/__config>

#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 <cuda/experimental/__binutils/demangle.cuh>

#endif // _CUDAX_BINUTILS
7 changes: 6 additions & 1 deletion cudax/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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}")
Expand Down
50 changes: 50 additions & 0 deletions cudax/test/binutils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: replace cn_target with cudax_target in this new code. I'm making this change for existing code in #6346 -- cudax used to be called "cuda next" and this prefix is a legacy artifact that no longer makes sense.

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 "$<$<CXX_COMPILER_ID:MSVC>:/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 "$<TARGET_FILE:${test_target}>")

# 2. missing <nvdecode.h> 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 "$<$<CXX_COMPILER_ID:MSVC>:/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 "$<TARGET_FILE:${test_target}>")

# 3. missing <nvdecode.h> 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
Copy link
Contributor

@alliepiper alliepiper Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--build-and-test isn't what we want here, it's used for configuring/building a project, not an individual translation unit.

Check out what we do for CUB "fail" tests:

https://github.com/NVIDIA/cccl/blob/main/cub/test/CMakeLists.txt#L174-L199

Basically:

  1. Add the test executable as normal.
  2. Exclude from the all build target:
      set_target_properties(${test_target} PROPERTIES EXCLUDE_FROM_ALL true
                                           EXCLUDE_FROM_DEFAULT_BUILD true)
  1. Add a test that explicitly invokes the build step for the excluded target:
      add_test(NAME ${test_target}
               COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}"
                                        --target ${test_target}
                                        --config $<CONFIGURATION>)
  1. Keep the WILL_FAIL property on the test:
set_tests_properties(${test_target} PROPERTIES WILL_FAIL true)

...or for more robustness, instead check for output that confirms the expected failure mode, if a cross-platform regex exists for this:

set_tests_properties(${test_target} PROPERTIES PASS_REGULAR_EXPRESSION "<cross-platform error regex>")

(Remove the WILL_FAIL prop if the regex approach is used)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, the regex approach is preferred because the test is currently passing without actually testing what it's supposed to:

    Start 19: cudax.cpp17.test.binutils_demangle_no_nvdecode_fail

19: Test command: /usr/bin/ctest "--build-and-test" "/home/coder/cccl/cudax/test/binutils/binutils_demangle_no_nvdecode_fail" "/home/coder/cccl/build/preset-latest/cudax/test/binutils/binutils_demangle_no_nvdecode_fail" "--build-generator" "Ninja" "--test-command" "/usr/bin/ctest"
19: Working Directory: /home/coder/cccl/build/preset-latest/cudax/test/binutils
19: Test timeout computed to be: 1500
19: Internal cmake changing into directory: /home/coder/cccl/build/preset-latest/cudax/test/binutils/binutils_demangle_no_nvdecode_fail
19: ======== CMake output     ======
19: CMake Error: The source directory "/home/coder/cccl/cudax/test/binutils/binutils_demangle_no_nvdecode_fail" does not exist.
19: Specify --help for usage, or press the help button on the CMake GUI.
19: ======== End CMake output ======
19: Error: cmake execution failed
1/1 Test #19: cudax.cpp17.test.binutils_demangle_no_nvdecode_fail ...   Passed    0.01 sec

The following tests passed:
        cudax.cpp17.test.binutils_demangle_no_nvdecode_fail

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #6434. Repeat the steps for creating the executable target like you did for (2), but pass it to cccl_add_xfail_compile_target_test(${target_name} [REGEX <regex>) instead of doing add_test / set_test_properties.

${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()
65 changes: 65 additions & 0 deletions cudax/test/binutils/demangle.pass.cpp
Original file line number Diff line number Diff line change
@@ -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 <cuda/std/cassert>
#include <cuda/std/string_view>
#include <cuda/std/type_traits>

#include <cuda/experimental/binutils.cuh>

#include <stdexcept>
#include <string>

namespace cudax = cuda::experimental;

bool test()
{
// 1. Test signature
static_assert(cuda::std::is_same_v<std::string, decltype(cudax::demangle(cuda::std::string_view{}))>);
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<cls01>::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;
}
37 changes: 37 additions & 0 deletions cudax/test/binutils/demangle_no_nvdecode.fail.cpp
Original file line number Diff line number Diff line change
@@ -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 <cuda/std/cassert>

#include <cuda/experimental/binutils.cuh>

#if _CCCL_HAS_INCLUDE(<nv_decode.h>)
# error "This test requires <nv_decode.h> 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<cls01>::clstmp01_mf01()");

return true;
}

int main(int, char**)
{
NV_IF_TARGET(NV_IS_HOST, (test();))
return 0;
}
20 changes: 20 additions & 0 deletions cudax/test/binutils/demangle_no_nvdecode.pass.cpp
Original file line number Diff line number Diff line change
@@ -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 <cuda/experimental/binutils.cuh>

#if _CCCL_HAS_INCLUDE(<nv_decode.h>)
# error "This test requires <nv_decode.h> not to be findable in PATH."
#endif

int main(int, char**)
{
return 0;
}