[INFRA] Import NVIDIA/CCCL upstream as optimization reference library
CCCL (CUDA C++ Core Libraries) provides: - CUB: device/block/warp-level GPU primitives (reduce, scan, sort, topk) - Thrust: high-level parallel algorithms (transform_reduce, sort, scan) - libcudacxx: CUDA C++ standard library (atomics, barriers, memory) - cudax: experimental features (memory resources, allocators) - Tuning policies: per-SM hardware-specific algorithm parameters Competition optimization vectors mapped to CCCL: - Output TPS (83% weight): warp_reduce, block_reduce, device_topk - Input TPS (14% weight): device_scan, block_load, prefetch - Cache TPS (3% weight): prefix caching strategy patterns - Memory (0.9 util): pooled/cached/buddy allocators Source: https://github.com/NVIDIA/cccl (shallow clone, HEAD only) License: Apache-2.0
This commit is contained in:
120
cccl_upstream/c/parallel.v2/CMakeLists.txt
Normal file
120
cccl_upstream/c/parallel.v2/CMakeLists.txt
Normal file
@@ -0,0 +1,120 @@
|
||||
# 3.30 is required for FindCUDAToolkit's CUDA::nvfatbin / CUDA::nvfatbin_static
|
||||
# imported targets, which the HostJIT linker chain depends on.
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
project(CCCL_C_Parallel_V2 LANGUAGES CUDA CXX C)
|
||||
|
||||
# Bootstrap CCCL cmake helpers when building c/parallel.v2 in isolation
|
||||
# (i.e. not as a subdirectory of the CCCL super-project).
|
||||
if (NOT COMMAND cccl_configure_target)
|
||||
# Repo root is two levels up from this file (c/parallel.v2 -> c -> cccl)
|
||||
get_filename_component(
|
||||
_cccl_root
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../.."
|
||||
ABSOLUTE
|
||||
)
|
||||
set(CCCL_SOURCE_DIR "${_cccl_root}" CACHE PATH "CCCL repo root" FORCE)
|
||||
set(
|
||||
CCCL_BINARY_DIR
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
CACHE PATH
|
||||
"CCCL binary root"
|
||||
FORCE
|
||||
)
|
||||
include("${_cccl_root}/cmake/CCCLUtilities.cmake")
|
||||
include("${_cccl_root}/cmake/CCCLConfigureTarget.cmake")
|
||||
include("${_cccl_root}/cmake/CCCLGetDependencies.cmake")
|
||||
if (NOT TARGET cccl.compiler_interface)
|
||||
add_library(cccl.compiler_interface INTERFACE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(CCCL_C_Parallel_V2_ENABLE_TESTING "Build cccl.c.parallel.v2 tests." OFF)
|
||||
|
||||
set(
|
||||
CCCL_C_PARALLEL_V2_LIBRARY_OUTPUT_DIRECTORY
|
||||
""
|
||||
CACHE PATH
|
||||
"Override output directory for the cccl.c.parallel.v2 library"
|
||||
)
|
||||
mark_as_advanced(CCCL_C_PARALLEL_V2_LIBRARY_OUTPUT_DIRECTORY)
|
||||
|
||||
file(
|
||||
GLOB_RECURSE srcs
|
||||
RELATIVE "${CMAKE_CURRENT_LIST_DIR}"
|
||||
CONFIGURE_DEPENDS
|
||||
"src/*.cu"
|
||||
"src/*.cpp"
|
||||
)
|
||||
# hostjit sources are built as a separate library
|
||||
list(FILTER srcs EXCLUDE REGEX "^src/hostjit/")
|
||||
# Editor lock/temp files
|
||||
list(FILTER srcs EXCLUDE REGEX "/\\.#")
|
||||
|
||||
add_library(cccl.c.parallel.v2 SHARED ${srcs})
|
||||
set_property(TARGET cccl.c.parallel.v2 PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
cccl_configure_target(cccl.c.parallel.v2 DIALECT 20)
|
||||
|
||||
if (CCCL_C_PARALLEL_V2_LIBRARY_OUTPUT_DIRECTORY)
|
||||
set_target_properties(
|
||||
cccl.c.parallel.v2
|
||||
PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CCCL_C_PARALLEL_V2_LIBRARY_OUTPUT_DIRECTORY}"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CCCL_C_PARALLEL_V2_LIBRARY_OUTPUT_DIRECTORY}"
|
||||
RUNTIME_OUTPUT_DIRECTORY "${CCCL_C_PARALLEL_V2_LIBRARY_OUTPUT_DIRECTORY}"
|
||||
)
|
||||
endif()
|
||||
|
||||
cccl_get_cub()
|
||||
cccl_get_cudatoolkit()
|
||||
cccl_get_thrust()
|
||||
|
||||
add_subdirectory(src/hostjit)
|
||||
|
||||
set_target_properties(cccl.c.parallel.v2 PROPERTIES CUDA_RUNTIME_LIBRARY STATIC)
|
||||
target_link_libraries(
|
||||
cccl.c.parallel.v2
|
||||
PRIVATE
|
||||
cccl.compiler_interface
|
||||
CUDA::cudart_static
|
||||
CUDA::cuda_driver
|
||||
CUB::CUB
|
||||
Thrust::Thrust
|
||||
cccl.c.parallel.v2.hostjit_lib # transitively brings in nvJitLink, nvfatbin, nvptxcompiler
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(cccl.c.parallel.v2 PRIVATE Dbghelp)
|
||||
# We are shadowing a lot of variables with the globals like num_items
|
||||
target_compile_options(
|
||||
cccl.c.parallel.v2
|
||||
PRIVATE $<$<COMPILE_LANG_AND_ID:CUDA,NVIDIA>:-Xcompiler=/wd4459>
|
||||
)
|
||||
target_compile_definitions(
|
||||
cccl.c.parallel.v2
|
||||
PRIVATE CATCH_CONFIG_NO_WINDOWS_SEH
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(
|
||||
cccl.c.parallel.v2
|
||||
PUBLIC CCCL_C_EXPERIMENTAL=1
|
||||
PRIVATE #
|
||||
NVRTC_GET_TYPE_NAME=1
|
||||
CUB_DISABLE_CDP=1
|
||||
CUB_DEFINE_RUNTIME_POLICIES
|
||||
)
|
||||
target_compile_options(
|
||||
cccl.c.parallel.v2
|
||||
PRIVATE $<$<COMPILE_LANG_AND_ID:CUDA,NVIDIA>:--extended-lambda>
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
cccl.c.parallel.v2 #
|
||||
PUBLIC "include"
|
||||
PRIVATE "src" "src/hostjit/include"
|
||||
)
|
||||
|
||||
if (CCCL_C_Parallel_V2_ENABLE_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
82
cccl_upstream/c/parallel.v2/include/cccl/c/binary_search.h
Normal file
82
cccl_upstream/c/parallel.v2/include/cccl/c/binary_search.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_binary_search_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler; // hostjit::JITCompiler*
|
||||
#if defined(_WIN32)
|
||||
// Opaque state for serializing CUB's lazy first-call initialization.
|
||||
void* first_call_state;
|
||||
#endif // _WIN32
|
||||
void* binary_search_fn; // int(*)(void*, ull, void*, ull, void*, void*, void*)
|
||||
} cccl_device_binary_search_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_binary_search_build(
|
||||
cccl_device_binary_search_build_result_t* build,
|
||||
cccl_binary_search_mode_t mode,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_iterator_t d_values,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_binary_search_build_ex(
|
||||
cccl_device_binary_search_build_result_t* build,
|
||||
cccl_binary_search_mode_t mode,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_iterator_t d_values,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_binary_search(
|
||||
cccl_device_binary_search_build_result_t build,
|
||||
cccl_iterator_t d_data,
|
||||
uint64_t num_items,
|
||||
cccl_iterator_t d_values,
|
||||
uint64_t num_values,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_binary_search_cleanup(cccl_device_binary_search_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
23
cccl_upstream/c/parallel.v2/include/cccl/c/extern_c.h
Normal file
23
cccl_upstream/c/parallel.v2/include/cccl/c/extern_c.h
Normal file
@@ -0,0 +1,23 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
# define CCCL_C_EXTERN_C_BEGIN extern "C" {
|
||||
# define CCCL_C_EXTERN_C_END }
|
||||
|
||||
#else
|
||||
|
||||
# define CCCL_C_EXTERN_C_BEGIN
|
||||
# define CCCL_C_EXTERN_C_END
|
||||
|
||||
#endif
|
||||
65
cccl_upstream/c/parallel.v2/include/cccl/c/for.h
Normal file
65
cccl_upstream/c/parallel.v2/include/cccl/c/for.h
Normal file
@@ -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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_for_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler; // hostjit::JITCompiler*
|
||||
void* for_fn; // int(*)(void*, unsigned long long, void*)
|
||||
} cccl_device_for_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_for_build(
|
||||
cccl_device_for_build_result_t* build,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_for_build_ex(
|
||||
cccl_device_for_build_result_t* build,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_for(
|
||||
cccl_device_for_build_result_t build, cccl_iterator_t d_data, uint64_t num_items, cccl_op_t op, CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_for_cleanup(cccl_device_for_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
96
cccl_upstream/c/parallel.v2/include/cccl/c/histogram.h
Normal file
96
cccl_upstream/c/parallel.v2/include/cccl/c/histogram.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_histogram_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler;
|
||||
void* histogram_fn;
|
||||
cccl_type_info counter_type;
|
||||
cccl_type_info level_type;
|
||||
cccl_type_info sample_type;
|
||||
int num_channels;
|
||||
int num_active_channels;
|
||||
} cccl_device_histogram_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_histogram_build(
|
||||
cccl_device_histogram_build_result_t* build,
|
||||
int num_channels,
|
||||
int num_active_channels,
|
||||
cccl_iterator_t d_samples,
|
||||
int num_output_levels_val,
|
||||
cccl_iterator_t d_output_histograms,
|
||||
cccl_type_info level_type,
|
||||
int64_t num_rows,
|
||||
int64_t row_stride_samples,
|
||||
bool is_evenly_segmented,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_histogram_build_ex(
|
||||
cccl_device_histogram_build_result_t* build,
|
||||
int num_channels,
|
||||
int num_active_channels,
|
||||
cccl_iterator_t d_samples,
|
||||
int num_output_levels_val,
|
||||
cccl_iterator_t d_output_histograms,
|
||||
cccl_type_info level_type,
|
||||
int64_t num_rows,
|
||||
int64_t row_stride_samples,
|
||||
bool is_evenly_segmented,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_histogram_even(
|
||||
cccl_device_histogram_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_samples,
|
||||
cccl_iterator_t d_output_histograms,
|
||||
cccl_value_t num_output_levels,
|
||||
cccl_value_t lower_level,
|
||||
cccl_value_t upper_level,
|
||||
int64_t num_row_pixels,
|
||||
int64_t num_rows,
|
||||
int64_t row_stride_samples,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_histogram_cleanup(cccl_device_histogram_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
86
cccl_upstream/c/parallel.v2/include/cccl/c/merge_sort.h
Normal file
86
cccl_upstream/c/parallel.v2/include/cccl/c/merge_sort.h
Normal file
@@ -0,0 +1,86 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_merge_sort_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler;
|
||||
void* sort_fn;
|
||||
// 1 if the build compiled SortKeysCopy (no items), 0 if SortPairsCopy. The
|
||||
// run function dispatches on this so the value-vs-pairs decision doesn't
|
||||
// have to be re-derived from the iterator arguments.
|
||||
int keys_only;
|
||||
cccl_type_info key_type;
|
||||
cccl_type_info item_type;
|
||||
} cccl_device_merge_sort_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_merge_sort_build(
|
||||
cccl_device_merge_sort_build_result_t* build,
|
||||
cccl_iterator_t d_in_keys,
|
||||
cccl_iterator_t d_in_items,
|
||||
cccl_iterator_t d_out_keys,
|
||||
cccl_iterator_t d_out_items,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_merge_sort_build_ex(
|
||||
cccl_device_merge_sort_build_result_t* build,
|
||||
cccl_iterator_t d_in_keys,
|
||||
cccl_iterator_t d_in_items,
|
||||
cccl_iterator_t d_out_keys,
|
||||
cccl_iterator_t d_out_items,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_merge_sort(
|
||||
cccl_device_merge_sort_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in_keys,
|
||||
cccl_iterator_t d_in_items,
|
||||
cccl_iterator_t d_out_keys,
|
||||
cccl_iterator_t d_out_items,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_merge_sort_cleanup(cccl_device_merge_sort_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
90
cccl_upstream/c/parallel.v2/include/cccl/c/radix_sort.h
Normal file
90
cccl_upstream/c/parallel.v2/include/cccl/c/radix_sort.h
Normal file
@@ -0,0 +1,90 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_radix_sort_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler; /* Owns both wrappers below — one TU, one cubin */
|
||||
void* sort_fn; /* Wrapper around CUB's copy-overload (selector always 0) */
|
||||
void* sort_fn_overwrite; /* Wrapper around CUB's DoubleBuffer overload; reports selector */
|
||||
cccl_type_info key_type;
|
||||
cccl_type_info value_type;
|
||||
cccl_sort_order_t order;
|
||||
int keys_only; /* 1 if keys-only sort, 0 if key-value pairs */
|
||||
} cccl_device_radix_sort_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_radix_sort_build(
|
||||
cccl_device_radix_sort_build_result_t* build,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t input_keys_it,
|
||||
cccl_iterator_t input_values_it,
|
||||
cccl_op_t decomposer,
|
||||
const char* decomposer_return_type,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_radix_sort_build_ex(
|
||||
cccl_device_radix_sort_build_result_t* build,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t input_keys_it,
|
||||
cccl_iterator_t input_values_it,
|
||||
cccl_op_t decomposer,
|
||||
const char* decomposer_return_type,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_radix_sort(
|
||||
cccl_device_radix_sort_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_op_t decomposer,
|
||||
uint64_t num_items,
|
||||
int begin_bit,
|
||||
int end_bit,
|
||||
bool is_overwrite_okay,
|
||||
int* selector,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_radix_sort_cleanup(cccl_device_radix_sort_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
94
cccl_upstream/c/parallel.v2/include/cccl/c/reduce.h
Normal file
94
cccl_upstream/c/parallel.v2/include/cccl/c/reduce.h
Normal file
@@ -0,0 +1,94 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_reduce_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler; // hostjit::JITCompiler*
|
||||
void* reduce_fn; // int(*)(void*, size_t*, void*, void*, unsigned long long, void*, void*, void*) — trailing void* is
|
||||
// the CUstream
|
||||
uint64_t accumulator_size;
|
||||
cccl_determinism_t determinism;
|
||||
} cccl_device_reduce_build_result_t;
|
||||
|
||||
// TODO return a union of nvtx/cuda/nvrtc errors or a string?
|
||||
CCCL_C_API CUresult cccl_device_reduce_build(
|
||||
cccl_device_reduce_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
cccl_determinism_t determinism,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_reduce_build_ex(
|
||||
cccl_device_reduce_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
cccl_determinism_t determinism,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_reduce(
|
||||
cccl_device_reduce_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_reduce_nondeterministic(
|
||||
cccl_device_reduce_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_reduce_cleanup(cccl_device_reduce_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
127
cccl_upstream/c/parallel.v2/include/cccl/c/scan.h
Normal file
127
cccl_upstream/c/parallel.v2/include/cccl/c/scan.h
Normal file
@@ -0,0 +1,127 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_scan_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler;
|
||||
void* scan_fn;
|
||||
bool force_inclusive;
|
||||
cccl_init_kind_t init_kind;
|
||||
} cccl_device_scan_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_scan_build(
|
||||
cccl_device_scan_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
cccl_type_info init,
|
||||
bool force_inclusive,
|
||||
cccl_init_kind_t init_kind,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_scan_build_ex(
|
||||
cccl_device_scan_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
cccl_type_info init,
|
||||
bool force_inclusive,
|
||||
cccl_init_kind_t init_kind,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_exclusive_scan(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_inclusive_scan(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_exclusive_scan_future_value(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_iterator_t init,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_inclusive_scan_future_value(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_iterator_t init,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_inclusive_scan_no_init(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_scan_cleanup(cccl_device_scan_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
@@ -0,0 +1,84 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_segmented_reduce_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler;
|
||||
void* segmented_reduce_fn;
|
||||
} cccl_device_segmented_reduce_build_result_t;
|
||||
|
||||
// TODO return a union of nvtx/cuda/nvrtc errors or a string?
|
||||
CCCL_C_API CUresult cccl_device_segmented_reduce_build(
|
||||
cccl_device_segmented_reduce_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_segmented_reduce_build_ex(
|
||||
cccl_device_segmented_reduce_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_segmented_reduce(
|
||||
cccl_device_segmented_reduce_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_segments,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_segmented_reduce_cleanup(cccl_device_segmented_reduce_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
91
cccl_upstream/c/parallel.v2/include/cccl/c/segmented_sort.h
Normal file
91
cccl_upstream/c/parallel.v2/include/cccl/c/segmented_sort.h
Normal file
@@ -0,0 +1,91 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_segmented_sort_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler; /* Owns both wrappers below — one TU, one cubin */
|
||||
void* sort_fn; /* Wrapper around CUB's copy-overload (selector always 0) */
|
||||
void* sort_fn_overwrite; /* Wrapper around CUB's DoubleBuffer overload; reports selector */
|
||||
cccl_type_info key_type;
|
||||
cccl_type_info value_type;
|
||||
cccl_sort_order_t order;
|
||||
int keys_only; /* 1 if keys-only sort, 0 if key-value pairs */
|
||||
} cccl_device_segmented_sort_build_result_t;
|
||||
|
||||
// TODO return a union of nvtx/cuda/nvrtc errors or a string?
|
||||
CCCL_C_API CUresult cccl_device_segmented_sort_build(
|
||||
cccl_device_segmented_sort_build_result_t* build,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_segmented_sort_build_ex(
|
||||
cccl_device_segmented_sort_build_result_t* build,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_segmented_sort(
|
||||
cccl_device_segmented_sort_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_values_out,
|
||||
uint64_t num_items,
|
||||
uint64_t num_segments,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
bool is_overwrite_okay,
|
||||
int* selector,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_segmented_sort_cleanup(cccl_device_segmented_sort_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
@@ -0,0 +1,87 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_three_way_partition_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler;
|
||||
void* three_way_partition_fn;
|
||||
} cccl_device_three_way_partition_build_result_t;
|
||||
|
||||
// TODO return a union of nvtx/cuda/nvrtc errors or a string?
|
||||
CCCL_C_API CUresult cccl_device_three_way_partition_build(
|
||||
cccl_device_three_way_partition_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_first_part_out,
|
||||
cccl_iterator_t d_second_part_out,
|
||||
cccl_iterator_t d_unselected_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t select_first_part_op,
|
||||
cccl_op_t select_second_part_op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_three_way_partition_build_ex(
|
||||
cccl_device_three_way_partition_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_first_part_out,
|
||||
cccl_iterator_t d_second_part_out,
|
||||
cccl_iterator_t d_unselected_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t select_first_part_op,
|
||||
cccl_op_t select_second_part_op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_three_way_partition(
|
||||
cccl_device_three_way_partition_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_first_part_out,
|
||||
cccl_iterator_t d_second_part_out,
|
||||
cccl_iterator_t d_unselected_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t select_first_part_op,
|
||||
cccl_op_t select_second_part_op,
|
||||
uint64_t num_items,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_three_way_partition_cleanup(cccl_device_three_way_partition_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
113
cccl_upstream/c/parallel.v2/include/cccl/c/transform.h
Normal file
113
cccl_upstream/c/parallel.v2/include/cccl/c/transform.h
Normal file
@@ -0,0 +1,113 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_transform_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler;
|
||||
#if defined(_WIN32)
|
||||
// Opaque state for serializing CUB's lazy first-call initialization.
|
||||
void* first_call_state;
|
||||
#endif // _WIN32
|
||||
void* transform_fn;
|
||||
} cccl_device_transform_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_unary_transform_build(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_unary_transform_build_ex(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_unary_transform(
|
||||
cccl_device_transform_build_result_t build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_binary_transform_build(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in1,
|
||||
cccl_iterator_t d_in2,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_binary_transform_build_ex(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in1,
|
||||
cccl_iterator_t d_in2,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_binary_transform(
|
||||
cccl_device_transform_build_result_t build,
|
||||
cccl_iterator_t d_in1,
|
||||
cccl_iterator_t d_in2,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_transform_cleanup(cccl_device_transform_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
188
cccl_upstream/c/parallel.v2/include/cccl/c/types.h
Normal file
188
cccl_upstream/c/parallel.v2/include/cccl/c/types.h
Normal file
@@ -0,0 +1,188 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define CCCL_C_API __declspec(dllexport)
|
||||
#else // ^^^ _WIN32 ^^^ / vvv !_WIN32 vvv
|
||||
# define CCCL_C_API __attribute__((__visibility__("default")))
|
||||
#endif // !_WIN32
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef enum cccl_type_enum
|
||||
{
|
||||
CCCL_INT8 = 0,
|
||||
CCCL_INT16 = 1,
|
||||
CCCL_INT32 = 2,
|
||||
CCCL_INT64 = 3,
|
||||
CCCL_UINT8 = 4,
|
||||
CCCL_UINT16 = 5,
|
||||
CCCL_UINT32 = 6,
|
||||
CCCL_UINT64 = 7,
|
||||
CCCL_FLOAT16 = 8, // This may be unsupported if _CCCL_HAS_NVFP16() is false but we can't include the header to check
|
||||
// that here
|
||||
CCCL_FLOAT32 = 9,
|
||||
CCCL_FLOAT64 = 10,
|
||||
CCCL_STORAGE = 11,
|
||||
CCCL_BOOLEAN = 12,
|
||||
} cccl_type_enum;
|
||||
|
||||
typedef struct cccl_type_info
|
||||
{
|
||||
size_t size;
|
||||
size_t alignment;
|
||||
cccl_type_enum type;
|
||||
} cccl_type_info;
|
||||
|
||||
typedef enum cccl_op_kind_t
|
||||
{
|
||||
// Arbitrary semantics, without state.
|
||||
CCCL_STATELESS = 0,
|
||||
// Arbitrary semantics, with state.
|
||||
CCCL_STATEFUL = 1,
|
||||
// Well-known semantics, required to be stateless.
|
||||
// Equivalent to corresponding function objects in C++'s <functional>.
|
||||
// If the types involved are primitive, only the kind field is necessary.
|
||||
// Otherwise, the cccl_op_t object must also contain the rest of the fields,
|
||||
// as appropriate.
|
||||
CCCL_PLUS = 2,
|
||||
CCCL_MINUS = 3,
|
||||
CCCL_MULTIPLIES = 4,
|
||||
CCCL_DIVIDES = 5,
|
||||
CCCL_MODULUS = 6,
|
||||
CCCL_EQUAL_TO = 7,
|
||||
CCCL_NOT_EQUAL_TO = 8,
|
||||
CCCL_GREATER = 9,
|
||||
CCCL_LESS = 10,
|
||||
CCCL_GREATER_EQUAL = 11,
|
||||
CCCL_LESS_EQUAL = 12,
|
||||
CCCL_LOGICAL_AND = 13,
|
||||
CCCL_LOGICAL_OR = 14,
|
||||
CCCL_LOGICAL_NOT = 15,
|
||||
CCCL_BIT_AND = 16,
|
||||
CCCL_BIT_OR = 17,
|
||||
CCCL_BIT_XOR = 18,
|
||||
CCCL_BIT_NOT = 19,
|
||||
CCCL_IDENTITY = 20,
|
||||
CCCL_NEGATE = 21,
|
||||
CCCL_MINIMUM = 22,
|
||||
CCCL_MAXIMUM = 23,
|
||||
} cccl_op_kind_t;
|
||||
|
||||
typedef enum cccl_op_code_type
|
||||
{
|
||||
CCCL_OP_LTOIR = 0, // Pre-compiled LTO-IR (escape hatch for callers with existing nvcc -dlto artifacts).
|
||||
// LTO-IR is a binary container passed to nvJitLink at the PTX level — the LLVM optimizer
|
||||
// never sees it, so the operator cannot be inlined into the CUB kernel and pays a real
|
||||
// CALL on every iteration. CCCL_OP_LLVM_IR feeds LLVM's bitcode linker instead, which
|
||||
// merges the operator into the CUB module before PTX codegen and enables full inlining.
|
||||
// Prefer CCCL_OP_LLVM_IR or CCCL_OP_CPP_SOURCE for any new code.
|
||||
CCCL_OP_CPP_SOURCE = 1, // C++ source code (compiled to LLVM bitcode by hostjit's Clang).
|
||||
CCCL_OP_LLVM_IR = 2 // LLVM bitcode (recommended) — merges into the CUB module before PTX gen, so inlines.
|
||||
} cccl_op_code_type;
|
||||
|
||||
typedef struct cccl_op_t
|
||||
{
|
||||
cccl_op_kind_t type;
|
||||
const char* name;
|
||||
const char* code;
|
||||
size_t code_size;
|
||||
cccl_op_code_type code_type;
|
||||
size_t size;
|
||||
size_t alignment;
|
||||
void* state;
|
||||
const char** extra_ltoirs;
|
||||
size_t* extra_ltoir_sizes;
|
||||
size_t num_extra_ltoirs;
|
||||
cccl_op_code_type* extra_code_types;
|
||||
} cccl_op_t;
|
||||
|
||||
typedef struct cccl_build_config
|
||||
{
|
||||
const char** extra_compile_flags; // e.g., {"-DENABLE_FAST_MATH", "-O3"}
|
||||
size_t num_extra_compile_flags;
|
||||
const char** extra_include_dirs; // e.g., {"/path/to/my/headers"}
|
||||
size_t num_extra_include_dirs;
|
||||
int enable_pch; // Cache precompiled headers on disk to speed up repeated builds
|
||||
int verbose; // Log PCH generation/usage and compiler args to build diagnostics
|
||||
} cccl_build_config;
|
||||
|
||||
typedef enum cccl_iterator_kind_t
|
||||
{
|
||||
CCCL_POINTER = 0,
|
||||
CCCL_ITERATOR = 1,
|
||||
} cccl_iterator_kind_t;
|
||||
|
||||
typedef struct cccl_value_t
|
||||
{
|
||||
cccl_type_info type;
|
||||
void* state;
|
||||
} cccl_value_t;
|
||||
|
||||
typedef union
|
||||
{
|
||||
int64_t signed_offset;
|
||||
uint64_t unsigned_offset;
|
||||
} cccl_increment_t;
|
||||
|
||||
typedef void (*cccl_host_op_fn_ptr_t)(void*, cccl_increment_t);
|
||||
|
||||
typedef struct cccl_iterator_t
|
||||
{
|
||||
size_t size;
|
||||
size_t alignment;
|
||||
cccl_iterator_kind_t type;
|
||||
cccl_op_t advance;
|
||||
cccl_op_t dereference;
|
||||
cccl_type_info value_type;
|
||||
void* state;
|
||||
cccl_host_op_fn_ptr_t host_advance;
|
||||
} cccl_iterator_t;
|
||||
|
||||
typedef enum cccl_sort_order_t
|
||||
{
|
||||
CCCL_ASCENDING = 0,
|
||||
CCCL_DESCENDING = 1,
|
||||
} cccl_sort_order_t;
|
||||
|
||||
typedef enum cccl_init_kind_t
|
||||
{
|
||||
CCCL_VALUE_INIT = 0,
|
||||
CCCL_FUTURE_VALUE_INIT = 1,
|
||||
CCCL_NO_INIT = 2,
|
||||
} cccl_init_kind_t;
|
||||
|
||||
typedef enum cccl_determinism_t
|
||||
{
|
||||
CCCL_NOT_GUARANTEED = 0,
|
||||
CCCL_RUN_TO_RUN = 1,
|
||||
CCCL_GPU_TO_GPU = 2,
|
||||
} cccl_determinism_t;
|
||||
|
||||
typedef enum cccl_binary_search_mode_t
|
||||
{
|
||||
CCCL_BINARY_SEARCH_LOWER_BOUND = 0,
|
||||
CCCL_BINARY_SEARCH_UPPER_BOUND = 1,
|
||||
} cccl_binary_search_mode_t;
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
83
cccl_upstream/c/parallel.v2/include/cccl/c/unique_by_key.h
Normal file
83
cccl_upstream/c/parallel.v2/include/cccl/c/unique_by_key.h
Normal file
@@ -0,0 +1,83 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
// NOLINTBEGIN(modernize-use-using)
|
||||
|
||||
#ifndef CCCL_C_EXPERIMENTAL
|
||||
# error "C exposure is experimental and subject to change. Define CCCL_C_EXPERIMENTAL to acknowledge this notice."
|
||||
#endif // !CCCL_C_EXPERIMENTAL
|
||||
|
||||
#include <cuda.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cccl/c/extern_c.h>
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
CCCL_C_EXTERN_C_BEGIN
|
||||
|
||||
typedef struct cccl_device_unique_by_key_build_result_t
|
||||
{
|
||||
int cc;
|
||||
void* payload;
|
||||
size_t payload_size;
|
||||
void* jit_compiler;
|
||||
void* unique_by_key_fn;
|
||||
} cccl_device_unique_by_key_build_result_t;
|
||||
|
||||
CCCL_C_API CUresult cccl_device_unique_by_key_build(
|
||||
cccl_device_unique_by_key_build_result_t* build,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path);
|
||||
|
||||
// Extended version with build configuration
|
||||
CCCL_C_API CUresult cccl_device_unique_by_key_build_ex(
|
||||
cccl_device_unique_by_key_build_result_t* build,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_unique_by_key(
|
||||
cccl_device_unique_by_key_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t op,
|
||||
uint64_t num_items,
|
||||
CUstream stream);
|
||||
|
||||
CCCL_C_API CUresult cccl_device_unique_by_key_cleanup(cccl_device_unique_by_key_build_result_t* bld_ptr);
|
||||
|
||||
CCCL_C_EXTERN_C_END
|
||||
// NOLINTEND(modernize-use-using)
|
||||
180
cccl_upstream/c/parallel.v2/src/binary_search.cu
Normal file
180
cccl_upstream/c/parallel.v2/src/binary_search.cu
Normal file
@@ -0,0 +1,180 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cuda/std/version>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <cccl/c/binary_search.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
#include <util/first_call_gate.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// (d_in_0, num_items, d_in_1, num_values, d_out_0, op_0_state, stream)
|
||||
using binary_search_fn_t = int (*)(void*, unsigned long long, void*, unsigned long long, void*, void*, void*);
|
||||
|
||||
CUresult cccl_device_binary_search_build_ex(
|
||||
cccl_device_binary_search_build_result_t* build_ptr,
|
||||
cccl_binary_search_mode_t mode,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_iterator_t d_values,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
#if CCCL_OS(WINDOWS)
|
||||
build_ptr->first_call_state = nullptr;
|
||||
#endif
|
||||
const std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
const std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* const cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* const ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
#if CCCL_OS(WINDOWS)
|
||||
auto first_call_state = std::make_unique<cccl::detail::first_call_gate>();
|
||||
#endif
|
||||
|
||||
const char* find_fn =
|
||||
(mode == CCCL_BINARY_SEARCH_LOWER_BOUND) ? "cub::DeviceFind::LowerBound" : "cub::DeviceFind::UpperBound";
|
||||
|
||||
// env_stream uses the env-based DeviceFind overload so CUB manages its own
|
||||
// temp storage via the env's memory_resource — no caller-managed buffer.
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_find.cuh")
|
||||
.run(find_fn)
|
||||
.name("cccl_jit_binary_search")
|
||||
.with(in(d_data), num_haystack, in(d_values), num_needles, out(d_out), cmp(op), env_stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
#if CCCL_OS(WINDOWS)
|
||||
build_ptr->first_call_state = first_call_state.release();
|
||||
#endif
|
||||
build_ptr->binary_search_fn = result.fn_ptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_binary_search_build_ex(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_binary_search(
|
||||
cccl_device_binary_search_build_result_t build,
|
||||
cccl_iterator_t d_data,
|
||||
uint64_t num_items,
|
||||
cccl_iterator_t d_values,
|
||||
uint64_t num_values,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.binary_search_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
const auto fn = reinterpret_cast<binary_search_fn_t>(build.binary_search_fn);
|
||||
|
||||
#if CCCL_OS(WINDOWS)
|
||||
if (!build.first_call_state)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
const auto invoke = [&] {
|
||||
return fn(
|
||||
d_data.state, num_items, d_values.state, num_values, d_out.state, op.state, reinterpret_cast<void*>(stream));
|
||||
};
|
||||
// Empty calls return before DeviceTransform initializes its static launch configuration,
|
||||
// so they must not complete the first-call gate.
|
||||
const int status =
|
||||
num_values == 0 ? invoke() : static_cast<cccl::detail::first_call_gate*>(build.first_call_state)->invoke(invoke);
|
||||
#else
|
||||
const int status =
|
||||
fn(d_data.state, num_items, d_values.state, num_values, d_out.state, op.state, reinterpret_cast<void*>(stream));
|
||||
#endif
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_binary_search(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_binary_search_build(
|
||||
cccl_device_binary_search_build_result_t* build,
|
||||
cccl_binary_search_mode_t mode,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_iterator_t d_values,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_binary_search_build_ex(
|
||||
build,
|
||||
mode,
|
||||
d_data,
|
||||
d_values,
|
||||
d_out,
|
||||
op,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CUresult cccl_device_binary_search_cleanup(cccl_device_binary_search_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
#if CCCL_OS(WINDOWS)
|
||||
delete static_cast<cccl::detail::first_call_gate*>(build_ptr->first_call_state);
|
||||
build_ptr->first_call_state = nullptr;
|
||||
#endif
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->binary_search_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_binary_search_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
117
cccl_upstream/c/parallel.v2/src/for.cu
Normal file
117
cccl_upstream/c/parallel.v2/src/for.cu
Normal file
@@ -0,0 +1,117 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <cccl/c/for.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// d_in_0, num_items, op_0_state, stream
|
||||
using for_fn_t = int (*)(void*, unsigned long long, void*, void*);
|
||||
|
||||
CUresult cccl_device_for_build_ex(
|
||||
cccl_device_for_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_for.cuh")
|
||||
.run("cub::DeviceFor::ForEachN")
|
||||
.name("cccl_jit_for")
|
||||
.with(in(d_data), num_items, for_each_op(op), stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->for_fn = result.fn_ptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_for_build_ex(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_for(
|
||||
cccl_device_for_build_result_t build, cccl_iterator_t d_data, uint64_t num_items, cccl_op_t op, CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.for_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
auto fn = reinterpret_cast<for_fn_t>(build.for_fn);
|
||||
|
||||
const int status = fn(d_data.state, num_items, op.state, reinterpret_cast<void*>(stream));
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_for(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_for_build(
|
||||
cccl_device_for_build_result_t* build,
|
||||
cccl_iterator_t d_data,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_for_build_ex(
|
||||
build, d_data, op, cc_major, cc_minor, cub_path, thrust_path, libcudacxx_path, ctk_path, nullptr);
|
||||
}
|
||||
|
||||
CUresult cccl_device_for_cleanup(cccl_device_for_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->for_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_for_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
209
cccl_upstream/c/parallel.v2/src/histogram.cu
Normal file
209
cccl_upstream/c/parallel.v2/src/histogram.cu
Normal file
@@ -0,0 +1,209 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <cccl/c/histogram.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// JIT wrapper produced by CubCall:
|
||||
// fn(temp, temp_bytes,
|
||||
// d_samples, // input iterator state
|
||||
// d_histogram, // output pointer (counter_t*)
|
||||
// &num_levels, // int (host pointer)
|
||||
// &lower_level, &upper_level, // level_t (host pointer)
|
||||
// &num_row_pixels, // long long (host pointer)
|
||||
// &num_rows, // long long (host pointer)
|
||||
// &row_stride_bytes, // size_t (host-precomputed: row_stride_samples * sizeof(sample_t))
|
||||
// stream)
|
||||
using histogram_fn_t = int (*)(void*, size_t*, void*, void*, void*, void*, void*, void*, void*, void*, void*);
|
||||
|
||||
static constexpr cccl_type_info k_int_type{sizeof(int), alignof(int), CCCL_INT32};
|
||||
static constexpr cccl_type_info k_int64_type{sizeof(long long), alignof(long long), CCCL_INT64};
|
||||
static constexpr cccl_type_info k_size_type{sizeof(unsigned long long), alignof(unsigned long long), CCCL_UINT64};
|
||||
|
||||
CUresult cccl_device_histogram_build_ex(
|
||||
cccl_device_histogram_build_result_t* build_ptr,
|
||||
int num_channels,
|
||||
int num_active_channels,
|
||||
cccl_iterator_t d_samples,
|
||||
int /*num_output_levels_val*/,
|
||||
cccl_iterator_t d_output_histograms,
|
||||
cccl_type_info level_type,
|
||||
int64_t /*num_rows*/,
|
||||
int64_t /*row_stride_samples*/,
|
||||
bool /*is_evenly_segmented*/,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (num_channels != 1 || num_active_channels != 1)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"\nERROR in cccl_device_histogram_build(): only num_channels=1, num_active_channels=1 is "
|
||||
"supported in the HostJIT path.\n");
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
// level_t comes from the build-time type info. CUB infers
|
||||
// sample_t / counter_t from the iterator and output pointer respectively.
|
||||
CubCallResult result =
|
||||
CubCall::from("cub/device/device_histogram.cuh")
|
||||
.run("cub::DeviceHistogram::HistogramEven")
|
||||
.name("cccl_jit_histogram_even")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(d_samples),
|
||||
out(d_output_histograms),
|
||||
typed_scalar(k_int_type, "num_levels"),
|
||||
typed_scalar(level_type, "lower_level"),
|
||||
typed_scalar(level_type, "upper_level"),
|
||||
typed_scalar(k_int64_type, "num_row_pixels"),
|
||||
typed_scalar(k_int64_type, "num_rows"),
|
||||
typed_scalar(k_size_type, "row_stride_bytes"),
|
||||
stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->histogram_fn = result.fn_ptr;
|
||||
build_ptr->counter_type = d_output_histograms.value_type;
|
||||
build_ptr->level_type = level_type;
|
||||
build_ptr->sample_type = d_samples.value_type;
|
||||
build_ptr->num_channels = num_channels;
|
||||
build_ptr->num_active_channels = num_active_channels;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_histogram_build(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_histogram_build(
|
||||
cccl_device_histogram_build_result_t* build,
|
||||
int num_channels,
|
||||
int num_active_channels,
|
||||
cccl_iterator_t d_samples,
|
||||
int num_output_levels_val,
|
||||
cccl_iterator_t d_output_histograms,
|
||||
cccl_type_info level_type,
|
||||
int64_t num_rows,
|
||||
int64_t row_stride_samples,
|
||||
bool is_evenly_segmented,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_histogram_build_ex(
|
||||
build,
|
||||
num_channels,
|
||||
num_active_channels,
|
||||
d_samples,
|
||||
num_output_levels_val,
|
||||
d_output_histograms,
|
||||
level_type,
|
||||
num_rows,
|
||||
row_stride_samples,
|
||||
is_evenly_segmented,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CUresult cccl_device_histogram_even(
|
||||
cccl_device_histogram_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_samples,
|
||||
cccl_iterator_t d_output_histograms,
|
||||
cccl_value_t num_output_levels,
|
||||
cccl_value_t lower_level,
|
||||
cccl_value_t upper_level,
|
||||
int64_t num_row_pixels,
|
||||
int64_t num_rows,
|
||||
int64_t row_stride_samples,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.histogram_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
// CUB takes row_stride_bytes (not samples). Pre-compute on the host so the
|
||||
// JIT wrapper doesn't need a sizeof(sample_t) computation.
|
||||
long long num_row_pixels_ll = static_cast<long long>(num_row_pixels);
|
||||
long long num_rows_ll = static_cast<long long>(num_rows);
|
||||
size_t row_stride_bytes = static_cast<size_t>(row_stride_samples) * build.sample_type.size;
|
||||
|
||||
auto fn = reinterpret_cast<histogram_fn_t>(build.histogram_fn);
|
||||
const int status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_samples.state,
|
||||
d_output_histograms.state,
|
||||
num_output_levels.state,
|
||||
lower_level.state,
|
||||
upper_level.state,
|
||||
&num_row_pixels_ll,
|
||||
&num_rows_ll,
|
||||
&row_stride_bytes,
|
||||
reinterpret_cast<void*>(stream));
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_histogram_even(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_histogram_cleanup(cccl_device_histogram_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->histogram_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_histogram_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
283
cccl_upstream/c/parallel.v2/src/hostjit/CMakeLists.txt
Normal file
283
cccl_upstream/c/parallel.v2/src/hostjit/CMakeLists.txt
Normal file
@@ -0,0 +1,283 @@
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# LLVM/Clang/LLD — fetched via CPM as static libraries
|
||||
# --------------------------------------------------------------------------
|
||||
# CPM.cmake is at the cccl repo root: cccl/cmake/CPM.cmake
|
||||
# From c/parallel.v2/src/hostjit/ that's ../../../../cmake/CPM.cmake
|
||||
set(_cccl_cmake_dir "${CMAKE_CURRENT_SOURCE_DIR}/../../../../cmake")
|
||||
if (EXISTS "${_cccl_cmake_dir}/CPM.cmake")
|
||||
include("${_cccl_cmake_dir}/CPM.cmake")
|
||||
else()
|
||||
message(FATAL_ERROR "CPM.cmake not found at ${_cccl_cmake_dir}/CPM.cmake")
|
||||
endif()
|
||||
|
||||
if (MSVC AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"hostjit does not support Debug builds on Windows. "
|
||||
"The statically-linked LLVM Debug build is too large and causes stack "
|
||||
"overflows at runtime. Use MinSizeRel, Release, or RelWithDebInfo instead."
|
||||
)
|
||||
endif()
|
||||
|
||||
set(HOSTJIT_LLVM_VERSION "llvmorg-22.1.1" CACHE STRING "LLVM git tag to fetch")
|
||||
|
||||
# List options must be set before CPMAddPackage
|
||||
set(LLVM_ENABLE_PROJECTS "clang;lld" CACHE STRING "" FORCE)
|
||||
set(LLVM_TARGETS_TO_BUILD "X86;NVPTX" CACHE STRING "" FORCE)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME llvm_project
|
||||
GIT_REPOSITORY https://github.com/llvm/llvm-project.git
|
||||
GIT_TAG ${HOSTJIT_LLVM_VERSION}
|
||||
GIT_SHALLOW ON
|
||||
SOURCE_SUBDIR llvm
|
||||
EXCLUDE_FROM_ALL YES
|
||||
OPTIONS
|
||||
"LLVM_BUILD_LLVM_C_DYLIB OFF"
|
||||
"LLVM_BUILD_TOOLS OFF"
|
||||
"LLVM_BUILD_UTILS OFF"
|
||||
"LLVM_BUILD_RUNTIME OFF"
|
||||
"LLVM_BUILD_RUNTIMES OFF"
|
||||
"LLVM_INCLUDE_BENCHMARKS OFF"
|
||||
"LLVM_INCLUDE_DOCS OFF"
|
||||
"LLVM_INCLUDE_EXAMPLES OFF"
|
||||
"LLVM_INCLUDE_RUNTIMES OFF"
|
||||
"LLVM_INCLUDE_TESTS OFF"
|
||||
"LLVM_INCLUDE_TOOLS ON"
|
||||
"LLVM_INCLUDE_UTILS OFF"
|
||||
"LLVM_ENABLE_ZLIB OFF"
|
||||
"LLVM_ENABLE_ZSTD OFF"
|
||||
"LLVM_ENABLE_TERMINFO OFF"
|
||||
"LLVM_ENABLE_BINDINGS OFF"
|
||||
"CLANG_BUILD_TOOLS OFF"
|
||||
"CLANG_ENABLE_ARCMT OFF"
|
||||
"CLANG_ENABLE_STATIC_ANALYZER OFF"
|
||||
)
|
||||
|
||||
# Ensure the clang resource directory exists
|
||||
file(
|
||||
MAKE_DIRECTORY "${llvm_project_BINARY_DIR}/lib/clang/${LLVM_VERSION_MAJOR}"
|
||||
)
|
||||
|
||||
# Find CUDA toolkit (may already be found by parent)
|
||||
if (NOT CUDAToolkit_FOUND)
|
||||
find_package(CUDAToolkit)
|
||||
endif()
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# hostjit library
|
||||
# --------------------------------------------------------------------------
|
||||
add_library(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
compiler.cpp
|
||||
config.cpp
|
||||
loader.cpp
|
||||
jit_compiler.cpp
|
||||
codegen/types.cpp
|
||||
codegen/iterators.cpp
|
||||
codegen/operators.cpp
|
||||
codegen/bitcode.cpp
|
||||
codegen/cub_call.cpp
|
||||
)
|
||||
|
||||
# CCCL_SOURCE_DIR points to the cccl repo root
|
||||
# From c/parallel.v2/src/hostjit -> c/parallel.v2/src -> c/parallel.v2 -> c -> cccl
|
||||
cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH _src_dir) # c/parallel.v2/src
|
||||
cmake_path(GET _src_dir PARENT_PATH _c_parallel_dir) # c/parallel.v2
|
||||
cmake_path(GET _c_parallel_dir PARENT_PATH _c_dir) # c
|
||||
cmake_path(GET _c_dir PARENT_PATH _cccl_root) # cccl
|
||||
|
||||
target_include_directories(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${_c_parallel_dir}/include
|
||||
${llvm_project_SOURCE_DIR}/llvm/include
|
||||
${llvm_project_BINARY_DIR}/include
|
||||
${llvm_project_SOURCE_DIR}/clang/include
|
||||
${llvm_project_BINARY_DIR}/tools/clang/include
|
||||
${llvm_project_SOURCE_DIR}/lld/include
|
||||
${llvm_project_BINARY_DIR}/tools/lld/include
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PRIVATE
|
||||
CCCL_C_EXPERIMENTAL=1
|
||||
CCCL_SOURCE_DIR="${_cccl_root}"
|
||||
CLANG_RESOURCE_DIR="${llvm_project_BINARY_DIR}/lib/clang/${LLVM_VERSION_MAJOR}"
|
||||
CLANG_HEADERS_DIR="${llvm_project_SOURCE_DIR}/clang/lib/Headers"
|
||||
HOSTJIT_INCLUDE_DIR="${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
if (CUDAToolkit_FOUND)
|
||||
target_include_directories(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PUBLIC ${CUDAToolkit_INCLUDE_DIRS}
|
||||
)
|
||||
cmake_path(GET CUDAToolkit_BIN_DIR PARENT_PATH CUDA_TOOLKIT_ROOT_FROM_CMAKE)
|
||||
target_compile_definitions(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PRIVATE
|
||||
CUDA_TOOLKIT_PATH="${CUDA_TOOLKIT_ROOT_FROM_CMAKE}"
|
||||
CUDA_SDK_VERSION="${CUDAToolkit_VERSION_MAJOR}.0"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Link against LLVM/Clang/LLD
|
||||
target_link_libraries(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PUBLIC
|
||||
# LLVM
|
||||
LLVMCore
|
||||
LLVMSupport
|
||||
LLVMIRReader
|
||||
LLVMMC
|
||||
LLVMObject
|
||||
LLVMX86CodeGen
|
||||
LLVMX86AsmParser
|
||||
LLVMX86Desc
|
||||
LLVMX86Info
|
||||
LLVMNVPTXCodeGen
|
||||
LLVMNVPTXDesc
|
||||
LLVMNVPTXInfo
|
||||
LLVMLinker
|
||||
LLVMPasses
|
||||
# Clang
|
||||
clangAST
|
||||
clangBasic
|
||||
clangCodeGen
|
||||
clangDriver
|
||||
clangFrontend
|
||||
clangFrontendTool
|
||||
clangLex
|
||||
clangParse
|
||||
clangSema
|
||||
clangEdit
|
||||
clangAnalysis
|
||||
clangRewrite
|
||||
clangSerialization
|
||||
# LLD
|
||||
$<IF:$<PLATFORM_ID:Windows>,lldCOFF,lldELF>
|
||||
lldCommon
|
||||
)
|
||||
|
||||
if (NOT WIN32)
|
||||
target_link_libraries(cccl.c.parallel.v2.hostjit_lib PUBLIC dl)
|
||||
endif()
|
||||
|
||||
if (CUDAToolkit_FOUND)
|
||||
target_link_libraries(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PUBLIC CUDA::cuda_driver CUDA::cudart
|
||||
)
|
||||
if (WIN32)
|
||||
# On Windows, static CUDA libs are built with /MT which conflicts with
|
||||
# the project's dynamic CRT (/MD). Use dynamic variants instead.
|
||||
target_link_libraries(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PUBLIC CUDA::nvJitLink CUDA::nvfatbin
|
||||
)
|
||||
else()
|
||||
# Prefer static CUDA libs on Linux for self-contained binaries. If the
|
||||
# toolchain (e.g. lite/pip CUDA installs or some Docker images) only ships
|
||||
# the dynamic variants, fall back to those rather than failing configure.
|
||||
foreach (_cudalib nvJitLink nvptxcompiler nvfatbin)
|
||||
if (TARGET "CUDA::${_cudalib}_static")
|
||||
target_link_libraries(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PUBLIC "CUDA::${_cudalib}_static"
|
||||
)
|
||||
elseif (TARGET "CUDA::${_cudalib}")
|
||||
target_link_libraries(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PUBLIC "CUDA::${_cudalib}"
|
||||
)
|
||||
else()
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"hostjit needs CUDA::${_cudalib}[_static] but neither variant was "
|
||||
"found by FindCUDAToolkit. Install the full CUDA toolkit "
|
||||
"(libnvjitlink-dev / libnvfatbin-dev or equivalent)."
|
||||
)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT MSVC)
|
||||
target_compile_options(cccl.c.parallel.v2.hostjit_lib PRIVATE -fno-rtti)
|
||||
endif()
|
||||
|
||||
set_target_properties(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PROPERTIES CXX_STANDARD 20 POSITION_INDEPENDENT_CODE ON
|
||||
)
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Install clang headers into wheel (for self-sufficient packaging)
|
||||
# --------------------------------------------------------------------------
|
||||
# Clang CUDA headers we still use from the LLVM source tree.
|
||||
# We DON'T install device_functions, math, or libdevice_declares — our local
|
||||
# copies in cuda_minimal/ replace them.
|
||||
set(
|
||||
_clang_cuda_headers_needed
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__clang_cuda_math_forward_declares.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__clang_cuda_builtin_vars.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__clang_cuda_cmath.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__clang_cuda_intrinsics.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__clang_cuda_complex_builtins.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__clang_cuda_texture_intrinsics.h"
|
||||
)
|
||||
install(
|
||||
FILES ${_clang_cuda_headers_needed}
|
||||
DESTINATION "cuda/cccl/headers/clang"
|
||||
)
|
||||
|
||||
# Clang builtin C headers needed by our stubs and CUDA toolkit headers.
|
||||
file(
|
||||
GLOB _clang_stddef_headers
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__stddef_*.h"
|
||||
)
|
||||
set(
|
||||
_clang_c_headers
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/limits.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/stddef.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/stdint.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__stddef_header_macro.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/float.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/__float_header_macro.h"
|
||||
"${llvm_project_SOURCE_DIR}/clang/lib/Headers/inttypes.h"
|
||||
${_clang_stddef_headers}
|
||||
)
|
||||
install(FILES ${_clang_c_headers} DESTINATION "cuda/cccl/headers/clang")
|
||||
|
||||
# Hostjit's minimal CUDA runtime headers (replacements for upstream clang headers)
|
||||
set(
|
||||
_hostjit_cuda_minimal_dir
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/hostjit/cuda_minimal"
|
||||
)
|
||||
file(GLOB _hostjit_cuda_minimal_headers "${_hostjit_cuda_minimal_dir}/*.h")
|
||||
install(
|
||||
FILES ${_hostjit_cuda_minimal_headers}
|
||||
DESTINATION "cuda/cccl/headers/hostjit/cuda_minimal"
|
||||
)
|
||||
|
||||
# Hostjit's stub headers (minimal C++ standard library stubs for device compilation)
|
||||
# Use GLOB_RECURSE + DIRECTORY so subdirectory overrides (e.g. cuda/std/__cstdlib/)
|
||||
# are also installed alongside the top-level stubs.
|
||||
install(
|
||||
DIRECTORY "${_hostjit_cuda_minimal_dir}/stubs/"
|
||||
DESTINATION "cuda/cccl/headers/hostjit/cuda_minimal/stubs"
|
||||
)
|
||||
|
||||
# On Windows with multi-config generators (Visual Studio), exclude hostjit
|
||||
# targets from Debug builds — the LLVM Debug build causes stack overflows.
|
||||
if (MSVC)
|
||||
set_target_properties(
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_DEBUG TRUE
|
||||
)
|
||||
endif()
|
||||
237
cccl_upstream/c/parallel.v2/src/hostjit/codegen/bitcode.cpp
Normal file
237
cccl_upstream/c/parallel.v2/src/hostjit/codegen/bitcode.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
#include <hostjit/codegen/bitcode.hpp>
|
||||
#include <hostjit/compiler.hpp>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool write_file(const char* data, size_t size, const std::string& path)
|
||||
{
|
||||
std::ofstream f(path, std::ios::binary);
|
||||
if (!f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
f.write(data, static_cast<std::streamsize>(size));
|
||||
return f.good();
|
||||
}
|
||||
|
||||
std::string make_temp_path(const std::string& prefix, uintptr_t id, const std::string& ext)
|
||||
{
|
||||
return (std::filesystem::temp_directory_path() / (prefix + std::to_string(id) + ext)).string();
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
BitcodeCollector::BitcodeCollector(CompilerConfig& config, uintptr_t unique_id)
|
||||
: config_(config)
|
||||
, unique_id_(unique_id)
|
||||
{}
|
||||
|
||||
bool BitcodeCollector::is_bitcode_op(cccl_op_t op)
|
||||
{
|
||||
return (op.code_type == CCCL_OP_LLVM_IR || op.code_type == CCCL_OP_LTOIR) && op.code != nullptr && op.code_size > 0;
|
||||
}
|
||||
|
||||
void BitcodeCollector::add_raw_bitcode(const char* data, size_t size, const std::string& name)
|
||||
{
|
||||
if (!data || size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Dedup by content hash: identical bitcode bytes define identical symbols
|
||||
// (e.g. two PointerIterator<int>s sharing the same advance LTOIR). Adding
|
||||
// both would make nvJitLink fail with "symbol multiply defined".
|
||||
const auto hash = std::hash<std::string_view>{}(std::string_view(data, size));
|
||||
if (!added_content_hashes_.insert(hash).second)
|
||||
{
|
||||
return; // exact same bytes already added
|
||||
}
|
||||
|
||||
// Magic-byte routing: LLVM bitcode starts with "BC" (0x42 0x43) and goes to
|
||||
// LLVM's bitcode linker so it can be inlined into the CUB module at the IR
|
||||
// level. Anything else is treated as LTO-IR (binary fatbin container) and
|
||||
// fed to nvJitLink. CPP_SOURCE never reaches here: main ops are dispatched
|
||||
// by code_type in add_op_code, and per-extra C++ source is dispatched by
|
||||
// extra_code_types[i] in the extras loop below — both call compile_and_add
|
||||
// directly.
|
||||
const bool is_llvm_bitcode =
|
||||
size >= 2 && static_cast<unsigned char>(data[0]) == 0x42 && static_cast<unsigned char>(data[1]) == 0x43;
|
||||
|
||||
const char* ext = is_llvm_bitcode ? ".bc" : ".ltoir";
|
||||
auto path = make_temp_path("cccl_" + name + "_", unique_id_, ext);
|
||||
if (!write_file(data, size, path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (is_llvm_bitcode)
|
||||
{
|
||||
config_.device_bitcode_files.push_back(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
config_.device_ltoir_files.push_back(path);
|
||||
}
|
||||
temp_paths_.push_back(path);
|
||||
}
|
||||
|
||||
bool BitcodeCollector::compile_and_add(const char* source, size_t source_size, const std::string& name)
|
||||
{
|
||||
// Dedup by source-content hash: two PointerIterator<int> children in the
|
||||
// same zip produce identical CPP source that defines the same symbol; without
|
||||
// this guard the LLVM linker fails with "symbol multiply defined".
|
||||
const auto hash = std::hash<std::string_view>{}(std::string_view(source, source_size));
|
||||
if (!added_content_hashes_.insert(hash).second)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
hostjit::CUDACompiler compiler;
|
||||
std::string src(source, source_size);
|
||||
auto result = compiler.compileToDeviceBitcode(src, config_);
|
||||
if (!result.success)
|
||||
{
|
||||
fprintf(stderr, "\nERROR compiling %s to bitcode: %s\n", name.c_str(), result.diagnostics.c_str());
|
||||
return false;
|
||||
}
|
||||
auto path = make_temp_path("cccl_" + name + "_", unique_id_, ".bc");
|
||||
if (write_file(result.bitcode.data(), result.bitcode.size(), path))
|
||||
{
|
||||
config_.device_bitcode_files.push_back(path);
|
||||
temp_paths_.push_back(path);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BitcodeCollector::add_op_code(cccl_op_t& op, const std::string& name)
|
||||
{
|
||||
if (!op.code || op.code_size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduplicate: if two iterators share the same symbol (e.g. two CountingIterators
|
||||
// of the same type), only compile/link the bitcode once.
|
||||
if (op.name && op.name[0])
|
||||
{
|
||||
if (!added_symbols_.insert(std::string(op.name)).second)
|
||||
{
|
||||
return; // already added
|
||||
}
|
||||
}
|
||||
|
||||
if (op.code_type == CCCL_OP_CPP_SOURCE)
|
||||
{
|
||||
compile_and_add(op.code, op.code_size, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_raw_bitcode(op.code, op.code_size, name);
|
||||
}
|
||||
|
||||
// Also link any extra modules (child iterator ops, numba-compiled ops).
|
||||
int extra_counter = 0;
|
||||
if (op.num_extra_ltoirs > 0 && (!op.extra_ltoirs || !op.extra_ltoir_sizes))
|
||||
{
|
||||
throw std::runtime_error("cccl_op_t: extra_ltoirs and extra_ltoir_sizes must be non-null when num_extra_ltoirs > "
|
||||
"0");
|
||||
}
|
||||
for (size_t i = 0; i < op.num_extra_ltoirs; ++i)
|
||||
{
|
||||
if (op.extra_ltoirs[i] && op.extra_ltoir_sizes[i] > 0)
|
||||
{
|
||||
auto extra_name = name + "_extra" + std::to_string(extra_counter++);
|
||||
const auto* data = op.extra_ltoirs[i];
|
||||
const auto data_sz = op.extra_ltoir_sizes[i];
|
||||
if (!op.extra_code_types)
|
||||
{
|
||||
throw std::runtime_error("cccl_op_t: extra_code_types must be non-null when num_extra_ltoirs > 0");
|
||||
}
|
||||
const cccl_op_code_type t = op.extra_code_types[i];
|
||||
if (t == CCCL_OP_CPP_SOURCE)
|
||||
{
|
||||
compile_and_add(data, data_sz, extra_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_raw_bitcode(data, data_sz, extra_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitcodeCollector::add_op(cccl_op_t op, const std::string& label)
|
||||
{
|
||||
// Only add bitcode for LTOIR/LLVM_IR ops (CPP_SOURCE is embedded inline in the generated source)
|
||||
if (is_bitcode_op(op))
|
||||
{
|
||||
add_raw_bitcode(op.code, op.code_size, label);
|
||||
}
|
||||
|
||||
// Always process extras with per-entry dispatch.
|
||||
int extra_counter = 0;
|
||||
if (op.num_extra_ltoirs > 0 && (!op.extra_ltoirs || !op.extra_ltoir_sizes))
|
||||
{
|
||||
throw std::runtime_error("cccl_op_t: extra_ltoirs and extra_ltoir_sizes must be non-null when num_extra_ltoirs > "
|
||||
"0");
|
||||
}
|
||||
for (size_t i = 0; i < op.num_extra_ltoirs; ++i)
|
||||
{
|
||||
if (op.extra_ltoirs[i] && op.extra_ltoir_sizes[i] > 0)
|
||||
{
|
||||
auto extra_name = label + "_extra" + std::to_string(extra_counter++);
|
||||
const auto* data = op.extra_ltoirs[i];
|
||||
const auto data_sz = op.extra_ltoir_sizes[i];
|
||||
if (!op.extra_code_types)
|
||||
{
|
||||
throw std::runtime_error("cccl_op_t: extra_code_types must be non-null when num_extra_ltoirs > 0");
|
||||
}
|
||||
const cccl_op_code_type t = op.extra_code_types[i];
|
||||
if (t == CCCL_OP_CPP_SOURCE)
|
||||
{
|
||||
compile_and_add(data, data_sz, extra_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
add_raw_bitcode(data, data_sz, extra_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BitcodeCollector::add_iterator(cccl_iterator_t it, const std::string& label_prefix)
|
||||
{
|
||||
if (it.type != CCCL_ITERATOR)
|
||||
{
|
||||
return;
|
||||
}
|
||||
add_op_code(it.advance, label_prefix + "_adv");
|
||||
add_op_code(it.dereference, label_prefix + "_deref");
|
||||
}
|
||||
|
||||
void BitcodeCollector::cleanup()
|
||||
{
|
||||
for (const auto& p : temp_paths_)
|
||||
{
|
||||
std::filesystem::remove(p);
|
||||
}
|
||||
temp_paths_.clear();
|
||||
}
|
||||
} // namespace hostjit::codegen
|
||||
812
cccl_upstream/c/parallel.v2/src/hostjit/codegen/cub_call.cpp
Normal file
812
cccl_upstream/c/parallel.v2/src/hostjit/codegen/cub_call.cpp
Normal file
@@ -0,0 +1,812 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <hostjit/codegen/bitcode.hpp>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <hostjit/codegen/iterators.hpp>
|
||||
#include <hostjit/codegen/operators.hpp>
|
||||
#include <hostjit/codegen/types.hpp>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
CubCall CubCall::from(const char* include_header)
|
||||
{
|
||||
CubCall c;
|
||||
c.include_ = include_header;
|
||||
return c;
|
||||
}
|
||||
|
||||
CubCall& CubCall::run(const char* cub_function)
|
||||
{
|
||||
cub_function_ = cub_function;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CubCall& CubCall::name(const char* export_name)
|
||||
{
|
||||
fn_name_ = export_name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Helper to find the accumulator type from the argument list.
|
||||
// Priority: first cccl_value_t, then first input_t's value_type.
|
||||
namespace
|
||||
{
|
||||
cccl_type_info find_accum_type(const std::vector<Arg>& args)
|
||||
{
|
||||
// Highest priority: explicit override
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (auto* fa = std::get_if<force_accum_type_t>(&arg))
|
||||
{
|
||||
return fa->type;
|
||||
}
|
||||
}
|
||||
// First: look for cccl_value_t (init value defines accum type)
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (auto* val = std::get_if<cccl_value_t>(&arg))
|
||||
{
|
||||
return val->type;
|
||||
}
|
||||
}
|
||||
// Second: future_val_t carries explicit type info
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (auto* fv = std::get_if<future_val_t>(&arg))
|
||||
{
|
||||
return fv->type;
|
||||
}
|
||||
}
|
||||
// Fallback: first input iterator's value_type
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (auto* inp = std::get_if<input_t>(&arg))
|
||||
{
|
||||
return inp->it.value_type;
|
||||
}
|
||||
}
|
||||
// Last resort: first output iterator
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (auto* outp = std::get_if<output_t>(&arg))
|
||||
{
|
||||
return outp->it.value_type;
|
||||
}
|
||||
}
|
||||
return cccl_type_info{sizeof(int), alignof(int), CCCL_INT32};
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
namespace
|
||||
{
|
||||
// Returns true if `args` contains an env_stream_t — used to decide whether the
|
||||
// shared-includes block needs to pull in <cuda/std/__execution/env.h>.
|
||||
bool needs_env_include(const std::vector<Arg>& args)
|
||||
{
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (std::holds_alternative<env_stream_t>(arg))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Emits the system #includes + the CUB header. Hoisted from source() so
|
||||
// multi-function compiles can emit this once and wrap N function bodies in N
|
||||
// namespaces below.
|
||||
std::string shared_includes(const std::string& cub_include, bool needs_tuple, bool needs_env)
|
||||
{
|
||||
std::string src = R"(#include <cuda_runtime.h>
|
||||
#include <cuda_fp16.h>
|
||||
#include <cuda/std/iterator>
|
||||
#include <cuda/std/functional>
|
||||
#include <cuda/functional>
|
||||
)";
|
||||
if (needs_tuple)
|
||||
{
|
||||
src += "#include <cuda/std/tuple>\n";
|
||||
}
|
||||
if (needs_env)
|
||||
{
|
||||
// Use the narrow internal env.h header rather than <cuda/std/execution>
|
||||
// — the umbrella header pulls in pstl machinery that depends on <vector>
|
||||
// and exception types not available in the hostjit environment.
|
||||
src += "#include <cuda/std/__execution/env.h>\n";
|
||||
src += "#include <cuda/stream_ref>\n";
|
||||
}
|
||||
src += std::format("#include <{}>\n\n", cub_include);
|
||||
return src;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string CubCall::source() const
|
||||
{
|
||||
// Single-function source = shared includes + this CubCall's body.
|
||||
return shared_includes(include_, tuple_inputs_, needs_env_include(args_)) + body();
|
||||
}
|
||||
|
||||
std::string CubCall::body() const
|
||||
{
|
||||
// Pass 1: determine accumulator type
|
||||
cccl_type_info accum_info = find_accum_type(args_);
|
||||
std::string accum_preamble;
|
||||
std::string accum_type = resolve_type(accum_info, "storage_t", accum_preamble);
|
||||
|
||||
// Counters for unique naming
|
||||
int in_count = 0;
|
||||
int out_count = 0;
|
||||
int op_count = 0;
|
||||
int val_count = 0;
|
||||
|
||||
// Accumulated sections
|
||||
std::string preamble;
|
||||
std::vector<std::string> params;
|
||||
std::vector<std::string> setup_lines;
|
||||
std::vector<std::string> cub_args;
|
||||
// Lines emitted after the cub::DeviceX::Y(...) call and before the return —
|
||||
// populated by post-call tags (e.g. selector_out_t capturing a DoubleBuffer's
|
||||
// selector member).
|
||||
std::vector<std::string> post_call_lines;
|
||||
|
||||
// Emit accum type
|
||||
if (!accum_preamble.empty())
|
||||
{
|
||||
preamble += accum_preamble;
|
||||
}
|
||||
preamble += std::format("using accum_t = {};\n\n", accum_type);
|
||||
|
||||
// Shared alias cache: (size, alignment) → type name.
|
||||
// Multiple iterators with the same unknown struct layout must share a single C++
|
||||
// type so that CUB can move data between them (e.g. merge sort block loads).
|
||||
std::map<std::pair<size_t, size_t>, std::string> struct_type_map;
|
||||
int struct_type_counter = 0;
|
||||
|
||||
// Return a stable C++ element-type name for an iterator's value_type:
|
||||
// - Known C type → C++ keyword (e.g. "int", "float")
|
||||
// - Struct matching accum_t → "accum_t" (preserves operator compatibility)
|
||||
// - Other struct → shared alias for this (size, alignment) layout
|
||||
// Built-in C type sizes (CCCL_TYPE_ENUM → bytes). Used to detect a
|
||||
// mismatch where the caller reports a primitive `vt.type` but `vt.size`
|
||||
// says the element is wider — common when a custom struct happens to
|
||||
// share the primitive's tag. In that case fall through to a storage
|
||||
// struct so the iterator strides correctly.
|
||||
auto builtin_size = [](cccl_type_enum t) -> size_t {
|
||||
switch (t)
|
||||
{
|
||||
case CCCL_INT8:
|
||||
case CCCL_UINT8:
|
||||
case CCCL_BOOLEAN:
|
||||
return 1;
|
||||
case CCCL_INT16:
|
||||
case CCCL_UINT16:
|
||||
case CCCL_FLOAT16:
|
||||
return 2;
|
||||
case CCCL_INT32:
|
||||
case CCCL_UINT32:
|
||||
case CCCL_FLOAT32:
|
||||
return 4;
|
||||
case CCCL_INT64:
|
||||
case CCCL_UINT64:
|
||||
case CCCL_FLOAT64:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
auto iter_elem_type_name = [&](const cccl_type_info& vt) -> std::string {
|
||||
auto name = get_type_name(vt.type);
|
||||
if (!name.empty() && vt.size == builtin_size(vt.type))
|
||||
{
|
||||
return name;
|
||||
}
|
||||
if (vt.size == accum_info.size && vt.alignment == accum_info.alignment && vt.type == accum_info.type)
|
||||
{
|
||||
return "accum_t";
|
||||
}
|
||||
auto key = std::make_pair(vt.size, vt.alignment);
|
||||
auto it = struct_type_map.find(key);
|
||||
if (it != struct_type_map.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
auto alias = std::format("__cccl_struct_{}_t", struct_type_counter++);
|
||||
preamble += make_storage_type(alias.c_str(), vt.size, vt.alignment);
|
||||
struct_type_map[key] = alias;
|
||||
return alias;
|
||||
};
|
||||
|
||||
// Pass 2: process each argument
|
||||
for (const auto& arg : args_)
|
||||
{
|
||||
std::visit(
|
||||
[&](auto&& a) {
|
||||
using T = std::decay_t<decltype(a)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, temp_storage_t>)
|
||||
{
|
||||
params.push_back("void* d_temp_storage");
|
||||
cub_args.push_back("d_temp_storage");
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, temp_bytes_t>)
|
||||
{
|
||||
params.push_back("size_t* temp_storage_bytes");
|
||||
cub_args.push_back("*temp_storage_bytes");
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, num_items_t>)
|
||||
{
|
||||
params.push_back(std::format("unsigned long long {}", a.name));
|
||||
cub_args.push_back(std::format("(unsigned long long){}", a.name));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, stream_t>)
|
||||
{
|
||||
params.push_back("void* stream");
|
||||
cub_args.push_back("(cudaStream_t)stream");
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, env_stream_t>)
|
||||
{
|
||||
params.push_back("void* stream");
|
||||
cub_args.push_back("::cuda::std::execution::env{::cuda::stream_ref{(cudaStream_t)stream}}");
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, input_t>)
|
||||
{
|
||||
auto idx = in_count++;
|
||||
auto struct_name = std::format("in_{}_it_t", idx);
|
||||
auto var_name = std::format("in_{}", idx);
|
||||
auto param_name = std::format("d_in_{}", idx);
|
||||
|
||||
auto value_type = iter_elem_type_name(a.it.value_type);
|
||||
auto code = make_input_iterator(a.it, value_type, "accum_t", struct_name, var_name, param_name);
|
||||
|
||||
preamble += code.preamble;
|
||||
params.push_back(std::format("void* {}", param_name));
|
||||
setup_lines.push_back(code.setup_code);
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, output_t>)
|
||||
{
|
||||
auto idx = out_count++;
|
||||
auto struct_name = std::format("out_{}_it_t", idx);
|
||||
auto var_name = std::format("out_{}", idx);
|
||||
auto param_name = std::format("d_out_{}", idx);
|
||||
|
||||
auto value_type = iter_elem_type_name(a.it.value_type);
|
||||
auto code = make_output_iterator(a.it, "accum_t", struct_name, var_name, param_name, value_type);
|
||||
|
||||
preamble += code.preamble;
|
||||
params.push_back(std::format("void* {}", param_name));
|
||||
setup_lines.push_back(code.setup_code);
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, cccl_op_t>)
|
||||
{
|
||||
auto idx = op_count++;
|
||||
auto functor_name = std::format("Op_{}", idx);
|
||||
auto var_name = std::format("op_{}", idx);
|
||||
auto state_param = std::format("op_{}_state", idx);
|
||||
bool has_bc = BitcodeCollector::is_bitcode_op(a);
|
||||
|
||||
auto code = make_binary_op(a, accum_type, functor_name, var_name, state_param, has_bc);
|
||||
|
||||
preamble += code.preamble;
|
||||
// Always emit op_state param for ABI stability (unused for stateless ops)
|
||||
params.push_back(std::format("void* {}", state_param));
|
||||
setup_lines.push_back(code.setup_code);
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, cmp_t>)
|
||||
{
|
||||
auto idx = op_count++;
|
||||
auto functor_name = std::format("CmpOp_{}", idx);
|
||||
auto var_name = std::format("cmp_{}", idx);
|
||||
auto state_param = std::format("cmp_{}_state", idx);
|
||||
bool has_bc = BitcodeCollector::is_bitcode_op(a.op);
|
||||
|
||||
auto code = make_comparison_op(a.op, accum_type, functor_name, var_name, state_param, has_bc);
|
||||
|
||||
preamble += code.preamble;
|
||||
params.push_back(std::format("void* {}", state_param));
|
||||
setup_lines.push_back(code.setup_code);
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, for_each_op_t>)
|
||||
{
|
||||
auto idx = op_count++;
|
||||
auto functor_name = std::format("ForEachOp_{}", idx);
|
||||
auto var_name = std::format("op_{}", idx);
|
||||
auto state_param = std::format("op_{}_state", idx);
|
||||
bool has_bc = BitcodeCollector::is_bitcode_op(a.op);
|
||||
|
||||
// The element type is the first input iterator's value_type, which
|
||||
// CubCall has already resolved via find_accum_type.
|
||||
const std::string elem_type = iter_elem_type_name(accum_info);
|
||||
|
||||
auto code = make_for_each_op(a.op, elem_type, functor_name, var_name, state_param, has_bc);
|
||||
|
||||
preamble += code.preamble;
|
||||
params.push_back(std::format("void* {}", state_param));
|
||||
setup_lines.push_back(code.setup_code);
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double_buffer_t>)
|
||||
{
|
||||
// Emit two void* params (in/out buffer state pointers), construct a
|
||||
// cub::DoubleBuffer<elem_t> local with the given var_name, and pass
|
||||
// the buffer to the CUB call. iter_elem_type_name resolves the
|
||||
// element type the same way input/output iterators do.
|
||||
const std::string elem_type = iter_elem_type_name(a.in_it.value_type);
|
||||
const std::string var_name = a.var_name;
|
||||
const auto in_param = var_name + "_in_state";
|
||||
const auto out_param = var_name + "_out_state";
|
||||
|
||||
params.push_back(std::format("void* {}", in_param));
|
||||
params.push_back(std::format("void* {}", out_param));
|
||||
setup_lines.push_back(std::format(
|
||||
"cub::DoubleBuffer<{0}> {1}(static_cast<{0}*>({2}), static_cast<{0}*>({3}));",
|
||||
elem_type,
|
||||
var_name,
|
||||
in_param,
|
||||
out_param));
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, selector_out_t>)
|
||||
{
|
||||
// Emit a void* selector_out param and capture <buffer>.selector after
|
||||
// the CUB call. Paired with a double_buffer_t whose var_name matches.
|
||||
params.push_back("void* selector_out");
|
||||
post_call_lines.push_back(std::format("*static_cast<int*>(selector_out) = {}.selector;", a.buffer_var_name));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, unary_op_t>)
|
||||
{
|
||||
auto idx = op_count++;
|
||||
auto functor_name = std::format("UnaryOp_{}", idx);
|
||||
auto var_name = std::format("op_{}", idx);
|
||||
auto state_param = std::format("op_{}_state", idx);
|
||||
bool has_bc = BitcodeCollector::is_bitcode_op(a.op);
|
||||
|
||||
// For unknown types the iterators use accum_t as fallback; the unary
|
||||
// op functor must use the same names so CUB can match the types.
|
||||
// Reuse the iterator's element-type resolver so a primitive `vt.type`
|
||||
// with a custom-sized `vt.size` falls back to the same storage alias
|
||||
// the iterator uses, rather than naming the wider element "int".
|
||||
std::string in_type = iter_elem_type_name(a.in_type);
|
||||
std::string out_type = iter_elem_type_name(a.out_type);
|
||||
|
||||
auto code = make_unary_op(a.op, in_type, out_type, functor_name, var_name, state_param, has_bc);
|
||||
|
||||
preamble += code.preamble;
|
||||
params.push_back(std::format("void* {}", state_param));
|
||||
setup_lines.push_back(code.setup_code);
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, force_accum_type_t>)
|
||||
{
|
||||
// No-op: only influences accum type resolution, generates no code.
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, future_val_t>)
|
||||
{
|
||||
auto idx = val_count++;
|
||||
auto var_name = std::format("future_{}", idx);
|
||||
auto param_name = std::format("future_{}_param", idx);
|
||||
|
||||
// The caller passes a device pointer; we wrap it in FutureValue<accum_t>
|
||||
// so CUB fetches the init value from device memory at scan time.
|
||||
params.push_back(std::format("void* {}", param_name));
|
||||
setup_lines.push_back(
|
||||
std::format("cub::FutureValue<accum_t> {}(static_cast<accum_t*>({}));", var_name, param_name));
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, cccl_value_t>)
|
||||
{
|
||||
auto idx = val_count++;
|
||||
auto var_name = std::format("val_{}", idx);
|
||||
auto param_name = std::format("val_{}_ptr", idx);
|
||||
|
||||
params.push_back(std::format("void* {}", param_name));
|
||||
setup_lines.push_back(std::format(
|
||||
"accum_t {};\n __builtin_memcpy(&{}, {}, sizeof(accum_t));", var_name, var_name, param_name));
|
||||
cub_args.push_back(var_name);
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, typed_scalar_t>)
|
||||
{
|
||||
// Caller passes a host pointer; we memcpy onto the stack as the
|
||||
// requested C++ type before calling CUB. The C++ type comes from
|
||||
// a.type (cccl_type_info), the wrapper parameter is named
|
||||
// `<a.name>_ptr`, and the local that CUB sees is `<a.name>`.
|
||||
const std::string cpp_type = resolve_type(a.type, a.name, preamble);
|
||||
const auto param_name = std::string(a.name) + "_ptr";
|
||||
params.push_back(std::format("void* {}", param_name));
|
||||
setup_lines.push_back(
|
||||
std::format("{0} {1};\n __builtin_memcpy(&{1}, {2}, sizeof({0}));", cpp_type, a.name, param_name));
|
||||
cub_args.push_back(a.name);
|
||||
}
|
||||
},
|
||||
arg);
|
||||
}
|
||||
|
||||
// When tuple_inputs_ is set, replace the individual input cub_args with a
|
||||
// single make_tuple(...) expression covering all of them.
|
||||
if (tuple_inputs_ && in_count > 1)
|
||||
{
|
||||
// Collect the first in_count cub_args that correspond to input iterators.
|
||||
// Inputs are emitted first among iterator args, so they occupy the leading
|
||||
// cub_args entries (after temp_storage/temp_bytes if present).
|
||||
// Reconstruct: find and replace the in_0..in_N-1 vars with make_tuple.
|
||||
std::vector<std::string> input_vars;
|
||||
std::vector<std::string> other_args;
|
||||
for (const auto& a : cub_args)
|
||||
{
|
||||
// Input vars are named "in_0", "in_1", etc.
|
||||
if (a.starts_with("in_") && a.size() >= 4 && std::isdigit(a[3]))
|
||||
{
|
||||
input_vars.push_back(a);
|
||||
}
|
||||
else
|
||||
{
|
||||
other_args.push_back(a);
|
||||
}
|
||||
}
|
||||
std::string tuple_arg = "::cuda::std::make_tuple(";
|
||||
for (size_t i = 0; i < input_vars.size(); ++i)
|
||||
{
|
||||
if (i)
|
||||
{
|
||||
tuple_arg += ", ";
|
||||
}
|
||||
tuple_arg += input_vars[i];
|
||||
}
|
||||
tuple_arg += ")";
|
||||
// Rebuild cub_args: replace all in_* with the single tuple arg (at original position of in_0)
|
||||
cub_args.clear();
|
||||
cub_args.push_back(tuple_arg);
|
||||
for (const auto& a : other_args)
|
||||
{
|
||||
cub_args.push_back(a);
|
||||
}
|
||||
}
|
||||
|
||||
// Assemble the per-function body: preamble + extern "C" function defn.
|
||||
// System #includes live in shared_includes(), emitted once at TU scope by
|
||||
// either source() (single-fn) or compile() (multi-fn).
|
||||
std::string src = preamble;
|
||||
|
||||
// Function signature
|
||||
src += std::format("extern \"C\" _CCCL_VISIBILITY_EXPORT int {}(\n", fn_name_);
|
||||
for (size_t i = 0; i < params.size(); ++i)
|
||||
{
|
||||
src += " " + params[i];
|
||||
if (i + 1 < params.size())
|
||||
{
|
||||
src += ",\n";
|
||||
}
|
||||
}
|
||||
src += ")\n{\n";
|
||||
|
||||
// Setup code
|
||||
for (const auto& line : setup_lines)
|
||||
{
|
||||
src += " " + line + "\n";
|
||||
}
|
||||
src += "\n";
|
||||
|
||||
// CUB call
|
||||
src += std::format(" cudaError_t err = {}(\n", cub_function_);
|
||||
for (size_t i = 0; i < cub_args.size(); ++i)
|
||||
{
|
||||
src += " " + cub_args[i];
|
||||
if (i + 1 < cub_args.size())
|
||||
{
|
||||
src += ",\n";
|
||||
}
|
||||
}
|
||||
src += ");\n\n";
|
||||
|
||||
// Post-call lines (e.g., capturing a DoubleBuffer's selector).
|
||||
for (const auto& line : post_call_lines)
|
||||
{
|
||||
src += " " + line + "\n";
|
||||
}
|
||||
if (!post_call_lines.empty())
|
||||
{
|
||||
src += "\n";
|
||||
}
|
||||
|
||||
// Error return
|
||||
src += R"( return (int)err;
|
||||
}
|
||||
)";
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
hostjit::CompilerConfig CubCall::make_jit_config(
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
cccl_build_config* config,
|
||||
const char* ctk_path,
|
||||
const char* cccl_include_path,
|
||||
const std::string& entry_point_name)
|
||||
{
|
||||
auto jit_config = hostjit::detectDefaultConfig();
|
||||
jit_config.sm_version = cc_major * 10 + cc_minor;
|
||||
jit_config.verbose = false;
|
||||
jit_config.entry_point_name = entry_point_name;
|
||||
|
||||
if (ctk_path && ctk_path[0] != '\0')
|
||||
{
|
||||
jit_config.cuda_toolkit_path = ctk_path;
|
||||
// Rebuild library_paths from the new toolkit root so the linker
|
||||
// can find libcudart.so in the pip-installed layout.
|
||||
jit_config.library_paths.clear();
|
||||
for (const char* subdir : {"lib64", "lib"})
|
||||
{
|
||||
auto candidate = std::filesystem::path(ctk_path) / subdir;
|
||||
if (std::filesystem::exists(candidate))
|
||||
{
|
||||
jit_config.library_paths.push_back(candidate.string());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cccl_include_path && cccl_include_path[0] != '\0')
|
||||
{
|
||||
jit_config.cccl_include_path = cccl_include_path;
|
||||
// When CCCL headers are pip-installed, the hostjit cuda_minimal headers
|
||||
// are installed alongside them under the parent directory:
|
||||
// cccl_include_path = .../cuda/cccl/headers/include/
|
||||
// hostjit headers = .../cuda/cccl/headers/hostjit/cuda_minimal/
|
||||
// So derive hostjit_include_path as the parent of cccl_include_path.
|
||||
if (jit_config.hostjit_include_path.empty()
|
||||
|| !std::filesystem::exists(jit_config.hostjit_include_path + "/hostjit/cuda_minimal"))
|
||||
{
|
||||
auto parent = std::filesystem::path(cccl_include_path).parent_path().string();
|
||||
if (std::filesystem::exists(parent + "/hostjit/cuda_minimal"))
|
||||
{
|
||||
jit_config.hostjit_include_path = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config)
|
||||
{
|
||||
for (size_t i = 0; i < config->num_extra_include_dirs; ++i)
|
||||
{
|
||||
jit_config.include_paths.push_back(config->extra_include_dirs[i]);
|
||||
}
|
||||
for (size_t i = 0; i < config->num_extra_compile_flags; ++i)
|
||||
{
|
||||
std::string_view flag = config->extra_compile_flags[i];
|
||||
if (flag.starts_with("-D"))
|
||||
{
|
||||
flag.remove_prefix(2);
|
||||
if (auto eq = flag.find('='); eq != std::string_view::npos)
|
||||
{
|
||||
jit_config.macro_definitions[std::string{flag.substr(0, eq)}] = std::string{flag.substr(eq + 1)};
|
||||
}
|
||||
else
|
||||
{
|
||||
jit_config.macro_definitions[std::string{flag}] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
jit_config.enable_pch = config->enable_pch != 0;
|
||||
jit_config.verbose = config->verbose != 0;
|
||||
}
|
||||
|
||||
return jit_config;
|
||||
}
|
||||
|
||||
CubCallResult CubCall::compile(
|
||||
int cc_major, int cc_minor, cccl_build_config* config, const char* ctk_path, const char* cccl_include_path) const
|
||||
{
|
||||
// 1. Configure compiler
|
||||
auto jit_config = make_jit_config(cc_major, cc_minor, config, ctk_path, cccl_include_path, fn_name_);
|
||||
|
||||
// 2. Auto-collect bitcode from ops and iterators
|
||||
uintptr_t unique_id = reinterpret_cast<uintptr_t>(this);
|
||||
BitcodeCollector bitcode(jit_config, unique_id);
|
||||
|
||||
int op_idx = 0;
|
||||
int in_idx = 0;
|
||||
int out_idx = 0;
|
||||
collect_bitcode(bitcode, op_idx, in_idx, out_idx);
|
||||
|
||||
// 3. Generate source
|
||||
std::string cuda_source = source();
|
||||
if (const char* dump_path = std::getenv("CUBCALL_DUMP_SOURCE"))
|
||||
{
|
||||
std::ofstream f(dump_path);
|
||||
f << cuda_source;
|
||||
}
|
||||
|
||||
// 4. Compile. unique_ptr ensures the JITCompiler is freed if the next two
|
||||
// checks throw; .release() transfers ownership to CubCallResult on success.
|
||||
auto compiler = std::make_unique<JITCompiler>(jit_config);
|
||||
if (!compiler->compile(cuda_source))
|
||||
{
|
||||
std::string err = compiler->getLastError();
|
||||
bitcode.cleanup();
|
||||
throw std::runtime_error("CubCall compilation failed: " + err);
|
||||
}
|
||||
|
||||
bitcode.cleanup();
|
||||
|
||||
// 5. Extract function pointer
|
||||
using fn_t = int (*)(void*, ...);
|
||||
auto fn = compiler->getFunction<fn_t>(fn_name_);
|
||||
if (!fn)
|
||||
{
|
||||
throw std::runtime_error("CubCall function lookup failed: " + compiler->getLastError());
|
||||
}
|
||||
|
||||
// 6. Copy cubin
|
||||
auto cubin = compiler->getCubin();
|
||||
|
||||
return CubCallResult{compiler.release(), reinterpret_cast<void*>(fn), std::move(cubin)};
|
||||
}
|
||||
|
||||
void CubCall::collect_bitcode(BitcodeCollector& bitcode, int& op_idx, int& in_idx, int& out_idx) const
|
||||
{
|
||||
for (const auto& arg : args_)
|
||||
{
|
||||
std::visit(
|
||||
[&](auto&& a) {
|
||||
using T = std::decay_t<decltype(a)>;
|
||||
if constexpr (std::is_same_v<T, cccl_op_t>)
|
||||
{
|
||||
bitcode.add_op(a, std::format("op_{}", op_idx++));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, cmp_t>)
|
||||
{
|
||||
bitcode.add_op(a.op, std::format("cmp_{}", op_idx++));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, unary_op_t>)
|
||||
{
|
||||
bitcode.add_op(a.op, std::format("op_{}", op_idx++));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, for_each_op_t>)
|
||||
{
|
||||
bitcode.add_op(a.op, std::format("op_{}", op_idx++));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, input_t>)
|
||||
{
|
||||
bitcode.add_iterator(a.it, std::format("in_{}", in_idx++));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, output_t>)
|
||||
{
|
||||
bitcode.add_iterator(a.it, std::format("out_{}", out_idx++));
|
||||
}
|
||||
},
|
||||
arg);
|
||||
}
|
||||
}
|
||||
|
||||
MultiCubCallResult CubCall::compile(
|
||||
std::initializer_list<CubCall> calls,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
cccl_build_config* config,
|
||||
const char* ctk_path,
|
||||
const char* cccl_include_path)
|
||||
{
|
||||
if (calls.size() == 0)
|
||||
{
|
||||
throw std::runtime_error("CubCall::compile: empty CubCall list");
|
||||
}
|
||||
|
||||
// All CubCalls must share the same CUB header — we emit it once at the top
|
||||
// of the merged TU. (If a future use case needs heterogeneous includes,
|
||||
// extend this to union the set; for now keep it strict so silent mismatches
|
||||
// can't slip through.)
|
||||
const std::string& shared_include = calls.begin()->include_;
|
||||
for (const auto& cb : calls)
|
||||
{
|
||||
if (cb.include_ != shared_include)
|
||||
{
|
||||
throw std::runtime_error("CubCall::compile: all CubCalls in a multi-compile must share the same .from(include) "
|
||||
"header");
|
||||
}
|
||||
}
|
||||
|
||||
// Detect whether any CubCall needs the env / tuple system includes.
|
||||
bool any_tuple = false;
|
||||
bool any_env = false;
|
||||
for (const auto& cb : calls)
|
||||
{
|
||||
any_tuple = any_tuple || cb.tuple_inputs_;
|
||||
any_env = any_env || needs_env_include(cb.args_);
|
||||
}
|
||||
|
||||
// entry_point_name is used to mark a single function as preserved during
|
||||
// internalization. Use the first CubCall's name as the primary entry; the
|
||||
// others will still be exported via extern "C" _CCCL_VISIBILITY_EXPORT so dlsym finds them.
|
||||
auto jit_config = make_jit_config(cc_major, cc_minor, config, ctk_path, cccl_include_path, calls.begin()->fn_name_);
|
||||
|
||||
// Shared BitcodeCollector across all CubCalls — identical user-op or
|
||||
// iterator bitcode referenced from multiple wrappers gets deduplicated by
|
||||
// content hash + symbol name inside the collector.
|
||||
uintptr_t unique_id = reinterpret_cast<uintptr_t>(&*calls.begin());
|
||||
BitcodeCollector bitcode(jit_config, unique_id);
|
||||
|
||||
int op_idx = 0;
|
||||
int in_idx = 0;
|
||||
int out_idx = 0;
|
||||
for (const auto& cb : calls)
|
||||
{
|
||||
cb.collect_bitcode(bitcode, op_idx, in_idx, out_idx);
|
||||
}
|
||||
|
||||
// Build the merged source: shared includes at TU scope, then one
|
||||
// `namespace fn_<i> { ... body() }` per CubCall.
|
||||
// The extern "C" _CCCL_VISIBILITY_EXPORT symbols defined inside each
|
||||
// namespace export under the global C-linkage name (no mangling),
|
||||
// so dlsym(handle, cb.fn_name_) finds them.
|
||||
std::string cuda_source = shared_includes(shared_include, any_tuple, any_env);
|
||||
int i = 0;
|
||||
for (const auto& cb : calls)
|
||||
{
|
||||
cuda_source += std::format("namespace fn_{} {{\n", i);
|
||||
cuda_source += cb.body();
|
||||
cuda_source += std::format("}} // namespace fn_{}\n\n", i);
|
||||
++i;
|
||||
}
|
||||
|
||||
if (const char* dump_path = std::getenv("CUBCALL_DUMP_SOURCE"))
|
||||
{
|
||||
std::ofstream f(dump_path);
|
||||
f << cuda_source;
|
||||
}
|
||||
if (std::getenv("CUBCALL_PRINT_SOURCE"))
|
||||
{
|
||||
std::fprintf(stderr,
|
||||
"\n===== CubCall merged JIT source [%zu fns] =====\n%s\n===== end =====\n",
|
||||
calls.size(),
|
||||
cuda_source.c_str());
|
||||
}
|
||||
|
||||
// Single Clang compile for the whole TU.
|
||||
auto compiler = std::make_unique<JITCompiler>(jit_config);
|
||||
if (!compiler->compile(cuda_source))
|
||||
{
|
||||
std::string err = compiler->getLastError();
|
||||
bitcode.cleanup();
|
||||
throw std::runtime_error("CubCall::compile (multi) compilation failed: " + err);
|
||||
}
|
||||
bitcode.cleanup();
|
||||
|
||||
// dlsym each function by its export name (positional order matches input).
|
||||
using fn_t = int (*)(void*, ...);
|
||||
std::vector<void*> fn_ptrs;
|
||||
fn_ptrs.reserve(calls.size());
|
||||
for (const auto& cb : calls)
|
||||
{
|
||||
auto fn = compiler->getFunction<fn_t>(cb.fn_name_);
|
||||
if (!fn)
|
||||
{
|
||||
throw std::runtime_error("CubCall::compile (multi) function lookup failed: " + cb.fn_name_);
|
||||
}
|
||||
fn_ptrs.push_back(reinterpret_cast<void*>(fn));
|
||||
}
|
||||
|
||||
auto cubin = compiler->getCubin();
|
||||
return MultiCubCallResult{compiler.release(), std::move(cubin), std::move(fn_ptrs)};
|
||||
}
|
||||
} // namespace hostjit::codegen
|
||||
288
cccl_upstream/c/parallel.v2/src/hostjit/codegen/iterators.cpp
Normal file
288
cccl_upstream/c/parallel.v2/src/hostjit/codegen/iterators.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <format>
|
||||
|
||||
#include <hostjit/codegen/iterators.hpp>
|
||||
#include <hostjit/codegen/types.hpp>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// The iterator struct holds a `long long _delta` lazy-offset field, so its
|
||||
// natural alignment is at least alignof(long long)==8. C++ rejects alignas
|
||||
// values smaller than the natural alignment; clamp here so user iterators with
|
||||
// small `it.alignment` (e.g. 1 for a `char` state) still produce a valid struct.
|
||||
inline std::size_t struct_alignas(std::size_t it_alignment)
|
||||
{
|
||||
const std::size_t base = it_alignment > 0 ? it_alignment : 1;
|
||||
return base < alignof(long long) ? alignof(long long) : base;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
IteratorCode make_input_iterator(
|
||||
cccl_iterator_t it,
|
||||
const std::string& value_type_name,
|
||||
const std::string& accum_type_name,
|
||||
const std::string& struct_name,
|
||||
const std::string& var_name,
|
||||
const std::string& state_param)
|
||||
{
|
||||
IteratorCode result;
|
||||
result.local_var = var_name;
|
||||
|
||||
if (it.type == CCCL_POINTER)
|
||||
{
|
||||
// For pointer iterators, the element type is value_type.
|
||||
// When value_type_name is empty (unknown/struct type), resolve it from the iterator's
|
||||
// value_type info to get a correctly-sized storage struct — falling back to accum_t
|
||||
// would use the wrong element size if the value type differs from the accumulator.
|
||||
std::string elem_type;
|
||||
if (value_type_name.empty())
|
||||
{
|
||||
auto elem_alias = struct_name + "_elem_t";
|
||||
elem_type = resolve_type(it.value_type, elem_alias.c_str(), result.preamble);
|
||||
}
|
||||
else
|
||||
{
|
||||
elem_type = value_type_name;
|
||||
}
|
||||
result.type_name = elem_type + "*";
|
||||
result.preamble += std::format("using {} = {}*;\n\n", struct_name, elem_type);
|
||||
result.setup_code = std::format("{} {} = static_cast<{}>({}); ", struct_name, var_name, struct_name, state_param);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Custom iterator with state + advance + dereference
|
||||
const std::string adv_name = (it.advance.name && it.advance.name[0]) ? it.advance.name : (var_name + "_advance");
|
||||
const std::string deref_name =
|
||||
(it.dereference.name && it.dereference.name[0]) ? it.dereference.name : (var_name + "_dereference");
|
||||
|
||||
auto input_val_type = value_type_name.empty() ? accum_type_name : value_type_name;
|
||||
auto val_alias = var_name + "_value_t";
|
||||
|
||||
result.type_name = struct_name;
|
||||
result.preamble = std::format("using {} = {};\n", val_alias, input_val_type);
|
||||
|
||||
result.preamble += std::format(
|
||||
R"cpp(extern "C" __device__ void {}(void* state, const void* offset);
|
||||
extern "C" __device__ void {}(const void* state, {}* result);
|
||||
|
||||
)cpp",
|
||||
adv_name,
|
||||
deref_name,
|
||||
val_alias);
|
||||
|
||||
// Positional args: {0}=struct_name, {1}=val_alias, {2}=it.size, {3}=adv_name, {4}=deref_name, {5}=it.alignment
|
||||
//
|
||||
// Arithmetic ops (+, +=, ++) are __host__ __device__ so CUB's host
|
||||
// dispatch (which does `iter += n` etc.) compiles in the freestanding
|
||||
// host pass. They accumulate into `_delta` rather than calling the
|
||||
// device-only `advance` bitcode. `operator*` (device-only) applies the
|
||||
// accumulated `_delta` to a copy of state via `advance`, then derefs.
|
||||
// `alignas({5})` matches the iterator's declared state alignment so the
|
||||
// user-supplied advance/dereference (which casts state as a pointer/etc.)
|
||||
// sees properly-aligned memory.
|
||||
result.preamble += std::format(
|
||||
R"cpp(struct alignas({5}) {0} {{
|
||||
using value_type = {1};
|
||||
using difference_type = long long;
|
||||
using pointer = {1}*;
|
||||
using reference = {1};
|
||||
using iterator_category = cuda::std::random_access_iterator_tag;
|
||||
|
||||
alignas({5}) char state[{2}];
|
||||
long long _delta = 0;
|
||||
|
||||
__host__ __device__ {0} operator+(difference_type n) const {{
|
||||
{0} copy = *this;
|
||||
copy._delta += n;
|
||||
return copy;
|
||||
}}
|
||||
__host__ __device__ {0}& operator+=(difference_type n) {{
|
||||
_delta += n;
|
||||
return *this;
|
||||
}}
|
||||
__host__ __device__ {0}& operator++() {{ return *this += 1; }}
|
||||
__host__ __device__ {0} operator++(int) {{ {0} tmp = *this; ++(*this); return tmp; }}
|
||||
__host__ __device__ difference_type operator-(const {0}&) const {{ return 0; }}
|
||||
__device__ {1} operator*() const {{
|
||||
{0} copy = *this;
|
||||
if (copy._delta != 0) {{
|
||||
long long offset = copy._delta;
|
||||
{3}(copy.state, &offset);
|
||||
}}
|
||||
{1} result;
|
||||
{4}(copy.state, &result);
|
||||
return result;
|
||||
}}
|
||||
__device__ {1} operator[](difference_type n) const {{ return *(*this + n); }}
|
||||
__host__ __device__ bool operator==(const {0}&) const {{ return false; }}
|
||||
__host__ __device__ bool operator!=(const {0}&) const {{ return true; }}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
struct_name, // {0}
|
||||
val_alias, // {1}
|
||||
it.size, // {2}
|
||||
adv_name, // {3}
|
||||
deref_name, // {4}
|
||||
struct_alignas(it.alignment)); // {5}
|
||||
|
||||
result.setup_code = std::format(
|
||||
R"cpp({} {};
|
||||
__builtin_memcpy({}.state, {}, {});)cpp",
|
||||
struct_name,
|
||||
var_name,
|
||||
var_name,
|
||||
state_param,
|
||||
it.size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
IteratorCode make_output_iterator(
|
||||
cccl_iterator_t it,
|
||||
const std::string& accum_type_name,
|
||||
const std::string& struct_name,
|
||||
const std::string& var_name,
|
||||
const std::string& state_param,
|
||||
const std::string& value_type_name)
|
||||
{
|
||||
IteratorCode result;
|
||||
result.local_var = var_name;
|
||||
|
||||
// For custom iterators the element type comes from the dereference function so the
|
||||
// accum_t fallback is fine; for pointer iterators we resolve the actual value_type
|
||||
// below to get the correct element size.
|
||||
const std::string elem_type = value_type_name.empty() ? accum_type_name : value_type_name;
|
||||
|
||||
if (it.type == CCCL_POINTER)
|
||||
{
|
||||
// When value_type_name is empty (unknown/struct type), resolve from the iterator's own
|
||||
// value_type info so the element size is correct — not from accum_t which may differ.
|
||||
std::string ptr_elem_type;
|
||||
if (value_type_name.empty())
|
||||
{
|
||||
auto elem_alias = struct_name + "_elem_t";
|
||||
ptr_elem_type = resolve_type(it.value_type, elem_alias.c_str(), result.preamble);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr_elem_type = value_type_name;
|
||||
}
|
||||
result.type_name = ptr_elem_type + "*";
|
||||
result.preamble += std::format("using {} = {}*;\n\n", struct_name, ptr_elem_type);
|
||||
result.setup_code = std::format("{} {} = static_cast<{}*>({});", struct_name, var_name, ptr_elem_type, state_param);
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::string adv_name = (it.advance.name && it.advance.name[0]) ? it.advance.name : (var_name + "_advance");
|
||||
const std::string deref_name =
|
||||
(it.dereference.name && it.dereference.name[0]) ? it.dereference.name : (var_name + "_dereference");
|
||||
|
||||
auto proxy_name = var_name + "_proxy_t";
|
||||
|
||||
result.type_name = struct_name;
|
||||
result.preamble = std::format(
|
||||
R"cpp(extern "C" __device__ void {}(void* state, const void* offset);
|
||||
extern "C" __device__ void {}(void* state, const void* value);
|
||||
|
||||
)cpp",
|
||||
adv_name,
|
||||
deref_name);
|
||||
|
||||
// The proxy carries a COPY of the iterator state, not a pointer to it.
|
||||
// This is critical for indexed writes (output_it[i] = val): operator[] creates
|
||||
// a temporary advanced iterator, calls operator* on it, and returns the proxy
|
||||
// by value. After operator[] returns the temporary is destroyed, so a pointer
|
||||
// to its state would be dangling. Storing the state bytes in the proxy itself
|
||||
// makes the proxy self-contained and safe across that return.
|
||||
// Proxy contains only `char state[N]` so its natural alignment is 1; the
|
||||
// struct alignas is the bigger of the iterator's declared alignment and 1.
|
||||
const std::size_t proxy_align = it.alignment > 0 ? it.alignment : 1;
|
||||
result.preamble += std::format(
|
||||
R"cpp(struct alignas({1}) {0} {{
|
||||
alignas({1}) char state[{2}];
|
||||
__device__ void operator=(const {3}& val) {{
|
||||
{4}(state, &val);
|
||||
}}
|
||||
}};
|
||||
)cpp",
|
||||
proxy_name, // {0}
|
||||
proxy_align, // {1}
|
||||
it.size, // {2}
|
||||
elem_type, // {3}
|
||||
deref_name); // {4}
|
||||
|
||||
// Arithmetic ops (+, +=, ++) are __host__ __device__ so CUB's host
|
||||
// dispatch compiles; they accumulate `_delta` instead of calling the
|
||||
// device-only `advance` bitcode. operator* (device only) applies the
|
||||
// accumulated `_delta` before constructing the proxy.
|
||||
result.preamble += std::format(
|
||||
R"cpp(struct alignas({5}) {0} {{
|
||||
using value_type = {1};
|
||||
using difference_type = long long;
|
||||
using pointer = {1}*;
|
||||
using reference = {2};
|
||||
using iterator_category = cuda::std::random_access_iterator_tag;
|
||||
|
||||
alignas({5}) char state[{3}];
|
||||
long long _delta = 0;
|
||||
|
||||
__host__ __device__ {0} operator+(difference_type n) const {{
|
||||
{0} copy = *this;
|
||||
copy._delta += n;
|
||||
return copy;
|
||||
}}
|
||||
__host__ __device__ {0}& operator+=(difference_type n) {{
|
||||
_delta += n;
|
||||
return *this;
|
||||
}}
|
||||
__host__ __device__ {0}& operator++() {{ return *this += 1; }}
|
||||
__host__ __device__ {0} operator++(int) {{ {0} tmp = *this; ++(*this); return tmp; }}
|
||||
__host__ __device__ difference_type operator-(const {0}&) const {{ return 0; }}
|
||||
__device__ reference operator*() const {{
|
||||
{2} proxy;
|
||||
__builtin_memcpy(proxy.state, state, {3});
|
||||
if (_delta != 0) {{
|
||||
long long offset = _delta;
|
||||
{4}(proxy.state, &offset);
|
||||
}}
|
||||
return proxy;
|
||||
}}
|
||||
__device__ reference operator[](difference_type n) const {{ return *(*this + n); }}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
struct_name, // {0}
|
||||
elem_type, // {1}
|
||||
proxy_name, // {2}
|
||||
it.size, // {3}
|
||||
adv_name, // {4}
|
||||
struct_alignas(it.alignment)); // {5}
|
||||
|
||||
result.setup_code = std::format(
|
||||
R"cpp({} {};
|
||||
__builtin_memcpy({}.state, {}, {});)cpp",
|
||||
struct_name,
|
||||
var_name,
|
||||
var_name,
|
||||
state_param,
|
||||
it.size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace hostjit::codegen
|
||||
613
cccl_upstream/c/parallel.v2/src/hostjit/codegen/operators.cpp
Normal file
613
cccl_upstream/c/parallel.v2/src/hostjit/codegen/operators.cpp
Normal file
@@ -0,0 +1,613 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <format>
|
||||
|
||||
#include <hostjit/codegen/operators.hpp>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::string generate_op_source(cccl_op_t op, bool has_bitcode, bool is_stateful)
|
||||
{
|
||||
const std::string op_name = (op.name && op.name[0]) ? op.name : "user_op";
|
||||
std::string src;
|
||||
|
||||
if (op.code_type == CCCL_OP_CPP_SOURCE && op.code && op.code_size > 0)
|
||||
{
|
||||
// Embed C++ source directly
|
||||
src += std::string(op.code, op.code_size) + "\n\n";
|
||||
}
|
||||
else if (has_bitcode)
|
||||
{
|
||||
// Extern declaration for bitcode-linked operation
|
||||
if (is_stateful)
|
||||
{
|
||||
src += std::format("extern \"C\" __device__ void {}(void* state, void* a_ptr, void* b_ptr, void* out_ptr);\n\n",
|
||||
op_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
src += std::format("extern \"C\" __device__ void {}(void* a_ptr, void* b_ptr, void* out_ptr);\n\n", op_name);
|
||||
}
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
std::string generate_binary_functor(cccl_op_t op, const std::string& accum_type, const std::string& functor_name)
|
||||
{
|
||||
const std::string op_name = (op.name && op.name[0]) ? op.name : "user_op";
|
||||
const bool is_stateful = (op.type == CCCL_STATEFUL);
|
||||
|
||||
// Templated operator() lets CUB instantiate the functor with whatever
|
||||
// element types its kernel deduces (important for binary transform with
|
||||
// two differently-typed input iterators). The user's bitcode hop takes
|
||||
// void* anyway, so the concrete arg types only need to be addressable.
|
||||
if (is_stateful)
|
||||
{
|
||||
// Embed the user's state bytes inline. When CUB launches a kernel with
|
||||
// this functor by value, the bytes ride along in the launch-arg buffer
|
||||
// into device constant memory, so the address handed to the user's op
|
||||
// (`state_bytes`) is a valid device-side pointer. Storing a host pointer
|
||||
// here would crash on first device-side dereference.
|
||||
const size_t state_size = op.size > 0 ? op.size : 1;
|
||||
const size_t state_align = op.alignment > 0 ? op.alignment : 1;
|
||||
return std::format(
|
||||
R"cpp(struct {0} {{
|
||||
alignas({3}) unsigned char state_bytes[{4}];
|
||||
template <typename _A, typename _B>
|
||||
__host__ __device__ __forceinline__
|
||||
{1} operator()(const _A& a, const _B& b) const {{
|
||||
{1} result;
|
||||
{2}((void*)state_bytes, (void*)&a, (void*)&b, (void*)&result);
|
||||
return result;
|
||||
}}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
accum_type,
|
||||
op_name,
|
||||
state_align,
|
||||
state_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::format(
|
||||
R"cpp(struct {0} {{
|
||||
template <typename _A, typename _B>
|
||||
__host__ __device__ __forceinline__
|
||||
{1} operator()(const _A& a, const _B& b) const {{
|
||||
{1} result;
|
||||
{2}((void*)&a, (void*)&b, (void*)&result);
|
||||
return result;
|
||||
}}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
accum_type,
|
||||
op_name);
|
||||
}
|
||||
}
|
||||
|
||||
std::string generate_comparison_functor(cccl_op_t op, const std::string& key_type, const std::string& functor_name)
|
||||
{
|
||||
const std::string op_name = (op.name && op.name[0]) ? op.name : "user_op";
|
||||
const bool is_stateful = (op.type == CCCL_STATEFUL);
|
||||
|
||||
if (is_stateful)
|
||||
{
|
||||
// See generate_binary_functor: state must travel by value via kernel-arg
|
||||
// copy, not by host pointer, or the device-side deref crashes.
|
||||
const size_t state_size = op.size > 0 ? op.size : 1;
|
||||
const size_t state_align = op.alignment > 0 ? op.alignment : 1;
|
||||
return std::format(
|
||||
R"cpp(struct {0} {{
|
||||
alignas({3}) unsigned char state_bytes[{4}];
|
||||
__host__ __device__ __forceinline__
|
||||
bool operator()(const {1}& a, const {2}& b) const {{
|
||||
bool result;
|
||||
{5}((void*)state_bytes, (void*)&a, (void*)&b, (void*)&result);
|
||||
return result;
|
||||
}}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
key_type,
|
||||
key_type,
|
||||
state_align,
|
||||
state_size,
|
||||
op_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::format(
|
||||
R"cpp(struct {} {{
|
||||
__host__ __device__ __forceinline__
|
||||
bool operator()(const {}& a, const {}& b) const {{
|
||||
bool result;
|
||||
{}((void*)&a, (void*)&b, (void*)&result);
|
||||
return result;
|
||||
}}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
key_type,
|
||||
key_type,
|
||||
op_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the cuda::std (or cuda::) functor type string for a well-known binary op, or nullptr if not well-known.
|
||||
const char* get_well_known_binary_functor_type(cccl_op_kind_t kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case CCCL_PLUS:
|
||||
return "::cuda::std::plus<>";
|
||||
case CCCL_MINUS:
|
||||
return "::cuda::std::minus<>";
|
||||
case CCCL_MULTIPLIES:
|
||||
return "::cuda::std::multiplies<>";
|
||||
case CCCL_DIVIDES:
|
||||
return "::cuda::std::divides<>";
|
||||
case CCCL_MODULUS:
|
||||
return "::cuda::std::modulus<>";
|
||||
case CCCL_EQUAL_TO:
|
||||
return "::cuda::std::equal_to<>";
|
||||
case CCCL_NOT_EQUAL_TO:
|
||||
return "::cuda::std::not_equal_to<>";
|
||||
case CCCL_GREATER:
|
||||
return "::cuda::std::greater<>";
|
||||
case CCCL_LESS:
|
||||
return "::cuda::std::less<>";
|
||||
case CCCL_GREATER_EQUAL:
|
||||
return "::cuda::std::greater_equal<>";
|
||||
case CCCL_LESS_EQUAL:
|
||||
return "::cuda::std::less_equal<>";
|
||||
case CCCL_LOGICAL_AND:
|
||||
return "::cuda::std::logical_and<>";
|
||||
case CCCL_LOGICAL_OR:
|
||||
return "::cuda::std::logical_or<>";
|
||||
case CCCL_BIT_AND:
|
||||
return "::cuda::std::bit_and<>";
|
||||
case CCCL_BIT_OR:
|
||||
return "::cuda::std::bit_or<>";
|
||||
case CCCL_BIT_XOR:
|
||||
return "::cuda::std::bit_xor<>";
|
||||
case CCCL_MINIMUM:
|
||||
return "::cuda::minimum<>";
|
||||
case CCCL_MAXIMUM:
|
||||
return "::cuda::maximum<>";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the cuda::std functor type string for a well-known unary op, or nullptr if not well-known.
|
||||
const char* get_well_known_unary_functor_type(cccl_op_kind_t kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case CCCL_LOGICAL_NOT:
|
||||
return "::cuda::std::logical_not<>";
|
||||
case CCCL_BIT_NOT:
|
||||
return "::cuda::std::bit_not<>";
|
||||
case CCCL_IDENTITY:
|
||||
return "::cuda::std::identity";
|
||||
case CCCL_NEGATE:
|
||||
return "::cuda::std::negate<>";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the C++ operator symbol for a well-known op, or nullptr if none.
|
||||
const char* get_well_known_op_symbol(cccl_op_kind_t kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case CCCL_PLUS:
|
||||
return "+";
|
||||
case CCCL_MINUS:
|
||||
return "-";
|
||||
case CCCL_MULTIPLIES:
|
||||
return "*";
|
||||
case CCCL_DIVIDES:
|
||||
return "/";
|
||||
case CCCL_MODULUS:
|
||||
return "%";
|
||||
case CCCL_EQUAL_TO:
|
||||
return "==";
|
||||
case CCCL_NOT_EQUAL_TO:
|
||||
return "!=";
|
||||
case CCCL_GREATER:
|
||||
return ">";
|
||||
case CCCL_LESS:
|
||||
return "<";
|
||||
case CCCL_GREATER_EQUAL:
|
||||
return ">=";
|
||||
case CCCL_LESS_EQUAL:
|
||||
return "<=";
|
||||
case CCCL_LOGICAL_AND:
|
||||
return "&&";
|
||||
case CCCL_LOGICAL_OR:
|
||||
return "||";
|
||||
case CCCL_LOGICAL_NOT:
|
||||
return "!";
|
||||
case CCCL_BIT_AND:
|
||||
return "&";
|
||||
case CCCL_BIT_OR:
|
||||
return "|";
|
||||
case CCCL_BIT_XOR:
|
||||
return "^";
|
||||
case CCCL_BIT_NOT:
|
||||
return "~";
|
||||
case CCCL_NEGATE:
|
||||
return "-";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate preamble for a well-known binary op.
|
||||
// For custom types with user-provided code, declares the extern "C" function
|
||||
// and generates an operator overload that calls it.
|
||||
// For primitive types without user code, no preamble is needed.
|
||||
std::string
|
||||
generate_well_known_preamble(cccl_op_t op, const std::string& accum_type, bool has_bitcode, bool is_comparison)
|
||||
{
|
||||
const std::string op_name = (op.name && op.name[0]) ? op.name : "user_op";
|
||||
const std::string return_type = is_comparison ? "bool" : accum_type;
|
||||
const char* symbol = get_well_known_op_symbol(op.type);
|
||||
bool has_user_code = has_bitcode || (op.code_type == CCCL_OP_CPP_SOURCE && op.code && op.code_size > 0);
|
||||
|
||||
if (!has_user_code)
|
||||
{
|
||||
// Pure well-known op on a primitive type — no preamble needed.
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string src;
|
||||
|
||||
if (op.code_type == CCCL_OP_CPP_SOURCE && op.code && op.code_size > 0)
|
||||
{
|
||||
// Embed C++ source directly (may contain type definitions).
|
||||
src += std::string(op.code, op.code_size) + "\n\n";
|
||||
}
|
||||
|
||||
// Declare the extern "C" function from bitcode.
|
||||
if (has_bitcode)
|
||||
{
|
||||
src += std::format("extern \"C\" __device__ void {}(void* a_ptr, void* b_ptr, void* out_ptr);\n\n", op_name);
|
||||
}
|
||||
|
||||
// Generate an operator overload that calls the user-provided function,
|
||||
// so cuda::std::plus<> (etc.) can use it on custom types.
|
||||
if (symbol)
|
||||
{
|
||||
src += std::format(
|
||||
R"cpp(__device__ {0} operator{1}(const {2}& lhs, const {2}& rhs) {{
|
||||
{0} ret;
|
||||
{3}((void*)&lhs, (void*)&rhs, (void*)&ret);
|
||||
return ret;
|
||||
}}
|
||||
|
||||
)cpp",
|
||||
return_type,
|
||||
symbol,
|
||||
accum_type,
|
||||
op_name);
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
|
||||
// Generate preamble for a well-known unary op with user-provided code.
|
||||
// The operator overload lets the cuda::std functor invoke that code for a
|
||||
// custom type. Primitive types without user code need no preamble.
|
||||
std::string generate_well_known_unary_preamble(
|
||||
cccl_op_t op, const std::string& in_type, const std::string& out_type, bool has_bitcode)
|
||||
{
|
||||
const std::string op_name = (op.name && op.name[0]) ? op.name : "user_op";
|
||||
const char* symbol = get_well_known_op_symbol(op.type);
|
||||
bool has_user_code = has_bitcode || (op.code_type == CCCL_OP_CPP_SOURCE && op.code && op.code_size > 0);
|
||||
|
||||
if (!has_user_code)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string src;
|
||||
|
||||
if (op.code_type == CCCL_OP_CPP_SOURCE && op.code && op.code_size > 0)
|
||||
{
|
||||
src += std::string(op.code, op.code_size) + "\n\n";
|
||||
}
|
||||
|
||||
if (has_bitcode)
|
||||
{
|
||||
src += std::format("extern \"C\" __device__ void {}(void* a_ptr, void* out_ptr);\n\n", op_name);
|
||||
}
|
||||
|
||||
if (symbol)
|
||||
{
|
||||
src += std::format(
|
||||
R"cpp(__device__ {0} operator{1}(const {2}& value) {{
|
||||
{0} ret;
|
||||
{3}((void*)&value, (void*)&ret);
|
||||
return ret;
|
||||
}}
|
||||
|
||||
)cpp",
|
||||
out_type,
|
||||
symbol,
|
||||
in_type,
|
||||
op_name);
|
||||
}
|
||||
|
||||
return src;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
OperatorCode make_binary_op(
|
||||
cccl_op_t op,
|
||||
const std::string& accum_type,
|
||||
const std::string& functor_name,
|
||||
const std::string& var_name,
|
||||
const std::string& state_param,
|
||||
bool has_bitcode)
|
||||
{
|
||||
// For well-known operations, use cuda::std functors directly.
|
||||
// For custom types, generate an operator overload that wraps the user-provided function.
|
||||
// If the caller provided bitcode, prefer it: the well-known functor (e.g.
|
||||
// cuda::std::plus<void>) may not be invocable on the custom value type.
|
||||
const char* well_known_type = get_well_known_binary_functor_type(op.type);
|
||||
if (well_known_type && !has_bitcode)
|
||||
{
|
||||
OperatorCode result;
|
||||
result.local_var = var_name;
|
||||
result.preamble = generate_well_known_preamble(op, accum_type, has_bitcode, /*is_comparison=*/false);
|
||||
result.setup_code = std::format("{} {}{{}};", well_known_type, var_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
const bool is_stateful = (op.type == CCCL_STATEFUL);
|
||||
|
||||
OperatorCode result;
|
||||
result.local_var = var_name;
|
||||
result.preamble = generate_op_source(op, has_bitcode, is_stateful);
|
||||
result.preamble += generate_binary_functor(op, accum_type, functor_name);
|
||||
|
||||
if (is_stateful)
|
||||
{
|
||||
const size_t state_size = op.size > 0 ? op.size : 1;
|
||||
result.setup_code = std::format(
|
||||
"{0} {1}; __builtin_memcpy({1}.state_bytes, {2}, {3});", functor_name, var_name, state_param, state_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.setup_code = std::format("{} {};", functor_name, var_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OperatorCode make_unary_op(
|
||||
cccl_op_t op,
|
||||
const std::string& in_type,
|
||||
const std::string& out_type,
|
||||
const std::string& functor_name,
|
||||
const std::string& var_name,
|
||||
const std::string& state_param,
|
||||
bool has_bitcode)
|
||||
{
|
||||
// Well-known operations map directly to cuda::std unary functors. If the
|
||||
// caller provided bitcode, prefer it because the functor may not be
|
||||
// invocable on the user's custom value type.
|
||||
const char* well_known_type = get_well_known_unary_functor_type(op.type);
|
||||
if (well_known_type && !has_bitcode)
|
||||
{
|
||||
OperatorCode result;
|
||||
result.local_var = var_name;
|
||||
result.preamble = generate_well_known_unary_preamble(op, in_type, out_type, has_bitcode);
|
||||
result.setup_code = std::format("{} {}{{}};", well_known_type, var_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
const bool is_stateful = (op.type == CCCL_STATEFUL);
|
||||
const std::string op_name = (op.name && op.name[0]) ? op.name : "user_op";
|
||||
|
||||
OperatorCode result;
|
||||
result.local_var = var_name;
|
||||
|
||||
// Preamble: extern decl or embedded C++ source
|
||||
if (op.code_type == CCCL_OP_CPP_SOURCE && op.code && op.code_size > 0)
|
||||
{
|
||||
result.preamble += std::string(op.code, op.code_size) + "\n\n";
|
||||
}
|
||||
else if (has_bitcode)
|
||||
{
|
||||
if (is_stateful)
|
||||
{
|
||||
result.preamble +=
|
||||
std::format("extern \"C\" __device__ void {}(void* state, void* a_ptr, void* result_ptr);\n\n", op_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.preamble += std::format("extern \"C\" __device__ void {}(void* a_ptr, void* result_ptr);\n\n", op_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Functor struct
|
||||
if (is_stateful)
|
||||
{
|
||||
// See generate_binary_functor: state must travel by value via kernel-arg
|
||||
// copy, not by host pointer, or the device-side deref crashes.
|
||||
const size_t state_size = op.size > 0 ? op.size : 1;
|
||||
const size_t state_align = op.alignment > 0 ? op.alignment : 1;
|
||||
result.preamble += std::format(
|
||||
R"cpp(struct {0} {{
|
||||
alignas({4}) unsigned char state_bytes[{5}];
|
||||
__host__ __device__ __forceinline__
|
||||
{1} operator()(const {2}& a) const {{
|
||||
{3} result;
|
||||
{6}((void*)state_bytes, (void*)&a, (void*)&result);
|
||||
return result;
|
||||
}}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
out_type,
|
||||
in_type,
|
||||
out_type,
|
||||
state_align,
|
||||
state_size,
|
||||
op_name);
|
||||
result.setup_code = std::format(
|
||||
"{0} {1}; __builtin_memcpy({1}.state_bytes, {2}, {3});", functor_name, var_name, state_param, state_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.preamble += std::format(
|
||||
R"cpp(struct {} {{
|
||||
__host__ __device__ __forceinline__
|
||||
{} operator()(const {}& a) const {{
|
||||
{} result;
|
||||
{}((void*)&a, (void*)&result);
|
||||
return result;
|
||||
}}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
out_type,
|
||||
in_type,
|
||||
out_type,
|
||||
op_name);
|
||||
result.setup_code = std::format("{} {};", functor_name, var_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OperatorCode make_comparison_op(
|
||||
cccl_op_t op,
|
||||
const std::string& key_type,
|
||||
const std::string& functor_name,
|
||||
const std::string& var_name,
|
||||
const std::string& state_param,
|
||||
bool has_bitcode)
|
||||
{
|
||||
const char* well_known_type = get_well_known_binary_functor_type(op.type);
|
||||
if (well_known_type && !has_bitcode)
|
||||
{
|
||||
OperatorCode result;
|
||||
result.local_var = var_name;
|
||||
result.preamble = generate_well_known_preamble(op, key_type, has_bitcode, /*is_comparison=*/true);
|
||||
result.setup_code = std::format("{} {}{{}};", well_known_type, var_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
const bool is_stateful = (op.type == CCCL_STATEFUL);
|
||||
|
||||
OperatorCode result;
|
||||
result.local_var = var_name;
|
||||
result.preamble = generate_op_source(op, has_bitcode, is_stateful);
|
||||
result.preamble += generate_comparison_functor(op, key_type, functor_name);
|
||||
|
||||
if (is_stateful)
|
||||
{
|
||||
const size_t state_size = op.size > 0 ? op.size : 1;
|
||||
result.setup_code = std::format(
|
||||
"{0} {1}; __builtin_memcpy({1}.state_bytes, {2}, {3});", functor_name, var_name, state_param, state_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.setup_code = std::format("{} {};", functor_name, var_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
OperatorCode make_for_each_op(
|
||||
cccl_op_t op,
|
||||
const std::string& elem_type,
|
||||
const std::string& functor_name,
|
||||
const std::string& var_name,
|
||||
const std::string& state_param,
|
||||
bool has_bitcode)
|
||||
{
|
||||
const std::string op_name = (op.name && op.name[0]) ? op.name : "user_op";
|
||||
const bool is_stateful = (op.type == CCCL_STATEFUL);
|
||||
|
||||
OperatorCode result;
|
||||
result.local_var = var_name;
|
||||
|
||||
// Forward declaration / embedded source.
|
||||
if (op.code_type == CCCL_OP_CPP_SOURCE && op.code && op.code_size > 0)
|
||||
{
|
||||
result.preamble += std::string(op.code, op.code_size) + "\n\n";
|
||||
}
|
||||
else if (has_bitcode)
|
||||
{
|
||||
if (is_stateful)
|
||||
{
|
||||
result.preamble +=
|
||||
std::format("extern \"C\" __device__ void {}(void* state, {}* input);\n\n", op_name, elem_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.preamble += std::format("extern \"C\" __device__ void {}({}* input);\n\n", op_name, elem_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_stateful)
|
||||
{
|
||||
const size_t state_size = op.size > 0 ? op.size : 1;
|
||||
const size_t state_align = op.alignment > 0 ? op.alignment : 1;
|
||||
result.preamble += std::format(
|
||||
R"cpp(struct {0} {{
|
||||
alignas({3}) unsigned char state_bytes[{4}];
|
||||
__device__ __forceinline__ void operator()({1}& elem) const {{ {2}((void*)state_bytes, &elem); }}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
elem_type,
|
||||
op_name,
|
||||
state_align,
|
||||
state_size);
|
||||
result.setup_code = std::format(
|
||||
"{0} {1}; __builtin_memcpy({1}.state_bytes, {2}, {3});", functor_name, var_name, state_param, state_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.preamble += std::format(
|
||||
R"cpp(struct {0} {{
|
||||
__device__ __forceinline__ void operator()({1}& elem) const {{ {2}(&elem); }}
|
||||
}};
|
||||
|
||||
)cpp",
|
||||
functor_name,
|
||||
elem_type,
|
||||
op_name);
|
||||
result.setup_code = std::format("{} {}{{}};", functor_name, var_name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace hostjit::codegen
|
||||
73
cccl_upstream/c/parallel.v2/src/hostjit/codegen/types.cpp
Normal file
73
cccl_upstream/c/parallel.v2/src/hostjit/codegen/types.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <format>
|
||||
|
||||
#include <hostjit/codegen/types.hpp>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
std::string get_type_name(cccl_type_enum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CCCL_INT8:
|
||||
return "char";
|
||||
case CCCL_INT16:
|
||||
return "short";
|
||||
case CCCL_INT32:
|
||||
return "int";
|
||||
case CCCL_INT64:
|
||||
return "long long";
|
||||
case CCCL_UINT8:
|
||||
return "unsigned char";
|
||||
case CCCL_UINT16:
|
||||
return "unsigned short";
|
||||
case CCCL_UINT32:
|
||||
return "unsigned int";
|
||||
case CCCL_UINT64:
|
||||
return "unsigned long long";
|
||||
case CCCL_FLOAT16:
|
||||
return "__half";
|
||||
case CCCL_FLOAT32:
|
||||
return "float";
|
||||
case CCCL_FLOAT64:
|
||||
return "double";
|
||||
case CCCL_BOOLEAN:
|
||||
return "bool";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string make_storage_type(const char* name, size_t size, size_t alignment)
|
||||
{
|
||||
return std::format(
|
||||
R"cpp(struct __align__({}) {} {{
|
||||
char data[{}];
|
||||
}};
|
||||
)cpp",
|
||||
alignment,
|
||||
name,
|
||||
size);
|
||||
}
|
||||
|
||||
std::string resolve_type(cccl_type_info info, const char* fallback_alias, std::string& out_preamble)
|
||||
{
|
||||
auto name = get_type_name(info.type);
|
||||
if (!name.empty())
|
||||
{
|
||||
return name;
|
||||
}
|
||||
// Custom type: emit storage struct definition, return alias
|
||||
out_preamble += make_storage_type(fallback_alias, info.size, info.alignment);
|
||||
return fallback_alias;
|
||||
}
|
||||
} // namespace hostjit::codegen
|
||||
1727
cccl_upstream/c/parallel.v2/src/hostjit/compiler.cpp
Normal file
1727
cccl_upstream/c/parallel.v2/src/hostjit/compiler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
175
cccl_upstream/c/parallel.v2/src/hostjit/config.cpp
Normal file
175
cccl_upstream/c/parallel.v2/src/hostjit/config.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include <hostjit/config.hpp>
|
||||
|
||||
namespace hostjit
|
||||
{
|
||||
CompilerConfig detectDefaultConfig()
|
||||
{
|
||||
CompilerConfig config;
|
||||
|
||||
// Detect CUDA toolkit path
|
||||
if (const char* env = std::getenv("CUDA_PATH"))
|
||||
{
|
||||
config.cuda_toolkit_path = env;
|
||||
}
|
||||
else if (const char* env = std::getenv("CUDA_HOME"))
|
||||
{
|
||||
config.cuda_toolkit_path = env;
|
||||
}
|
||||
#ifdef CUDA_TOOLKIT_PATH
|
||||
else
|
||||
{
|
||||
config.cuda_toolkit_path = CUDA_TOOLKIT_PATH;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Set up library paths if CUDA toolkit was found
|
||||
if (!config.cuda_toolkit_path.empty())
|
||||
{
|
||||
std::filesystem::path lib64_path = std::filesystem::path(config.cuda_toolkit_path) / "lib64";
|
||||
std::filesystem::path lib_path = std::filesystem::path(config.cuda_toolkit_path) / "lib";
|
||||
|
||||
if (std::filesystem::exists(lib64_path))
|
||||
{
|
||||
config.library_paths.push_back(lib64_path.string());
|
||||
}
|
||||
else if (std::filesystem::exists(lib_path))
|
||||
{
|
||||
config.library_paths.push_back(lib_path.string());
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-detect GPU compute capability using CUDA runtime
|
||||
int device = 0;
|
||||
if (cudaGetDevice(&device) == cudaSuccess)
|
||||
{
|
||||
cudaDeviceProp prop;
|
||||
if (cudaGetDeviceProperties(&prop, device) == cudaSuccess)
|
||||
{
|
||||
int detected_sm = prop.major * 10 + prop.minor;
|
||||
if (detected_sm >= 75)
|
||||
{
|
||||
config.sm_version = detected_sm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.sm_version == 0)
|
||||
{
|
||||
config.sm_version = 75;
|
||||
}
|
||||
|
||||
config.optimization_level = 2;
|
||||
config.debug = false;
|
||||
config.verbose = false;
|
||||
|
||||
// Detect hostjit include path
|
||||
if (const char* env = std::getenv("HOSTJIT_INCLUDE_PATH"))
|
||||
{
|
||||
config.hostjit_include_path = env;
|
||||
}
|
||||
#ifdef HOSTJIT_INCLUDE_DIR
|
||||
else
|
||||
{
|
||||
config.hostjit_include_path = HOSTJIT_INCLUDE_DIR;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Detect clang headers path. Build-time CLANG_HEADERS_DIR is the default;
|
||||
// HOSTJIT_CLANG_PATH overrides it (e.g. for pip-installed wheels with a
|
||||
// packaged copy of clang's CUDA headers).
|
||||
if (const char* env = std::getenv("HOSTJIT_CLANG_PATH"))
|
||||
{
|
||||
config.clang_headers_path = env;
|
||||
}
|
||||
#ifdef CLANG_HEADERS_DIR
|
||||
else
|
||||
{
|
||||
config.clang_headers_path = CLANG_HEADERS_DIR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
bool validateConfig(const CompilerConfig& config, std::string* error_message)
|
||||
{
|
||||
if (config.cuda_toolkit_path.empty())
|
||||
{
|
||||
if (error_message)
|
||||
{
|
||||
*error_message = "CUDA toolkit path not found. Please set CUDA_PATH or CUDA_HOME environment variable.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(config.cuda_toolkit_path))
|
||||
{
|
||||
if (error_message)
|
||||
{
|
||||
*error_message = "CUDA toolkit path does not exist: " + config.cuda_toolkit_path;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::path cuda_h = std::filesystem::path(config.cuda_toolkit_path) / "include" / "cuda.h";
|
||||
if (!std::filesystem::exists(cuda_h))
|
||||
{
|
||||
if (error_message)
|
||||
{
|
||||
*error_message = "CUDA headers not found at: " + cuda_h.string();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& include_path : config.include_paths)
|
||||
{
|
||||
if (!std::filesystem::exists(include_path))
|
||||
{
|
||||
if (error_message)
|
||||
{
|
||||
*error_message = "Include path does not exist: " + include_path;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& library_path : config.library_paths)
|
||||
{
|
||||
if (!std::filesystem::exists(library_path))
|
||||
{
|
||||
if (error_message)
|
||||
{
|
||||
*error_message = "Library path does not exist: " + library_path;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (config.sm_version < 30 || config.sm_version > 150)
|
||||
{
|
||||
if (error_message)
|
||||
{
|
||||
*error_message = "Invalid SM version: " + std::to_string(config.sm_version) + " (must be between 30 and 150)";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.optimization_level < 0 || config.optimization_level > 3)
|
||||
{
|
||||
if (error_message)
|
||||
{
|
||||
*error_message =
|
||||
"Invalid optimization level: " + std::to_string(config.optimization_level) + " (must be between 0 and 3)";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace hostjit
|
||||
@@ -0,0 +1,56 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <cccl/c/types.h>
|
||||
#include <hostjit/config.hpp>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
// Manages bitcode files needed for linking. Collects LTOIR, LLVM IR,
|
||||
// and C++ source (compiling the latter to bitcode on the fly).
|
||||
// Tracks temp file paths for cleanup.
|
||||
class BitcodeCollector
|
||||
{
|
||||
public:
|
||||
explicit BitcodeCollector(CompilerConfig& config, uintptr_t unique_id);
|
||||
|
||||
// Add bitcode from an operator (handles LTOIR, LLVM_IR, CPP_SOURCE,
|
||||
// and extra modules).
|
||||
void add_op(cccl_op_t op, const std::string& label);
|
||||
|
||||
// Add bitcode from a custom iterator's advance/dereference ops.
|
||||
void add_iterator(cccl_iterator_t it, const std::string& label_prefix);
|
||||
|
||||
// Returns true if the op has linked bitcode (LTOIR or LLVM_IR).
|
||||
static bool is_bitcode_op(cccl_op_t op);
|
||||
|
||||
// Clean up all temporary files.
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
void add_raw_bitcode(const char* data, size_t size, const std::string& name);
|
||||
bool compile_and_add(const char* source, size_t source_size, const std::string& name);
|
||||
void add_op_code(cccl_op_t& op, const std::string& name);
|
||||
|
||||
CompilerConfig& config_;
|
||||
uintptr_t unique_id_;
|
||||
std::vector<std::string> temp_paths_;
|
||||
std::set<std::string> added_symbols_; // dedup by op.name (when present)
|
||||
std::unordered_set<std::size_t> added_content_hashes_; // dedup by content hash for unnamed extras
|
||||
};
|
||||
} // namespace hostjit::codegen
|
||||
@@ -0,0 +1,304 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <cccl/c/types.h>
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
// Tags for non-cccl arguments (no runtime data, just control code generation)
|
||||
struct temp_storage_t
|
||||
{};
|
||||
struct temp_bytes_t
|
||||
{};
|
||||
// num_items_t carries a name so the same tag type can express num_segments,
|
||||
// num_needles, etc. — each becomes its own unsigned long long parameter.
|
||||
struct num_items_t
|
||||
{
|
||||
const char* name = "num_items";
|
||||
};
|
||||
struct stream_t
|
||||
{};
|
||||
|
||||
inline constexpr temp_storage_t temp_storage{};
|
||||
inline constexpr temp_bytes_t temp_bytes{};
|
||||
inline constexpr num_items_t num_items{};
|
||||
inline constexpr num_items_t num_segments{"num_segments"};
|
||||
inline constexpr num_items_t num_needles{"num_needles"};
|
||||
inline constexpr num_items_t num_haystack{"num_haystack"};
|
||||
inline constexpr stream_t stream{};
|
||||
|
||||
// Direction wrappers for iterators (cccl_iterator_t doesn't encode direction)
|
||||
struct input_t
|
||||
{
|
||||
cccl_iterator_t it;
|
||||
};
|
||||
struct output_t
|
||||
{
|
||||
cccl_iterator_t it;
|
||||
};
|
||||
|
||||
inline input_t in(cccl_iterator_t it)
|
||||
{
|
||||
return {it};
|
||||
}
|
||||
inline output_t out(cccl_iterator_t it)
|
||||
{
|
||||
return {it};
|
||||
}
|
||||
|
||||
// cmp_t: wraps a cccl_op_t that should generate a comparison functor
|
||||
// (bool operator()(const T&, const T&)) rather than the default binary reduce
|
||||
// functor (T operator()(T, T)). Use cmp(op) where sort/search operators go.
|
||||
struct cmp_t
|
||||
{
|
||||
cccl_op_t op;
|
||||
};
|
||||
inline cmp_t cmp(cccl_op_t op)
|
||||
{
|
||||
return {op};
|
||||
}
|
||||
|
||||
// future_val_t: the init value lives on the device at runtime. Generates
|
||||
// cub::FutureValue<accum_t>(static_cast<accum_t*>(param)) in the CUB call.
|
||||
// Carries type info so find_accum_type can resolve accum_t correctly.
|
||||
struct future_val_t
|
||||
{
|
||||
cccl_type_info type;
|
||||
};
|
||||
inline future_val_t future_val(cccl_type_info t)
|
||||
{
|
||||
return {t};
|
||||
}
|
||||
|
||||
// unary_op_t: wraps a cccl_op_t used as a unary transform operator (T -> U).
|
||||
// Carries the input/output type info so the functor can be typed correctly.
|
||||
struct unary_op_t
|
||||
{
|
||||
cccl_op_t op;
|
||||
cccl_type_info in_type;
|
||||
cccl_type_info out_type;
|
||||
};
|
||||
inline unary_op_t unary_op(cccl_op_t op, cccl_type_info in_t, cccl_type_info out_t)
|
||||
{
|
||||
return {op, in_t, out_t};
|
||||
}
|
||||
|
||||
// force_accum_type_t: overrides the accumulator type resolved by find_accum_type.
|
||||
// Use when the natural accum type (first input) differs from the desired type.
|
||||
// Generates no code — only influences type resolution.
|
||||
struct force_accum_type_t
|
||||
{
|
||||
cccl_type_info type;
|
||||
};
|
||||
inline force_accum_type_t force_accum_type(cccl_type_info t)
|
||||
{
|
||||
return {t};
|
||||
}
|
||||
|
||||
// pred(): shorthand for a unary bool predicate operator (e.g. for partition).
|
||||
// Equivalent to unary_op with out_type = bool.
|
||||
// Generates: bool operator()(const item_t& a) const { ... }
|
||||
inline unary_op_t pred(cccl_op_t op, cccl_type_info item_t)
|
||||
{
|
||||
return {op, item_t, cccl_type_info{sizeof(bool), alignof(bool), CCCL_BOOLEAN}};
|
||||
}
|
||||
|
||||
// typed_scalar_t: a by-value scalar of any cccl-known type, passed into the
|
||||
// JIT wrapper as a host pointer and memcpy'd onto the stack before the CUB
|
||||
// call. Use when the CUB API takes a small POD by value (e.g. radix_sort's
|
||||
// `int begin_bit`, histogram's `int num_levels` / `level_t lower_level`).
|
||||
// The caller supplies a void* host pointer to the value at the corresponding
|
||||
// run-time arg position.
|
||||
struct typed_scalar_t
|
||||
{
|
||||
cccl_type_info type;
|
||||
const char* name;
|
||||
};
|
||||
inline typed_scalar_t typed_scalar(cccl_type_info t, const char* name)
|
||||
{
|
||||
return {t, name};
|
||||
}
|
||||
|
||||
// env_stream_t: variant of stream_t that emits a cuda::std::execution::env
|
||||
// wrapping a cuda::stream_ref instead of a bare cudaStream_t. Use with CUB
|
||||
// algorithms that take an env (so CUB manages temp storage internally via
|
||||
// the env's memory_resource — caller doesn't have to thread it through).
|
||||
struct env_stream_t
|
||||
{};
|
||||
inline constexpr env_stream_t env_stream{};
|
||||
|
||||
// for_each_op_t: wraps a cccl_op_t with c.parallel's void op(T*) contract
|
||||
// into the void op(T&) functor that cub::DeviceFor::ForEachN expects.
|
||||
struct for_each_op_t
|
||||
{
|
||||
cccl_op_t op;
|
||||
};
|
||||
inline for_each_op_t for_each_op(cccl_op_t op)
|
||||
{
|
||||
return {op};
|
||||
}
|
||||
|
||||
// double_buffer_t: constructs a cub::DoubleBuffer<elem_t> from two host
|
||||
// pointers (one "in" buffer, one "out" buffer) and passes it to the CUB call.
|
||||
// Used by the DoubleBuffer overloads of DeviceRadixSort / DeviceSegmentedSort
|
||||
// where the caller is willing to let CUB swap buffers and report the final
|
||||
// location via the buffer's `selector` member. var_name controls the C++ name
|
||||
// of the generated local — pair a `selector_out_t` with the same name to read
|
||||
// `<var_name>.selector` after the call.
|
||||
struct double_buffer_t
|
||||
{
|
||||
cccl_iterator_t in_it;
|
||||
cccl_iterator_t out_it;
|
||||
const char* var_name;
|
||||
};
|
||||
inline double_buffer_t double_buffer(cccl_iterator_t in_it, cccl_iterator_t out_it, const char* var_name = "d_buffer")
|
||||
{
|
||||
return {in_it, out_it, var_name};
|
||||
}
|
||||
|
||||
// selector_out_t: emits a `void* selector_out` parameter and, after the CUB
|
||||
// call, writes `<buffer_var_name>.selector` to it. Must be paired with a
|
||||
// double_buffer_t whose var_name matches.
|
||||
struct selector_out_t
|
||||
{
|
||||
const char* buffer_var_name;
|
||||
};
|
||||
inline selector_out_t selector_out(const char* buffer_var_name = "d_buffer")
|
||||
{
|
||||
return {buffer_var_name};
|
||||
}
|
||||
|
||||
// Argument variant: everything that can appear in .with()
|
||||
using Arg = std::variant<
|
||||
temp_storage_t,
|
||||
temp_bytes_t,
|
||||
num_items_t,
|
||||
stream_t,
|
||||
env_stream_t,
|
||||
input_t,
|
||||
output_t,
|
||||
cccl_op_t,
|
||||
cmp_t,
|
||||
unary_op_t,
|
||||
for_each_op_t,
|
||||
double_buffer_t,
|
||||
selector_out_t,
|
||||
future_val_t,
|
||||
cccl_value_t,
|
||||
force_accum_type_t,
|
||||
typed_scalar_t>;
|
||||
|
||||
// Result of a successful single-function compilation.
|
||||
struct CubCallResult
|
||||
{
|
||||
JITCompiler* compiler; // caller takes ownership
|
||||
void* fn_ptr; // the exported function
|
||||
std::vector<char> cubin; // for SASS inspection
|
||||
};
|
||||
|
||||
// Result of a successful multi-function compilation (one TU, N functions).
|
||||
struct MultiCubCallResult
|
||||
{
|
||||
JITCompiler* compiler; // caller takes ownership; one compiler for the whole TU
|
||||
std::vector<char> cubin; // single cubin for the whole TU
|
||||
std::vector<void*> fn_ptrs; // exported functions in the same order as the input CubCalls
|
||||
};
|
||||
|
||||
class CubCall
|
||||
{
|
||||
public:
|
||||
// Start building: specify the CUB header to include.
|
||||
static CubCall from(const char* include_header);
|
||||
|
||||
// Specify the CUB function to call (e.g., "cub::DeviceReduce::Reduce").
|
||||
CubCall& run(const char* cub_function);
|
||||
|
||||
// Optionally override the exported function name (default: "cccl_jit_fn").
|
||||
CubCall& name(const char* export_name);
|
||||
|
||||
// Add arguments in CUB call order. Each argument is dispatched by type.
|
||||
template <typename... Args>
|
||||
CubCall& with(Args&&... args)
|
||||
{
|
||||
(args_.emplace_back(Arg{std::forward<Args>(args)}), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Wrap all input iterators in cuda::std::make_tuple() in the generated CUB call.
|
||||
// Required for cub::DeviceTransform::Transform with multiple inputs.
|
||||
CubCall& use_tuple_inputs()
|
||||
{
|
||||
tuple_inputs_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Generate the complete CUDA source string (useful for debugging).
|
||||
std::string source() const;
|
||||
|
||||
// Compile the generated source and return the function pointer.
|
||||
CubCallResult compile(
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
cccl_build_config* config = nullptr,
|
||||
const char* ctk_path = nullptr,
|
||||
const char* cccl_include_path = nullptr) const;
|
||||
|
||||
// Compile multiple CubCalls into a single translation unit. One Clang
|
||||
// invocation, one cubin, one JITCompiler; each function is dlsym'd by its
|
||||
// .name(...) and returned in the input order. All CubCalls must share the
|
||||
// same CUB include header (.from(...)). Per-function preambles are isolated
|
||||
// inside `namespace fn_<i> { ... }` blocks; extern "C" symbols escape the
|
||||
// namespace and stay globally dlsym-able.
|
||||
static MultiCubCallResult compile(
|
||||
std::initializer_list<CubCall> calls,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
cccl_build_config* config = nullptr,
|
||||
const char* ctk_path = nullptr,
|
||||
const char* cccl_include_path = nullptr);
|
||||
|
||||
private:
|
||||
std::string include_;
|
||||
std::string cub_function_;
|
||||
std::string fn_name_ = "cccl_jit_fn";
|
||||
std::vector<Arg> args_;
|
||||
bool tuple_inputs_ = false;
|
||||
|
||||
// Internal: just the per-function body (preamble + function defn), no
|
||||
// shared #includes. Used by the multi-compile path to wrap N bodies in
|
||||
// N namespaces under a single shared include block.
|
||||
std::string body() const;
|
||||
|
||||
// Internal: walk args_ and register any user-op / iterator bitcode with
|
||||
// the given collector. Factored out so the multi-compile path can share
|
||||
// one collector across several CubCalls.
|
||||
void collect_bitcode(class BitcodeCollector& bitcode, int& op_idx, int& in_idx, int& out_idx) const;
|
||||
|
||||
// Internal: builds a hostjit::CompilerConfig from the standard cc + paths +
|
||||
// build_config inputs every compile entry point takes. Shared between the
|
||||
// single-fn and multi-fn compile overloads so flag/path handling lives in
|
||||
// one place.
|
||||
static hostjit::CompilerConfig make_jit_config(
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
cccl_build_config* config,
|
||||
const char* ctk_path,
|
||||
const char* cccl_include_path,
|
||||
const std::string& entry_point_name);
|
||||
};
|
||||
} // namespace hostjit::codegen
|
||||
@@ -0,0 +1,50 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
// Result of generating iterator code.
|
||||
struct IteratorCode
|
||||
{
|
||||
std::string preamble; // type alias or struct definition (goes at file scope)
|
||||
std::string setup_code; // initialization inside function body
|
||||
std::string local_var; // e.g., "in_0"
|
||||
std::string type_name; // e.g., "in_0_it_t" or "accum_t*"
|
||||
};
|
||||
|
||||
// Generate code for an input iterator.
|
||||
// For CCCL_POINTER: emits a type alias and pointer cast.
|
||||
// For CCCL_ITERATOR: emits a full iterator struct with advance/dereference.
|
||||
IteratorCode make_input_iterator(
|
||||
cccl_iterator_t it,
|
||||
const std::string& value_type_name, // resolved C++ type of iterator's value
|
||||
const std::string& accum_type_name, // accumulator type alias (for pointer fallback)
|
||||
const std::string& struct_name, // e.g., "in_0_it_t"
|
||||
const std::string& var_name, // e.g., "in_0"
|
||||
const std::string& state_param); // e.g., "d_in_0" (void* param name)
|
||||
|
||||
// Generate code for an output iterator.
|
||||
// value_type_name: if non-empty, overrides accum_type_name as the element type
|
||||
// for the pointer/proxy. Use this when the output element type differs from the
|
||||
// accumulator (e.g. item values in a key-value sort).
|
||||
IteratorCode make_output_iterator(
|
||||
cccl_iterator_t it,
|
||||
const std::string& accum_type_name,
|
||||
const std::string& struct_name,
|
||||
const std::string& var_name,
|
||||
const std::string& state_param,
|
||||
const std::string& value_type_name = "");
|
||||
} // namespace hostjit::codegen
|
||||
@@ -0,0 +1,71 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
// Result of generating operator code.
|
||||
struct OperatorCode
|
||||
{
|
||||
std::string preamble; // extern decl + functor struct (goes at file scope)
|
||||
std::string setup_code; // initialization inside function body
|
||||
std::string local_var; // e.g., "op_0"
|
||||
};
|
||||
|
||||
// Generate code for a binary operator (reduce, scan).
|
||||
// Produces an extern "C" device function declaration (or inline for well-known ops)
|
||||
// and a functor struct that wraps it.
|
||||
OperatorCode make_binary_op(
|
||||
cccl_op_t op,
|
||||
const std::string& accum_type, // C++ type name for operands
|
||||
const std::string& functor_name, // e.g., "ReduceOp"
|
||||
const std::string& var_name, // e.g., "op_0"
|
||||
const std::string& state_param, // e.g., "op_0_state" (void* param name)
|
||||
bool has_bitcode);
|
||||
|
||||
// Generate code for a unary operator (transform).
|
||||
// Produces a functor with operator()(const in_type& a) const -> out_type.
|
||||
OperatorCode make_unary_op(
|
||||
cccl_op_t op,
|
||||
const std::string& in_type, // C++ type name for input operand
|
||||
const std::string& out_type, // C++ type name for result
|
||||
const std::string& functor_name, // e.g., "UnaryOp"
|
||||
const std::string& var_name, // e.g., "op_0"
|
||||
const std::string& state_param, // e.g., "op_0_state" (void* param name)
|
||||
bool has_bitcode);
|
||||
|
||||
// Generate code for a comparison operator (sort).
|
||||
// Same as binary op but the functor returns bool.
|
||||
OperatorCode make_comparison_op(
|
||||
cccl_op_t op,
|
||||
const std::string& key_type, // C++ type name for keys
|
||||
const std::string& functor_name, // e.g., "CompareOp"
|
||||
const std::string& var_name, // e.g., "cmp_0"
|
||||
const std::string& state_param, // e.g., "cmp_0_state"
|
||||
bool has_bitcode);
|
||||
|
||||
// Generate code for a for_each operator. Adapts c.parallel's user-op contract
|
||||
// (`void op(T*)`) to the contract that cub::DeviceFor::ForEachN expects
|
||||
// (`void op(T&)`). Functor is stateless for non-stateful ops; for stateful
|
||||
// ops it embeds the state bytes inline so they ride along into device
|
||||
// constant memory via the kernel-arg copy.
|
||||
OperatorCode make_for_each_op(
|
||||
cccl_op_t op,
|
||||
const std::string& elem_type, // C++ type name for the iterator's element
|
||||
const std::string& functor_name, // e.g., "ForEachOp"
|
||||
const std::string& var_name, // e.g., "op_0"
|
||||
const std::string& state_param, // e.g., "op_0_state"
|
||||
bool has_bitcode);
|
||||
} // namespace hostjit::codegen
|
||||
@@ -0,0 +1,32 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <cccl/c/types.h>
|
||||
|
||||
namespace hostjit::codegen
|
||||
{
|
||||
// Maps cccl_type_enum to plain C/C++ type names (e.g., "int", "float").
|
||||
// Returns "" for CCCL_STORAGE (caller must handle custom types).
|
||||
std::string get_type_name(cccl_type_enum type);
|
||||
|
||||
// Generates an aligned storage struct definition.
|
||||
// Example: "struct __align__(8) my_storage_t {\n char data[16];\n};\n"
|
||||
std::string make_storage_type(const char* name, size_t size, size_t alignment);
|
||||
|
||||
// Returns the C++ type name for a cccl_type_info.
|
||||
// For known types, returns the type name directly.
|
||||
// For CCCL_STORAGE, emits a storage struct definition into `out_preamble`
|
||||
// and returns `fallback_alias`.
|
||||
std::string resolve_type(cccl_type_info info, const char* fallback_alias, std::string& out_preamble);
|
||||
} // namespace hostjit::codegen
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hostjit
|
||||
{
|
||||
struct CompilationResult
|
||||
{
|
||||
bool success;
|
||||
std::string object_file_path; // Path to generated .o file
|
||||
std::string diagnostics; // Compiler messages
|
||||
std::vector<char> cubin; // Device cubin extracted during compilation
|
||||
};
|
||||
|
||||
struct BitcodeResult
|
||||
{
|
||||
bool success;
|
||||
std::string bitcode; // LLVM bitcode bytes
|
||||
std::string diagnostics;
|
||||
};
|
||||
|
||||
struct LinkResult
|
||||
{
|
||||
bool success;
|
||||
std::string library_path; // Path to .so file
|
||||
std::string diagnostics;
|
||||
};
|
||||
|
||||
// Forward declaration to avoid including heavy Clang headers
|
||||
struct CompilerConfig;
|
||||
|
||||
class CUDACompiler
|
||||
{
|
||||
public:
|
||||
CUDACompiler();
|
||||
~CUDACompiler();
|
||||
|
||||
// Compile CUDA device source to LLVM bitcode
|
||||
BitcodeResult compileToDeviceBitcode(const std::string& source_code, const CompilerConfig& config);
|
||||
|
||||
// Compile CUDA source code to object file
|
||||
CompilationResult
|
||||
compileToObject(const std::string& source_code, const std::string& output_path, const CompilerConfig& config);
|
||||
|
||||
// Link object files to shared library
|
||||
LinkResult linkToSharedLibrary(
|
||||
const std::vector<std::string>& object_files, const std::string& output_path, const CompilerConfig& config);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
Impl* impl_;
|
||||
};
|
||||
} // namespace hostjit
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace hostjit
|
||||
{
|
||||
struct CompilerConfig
|
||||
{
|
||||
std::string cuda_toolkit_path;
|
||||
std::string hostjit_include_path; // Path to hostjit include directory (for minimal CUDA runtime)
|
||||
std::string clang_headers_path; // Path to Clang's built-in CUDA headers (overrides CLANG_HEADERS_DIR)
|
||||
std::string cccl_include_path; // Path to CCCL headers (overrides CCCL_SOURCE_DIR); contains cub/, thrust/, cuda/
|
||||
std::vector<std::string> include_paths;
|
||||
std::vector<std::string> library_paths;
|
||||
std::vector<std::string> device_bitcode_files; // Raw LLVM bitcode (magic "BC") linked via LLVM's Linker
|
||||
std::vector<std::string> device_ltoir_files; // NVRTC LTOIR; linked at the nvJitLink stage with -lto
|
||||
std::unordered_map<std::string, std::string> macro_definitions; // key=macro name, value=macro value (empty for flag
|
||||
// macros)
|
||||
int sm_version = 70;
|
||||
int optimization_level = 2;
|
||||
bool debug = false;
|
||||
bool verbose = false;
|
||||
bool trace_includes = false; // Show all included headers during compilation (for debugging header search)
|
||||
bool keep_artifacts = false; // Keep compiled artifacts for inspection (PTX, object files, etc.)
|
||||
std::string entry_point_name; // Name of the exported entry point function (used for post-link optimization)
|
||||
bool enable_pch = false; // Cache precompiled headers on disk to speed up repeated builds
|
||||
};
|
||||
|
||||
// Auto-detect CUDA toolkit and create default configuration
|
||||
CompilerConfig detectDefaultConfig();
|
||||
|
||||
// Validate that the configuration is usable
|
||||
bool validateConfig(const CompilerConfig& config, std::string* error_message = nullptr);
|
||||
} // namespace hostjit
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,371 @@
|
||||
/*===-- __clang_cuda_libdevice_declares.h - decls for libdevice functions --===
|
||||
*
|
||||
* Part of the LLVM Project, 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
|
||||
*
|
||||
*===-----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef __CLANG_CUDA_LIBDEVICE_DECLARES_H__
|
||||
#define __CLANG_CUDA_LIBDEVICE_DECLARES_H__
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define __DEVICE__ __device__
|
||||
|
||||
__DEVICE__ int __nv_abs(int __a);
|
||||
__DEVICE__ double __nv_acos(double __a);
|
||||
__DEVICE__ float __nv_acosf(float __a);
|
||||
__DEVICE__ double __nv_acosh(double __a);
|
||||
__DEVICE__ float __nv_acoshf(float __a);
|
||||
__DEVICE__ double __nv_asin(double __a);
|
||||
__DEVICE__ float __nv_asinf(float __a);
|
||||
__DEVICE__ double __nv_asinh(double __a);
|
||||
__DEVICE__ float __nv_asinhf(float __a);
|
||||
__DEVICE__ double __nv_atan2(double __a, double __b);
|
||||
__DEVICE__ float __nv_atan2f(float __a, float __b);
|
||||
__DEVICE__ double __nv_atan(double __a);
|
||||
__DEVICE__ float __nv_atanf(float __a);
|
||||
__DEVICE__ double __nv_atanh(double __a);
|
||||
__DEVICE__ float __nv_atanhf(float __a);
|
||||
__DEVICE__ int __nv_brev(int __a);
|
||||
__DEVICE__ long long __nv_brevll(long long __a);
|
||||
__DEVICE__ int __nv_byte_perm(int __a, int __b, int __c);
|
||||
__DEVICE__ double __nv_cbrt(double __a);
|
||||
__DEVICE__ float __nv_cbrtf(float __a);
|
||||
__DEVICE__ double __nv_ceil(double __a);
|
||||
__DEVICE__ float __nv_ceilf(float __a);
|
||||
__DEVICE__ int __nv_clz(int __a);
|
||||
__DEVICE__ int __nv_clzll(long long __a);
|
||||
__DEVICE__ double __nv_copysign(double __a, double __b);
|
||||
__DEVICE__ float __nv_copysignf(float __a, float __b);
|
||||
__DEVICE__ double __nv_cos(double __a);
|
||||
__DEVICE__ float __nv_cosf(float __a);
|
||||
__DEVICE__ double __nv_cosh(double __a);
|
||||
__DEVICE__ float __nv_coshf(float __a);
|
||||
__DEVICE__ double __nv_cospi(double __a);
|
||||
__DEVICE__ float __nv_cospif(float __a);
|
||||
__DEVICE__ double __nv_cyl_bessel_i0(double __a);
|
||||
__DEVICE__ float __nv_cyl_bessel_i0f(float __a);
|
||||
__DEVICE__ double __nv_cyl_bessel_i1(double __a);
|
||||
__DEVICE__ float __nv_cyl_bessel_i1f(float __a);
|
||||
__DEVICE__ double __nv_dadd_rd(double __a, double __b);
|
||||
__DEVICE__ double __nv_dadd_rn(double __a, double __b);
|
||||
__DEVICE__ double __nv_dadd_ru(double __a, double __b);
|
||||
__DEVICE__ double __nv_dadd_rz(double __a, double __b);
|
||||
__DEVICE__ double __nv_ddiv_rd(double __a, double __b);
|
||||
__DEVICE__ double __nv_ddiv_rn(double __a, double __b);
|
||||
__DEVICE__ double __nv_ddiv_ru(double __a, double __b);
|
||||
__DEVICE__ double __nv_ddiv_rz(double __a, double __b);
|
||||
__DEVICE__ double __nv_dmul_rd(double __a, double __b);
|
||||
__DEVICE__ double __nv_dmul_rn(double __a, double __b);
|
||||
__DEVICE__ double __nv_dmul_ru(double __a, double __b);
|
||||
__DEVICE__ double __nv_dmul_rz(double __a, double __b);
|
||||
__DEVICE__ float __nv_double2float_rd(double __a);
|
||||
__DEVICE__ float __nv_double2float_rn(double __a);
|
||||
__DEVICE__ float __nv_double2float_ru(double __a);
|
||||
__DEVICE__ float __nv_double2float_rz(double __a);
|
||||
__DEVICE__ int __nv_double2hiint(double __a);
|
||||
__DEVICE__ int __nv_double2int_rd(double __a);
|
||||
__DEVICE__ int __nv_double2int_rn(double __a);
|
||||
__DEVICE__ int __nv_double2int_ru(double __a);
|
||||
__DEVICE__ int __nv_double2int_rz(double __a);
|
||||
__DEVICE__ long long __nv_double2ll_rd(double __a);
|
||||
__DEVICE__ long long __nv_double2ll_rn(double __a);
|
||||
__DEVICE__ long long __nv_double2ll_ru(double __a);
|
||||
__DEVICE__ long long __nv_double2ll_rz(double __a);
|
||||
__DEVICE__ int __nv_double2loint(double __a);
|
||||
__DEVICE__ unsigned int __nv_double2uint_rd(double __a);
|
||||
__DEVICE__ unsigned int __nv_double2uint_rn(double __a);
|
||||
__DEVICE__ unsigned int __nv_double2uint_ru(double __a);
|
||||
__DEVICE__ unsigned int __nv_double2uint_rz(double __a);
|
||||
__DEVICE__ unsigned long long __nv_double2ull_rd(double __a);
|
||||
__DEVICE__ unsigned long long __nv_double2ull_rn(double __a);
|
||||
__DEVICE__ unsigned long long __nv_double2ull_ru(double __a);
|
||||
__DEVICE__ unsigned long long __nv_double2ull_rz(double __a);
|
||||
__DEVICE__ unsigned long long __nv_double_as_longlong(double __a);
|
||||
__DEVICE__ double __nv_drcp_rd(double __a);
|
||||
__DEVICE__ double __nv_drcp_rn(double __a);
|
||||
__DEVICE__ double __nv_drcp_ru(double __a);
|
||||
__DEVICE__ double __nv_drcp_rz(double __a);
|
||||
__DEVICE__ double __nv_dsqrt_rd(double __a);
|
||||
__DEVICE__ double __nv_dsqrt_rn(double __a);
|
||||
__DEVICE__ double __nv_dsqrt_ru(double __a);
|
||||
__DEVICE__ double __nv_dsqrt_rz(double __a);
|
||||
__DEVICE__ double __nv_dsub_rd(double __a, double __b);
|
||||
__DEVICE__ double __nv_dsub_rn(double __a, double __b);
|
||||
__DEVICE__ double __nv_dsub_ru(double __a, double __b);
|
||||
__DEVICE__ double __nv_dsub_rz(double __a, double __b);
|
||||
__DEVICE__ double __nv_erfc(double __a);
|
||||
__DEVICE__ float __nv_erfcf(float __a);
|
||||
__DEVICE__ double __nv_erfcinv(double __a);
|
||||
__DEVICE__ float __nv_erfcinvf(float __a);
|
||||
__DEVICE__ double __nv_erfcx(double __a);
|
||||
__DEVICE__ float __nv_erfcxf(float __a);
|
||||
__DEVICE__ double __nv_erf(double __a);
|
||||
__DEVICE__ float __nv_erff(float __a);
|
||||
__DEVICE__ double __nv_erfinv(double __a);
|
||||
__DEVICE__ float __nv_erfinvf(float __a);
|
||||
__DEVICE__ double __nv_exp10(double __a);
|
||||
__DEVICE__ float __nv_exp10f(float __a);
|
||||
__DEVICE__ double __nv_exp2(double __a);
|
||||
__DEVICE__ float __nv_exp2f(float __a);
|
||||
__DEVICE__ double __nv_exp(double __a);
|
||||
__DEVICE__ float __nv_expf(float __a);
|
||||
__DEVICE__ double __nv_expm1(double __a);
|
||||
__DEVICE__ float __nv_expm1f(float __a);
|
||||
__DEVICE__ double __nv_fabs(double __a);
|
||||
__DEVICE__ float __nv_fabsf(float __a);
|
||||
__DEVICE__ float __nv_fadd_rd(float __a, float __b);
|
||||
__DEVICE__ float __nv_fadd_rn(float __a, float __b);
|
||||
__DEVICE__ float __nv_fadd_ru(float __a, float __b);
|
||||
__DEVICE__ float __nv_fadd_rz(float __a, float __b);
|
||||
__DEVICE__ float __nv_fast_cosf(float __a);
|
||||
__DEVICE__ float __nv_fast_exp10f(float __a);
|
||||
__DEVICE__ float __nv_fast_expf(float __a);
|
||||
__DEVICE__ float __nv_fast_fdividef(float __a, float __b);
|
||||
__DEVICE__ float __nv_fast_log10f(float __a);
|
||||
__DEVICE__ float __nv_fast_log2f(float __a);
|
||||
__DEVICE__ float __nv_fast_logf(float __a);
|
||||
__DEVICE__ float __nv_fast_powf(float __a, float __b);
|
||||
__DEVICE__ void __nv_fast_sincosf(float __a, float* __s, float* __c);
|
||||
__DEVICE__ float __nv_fast_sinf(float __a);
|
||||
__DEVICE__ float __nv_fast_tanf(float __a);
|
||||
__DEVICE__ double __nv_fdim(double __a, double __b);
|
||||
__DEVICE__ float __nv_fdimf(float __a, float __b);
|
||||
__DEVICE__ float __nv_fdiv_rd(float __a, float __b);
|
||||
__DEVICE__ float __nv_fdiv_rn(float __a, float __b);
|
||||
__DEVICE__ float __nv_fdiv_ru(float __a, float __b);
|
||||
__DEVICE__ float __nv_fdiv_rz(float __a, float __b);
|
||||
__DEVICE__ int __nv_ffs(int __a);
|
||||
__DEVICE__ int __nv_ffsll(long long __a);
|
||||
__DEVICE__ int __nv_finitef(float __a);
|
||||
__DEVICE__ unsigned short __nv_float2half_rn(float __a);
|
||||
__DEVICE__ int __nv_float2int_rd(float __a);
|
||||
__DEVICE__ int __nv_float2int_rn(float __a);
|
||||
__DEVICE__ int __nv_float2int_ru(float __a);
|
||||
__DEVICE__ int __nv_float2int_rz(float __a);
|
||||
__DEVICE__ long long __nv_float2ll_rd(float __a);
|
||||
__DEVICE__ long long __nv_float2ll_rn(float __a);
|
||||
__DEVICE__ long long __nv_float2ll_ru(float __a);
|
||||
__DEVICE__ long long __nv_float2ll_rz(float __a);
|
||||
__DEVICE__ unsigned int __nv_float2uint_rd(float __a);
|
||||
__DEVICE__ unsigned int __nv_float2uint_rn(float __a);
|
||||
__DEVICE__ unsigned int __nv_float2uint_ru(float __a);
|
||||
__DEVICE__ unsigned int __nv_float2uint_rz(float __a);
|
||||
__DEVICE__ unsigned long long __nv_float2ull_rd(float __a);
|
||||
__DEVICE__ unsigned long long __nv_float2ull_rn(float __a);
|
||||
__DEVICE__ unsigned long long __nv_float2ull_ru(float __a);
|
||||
__DEVICE__ unsigned long long __nv_float2ull_rz(float __a);
|
||||
__DEVICE__ int __nv_float_as_int(float __a);
|
||||
__DEVICE__ unsigned int __nv_float_as_uint(float __a);
|
||||
__DEVICE__ double __nv_floor(double __a);
|
||||
__DEVICE__ float __nv_floorf(float __a);
|
||||
__DEVICE__ double __nv_fma(double __a, double __b, double __c);
|
||||
__DEVICE__ float __nv_fmaf(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_ieee_rd(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_ieee_rn(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_ieee_ru(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_ieee_rz(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_rd(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_rn(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_ru(float __a, float __b, float __c);
|
||||
__DEVICE__ float __nv_fmaf_rz(float __a, float __b, float __c);
|
||||
__DEVICE__ double __nv_fma_rd(double __a, double __b, double __c);
|
||||
__DEVICE__ double __nv_fma_rn(double __a, double __b, double __c);
|
||||
__DEVICE__ double __nv_fma_ru(double __a, double __b, double __c);
|
||||
__DEVICE__ double __nv_fma_rz(double __a, double __b, double __c);
|
||||
__DEVICE__ double __nv_fmax(double __a, double __b);
|
||||
__DEVICE__ float __nv_fmaxf(float __a, float __b);
|
||||
__DEVICE__ double __nv_fmin(double __a, double __b);
|
||||
__DEVICE__ float __nv_fminf(float __a, float __b);
|
||||
__DEVICE__ double __nv_fmod(double __a, double __b);
|
||||
__DEVICE__ float __nv_fmodf(float __a, float __b);
|
||||
__DEVICE__ float __nv_fmul_rd(float __a, float __b);
|
||||
__DEVICE__ float __nv_fmul_rn(float __a, float __b);
|
||||
__DEVICE__ float __nv_fmul_ru(float __a, float __b);
|
||||
__DEVICE__ float __nv_fmul_rz(float __a, float __b);
|
||||
__DEVICE__ float __nv_frcp_rd(float __a);
|
||||
__DEVICE__ float __nv_frcp_rn(float __a);
|
||||
__DEVICE__ float __nv_frcp_ru(float __a);
|
||||
__DEVICE__ float __nv_frcp_rz(float __a);
|
||||
__DEVICE__ double __nv_frexp(double __a, int* __b);
|
||||
__DEVICE__ float __nv_frexpf(float __a, int* __b);
|
||||
__DEVICE__ float __nv_frsqrt_rn(float __a);
|
||||
__DEVICE__ float __nv_fsqrt_rd(float __a);
|
||||
__DEVICE__ float __nv_fsqrt_rn(float __a);
|
||||
__DEVICE__ float __nv_fsqrt_ru(float __a);
|
||||
__DEVICE__ float __nv_fsqrt_rz(float __a);
|
||||
__DEVICE__ float __nv_fsub_rd(float __a, float __b);
|
||||
__DEVICE__ float __nv_fsub_rn(float __a, float __b);
|
||||
__DEVICE__ float __nv_fsub_ru(float __a, float __b);
|
||||
__DEVICE__ float __nv_fsub_rz(float __a, float __b);
|
||||
__DEVICE__ int __nv_hadd(int __a, int __b);
|
||||
__DEVICE__ float __nv_half2float(unsigned short __h);
|
||||
__DEVICE__ double __nv_hiloint2double(int __a, int __b);
|
||||
__DEVICE__ double __nv_hypot(double __a, double __b);
|
||||
__DEVICE__ float __nv_hypotf(float __a, float __b);
|
||||
__DEVICE__ int __nv_ilogb(double __a);
|
||||
__DEVICE__ int __nv_ilogbf(float __a);
|
||||
__DEVICE__ double __nv_int2double_rn(int __a);
|
||||
__DEVICE__ float __nv_int2float_rd(int __a);
|
||||
__DEVICE__ float __nv_int2float_rn(int __a);
|
||||
__DEVICE__ float __nv_int2float_ru(int __a);
|
||||
__DEVICE__ float __nv_int2float_rz(int __a);
|
||||
__DEVICE__ float __nv_int_as_float(int __a);
|
||||
__DEVICE__ int __nv_isfinited(double __a);
|
||||
__DEVICE__ int __nv_isinfd(double __a);
|
||||
__DEVICE__ int __nv_isinff(float __a);
|
||||
__DEVICE__ int __nv_isnand(double __a);
|
||||
__DEVICE__ int __nv_isnanf(float __a);
|
||||
__DEVICE__ double __nv_j0(double __a);
|
||||
__DEVICE__ float __nv_j0f(float __a);
|
||||
__DEVICE__ double __nv_j1(double __a);
|
||||
__DEVICE__ float __nv_j1f(float __a);
|
||||
__DEVICE__ float __nv_jnf(int __a, float __b);
|
||||
__DEVICE__ double __nv_jn(int __a, double __b);
|
||||
__DEVICE__ double __nv_ldexp(double __a, int __b);
|
||||
__DEVICE__ float __nv_ldexpf(float __a, int __b);
|
||||
__DEVICE__ double __nv_lgamma(double __a);
|
||||
__DEVICE__ float __nv_lgammaf(float __a);
|
||||
__DEVICE__ double __nv_ll2double_rd(long long __a);
|
||||
__DEVICE__ double __nv_ll2double_rn(long long __a);
|
||||
__DEVICE__ double __nv_ll2double_ru(long long __a);
|
||||
__DEVICE__ double __nv_ll2double_rz(long long __a);
|
||||
__DEVICE__ float __nv_ll2float_rd(long long __a);
|
||||
__DEVICE__ float __nv_ll2float_rn(long long __a);
|
||||
__DEVICE__ float __nv_ll2float_ru(long long __a);
|
||||
__DEVICE__ float __nv_ll2float_rz(long long __a);
|
||||
__DEVICE__ long long __nv_llabs(long long __a);
|
||||
__DEVICE__ long long __nv_llmax(long long __a, long long __b);
|
||||
__DEVICE__ long long __nv_llmin(long long __a, long long __b);
|
||||
__DEVICE__ long long __nv_llrint(double __a);
|
||||
__DEVICE__ long long __nv_llrintf(float __a);
|
||||
__DEVICE__ long long __nv_llround(double __a);
|
||||
__DEVICE__ long long __nv_llroundf(float __a);
|
||||
__DEVICE__ double __nv_log10(double __a);
|
||||
__DEVICE__ float __nv_log10f(float __a);
|
||||
__DEVICE__ double __nv_log1p(double __a);
|
||||
__DEVICE__ float __nv_log1pf(float __a);
|
||||
__DEVICE__ double __nv_log2(double __a);
|
||||
__DEVICE__ float __nv_log2f(float __a);
|
||||
__DEVICE__ double __nv_logb(double __a);
|
||||
__DEVICE__ float __nv_logbf(float __a);
|
||||
__DEVICE__ double __nv_log(double __a);
|
||||
__DEVICE__ float __nv_logf(float __a);
|
||||
__DEVICE__ double __nv_longlong_as_double(long long __a);
|
||||
__DEVICE__ int __nv_max(int __a, int __b);
|
||||
__DEVICE__ int __nv_min(int __a, int __b);
|
||||
__DEVICE__ double __nv_modf(double __a, double* __b);
|
||||
__DEVICE__ float __nv_modff(float __a, float* __b);
|
||||
__DEVICE__ int __nv_mul24(int __a, int __b);
|
||||
__DEVICE__ long long __nv_mul64hi(long long __a, long long __b);
|
||||
__DEVICE__ int __nv_mulhi(int __a, int __b);
|
||||
__DEVICE__ double __nv_nan(const signed char* __a);
|
||||
__DEVICE__ float __nv_nanf(const signed char* __a);
|
||||
__DEVICE__ double __nv_nearbyint(double __a);
|
||||
__DEVICE__ float __nv_nearbyintf(float __a);
|
||||
__DEVICE__ double __nv_nextafter(double __a, double __b);
|
||||
__DEVICE__ float __nv_nextafterf(float __a, float __b);
|
||||
__DEVICE__ double __nv_norm3d(double __a, double __b, double __c);
|
||||
__DEVICE__ float __nv_norm3df(float __a, float __b, float __c);
|
||||
__DEVICE__ double __nv_norm4d(double __a, double __b, double __c, double __d);
|
||||
__DEVICE__ float __nv_norm4df(float __a, float __b, float __c, float __d);
|
||||
__DEVICE__ double __nv_normcdf(double __a);
|
||||
__DEVICE__ float __nv_normcdff(float __a);
|
||||
__DEVICE__ double __nv_normcdfinv(double __a);
|
||||
__DEVICE__ float __nv_normcdfinvf(float __a);
|
||||
__DEVICE__ float __nv_normf(int __a, const float* __b);
|
||||
__DEVICE__ double __nv_norm(int __a, const double* __b);
|
||||
__DEVICE__ int __nv_popc(unsigned int __a);
|
||||
__DEVICE__ int __nv_popcll(unsigned long long __a);
|
||||
__DEVICE__ double __nv_pow(double __a, double __b);
|
||||
__DEVICE__ float __nv_powf(float __a, float __b);
|
||||
__DEVICE__ double __nv_powi(double __a, int __b);
|
||||
__DEVICE__ float __nv_powif(float __a, int __b);
|
||||
__DEVICE__ double __nv_rcbrt(double __a);
|
||||
__DEVICE__ float __nv_rcbrtf(float __a);
|
||||
__DEVICE__ double __nv_rcp64h(double __a);
|
||||
__DEVICE__ double __nv_remainder(double __a, double __b);
|
||||
__DEVICE__ float __nv_remainderf(float __a, float __b);
|
||||
__DEVICE__ double __nv_remquo(double __a, double __b, int* __c);
|
||||
__DEVICE__ float __nv_remquof(float __a, float __b, int* __c);
|
||||
__DEVICE__ int __nv_rhadd(int __a, int __b);
|
||||
__DEVICE__ double __nv_rhypot(double __a, double __b);
|
||||
__DEVICE__ float __nv_rhypotf(float __a, float __b);
|
||||
__DEVICE__ double __nv_rint(double __a);
|
||||
__DEVICE__ float __nv_rintf(float __a);
|
||||
__DEVICE__ double __nv_rnorm3d(double __a, double __b, double __c);
|
||||
__DEVICE__ float __nv_rnorm3df(float __a, float __b, float __c);
|
||||
__DEVICE__ double __nv_rnorm4d(double __a, double __b, double __c, double __d);
|
||||
__DEVICE__ float __nv_rnorm4df(float __a, float __b, float __c, float __d);
|
||||
__DEVICE__ float __nv_rnormf(int __a, const float* __b);
|
||||
__DEVICE__ double __nv_rnorm(int __a, const double* __b);
|
||||
__DEVICE__ double __nv_round(double __a);
|
||||
__DEVICE__ float __nv_roundf(float __a);
|
||||
__DEVICE__ double __nv_rsqrt(double __a);
|
||||
__DEVICE__ float __nv_rsqrtf(float __a);
|
||||
__DEVICE__ int __nv_sad(int __a, int __b, int __c);
|
||||
__DEVICE__ float __nv_saturatef(float __a);
|
||||
__DEVICE__ double __nv_scalbn(double __a, int __b);
|
||||
__DEVICE__ float __nv_scalbnf(float __a, int __b);
|
||||
__DEVICE__ int __nv_signbitd(double __a);
|
||||
__DEVICE__ int __nv_signbitf(float __a);
|
||||
__DEVICE__ void __nv_sincos(double __a, double* __b, double* __c);
|
||||
__DEVICE__ void __nv_sincosf(float __a, float* __b, float* __c);
|
||||
__DEVICE__ void __nv_sincospi(double __a, double* __b, double* __c);
|
||||
__DEVICE__ void __nv_sincospif(float __a, float* __b, float* __c);
|
||||
__DEVICE__ double __nv_sin(double __a);
|
||||
__DEVICE__ float __nv_sinf(float __a);
|
||||
__DEVICE__ double __nv_sinh(double __a);
|
||||
__DEVICE__ float __nv_sinhf(float __a);
|
||||
__DEVICE__ double __nv_sinpi(double __a);
|
||||
__DEVICE__ float __nv_sinpif(float __a);
|
||||
__DEVICE__ double __nv_sqrt(double __a);
|
||||
__DEVICE__ float __nv_sqrtf(float __a);
|
||||
__DEVICE__ double __nv_tan(double __a);
|
||||
__DEVICE__ float __nv_tanf(float __a);
|
||||
__DEVICE__ double __nv_tanh(double __a);
|
||||
__DEVICE__ float __nv_tanhf(float __a);
|
||||
__DEVICE__ double __nv_tgamma(double __a);
|
||||
__DEVICE__ float __nv_tgammaf(float __a);
|
||||
__DEVICE__ double __nv_trunc(double __a);
|
||||
__DEVICE__ float __nv_truncf(float __a);
|
||||
__DEVICE__ int __nv_uhadd(unsigned int __a, unsigned int __b);
|
||||
__DEVICE__ double __nv_uint2double_rn(unsigned int __i);
|
||||
__DEVICE__ float __nv_uint2float_rd(unsigned int __a);
|
||||
__DEVICE__ float __nv_uint2float_rn(unsigned int __a);
|
||||
__DEVICE__ float __nv_uint2float_ru(unsigned int __a);
|
||||
__DEVICE__ float __nv_uint2float_rz(unsigned int __a);
|
||||
__DEVICE__ float __nv_uint_as_float(unsigned int __a);
|
||||
__DEVICE__ double __nv_ull2double_rd(unsigned long long __a);
|
||||
__DEVICE__ double __nv_ull2double_rn(unsigned long long __a);
|
||||
__DEVICE__ double __nv_ull2double_ru(unsigned long long __a);
|
||||
__DEVICE__ double __nv_ull2double_rz(unsigned long long __a);
|
||||
__DEVICE__ float __nv_ull2float_rd(unsigned long long __a);
|
||||
__DEVICE__ float __nv_ull2float_rn(unsigned long long __a);
|
||||
__DEVICE__ float __nv_ull2float_ru(unsigned long long __a);
|
||||
__DEVICE__ float __nv_ull2float_rz(unsigned long long __a);
|
||||
__DEVICE__ unsigned long long __nv_ullmax(unsigned long long __a, unsigned long long __b);
|
||||
__DEVICE__ unsigned long long __nv_ullmin(unsigned long long __a, unsigned long long __b);
|
||||
__DEVICE__ unsigned int __nv_umax(unsigned int __a, unsigned int __b);
|
||||
__DEVICE__ unsigned int __nv_umin(unsigned int __a, unsigned int __b);
|
||||
__DEVICE__ unsigned int __nv_umul24(unsigned int __a, unsigned int __b);
|
||||
__DEVICE__ unsigned long long __nv_umul64hi(unsigned long long __a, unsigned long long __b);
|
||||
__DEVICE__ unsigned int __nv_umulhi(unsigned int __a, unsigned int __b);
|
||||
__DEVICE__ unsigned int __nv_urhadd(unsigned int __a, unsigned int __b);
|
||||
__DEVICE__ unsigned int __nv_usad(unsigned int __a, unsigned int __b, unsigned int __c);
|
||||
__DEVICE__ double __nv_y0(double __a);
|
||||
__DEVICE__ float __nv_y0f(float __a);
|
||||
__DEVICE__ double __nv_y1(double __a);
|
||||
__DEVICE__ float __nv_y1f(float __a);
|
||||
__DEVICE__ float __nv_ynf(int __a, float __b);
|
||||
__DEVICE__ double __nv_yn(int __a, double __b);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
#endif // __CLANG_CUDA_LIBDEVICE_DECLARES_H__
|
||||
@@ -0,0 +1,809 @@
|
||||
/*===---- __clang_cuda_math.h - Device-side CUDA math support --------------===
|
||||
*
|
||||
* Part of the LLVM Project, 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
|
||||
*
|
||||
*===-----------------------------------------------------------------------===
|
||||
*/
|
||||
#ifndef __CLANG_CUDA_MATH_H__
|
||||
#define __CLANG_CUDA_MATH_H__
|
||||
#ifndef __CUDA__
|
||||
# error "This file is for CUDA compilation only."
|
||||
#endif
|
||||
|
||||
// The __CLANG_GPU_DISABLE_MATH_WRAPPERS macro provides a way to let standard
|
||||
// libcalls reach the link step instead of being eagerly replaced.
|
||||
#ifndef __CLANG_GPU_DISABLE_MATH_WRAPPERS
|
||||
|
||||
// __DEVICE__ is a helper macro with common set of attributes for the wrappers
|
||||
// we implement in this file. We need static in order to avoid emitting unused
|
||||
// functions and __forceinline__ helps inlining these wrappers at -O1.
|
||||
# pragma push_macro("__DEVICE__")
|
||||
# define __DEVICE__ static __device__ __forceinline__
|
||||
|
||||
// Specialized version of __DEVICE__ for functions with void return type.
|
||||
# pragma push_macro("__DEVICE_VOID__")
|
||||
# define __DEVICE_VOID__ __DEVICE__
|
||||
|
||||
// libdevice provides fast low precision and slow full-recision implementations
|
||||
// for some functions. Which one gets selected depends on
|
||||
// __CLANG_CUDA_APPROX_TRANSCENDENTALS__ which gets defined by clang if
|
||||
// -ffast-math or -fgpu-approx-transcendentals are in effect.
|
||||
# pragma push_macro("__FAST_OR_SLOW")
|
||||
# if defined(__CLANG_GPU_APPROX_TRANSCENDENTALS__)
|
||||
# define __FAST_OR_SLOW(fast, slow) fast
|
||||
# else
|
||||
# define __FAST_OR_SLOW(fast, slow) slow
|
||||
# endif
|
||||
|
||||
__DEVICE__ int abs(int __a)
|
||||
{
|
||||
return __nv_abs(__a);
|
||||
}
|
||||
__DEVICE__ double fabs(double __a)
|
||||
{
|
||||
return __nv_fabs(__a);
|
||||
}
|
||||
__DEVICE__ double acos(double __a)
|
||||
{
|
||||
return __nv_acos(__a);
|
||||
}
|
||||
__DEVICE__ float acosf(float __a)
|
||||
{
|
||||
return __nv_acosf(__a);
|
||||
}
|
||||
__DEVICE__ double acosh(double __a)
|
||||
{
|
||||
return __nv_acosh(__a);
|
||||
}
|
||||
__DEVICE__ float acoshf(float __a)
|
||||
{
|
||||
return __nv_acoshf(__a);
|
||||
}
|
||||
__DEVICE__ double asin(double __a)
|
||||
{
|
||||
return __nv_asin(__a);
|
||||
}
|
||||
__DEVICE__ float asinf(float __a)
|
||||
{
|
||||
return __nv_asinf(__a);
|
||||
}
|
||||
__DEVICE__ double asinh(double __a)
|
||||
{
|
||||
return __nv_asinh(__a);
|
||||
}
|
||||
__DEVICE__ float asinhf(float __a)
|
||||
{
|
||||
return __nv_asinhf(__a);
|
||||
}
|
||||
__DEVICE__ double atan(double __a)
|
||||
{
|
||||
return __nv_atan(__a);
|
||||
}
|
||||
__DEVICE__ double atan2(double __a, double __b)
|
||||
{
|
||||
return __nv_atan2(__a, __b);
|
||||
}
|
||||
__DEVICE__ float atan2f(float __a, float __b)
|
||||
{
|
||||
return __nv_atan2f(__a, __b);
|
||||
}
|
||||
__DEVICE__ float atanf(float __a)
|
||||
{
|
||||
return __nv_atanf(__a);
|
||||
}
|
||||
__DEVICE__ double atanh(double __a)
|
||||
{
|
||||
return __nv_atanh(__a);
|
||||
}
|
||||
__DEVICE__ float atanhf(float __a)
|
||||
{
|
||||
return __nv_atanhf(__a);
|
||||
}
|
||||
__DEVICE__ double cbrt(double __a)
|
||||
{
|
||||
return __nv_cbrt(__a);
|
||||
}
|
||||
__DEVICE__ float cbrtf(float __a)
|
||||
{
|
||||
return __nv_cbrtf(__a);
|
||||
}
|
||||
__DEVICE__ double ceil(double __a)
|
||||
{
|
||||
return __nv_ceil(__a);
|
||||
}
|
||||
__DEVICE__ float ceilf(float __a)
|
||||
{
|
||||
return __nv_ceilf(__a);
|
||||
}
|
||||
__DEVICE__ double copysign(double __a, double __b)
|
||||
{
|
||||
return __nv_copysign(__a, __b);
|
||||
}
|
||||
__DEVICE__ float copysignf(float __a, float __b)
|
||||
{
|
||||
return __nv_copysignf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double cos(double __a)
|
||||
{
|
||||
return __nv_cos(__a);
|
||||
}
|
||||
__DEVICE__ float cosf(float __a)
|
||||
{
|
||||
return __FAST_OR_SLOW(__nv_fast_cosf, __nv_cosf)(__a);
|
||||
}
|
||||
__DEVICE__ double cosh(double __a)
|
||||
{
|
||||
return __nv_cosh(__a);
|
||||
}
|
||||
__DEVICE__ float coshf(float __a)
|
||||
{
|
||||
return __nv_coshf(__a);
|
||||
}
|
||||
__DEVICE__ double cospi(double __a)
|
||||
{
|
||||
return __nv_cospi(__a);
|
||||
}
|
||||
__DEVICE__ float cospif(float __a)
|
||||
{
|
||||
return __nv_cospif(__a);
|
||||
}
|
||||
__DEVICE__ double cyl_bessel_i0(double __a)
|
||||
{
|
||||
return __nv_cyl_bessel_i0(__a);
|
||||
}
|
||||
__DEVICE__ float cyl_bessel_i0f(float __a)
|
||||
{
|
||||
return __nv_cyl_bessel_i0f(__a);
|
||||
}
|
||||
__DEVICE__ double cyl_bessel_i1(double __a)
|
||||
{
|
||||
return __nv_cyl_bessel_i1(__a);
|
||||
}
|
||||
__DEVICE__ float cyl_bessel_i1f(float __a)
|
||||
{
|
||||
return __nv_cyl_bessel_i1f(__a);
|
||||
}
|
||||
__DEVICE__ double erf(double __a)
|
||||
{
|
||||
return __nv_erf(__a);
|
||||
}
|
||||
__DEVICE__ double erfc(double __a)
|
||||
{
|
||||
return __nv_erfc(__a);
|
||||
}
|
||||
__DEVICE__ float erfcf(float __a)
|
||||
{
|
||||
return __nv_erfcf(__a);
|
||||
}
|
||||
__DEVICE__ double erfcinv(double __a)
|
||||
{
|
||||
return __nv_erfcinv(__a);
|
||||
}
|
||||
__DEVICE__ float erfcinvf(float __a)
|
||||
{
|
||||
return __nv_erfcinvf(__a);
|
||||
}
|
||||
__DEVICE__ double erfcx(double __a)
|
||||
{
|
||||
return __nv_erfcx(__a);
|
||||
}
|
||||
__DEVICE__ float erfcxf(float __a)
|
||||
{
|
||||
return __nv_erfcxf(__a);
|
||||
}
|
||||
__DEVICE__ float erff(float __a)
|
||||
{
|
||||
return __nv_erff(__a);
|
||||
}
|
||||
__DEVICE__ double erfinv(double __a)
|
||||
{
|
||||
return __nv_erfinv(__a);
|
||||
}
|
||||
__DEVICE__ float erfinvf(float __a)
|
||||
{
|
||||
return __nv_erfinvf(__a);
|
||||
}
|
||||
__DEVICE__ double exp(double __a)
|
||||
{
|
||||
return __nv_exp(__a);
|
||||
}
|
||||
__DEVICE__ double exp10(double __a)
|
||||
{
|
||||
return __nv_exp10(__a);
|
||||
}
|
||||
__DEVICE__ float exp10f(float __a)
|
||||
{
|
||||
return __nv_exp10f(__a);
|
||||
}
|
||||
__DEVICE__ double exp2(double __a)
|
||||
{
|
||||
return __nv_exp2(__a);
|
||||
}
|
||||
__DEVICE__ float exp2f(float __a)
|
||||
{
|
||||
return __nv_exp2f(__a);
|
||||
}
|
||||
__DEVICE__ float expf(float __a)
|
||||
{
|
||||
return __nv_expf(__a);
|
||||
}
|
||||
__DEVICE__ double expm1(double __a)
|
||||
{
|
||||
return __nv_expm1(__a);
|
||||
}
|
||||
__DEVICE__ float expm1f(float __a)
|
||||
{
|
||||
return __nv_expm1f(__a);
|
||||
}
|
||||
__DEVICE__ float fabsf(float __a)
|
||||
{
|
||||
return __nv_fabsf(__a);
|
||||
}
|
||||
__DEVICE__ double fdim(double __a, double __b)
|
||||
{
|
||||
return __nv_fdim(__a, __b);
|
||||
}
|
||||
__DEVICE__ float fdimf(float __a, float __b)
|
||||
{
|
||||
return __nv_fdimf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double fdivide(double __a, double __b)
|
||||
{
|
||||
return __a / __b;
|
||||
}
|
||||
__DEVICE__ float fdividef(float __a, float __b)
|
||||
{
|
||||
# if __FAST_MATH__ && !__CUDA_PREC_DIV
|
||||
return __nv_fast_fdividef(__a, __b);
|
||||
# else
|
||||
return __a / __b;
|
||||
# endif
|
||||
}
|
||||
__DEVICE__ double floor(double __f)
|
||||
{
|
||||
return __nv_floor(__f);
|
||||
}
|
||||
__DEVICE__ float floorf(float __f)
|
||||
{
|
||||
return __nv_floorf(__f);
|
||||
}
|
||||
__DEVICE__ double fma(double __a, double __b, double __c)
|
||||
{
|
||||
return __nv_fma(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ float fmaf(float __a, float __b, float __c)
|
||||
{
|
||||
return __nv_fmaf(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ double fmax(double __a, double __b)
|
||||
{
|
||||
return __nv_fmax(__a, __b);
|
||||
}
|
||||
__DEVICE__ float fmaxf(float __a, float __b)
|
||||
{
|
||||
return __nv_fmaxf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double fmin(double __a, double __b)
|
||||
{
|
||||
return __nv_fmin(__a, __b);
|
||||
}
|
||||
__DEVICE__ float fminf(float __a, float __b)
|
||||
{
|
||||
return __nv_fminf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double fmod(double __a, double __b)
|
||||
{
|
||||
return __nv_fmod(__a, __b);
|
||||
}
|
||||
__DEVICE__ float fmodf(float __a, float __b)
|
||||
{
|
||||
return __nv_fmodf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double frexp(double __a, int* __b)
|
||||
{
|
||||
return __nv_frexp(__a, __b);
|
||||
}
|
||||
__DEVICE__ float frexpf(float __a, int* __b)
|
||||
{
|
||||
return __nv_frexpf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double hypot(double __a, double __b)
|
||||
{
|
||||
return __nv_hypot(__a, __b);
|
||||
}
|
||||
__DEVICE__ float hypotf(float __a, float __b)
|
||||
{
|
||||
return __nv_hypotf(__a, __b);
|
||||
}
|
||||
__DEVICE__ int ilogb(double __a)
|
||||
{
|
||||
return __nv_ilogb(__a);
|
||||
}
|
||||
__DEVICE__ int ilogbf(float __a)
|
||||
{
|
||||
return __nv_ilogbf(__a);
|
||||
}
|
||||
__DEVICE__ double j0(double __a)
|
||||
{
|
||||
return __nv_j0(__a);
|
||||
}
|
||||
__DEVICE__ float j0f(float __a)
|
||||
{
|
||||
return __nv_j0f(__a);
|
||||
}
|
||||
__DEVICE__ double j1(double __a)
|
||||
{
|
||||
return __nv_j1(__a);
|
||||
}
|
||||
__DEVICE__ float j1f(float __a)
|
||||
{
|
||||
return __nv_j1f(__a);
|
||||
}
|
||||
__DEVICE__ double jn(int __n, double __a)
|
||||
{
|
||||
return __nv_jn(__n, __a);
|
||||
}
|
||||
__DEVICE__ float jnf(int __n, float __a)
|
||||
{
|
||||
return __nv_jnf(__n, __a);
|
||||
}
|
||||
# if defined(__LP64__) || defined(_WIN64)
|
||||
__DEVICE__ long labs(long __a)
|
||||
{
|
||||
return __nv_llabs(__a);
|
||||
};
|
||||
# else
|
||||
__DEVICE__ long labs(long __a)
|
||||
{
|
||||
return __nv_abs(__a);
|
||||
};
|
||||
# endif
|
||||
__DEVICE__ double ldexp(double __a, int __b)
|
||||
{
|
||||
return __nv_ldexp(__a, __b);
|
||||
}
|
||||
__DEVICE__ float ldexpf(float __a, int __b)
|
||||
{
|
||||
return __nv_ldexpf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double lgamma(double __a)
|
||||
{
|
||||
return __nv_lgamma(__a);
|
||||
}
|
||||
__DEVICE__ float lgammaf(float __a)
|
||||
{
|
||||
return __nv_lgammaf(__a);
|
||||
}
|
||||
__DEVICE__ long long llabs(long long __a)
|
||||
{
|
||||
return __nv_llabs(__a);
|
||||
}
|
||||
__DEVICE__ long long llmax(long long __a, long long __b)
|
||||
{
|
||||
return __nv_llmax(__a, __b);
|
||||
}
|
||||
__DEVICE__ long long llmin(long long __a, long long __b)
|
||||
{
|
||||
return __nv_llmin(__a, __b);
|
||||
}
|
||||
__DEVICE__ long long llrint(double __a)
|
||||
{
|
||||
return __nv_llrint(__a);
|
||||
}
|
||||
__DEVICE__ long long llrintf(float __a)
|
||||
{
|
||||
return __nv_llrintf(__a);
|
||||
}
|
||||
__DEVICE__ long long llround(double __a)
|
||||
{
|
||||
return __nv_llround(__a);
|
||||
}
|
||||
__DEVICE__ long long llroundf(float __a)
|
||||
{
|
||||
return __nv_llroundf(__a);
|
||||
}
|
||||
__DEVICE__ double round(double __a)
|
||||
{
|
||||
return __nv_round(__a);
|
||||
}
|
||||
__DEVICE__ float roundf(float __a)
|
||||
{
|
||||
return __nv_roundf(__a);
|
||||
}
|
||||
__DEVICE__ double log(double __a)
|
||||
{
|
||||
return __nv_log(__a);
|
||||
}
|
||||
__DEVICE__ double log10(double __a)
|
||||
{
|
||||
return __nv_log10(__a);
|
||||
}
|
||||
__DEVICE__ float log10f(float __a)
|
||||
{
|
||||
return __nv_log10f(__a);
|
||||
}
|
||||
__DEVICE__ double log1p(double __a)
|
||||
{
|
||||
return __nv_log1p(__a);
|
||||
}
|
||||
__DEVICE__ float log1pf(float __a)
|
||||
{
|
||||
return __nv_log1pf(__a);
|
||||
}
|
||||
__DEVICE__ double log2(double __a)
|
||||
{
|
||||
return __nv_log2(__a);
|
||||
}
|
||||
__DEVICE__ float log2f(float __a)
|
||||
{
|
||||
return __FAST_OR_SLOW(__nv_fast_log2f, __nv_log2f)(__a);
|
||||
}
|
||||
__DEVICE__ double logb(double __a)
|
||||
{
|
||||
return __nv_logb(__a);
|
||||
}
|
||||
__DEVICE__ float logbf(float __a)
|
||||
{
|
||||
return __nv_logbf(__a);
|
||||
}
|
||||
__DEVICE__ float logf(float __a)
|
||||
{
|
||||
return __FAST_OR_SLOW(__nv_fast_logf, __nv_logf)(__a);
|
||||
}
|
||||
# if defined(__LP64__) || defined(_WIN64)
|
||||
__DEVICE__ long lrint(double __a)
|
||||
{
|
||||
return llrint(__a);
|
||||
}
|
||||
__DEVICE__ long lrintf(float __a)
|
||||
{
|
||||
return __float2ll_rn(__a);
|
||||
}
|
||||
__DEVICE__ long lround(double __a)
|
||||
{
|
||||
return llround(__a);
|
||||
}
|
||||
__DEVICE__ long lroundf(float __a)
|
||||
{
|
||||
return llroundf(__a);
|
||||
}
|
||||
# else
|
||||
__DEVICE__ long lrint(double __a)
|
||||
{
|
||||
return (long) rint(__a);
|
||||
}
|
||||
__DEVICE__ long lrintf(float __a)
|
||||
{
|
||||
return __float2int_rn(__a);
|
||||
}
|
||||
__DEVICE__ long lround(double __a)
|
||||
{
|
||||
return round(__a);
|
||||
}
|
||||
__DEVICE__ long lroundf(float __a)
|
||||
{
|
||||
return roundf(__a);
|
||||
}
|
||||
# endif
|
||||
__DEVICE__ int max(int __a, int __b)
|
||||
{
|
||||
return __nv_max(__a, __b);
|
||||
}
|
||||
__DEVICE__ int min(int __a, int __b)
|
||||
{
|
||||
return __nv_min(__a, __b);
|
||||
}
|
||||
__DEVICE__ double modf(double __a, double* __b)
|
||||
{
|
||||
return __nv_modf(__a, __b);
|
||||
}
|
||||
__DEVICE__ float modff(float __a, float* __b)
|
||||
{
|
||||
return __nv_modff(__a, __b);
|
||||
}
|
||||
__DEVICE__ double nearbyint(double __a)
|
||||
{
|
||||
return __builtin_nearbyint(__a);
|
||||
}
|
||||
__DEVICE__ float nearbyintf(float __a)
|
||||
{
|
||||
return __builtin_nearbyintf(__a);
|
||||
}
|
||||
__DEVICE__ double nextafter(double __a, double __b)
|
||||
{
|
||||
return __nv_nextafter(__a, __b);
|
||||
}
|
||||
__DEVICE__ float nextafterf(float __a, float __b)
|
||||
{
|
||||
return __nv_nextafterf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double norm(int __dim, const double* __t)
|
||||
{
|
||||
return __nv_norm(__dim, __t);
|
||||
}
|
||||
__DEVICE__ double norm3d(double __a, double __b, double __c)
|
||||
{
|
||||
return __nv_norm3d(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ float norm3df(float __a, float __b, float __c)
|
||||
{
|
||||
return __nv_norm3df(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ double norm4d(double __a, double __b, double __c, double __d)
|
||||
{
|
||||
return __nv_norm4d(__a, __b, __c, __d);
|
||||
}
|
||||
__DEVICE__ float norm4df(float __a, float __b, float __c, float __d)
|
||||
{
|
||||
return __nv_norm4df(__a, __b, __c, __d);
|
||||
}
|
||||
__DEVICE__ double normcdf(double __a)
|
||||
{
|
||||
return __nv_normcdf(__a);
|
||||
}
|
||||
__DEVICE__ float normcdff(float __a)
|
||||
{
|
||||
return __nv_normcdff(__a);
|
||||
}
|
||||
__DEVICE__ double normcdfinv(double __a)
|
||||
{
|
||||
return __nv_normcdfinv(__a);
|
||||
}
|
||||
__DEVICE__ float normcdfinvf(float __a)
|
||||
{
|
||||
return __nv_normcdfinvf(__a);
|
||||
}
|
||||
__DEVICE__ float normf(int __dim, const float* __t)
|
||||
{
|
||||
return __nv_normf(__dim, __t);
|
||||
}
|
||||
__DEVICE__ double pow(double __a, double __b)
|
||||
{
|
||||
return __nv_pow(__a, __b);
|
||||
}
|
||||
__DEVICE__ float powf(float __a, float __b)
|
||||
{
|
||||
return __nv_powf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double powi(double __a, int __b)
|
||||
{
|
||||
return __nv_powi(__a, __b);
|
||||
}
|
||||
__DEVICE__ float powif(float __a, int __b)
|
||||
{
|
||||
return __nv_powif(__a, __b);
|
||||
}
|
||||
__DEVICE__ double rcbrt(double __a)
|
||||
{
|
||||
return __nv_rcbrt(__a);
|
||||
}
|
||||
__DEVICE__ float rcbrtf(float __a)
|
||||
{
|
||||
return __nv_rcbrtf(__a);
|
||||
}
|
||||
__DEVICE__ double remainder(double __a, double __b)
|
||||
{
|
||||
return __nv_remainder(__a, __b);
|
||||
}
|
||||
__DEVICE__ float remainderf(float __a, float __b)
|
||||
{
|
||||
return __nv_remainderf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double remquo(double __a, double __b, int* __c)
|
||||
{
|
||||
return __nv_remquo(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ float remquof(float __a, float __b, int* __c)
|
||||
{
|
||||
return __nv_remquof(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ double rhypot(double __a, double __b)
|
||||
{
|
||||
return __nv_rhypot(__a, __b);
|
||||
}
|
||||
__DEVICE__ float rhypotf(float __a, float __b)
|
||||
{
|
||||
return __nv_rhypotf(__a, __b);
|
||||
}
|
||||
// __nv_rint* in libdevice is buggy and produces incorrect results.
|
||||
__DEVICE__ double rint(double __a)
|
||||
{
|
||||
return __builtin_rint(__a);
|
||||
}
|
||||
__DEVICE__ float rintf(float __a)
|
||||
{
|
||||
return __builtin_rintf(__a);
|
||||
}
|
||||
__DEVICE__ double rnorm(int __a, const double* __b)
|
||||
{
|
||||
return __nv_rnorm(__a, __b);
|
||||
}
|
||||
__DEVICE__ double rnorm3d(double __a, double __b, double __c)
|
||||
{
|
||||
return __nv_rnorm3d(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ float rnorm3df(float __a, float __b, float __c)
|
||||
{
|
||||
return __nv_rnorm3df(__a, __b, __c);
|
||||
}
|
||||
__DEVICE__ double rnorm4d(double __a, double __b, double __c, double __d)
|
||||
{
|
||||
return __nv_rnorm4d(__a, __b, __c, __d);
|
||||
}
|
||||
__DEVICE__ float rnorm4df(float __a, float __b, float __c, float __d)
|
||||
{
|
||||
return __nv_rnorm4df(__a, __b, __c, __d);
|
||||
}
|
||||
__DEVICE__ float rnormf(int __dim, const float* __t)
|
||||
{
|
||||
return __nv_rnormf(__dim, __t);
|
||||
}
|
||||
__DEVICE__ double rsqrt(double __a)
|
||||
{
|
||||
return __nv_rsqrt(__a);
|
||||
}
|
||||
__DEVICE__ float rsqrtf(float __a)
|
||||
{
|
||||
return __nv_rsqrtf(__a);
|
||||
}
|
||||
__DEVICE__ double scalbn(double __a, int __b)
|
||||
{
|
||||
return __nv_scalbn(__a, __b);
|
||||
}
|
||||
__DEVICE__ float scalbnf(float __a, int __b)
|
||||
{
|
||||
return __nv_scalbnf(__a, __b);
|
||||
}
|
||||
__DEVICE__ double scalbln(double __a, long __b)
|
||||
{
|
||||
if (__b > INT_MAX)
|
||||
{
|
||||
return __a > 0 ? HUGE_VAL : -HUGE_VAL;
|
||||
}
|
||||
if (__b < INT_MIN)
|
||||
{
|
||||
return __a > 0 ? 0.0 : -0.0;
|
||||
}
|
||||
return scalbn(__a, (int) __b);
|
||||
}
|
||||
__DEVICE__ float scalblnf(float __a, long __b)
|
||||
{
|
||||
if (__b > INT_MAX)
|
||||
{
|
||||
return __a > 0 ? HUGE_VALF : -HUGE_VALF;
|
||||
}
|
||||
if (__b < INT_MIN)
|
||||
{
|
||||
return __a > 0 ? 0.f : -0.f;
|
||||
}
|
||||
return scalbnf(__a, (int) __b);
|
||||
}
|
||||
__DEVICE__ double sin(double __a)
|
||||
{
|
||||
return __nv_sin(__a);
|
||||
}
|
||||
__DEVICE_VOID__ void sincos(double __a, double* __s, double* __c)
|
||||
{
|
||||
return __nv_sincos(__a, __s, __c);
|
||||
}
|
||||
__DEVICE_VOID__ void sincosf(float __a, float* __s, float* __c)
|
||||
{
|
||||
return __FAST_OR_SLOW(__nv_fast_sincosf, __nv_sincosf)(__a, __s, __c);
|
||||
}
|
||||
__DEVICE_VOID__ void sincospi(double __a, double* __s, double* __c)
|
||||
{
|
||||
return __nv_sincospi(__a, __s, __c);
|
||||
}
|
||||
__DEVICE_VOID__ void sincospif(float __a, float* __s, float* __c)
|
||||
{
|
||||
return __nv_sincospif(__a, __s, __c);
|
||||
}
|
||||
__DEVICE__ float sinf(float __a)
|
||||
{
|
||||
return __FAST_OR_SLOW(__nv_fast_sinf, __nv_sinf)(__a);
|
||||
}
|
||||
__DEVICE__ double sinh(double __a)
|
||||
{
|
||||
return __nv_sinh(__a);
|
||||
}
|
||||
__DEVICE__ float sinhf(float __a)
|
||||
{
|
||||
return __nv_sinhf(__a);
|
||||
}
|
||||
__DEVICE__ double sinpi(double __a)
|
||||
{
|
||||
return __nv_sinpi(__a);
|
||||
}
|
||||
__DEVICE__ float sinpif(float __a)
|
||||
{
|
||||
return __nv_sinpif(__a);
|
||||
}
|
||||
__DEVICE__ double sqrt(double __a)
|
||||
{
|
||||
return __nv_sqrt(__a);
|
||||
}
|
||||
__DEVICE__ float sqrtf(float __a)
|
||||
{
|
||||
return __nv_sqrtf(__a);
|
||||
}
|
||||
__DEVICE__ double tan(double __a)
|
||||
{
|
||||
return __nv_tan(__a);
|
||||
}
|
||||
__DEVICE__ float tanf(float __a)
|
||||
{
|
||||
return __nv_tanf(__a);
|
||||
}
|
||||
__DEVICE__ double tanh(double __a)
|
||||
{
|
||||
return __nv_tanh(__a);
|
||||
}
|
||||
__DEVICE__ float tanhf(float __a)
|
||||
{
|
||||
return __nv_tanhf(__a);
|
||||
}
|
||||
__DEVICE__ double tgamma(double __a)
|
||||
{
|
||||
return __nv_tgamma(__a);
|
||||
}
|
||||
__DEVICE__ float tgammaf(float __a)
|
||||
{
|
||||
return __nv_tgammaf(__a);
|
||||
}
|
||||
__DEVICE__ double trunc(double __a)
|
||||
{
|
||||
return __nv_trunc(__a);
|
||||
}
|
||||
__DEVICE__ float truncf(float __a)
|
||||
{
|
||||
return __nv_truncf(__a);
|
||||
}
|
||||
__DEVICE__ unsigned long long ullmax(unsigned long long __a, unsigned long long __b)
|
||||
{
|
||||
return __nv_ullmax(__a, __b);
|
||||
}
|
||||
__DEVICE__ unsigned long long ullmin(unsigned long long __a, unsigned long long __b)
|
||||
{
|
||||
return __nv_ullmin(__a, __b);
|
||||
}
|
||||
__DEVICE__ unsigned int umax(unsigned int __a, unsigned int __b)
|
||||
{
|
||||
return __nv_umax(__a, __b);
|
||||
}
|
||||
__DEVICE__ unsigned int umin(unsigned int __a, unsigned int __b)
|
||||
{
|
||||
return __nv_umin(__a, __b);
|
||||
}
|
||||
__DEVICE__ double y0(double __a)
|
||||
{
|
||||
return __nv_y0(__a);
|
||||
}
|
||||
__DEVICE__ float y0f(float __a)
|
||||
{
|
||||
return __nv_y0f(__a);
|
||||
}
|
||||
__DEVICE__ double y1(double __a)
|
||||
{
|
||||
return __nv_y1(__a);
|
||||
}
|
||||
__DEVICE__ float y1f(float __a)
|
||||
{
|
||||
return __nv_y1f(__a);
|
||||
}
|
||||
__DEVICE__ double yn(int __a, double __b)
|
||||
{
|
||||
return __nv_yn(__a, __b);
|
||||
}
|
||||
__DEVICE__ float ynf(int __a, float __b)
|
||||
{
|
||||
return __nv_ynf(__a, __b);
|
||||
}
|
||||
|
||||
# pragma pop_macro("__DEVICE__")
|
||||
# pragma pop_macro("__DEVICE_VOID__")
|
||||
# pragma pop_macro("__FAST_OR_SLOW")
|
||||
|
||||
#endif // __CLANG_GPU_DISABLE_MATH_WRAPPERS
|
||||
#endif // __CLANG_CUDA_MATH_H__
|
||||
@@ -0,0 +1,438 @@
|
||||
/*===---- HostJIT CUDA runtime wrapper - replaces clang's wrapper ----------===
|
||||
*
|
||||
* This is a self-contained replacement for clang's __clang_cuda_runtime_wrapper.h.
|
||||
* Instead of #include_next-ing the real wrapper (which has fragile ordering
|
||||
* dependencies on system headers and CUDA toolkit version-specific branches),
|
||||
* we directly include only the clang-provided CUDA helper headers we need and
|
||||
* pull in the CUDA toolkit headers with explicit preprocessor guards.
|
||||
*
|
||||
* Key design decision: all clang-provided device function implementations and
|
||||
* CCCL-required intrinsics are defined BEFORE any CUDA toolkit headers that
|
||||
* might transitively include CCCL (via libcudacxx standard headers on our
|
||||
* include path). This eliminates the need for forward declarations.
|
||||
*
|
||||
* Assumptions:
|
||||
* - CUDA >= 9.0 (no legacy code paths)
|
||||
* - Clang CUDA compilation (__CUDA__ && __clang__)
|
||||
* - Freestanding: all standard headers are stubs or from libcudacxx
|
||||
* - cuda::std is bridged into std via using-directive
|
||||
*===-----------------------------------------------------------------------===*/
|
||||
#ifndef __CLANG_CUDA_RUNTIME_WRAPPER_H__
|
||||
#define __CLANG_CUDA_RUNTIME_WRAPPER_H__
|
||||
#pragma clang system_header
|
||||
|
||||
#if defined(__CUDA__) && defined(__clang__)
|
||||
|
||||
// ============================================================================
|
||||
// Phase 1: Forward-declare device math overloads before any <cmath> inclusion
|
||||
// ============================================================================
|
||||
// This prevents constexpr std library math functions from becoming implicitly
|
||||
// host+device, which would block our __device__ overloads later.
|
||||
# include <__clang_cuda_math_forward_declares.h>
|
||||
|
||||
// ============================================================================
|
||||
// Phase 2: Device-side definitions before any CUDA toolkit headers
|
||||
// ============================================================================
|
||||
// Everything here uses only compiler builtins and our stubs. No CUDA toolkit
|
||||
// headers are included yet, so nothing can transitively pull in CCCL.
|
||||
|
||||
# pragma push_macro("__THROW")
|
||||
# pragma push_macro("__CUDA_ARCH__")
|
||||
|
||||
# ifndef __CUDA_ARCH__
|
||||
# define __CUDA_ARCH__ 9999
|
||||
# endif
|
||||
|
||||
// host_defines.h provides __device__, __host__, __forceinline__ macros.
|
||||
// Its only transitive dep (ctype.h) hits our stub.
|
||||
# define __CUDA_INCLUDE_COMPILER_INTERNAL_HEADERS__
|
||||
# define __CUDACC__
|
||||
# define __CUDA_LIBDEVICE__
|
||||
# include "host_defines.h"
|
||||
|
||||
// ---- Builtin variables (threadIdx, blockIdx, etc.) ----
|
||||
# include "__clang_cuda_builtin_vars.h"
|
||||
|
||||
// ---- Stubs needed by clang device function headers below ----
|
||||
# include <climits>
|
||||
# include <cmath>
|
||||
# include <cstddef>
|
||||
// string.h must precede __clang_cuda_device_functions.h: cuda_fp16.hpp uses
|
||||
// memcpy from __host__ __device__ ctors. device_functions.h only declares a
|
||||
// __device__ memcpy, so the host-side call site needs the stub's host-callable
|
||||
// __builtin_memcpy overload visible first.
|
||||
# include <string.h>
|
||||
|
||||
// ---- Clang device function wrappers (local copies, CUDA < 9.0 removed) ----
|
||||
// NOTE: libdevice_declares.h must precede device_functions.h — the latter calls
|
||||
// __nv_* symbols that are declared in the former.
|
||||
// clang-format off
|
||||
# include "__clang_cuda_libdevice_declares.h"
|
||||
# include "__clang_cuda_device_functions.h"
|
||||
// clang-format on
|
||||
# include "__clang_cuda_math.h"
|
||||
|
||||
// ---- Address-space intrinsics needed by CCCL headers ----
|
||||
// (e.g. cuda/__memory/address_space.h, cuda/__ptx/ptx_helper_functions.h)
|
||||
static __device__ __forceinline__ __attribute__((const)) unsigned int __isGlobal(const void* p)
|
||||
{
|
||||
return __nvvm_isspacep_global(p);
|
||||
}
|
||||
static __device__ __forceinline__ __attribute__((const)) unsigned int __isShared(const void* p)
|
||||
{
|
||||
return __nvvm_isspacep_shared(p);
|
||||
}
|
||||
static __device__ __forceinline__ __attribute__((const)) unsigned int __isConstant(const void* p)
|
||||
{
|
||||
return __nvvm_isspacep_const(p);
|
||||
}
|
||||
static __device__ __forceinline__ __attribute__((const)) unsigned int __isLocal(const void* p)
|
||||
{
|
||||
return __nvvm_isspacep_local(p);
|
||||
}
|
||||
# define __FWD_DEVICE static __device__ __forceinline__
|
||||
__FWD_DEVICE unsigned int __isClusterShared(const void*);
|
||||
__FWD_DEVICE __SIZE_TYPE__ __cvta_generic_to_shared(const void*);
|
||||
__FWD_DEVICE __SIZE_TYPE__ __cvta_generic_to_global(const void*);
|
||||
__FWD_DEVICE void* __cvta_shared_to_generic(__SIZE_TYPE__);
|
||||
__FWD_DEVICE void* __cvta_global_to_generic(__SIZE_TYPE__);
|
||||
# undef __FWD_DEVICE
|
||||
# ifndef _MSC_VER
|
||||
__device__ bool __nv_fp128_isnan(__float128);
|
||||
__device__ __float128 __nv_fp128_fmax(__float128, __float128);
|
||||
__device__ __float128 __nv_fp128_fmin(__float128, __float128);
|
||||
# endif
|
||||
|
||||
// ---- Bridge cuda::std into std ----
|
||||
namespace cuda
|
||||
{
|
||||
namespace std
|
||||
{
|
||||
}
|
||||
} // namespace cuda
|
||||
namespace std
|
||||
{
|
||||
using namespace cuda::std;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Phase 3: CUDA toolkit headers
|
||||
// ============================================================================
|
||||
// By this point all device-side functions and intrinsics are defined, so
|
||||
// any transitive CCCL includes from these headers will find them.
|
||||
# pragma push_macro("__CUDA_INCLUDE_COMPILER_INTERNAL_HEADERS__")
|
||||
|
||||
# define __DEVICE_LAUNCH_PARAMETERS_H__
|
||||
|
||||
// Guard out CUDA's declaration-only headers; clang provides its own.
|
||||
# define __DEVICE_FUNCTIONS_H__
|
||||
# define __MATH_FUNCTIONS_H__
|
||||
# define __MATH_FUNCTIONS_HPP__
|
||||
# define __COMMON_FUNCTIONS_H__
|
||||
# define __DEVICE_FUNCTIONS_DECLS_H__
|
||||
|
||||
// ---- CUDA runtime types (cudaError_t, dim3, cudaStream_t, etc.) ----
|
||||
// (host_defines.h already included in Phase 2)
|
||||
# undef __CUDACC__
|
||||
# include "cuda.h"
|
||||
# include "driver_types.h"
|
||||
# include "host_config.h"
|
||||
# if !defined(CUDA_VERSION) || CUDA_VERSION < 9000
|
||||
# error "Unsupported CUDA version (need >= 9.0)!"
|
||||
# endif
|
||||
|
||||
// Clang does not have __nvvm_memcpy/__nvvm_memset; emulate with builtins.
|
||||
# define __nvvm_memcpy(s, d, n, a) __builtin_memcpy(s, d, n)
|
||||
# define __nvvm_memset(d, c, n, a) __builtin_memset(d, c, n)
|
||||
|
||||
// __THROW may be in a weird state; keep it empty for CUDA includes.
|
||||
# undef __THROW
|
||||
# define __THROW
|
||||
|
||||
// ============================================================================
|
||||
// Phase 4: Device-side function definitions from CUDA toolkit .hpp files
|
||||
// ============================================================================
|
||||
// Poison __host__ to ensure none of these definitions get host attributes.
|
||||
# pragma push_macro("__host__")
|
||||
# define __host__ UNEXPECTED_HOST_ATTRIBUTE
|
||||
|
||||
// Redefine __forceinline__ to include __device__.
|
||||
# pragma push_macro("__forceinline__")
|
||||
# define __forceinline__ __device__ __inline__ __attribute__((always_inline))
|
||||
|
||||
// Math functions: use fast or accurate variants based on compiler flag.
|
||||
# pragma push_macro("__USE_FAST_MATH__")
|
||||
# if defined(__CLANG_GPU_APPROX_TRANSCENDENTALS__)
|
||||
# define __USE_FAST_MATH__ 1
|
||||
# endif
|
||||
# include "crt/math_functions.hpp"
|
||||
# pragma pop_macro("__USE_FAST_MATH__")
|
||||
|
||||
# pragma pop_macro("__forceinline__")
|
||||
|
||||
# undef __MATH_FUNCTIONS_HPP__
|
||||
# undef __CUDABE__
|
||||
|
||||
// Re-include device functions with __host__ defined as empty to get
|
||||
// the "other branch" of #if/#else in the .hpp files.
|
||||
# define __host__
|
||||
# undef __CUDABE__
|
||||
# define __CUDACC__
|
||||
|
||||
// Atomic function declarations (became builtins in CUDA 9).
|
||||
# include "device_atomic_functions.h"
|
||||
# undef __DEVICE_FUNCTIONS_HPP__
|
||||
# include "crt/device_double_functions.hpp"
|
||||
# include "crt/device_functions.hpp"
|
||||
# include "device_atomic_functions.hpp"
|
||||
# include "sm_20_atomic_functions.hpp"
|
||||
|
||||
// sm_20_intrinsics.hpp defines __isGlobal etc. without const attribute.
|
||||
// Rename them so the definitions from Phase 4 (with const) prevail.
|
||||
# pragma push_macro("__isGlobal")
|
||||
# pragma push_macro("__isShared")
|
||||
# pragma push_macro("__isConstant")
|
||||
# pragma push_macro("__isLocal")
|
||||
# define __isGlobal __ignored_cuda___isGlobal
|
||||
# define __isShared __ignored_cuda___isShared
|
||||
# define __isConstant __ignored_cuda___isConstant
|
||||
# define __isLocal __ignored_cuda___isLocal
|
||||
# include "sm_20_intrinsics.hpp"
|
||||
# pragma pop_macro("__isGlobal")
|
||||
# pragma pop_macro("__isShared")
|
||||
# pragma pop_macro("__isConstant")
|
||||
# pragma pop_macro("__isLocal")
|
||||
|
||||
# include "sm_32_atomic_functions.hpp"
|
||||
|
||||
# pragma push_macro("__CUDA_ARCH__")
|
||||
# undef __CUDA_ARCH__
|
||||
# include "sm_60_atomic_functions.hpp"
|
||||
# include "sm_61_intrinsics.hpp"
|
||||
# pragma pop_macro("__CUDA_ARCH__")
|
||||
|
||||
# undef __MATH_FUNCTIONS_HPP__
|
||||
|
||||
// math_functions.hpp ::signbit conflicts with libstdc++ constexpr ::signbit.
|
||||
# pragma push_macro("signbit")
|
||||
# pragma push_macro("__GNUC__")
|
||||
# undef __GNUC__
|
||||
# define signbit __ignored_cuda_signbit
|
||||
# pragma push_macro("_GLIBCXX_MATH_H")
|
||||
# pragma push_macro("_LIBCPP_VERSION")
|
||||
# undef _GLIBCXX_MATH_H
|
||||
# ifdef _LIBCPP_VERSION
|
||||
# define _LIBCPP_VERSION 3700
|
||||
# endif
|
||||
# include "crt/math_functions.hpp"
|
||||
# pragma pop_macro("_GLIBCXX_MATH_H")
|
||||
# pragma pop_macro("_LIBCPP_VERSION")
|
||||
# pragma pop_macro("__GNUC__")
|
||||
# pragma pop_macro("signbit")
|
||||
|
||||
# pragma pop_macro("__host__")
|
||||
|
||||
// ============================================================================
|
||||
// Phase 5: cuda_runtime.h (first header that transitively pulls in CCCL)
|
||||
// ============================================================================
|
||||
// ============================================================================
|
||||
// Phase 5: cuda_runtime.h (first header that transitively pulls in CCCL)
|
||||
// ============================================================================
|
||||
// Verify no libcudacxx header was pulled in yet. If this fires, a header
|
||||
// above transitively included a system header that resolved to libcudacxx
|
||||
// before all device-side definitions were ready.
|
||||
# ifdef CCCL_VERSION
|
||||
# error "libcudacxx was included before device-side definitions were set up"
|
||||
# endif
|
||||
|
||||
# pragma push_macro("nv_weak")
|
||||
# define nv_weak weak
|
||||
# undef __CUDA_LIBDEVICE__
|
||||
# define __CUDACC__
|
||||
# include "cuda_runtime.h"
|
||||
# pragma pop_macro("nv_weak")
|
||||
# undef __CUDACC__
|
||||
# define __CUDABE__
|
||||
|
||||
# include "crt/host_runtime.h"
|
||||
|
||||
// device_runtime.h defines __cxa_* macros that conflict with cxxabi.h.
|
||||
# undef __cxa_vec_ctor
|
||||
# undef __cxa_vec_cctor
|
||||
# undef __cxa_vec_dtor
|
||||
# undef __cxa_vec_new
|
||||
# undef __cxa_vec_new2
|
||||
# undef __cxa_vec_new3
|
||||
# undef __cxa_vec_delete2
|
||||
# undef __cxa_vec_delete
|
||||
# undef __cxa_vec_delete3
|
||||
# undef __cxa_pure_virtual
|
||||
|
||||
// Texture intrinsics (requires C++11).
|
||||
# if __cplusplus >= 201103L
|
||||
# include <__clang_cuda_texture_intrinsics.h>
|
||||
# else
|
||||
template <typename T>
|
||||
struct __nv_tex_needs_cxx11
|
||||
{
|
||||
const static bool value = false;
|
||||
};
|
||||
template <class T>
|
||||
__host__ __device__ void __nv_tex_surf_handler(const char* name, T* ptr, cudaTextureObject_t obj, float x)
|
||||
{
|
||||
_Static_assert(__nv_tex_needs_cxx11<T>::value, "Texture support requires C++11");
|
||||
}
|
||||
# endif
|
||||
# include "surface_indirect_functions.h"
|
||||
# if CUDA_VERSION < 13000
|
||||
# include "texture_fetch_functions.h"
|
||||
# endif
|
||||
# include "texture_indirect_functions.h"
|
||||
|
||||
// ============================================================================
|
||||
// Phase 7: Restore saved state
|
||||
// ============================================================================
|
||||
# pragma pop_macro("__CUDA_ARCH__")
|
||||
# pragma pop_macro("__THROW")
|
||||
# undef __CUDABE__
|
||||
# define __CUDACC__
|
||||
|
||||
// ============================================================================
|
||||
// Phase 8: Device-side system calls & std wrappers
|
||||
// ============================================================================
|
||||
extern "C" {
|
||||
__device__ int vprintf(const char*, const char*);
|
||||
__device__ void free(void*) __attribute((nothrow));
|
||||
__device__ void* malloc(size_t) __attribute((nothrow)) __attribute__((malloc));
|
||||
__device__ void
|
||||
__assertfail(const char* __message, const char* __file, unsigned __line, const char* __function, size_t __charSize);
|
||||
__device__ static inline void
|
||||
__assert_fail(const char* __message, const char* __file, unsigned __line, const char* __function)
|
||||
{
|
||||
__assertfail(__message, __file, __line, __function, sizeof(char));
|
||||
}
|
||||
__device__ int printf(const char*, ...);
|
||||
} // extern "C"
|
||||
|
||||
namespace std
|
||||
{
|
||||
__device__ static inline void free(void* __ptr)
|
||||
{
|
||||
::free(__ptr);
|
||||
}
|
||||
__device__ static inline void* malloc(size_t __size)
|
||||
{
|
||||
return ::malloc(__size);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
// ============================================================================
|
||||
// Phase 9: Builtin variable conversion operators
|
||||
// ============================================================================
|
||||
// These need dim3 and uint3 to be fully defined (from vector_types.h, pulled
|
||||
// in by driver_types.h in Phase 5).
|
||||
__device__ inline __cuda_builtin_threadIdx_t::operator dim3() const
|
||||
{
|
||||
return dim3(x, y, z);
|
||||
}
|
||||
__device__ inline __cuda_builtin_threadIdx_t::operator uint3() const
|
||||
{
|
||||
return {x, y, z};
|
||||
}
|
||||
__device__ inline __cuda_builtin_blockIdx_t::operator dim3() const
|
||||
{
|
||||
return dim3(x, y, z);
|
||||
}
|
||||
__device__ inline __cuda_builtin_blockIdx_t::operator uint3() const
|
||||
{
|
||||
return {x, y, z};
|
||||
}
|
||||
__device__ inline __cuda_builtin_blockDim_t::operator dim3() const
|
||||
{
|
||||
return dim3(x, y, z);
|
||||
}
|
||||
__device__ inline __cuda_builtin_blockDim_t::operator uint3() const
|
||||
{
|
||||
return {x, y, z};
|
||||
}
|
||||
__device__ inline __cuda_builtin_gridDim_t::operator dim3() const
|
||||
{
|
||||
return dim3(x, y, z);
|
||||
}
|
||||
__device__ inline __cuda_builtin_gridDim_t::operator uint3() const
|
||||
{
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Phase 10: Remaining clang CUDA headers
|
||||
// ============================================================================
|
||||
# include <__clang_cuda_cmath.h>
|
||||
# include <__clang_cuda_intrinsics.h>
|
||||
|
||||
// __clang_cuda_intrinsics.h provides `long` overloads for __ldcs/__ldcg/__ldcv
|
||||
// but omits `unsigned long` (= uint64_t on 64-bit Linux). Add them here so
|
||||
// iterators using uint64_t pointers (e.g. CacheModifiedInputIterator) compile.
|
||||
# if defined(__LP64__)
|
||||
inline __device__ unsigned long __ldcs(const unsigned long* __ptr)
|
||||
{
|
||||
unsigned long __ret;
|
||||
asm("ld.global.cs.u64 %0, [%1];" : "=l"(__ret) : "l"(__ptr));
|
||||
return __ret;
|
||||
}
|
||||
inline __device__ unsigned long __ldcg(const unsigned long* __ptr)
|
||||
{
|
||||
unsigned long __ret;
|
||||
asm("ld.global.cg.u64 %0, [%1];" : "=l"(__ret) : "l"(__ptr));
|
||||
return __ret;
|
||||
}
|
||||
inline __device__ unsigned long __ldcv(const unsigned long* __ptr)
|
||||
{
|
||||
unsigned long __ret;
|
||||
asm("ld.global.cv.u64 %0, [%1];" : "=l"(__ret) : "l"(__ptr));
|
||||
return __ret;
|
||||
}
|
||||
# endif // __LP64__
|
||||
|
||||
# include <__clang_cuda_complex_builtins.h>
|
||||
|
||||
// curand_mtgp32_kernel redefines blockDim/threadIdx with dim3/uint3 types,
|
||||
// which is incompatible with our builtins. Force-include it with types
|
||||
// redefined to our builtin types.
|
||||
// Skip when cuRAND headers are unavailable (e.g. pip-installed toolkit).
|
||||
# if __has_include("curand_mtgp32_kernel.h")
|
||||
# pragma push_macro("dim3")
|
||||
# pragma push_macro("uint3")
|
||||
# define dim3 __cuda_builtin_blockDim_t
|
||||
# define uint3 __cuda_builtin_threadIdx_t
|
||||
# include "curand_mtgp32_kernel.h"
|
||||
# pragma pop_macro("dim3")
|
||||
# pragma pop_macro("uint3")
|
||||
# endif
|
||||
# pragma pop_macro("__CUDA_INCLUDE_COMPILER_INTERNAL_HEADERS__")
|
||||
|
||||
// Kernel launch configuration function.
|
||||
# if CUDA_VERSION >= 9020
|
||||
extern "C" unsigned __cudaPushCallConfiguration(dim3 gridDim, dim3 blockDim, size_t sharedMem = 0, void* stream = 0);
|
||||
# endif
|
||||
|
||||
// The JIT shared library is linked without the C runtime (no libc on the link
|
||||
// line) so atexit is unavailable. The CUDA module constructor calls atexit()
|
||||
// to register a cleanup function. Provide a no-op stub — the JIT library is
|
||||
// short-lived and unloaded explicitly.
|
||||
# if !defined(__HOSTJIT_DEVICE_COMPILATION__)
|
||||
# if defined(_MSC_VER)
|
||||
extern "C" int atexit(void(__cdecl*)(void))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
# else
|
||||
extern "C" int atexit(void (*)(void))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#endif // __CUDA__ && __clang__
|
||||
#endif // __CLANG_CUDA_RUNTIME_WRAPPER_H__
|
||||
@@ -0,0 +1,31 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Minimal freestanding-mode stub for <assert.h>.
|
||||
//
|
||||
// CUDA toolkit headers pulled in via libcudacxx's __floating_point/cuda_fp_types.h
|
||||
// (e.g. cuda_fp8.hpp) include <assert.h> unconditionally. In the JIT compile
|
||||
// environment we have no libc; treat assert(expr) as a no-op. This matches the
|
||||
// effect of `-DNDEBUG`, which CCCL/CUB device code already expects.
|
||||
#ifndef _HOSTJIT_ASSERT_H
|
||||
#define _HOSTJIT_ASSERT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#undef assert
|
||||
#define assert(expr) ((void) 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _HOSTJIT_ASSERT_H
|
||||
@@ -0,0 +1,6 @@
|
||||
// Minimal freestanding-mode stub for <cassert>.
|
||||
// Just delegate to <assert.h>'s no-op assert.
|
||||
#ifndef _HOSTJIT_CASSERT
|
||||
#define _HOSTJIT_CASSERT
|
||||
#include <assert.h>
|
||||
#endif // _HOSTJIT_CASSERT
|
||||
@@ -0,0 +1,7 @@
|
||||
// Minimal climits stub for CUDA JIT compilation
|
||||
#ifndef _HOSTJIT_CLIMITS
|
||||
#define _HOSTJIT_CLIMITS
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#endif // _HOSTJIT_CLIMITS
|
||||
@@ -0,0 +1,7 @@
|
||||
// Minimal cmath stub for CUDA JIT compilation
|
||||
#ifndef _HOSTJIT_CMATH
|
||||
#define _HOSTJIT_CMATH
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#endif // _HOSTJIT_CMATH
|
||||
@@ -0,0 +1,15 @@
|
||||
// Minimal cstddef stub for CUDA JIT compilation
|
||||
// Compatible with libcu++ which expects to pull types from global namespace
|
||||
#ifndef _HOSTJIT_CSTDDEF
|
||||
#define _HOSTJIT_CSTDDEF
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
namespace std {
|
||||
using ::size_t;
|
||||
using ::ptrdiff_t;
|
||||
using nullptr_t = decltype(nullptr);
|
||||
}
|
||||
|
||||
#endif // _HOSTJIT_CSTDDEF
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef _HOSTJIT_CSTDLIB
|
||||
#define _HOSTJIT_CSTDLIB
|
||||
#include <cstddef>
|
||||
#define EXIT_SUCCESS 0
|
||||
#define EXIT_FAILURE 1
|
||||
#define RAND_MAX 2147483647
|
||||
extern "C" {
|
||||
void* malloc(size_t); void* calloc(size_t, size_t);
|
||||
void* realloc(void*, size_t); void free(void*);
|
||||
void abort(void); void exit(int); void _Exit(int);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#ifndef _HOSTJIT_CTYPE_H
|
||||
#define _HOSTJIT_CTYPE_H
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// ClangJIT minimal stub for cuda/std/__cstdlib/aligned_alloc.h
|
||||
//
|
||||
// Problem: hostjit compiles with _CCCL_ENABLE_FREESTANDING=1 in both device
|
||||
// and host passes. The host pass needs ::cuda::std::__aligned_alloc_host, but
|
||||
// the real header gates that function on _CCCL_HOSTED(), which is 0 in a
|
||||
// freestanding build.
|
||||
//
|
||||
// Solution: replace the entire header with a bare-metal stub that uses only
|
||||
// compiler builtins (__builtin_malloc, __SIZE_TYPE__) and NO CCCL headers.
|
||||
// Including CCCL headers from within this stub caused __clang_cuda_device_functions.h
|
||||
// to be re-processed before __clang_cuda_libdevice_declares.h during device
|
||||
// compilation, producing "undeclared identifier __nv_ull2float_rz" errors.
|
||||
//
|
||||
// __builtin_malloc is a compiler intrinsic — no headers required.
|
||||
// __SIZE_TYPE__ is a compiler predefined macro equal to the platform size_t type.
|
||||
//
|
||||
// Neither path is ever actually called at runtime:
|
||||
// - Host pass: CUB dispatch never calls aligned_alloc in our generated source.
|
||||
// - Device pass: NV_IF_ELSE_TARGET discards the NV_IS_HOST branch at compile time.
|
||||
|
||||
#ifndef _CUDA_STD___CSTDLIB_ALIGNED_ALLOC_H
|
||||
#define _CUDA_STD___CSTDLIB_ALIGNED_ALLOC_H
|
||||
|
||||
#if defined(__CUDA_ARCH__)
|
||||
|
||||
// ── Device compilation ────────────────────────────────────────────────────
|
||||
// Provide cuda::std::aligned_alloc via the CUDA device syscall.
|
||||
// The NV_IS_HOST branch of the CUB include chain is discarded by Clang's
|
||||
// "if target" extension, so this function is never actually called.
|
||||
extern "C" __device__ void* __cuda_syscall_aligned_malloc(__SIZE_TYPE__, __SIZE_TYPE__);
|
||||
|
||||
namespace cuda
|
||||
{
|
||||
namespace std
|
||||
{
|
||||
inline __device__ void* aligned_alloc(__SIZE_TYPE__ __align, __SIZE_TYPE__ __nbytes) noexcept
|
||||
{
|
||||
return ::__cuda_syscall_aligned_malloc(__nbytes, __align);
|
||||
}
|
||||
} // namespace std
|
||||
} // namespace cuda
|
||||
|
||||
#else
|
||||
|
||||
// ── Host compilation ──────────────────────────────────────────────────────
|
||||
// Define __aligned_alloc_host unconditionally so the CUB include chain
|
||||
// compiles even when _CCCL_HOSTED() == 0. __builtin_malloc needs no headers.
|
||||
namespace cuda
|
||||
{
|
||||
namespace std
|
||||
{
|
||||
inline void* __aligned_alloc_host(__SIZE_TYPE__, __SIZE_TYPE__ __nbytes) noexcept
|
||||
{
|
||||
return __builtin_malloc(__nbytes);
|
||||
}
|
||||
inline void* aligned_alloc(__SIZE_TYPE__ __align, __SIZE_TYPE__ __nbytes) noexcept
|
||||
{
|
||||
return ::cuda::std::__aligned_alloc_host(__align, __nbytes);
|
||||
}
|
||||
} // namespace std
|
||||
} // namespace cuda
|
||||
|
||||
#endif // __CUDA_ARCH__
|
||||
|
||||
#endif // _CUDA_STD___CSTDLIB_ALIGNED_ALLOC_H
|
||||
@@ -0,0 +1,47 @@
|
||||
// Minimal initializer_list stub for CUDA JIT compilation
|
||||
#ifndef _HOSTJIT_INITIALIZER_LIST
|
||||
#define _HOSTJIT_INITIALIZER_LIST
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace std {
|
||||
|
||||
template<class T>
|
||||
class initializer_list {
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = const T&;
|
||||
using const_reference = const T&;
|
||||
using size_type = size_t;
|
||||
using iterator = const T*;
|
||||
using const_iterator = const T*;
|
||||
|
||||
private:
|
||||
const T* _begin;
|
||||
size_t _size;
|
||||
|
||||
// This constructor is called by the compiler
|
||||
constexpr initializer_list(const T* b, size_t s) noexcept
|
||||
: _begin(b), _size(s) {}
|
||||
|
||||
public:
|
||||
constexpr initializer_list() noexcept : _begin(nullptr), _size(0) {}
|
||||
|
||||
constexpr size_t size() const noexcept { return _size; }
|
||||
constexpr const T* begin() const noexcept { return _begin; }
|
||||
constexpr const T* end() const noexcept { return _begin + _size; }
|
||||
};
|
||||
|
||||
template<class T>
|
||||
constexpr const T* begin(initializer_list<T> il) noexcept {
|
||||
return il.begin();
|
||||
}
|
||||
|
||||
template<class T>
|
||||
constexpr const T* end(initializer_list<T> il) noexcept {
|
||||
return il.end();
|
||||
}
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // _HOSTJIT_INITIALIZER_LIST
|
||||
@@ -0,0 +1,61 @@
|
||||
// Minimal <limits> stub for hostjit device compilation.
|
||||
//
|
||||
// Clang's __clang_cuda_cmath.h includes <limits> unconditionally, then expands
|
||||
// __CUDA_CLANG_FN_INTEGER_OVERLOAD_1/2 macros that reference
|
||||
// std::numeric_limits<__T>::is_integer in return-type SFINAE at parse time.
|
||||
// Clang evaluates these dependent names during template parsing, so the struct
|
||||
// must be declared — not just forward-declared — before the macro expansion.
|
||||
//
|
||||
// In the hostjit device-compilation include path, <limits> would normally
|
||||
// resolve to libcudacxx/include/cuda/std/limits, which cascades through
|
||||
// numeric_limits, bit_cast, popcount, etc. — incompatible with freestanding.
|
||||
//
|
||||
// This stub (found first on -internal-isystem) stops that cascade, providing
|
||||
// only the two members that __clang_cuda_cmath.h actually inspects.
|
||||
#pragma once
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename _Tp>
|
||||
struct numeric_limits {
|
||||
static constexpr bool is_specialized = false;
|
||||
static constexpr bool is_integer = false;
|
||||
};
|
||||
|
||||
// Integer specializations — needed so the SFINAE in __clang_cuda_cmath.h
|
||||
// correctly dispatches integer arguments.
|
||||
#define _HOSTJIT_NUM_LIM_INT(_T) \
|
||||
template <> struct numeric_limits<_T> { \
|
||||
static constexpr bool is_specialized = true; \
|
||||
static constexpr bool is_integer = true; \
|
||||
};
|
||||
|
||||
_HOSTJIT_NUM_LIM_INT(bool)
|
||||
_HOSTJIT_NUM_LIM_INT(char)
|
||||
_HOSTJIT_NUM_LIM_INT(signed char)
|
||||
_HOSTJIT_NUM_LIM_INT(unsigned char)
|
||||
_HOSTJIT_NUM_LIM_INT(short)
|
||||
_HOSTJIT_NUM_LIM_INT(unsigned short)
|
||||
_HOSTJIT_NUM_LIM_INT(int)
|
||||
_HOSTJIT_NUM_LIM_INT(unsigned int)
|
||||
_HOSTJIT_NUM_LIM_INT(long)
|
||||
_HOSTJIT_NUM_LIM_INT(unsigned long)
|
||||
_HOSTJIT_NUM_LIM_INT(long long)
|
||||
_HOSTJIT_NUM_LIM_INT(unsigned long long)
|
||||
|
||||
#undef _HOSTJIT_NUM_LIM_INT
|
||||
|
||||
// Floating-point specializations.
|
||||
#define _HOSTJIT_NUM_LIM_FP(_T) \
|
||||
template <> struct numeric_limits<_T> { \
|
||||
static constexpr bool is_specialized = true; \
|
||||
static constexpr bool is_integer = false; \
|
||||
};
|
||||
|
||||
_HOSTJIT_NUM_LIM_FP(float)
|
||||
_HOSTJIT_NUM_LIM_FP(double)
|
||||
_HOSTJIT_NUM_LIM_FP(long double)
|
||||
|
||||
#undef _HOSTJIT_NUM_LIM_FP
|
||||
|
||||
} // namespace std
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef _HOSTJIT_MATH_H
|
||||
#define _HOSTJIT_MATH_H
|
||||
|
||||
// Macros needed by __clang_cuda_math.h
|
||||
#define HUGE_VAL __builtin_huge_val()
|
||||
#define HUGE_VALF __builtin_huge_valf()
|
||||
#define HUGE_VALL __builtin_huge_vall()
|
||||
#define INFINITY __builtin_inff()
|
||||
#define NAN __builtin_nanf("")
|
||||
#define MATH_ERRNO 1
|
||||
#define MATH_ERREXCEPT 2
|
||||
#define math_errhandling (MATH_ERRNO | MATH_ERREXCEPT)
|
||||
#define FP_NAN 0
|
||||
#define FP_INFINITE 1
|
||||
#define FP_ZERO 2
|
||||
#define FP_SUBNORMAL 3
|
||||
#define FP_NORMAL 4
|
||||
#define __signbit(x) __builtin_signbit(x)
|
||||
#define __signbitl(x) __builtin_signbitl(x)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifndef _HOSTJIT_MEMORY_H
|
||||
#define _HOSTJIT_MEMORY_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef _HOSTJIT_NEW
|
||||
#define _HOSTJIT_NEW
|
||||
#include <cstddef>
|
||||
|
||||
namespace std {
|
||||
struct nothrow_t { explicit nothrow_t() = default; };
|
||||
extern const nothrow_t nothrow;
|
||||
enum class align_val_t : size_t {};
|
||||
}
|
||||
|
||||
// Placement new — needs __host__ __device__ for CUDA
|
||||
#if defined(__CUDA__)
|
||||
__host__ __device__
|
||||
#endif
|
||||
inline void* operator new(std::size_t, void* p) noexcept { return p; }
|
||||
#if defined(__CUDA__)
|
||||
__host__ __device__
|
||||
#endif
|
||||
inline void* operator new[](std::size_t, void* p) noexcept { return p; }
|
||||
#if defined(__CUDA__)
|
||||
__host__ __device__
|
||||
#endif
|
||||
inline void operator delete(void*, void*) noexcept {}
|
||||
#if defined(__CUDA__)
|
||||
__host__ __device__
|
||||
#endif
|
||||
inline void operator delete[](void*, void*) noexcept {}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
// Minimal stdlib.h stub for CUDA JIT compilation
|
||||
#ifndef _HOSTJIT_STDLIB_H
|
||||
#define _HOSTJIT_STDLIB_H
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef _WIN32
|
||||
extern "C" int _fltused = 0;
|
||||
#endif // _WIN32
|
||||
|
||||
#endif // _HOSTJIT_STDLIB_H
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef _HOSTJIT_STRING_H
|
||||
#define _HOSTJIT_STRING_H
|
||||
|
||||
#include <stddef.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
inline void* memcpy(void* __s1, const void* __s2, size_t __n)
|
||||
{
|
||||
return __builtin_memcpy(__s1, __s2, __n);
|
||||
}
|
||||
inline void* memset(void* __s, int __c, size_t __n)
|
||||
{
|
||||
return __builtin_memset(__s, __c, __n);
|
||||
}
|
||||
inline void* memmove(void* __s1, const void* __s2, size_t __n)
|
||||
{
|
||||
return __builtin_memmove(__s1, __s2, __n);
|
||||
}
|
||||
inline int memcmp(const void* __s1, const void* __s2, size_t __n)
|
||||
{
|
||||
return __builtin_memcmp(__s1, __s2, __n);
|
||||
}
|
||||
inline char* strchr(char* __s, int __c)
|
||||
{
|
||||
return __builtin_strchr(__s, __c);
|
||||
}
|
||||
inline char* strpbrk(char* __s1, const char* __s2)
|
||||
{
|
||||
return __builtin_strpbrk(__s1, __s2);
|
||||
}
|
||||
inline char* strrchr(char* __s, int __c)
|
||||
{
|
||||
return __builtin_strrchr(__s, __c);
|
||||
}
|
||||
inline void* memchr(void* __s, int __c, size_t __n)
|
||||
{
|
||||
return __builtin_memchr(__s, __c, __n);
|
||||
}
|
||||
inline char* strstr(char* __s1, const char* __s2)
|
||||
{
|
||||
return __builtin_strstr(__s1, __s2);
|
||||
}
|
||||
inline char* strcpy(char* __s1, const char* __s2)
|
||||
{
|
||||
return __builtin_strcpy(__s1, __s2);
|
||||
}
|
||||
inline char* strncpy(char* __s1, const char* __s2, size_t __n)
|
||||
{
|
||||
return __builtin_strncpy(__s1, __s2, __n);
|
||||
}
|
||||
inline int strcmp(const char* __s1, const char* __s2)
|
||||
{
|
||||
return __builtin_strcmp(__s1, __s2);
|
||||
}
|
||||
inline int strncmp(const char* __s1, const char* __s2, size_t __n)
|
||||
{
|
||||
return __builtin_strncmp(__s1, __s2, __n);
|
||||
}
|
||||
inline size_t strlen(const char* __s)
|
||||
{
|
||||
return __builtin_strlen(__s);
|
||||
}
|
||||
}
|
||||
#else // ^^^ __cplusplus ^^^ / vvv !__cplusplus vvv
|
||||
void* memcpy(void*, const void*, size_t);
|
||||
void* memset(void*, int, size_t);
|
||||
int memcmp(const void*, const void*, size_t);
|
||||
void* memmove(void*, const void*, size_t);
|
||||
size_t strlen(const char*);
|
||||
#endif // !__cplusplus
|
||||
|
||||
#endif //_HOSTJIT_STRING_H
|
||||
@@ -0,0 +1,34 @@
|
||||
// Minimal <utility> stub for hostjit device compilation.
|
||||
//
|
||||
// cuda_runtime.h includes <utility> for std::forward/std::move. In the
|
||||
// hostjit device-compilation include path, <utility> resolves to
|
||||
// libcudacxx/include/cuda/std/utility, which cascades into the full CCCL
|
||||
// utility/iterator/concepts hierarchy — incompatible with freestanding mode.
|
||||
//
|
||||
// This stub (found first on -internal-isystem) stops that cascade.
|
||||
// Only std::forward and std::move are provided because that is all
|
||||
// cuda_runtime.h actually uses at the top level; the full CCCL hierarchy
|
||||
// is not required for a simple host+device kernel.
|
||||
#pragma once
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename _Tp> struct remove_reference { using type = _Tp; };
|
||||
template <typename _Tp> struct remove_reference<_Tp&> { using type = _Tp; };
|
||||
template <typename _Tp> struct remove_reference<_Tp&&>{ using type = _Tp; };
|
||||
template <typename _Tp>
|
||||
using remove_reference_t = typename remove_reference<_Tp>::type;
|
||||
|
||||
template <typename _Tp>
|
||||
__host__ __device__ constexpr _Tp&&
|
||||
forward(remove_reference_t<_Tp>& __t) noexcept { return static_cast<_Tp&&>(__t); }
|
||||
|
||||
template <typename _Tp>
|
||||
__host__ __device__ constexpr _Tp&&
|
||||
forward(remove_reference_t<_Tp>&& __t) noexcept { return static_cast<_Tp&&>(__t); }
|
||||
|
||||
template <typename _Tp>
|
||||
__host__ __device__ constexpr remove_reference_t<_Tp>&&
|
||||
move(_Tp&& __t) noexcept { return static_cast<remove_reference_t<_Tp>&&>(__t); }
|
||||
|
||||
} // namespace std
|
||||
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <hostjit/compiler.hpp>
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/loader.hpp>
|
||||
|
||||
namespace hostjit
|
||||
{
|
||||
class JITCompiler
|
||||
{
|
||||
public:
|
||||
// Create JIT compiler with default configuration (auto-detected)
|
||||
JITCompiler();
|
||||
|
||||
// Create JIT compiler with custom configuration
|
||||
explicit JITCompiler(const CompilerConfig& config);
|
||||
|
||||
~JITCompiler();
|
||||
|
||||
// Disable copy
|
||||
JITCompiler(const JITCompiler&) = delete;
|
||||
JITCompiler& operator=(const JITCompiler&) = delete;
|
||||
|
||||
// Compile CUDA source code to shared library and load it
|
||||
// Returns true on success, false on failure
|
||||
bool compile(const std::string& source_code);
|
||||
|
||||
// Get function pointer by name
|
||||
// Returns nullptr if function not found
|
||||
template <typename FuncType>
|
||||
FuncType getFunction(const std::string& name)
|
||||
{
|
||||
if (!library_.isLoaded())
|
||||
{
|
||||
last_error_ = "No library loaded";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto func = library_.getFunction<FuncType>(name);
|
||||
if (!func)
|
||||
{
|
||||
last_error_ = "Failed to find function '" + name + "': " + library_.getLastError();
|
||||
}
|
||||
return func;
|
||||
}
|
||||
|
||||
// Get the last error message
|
||||
std::string getLastError() const
|
||||
{
|
||||
return last_error_;
|
||||
}
|
||||
|
||||
// Get the configuration being used
|
||||
const CompilerConfig& getConfig() const
|
||||
{
|
||||
return config_;
|
||||
}
|
||||
|
||||
// Check if a library is currently loaded
|
||||
bool isLoaded() const
|
||||
{
|
||||
return library_.isLoaded();
|
||||
}
|
||||
|
||||
// Get the path to compiled artifacts (object file, shared library, etc.)
|
||||
// Only valid after successful compile() and if keep_artifacts is set
|
||||
std::string getArtifactsPath() const
|
||||
{
|
||||
return temp_dir_;
|
||||
}
|
||||
|
||||
// Get the cubin extracted during compilation
|
||||
const std::vector<char>& getCubin() const
|
||||
{
|
||||
return cubin_;
|
||||
}
|
||||
|
||||
// Unload the current library and clean up temporary files
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
std::string createTempDirectory();
|
||||
void removeTempDirectory();
|
||||
|
||||
CompilerConfig config_;
|
||||
CUDACompiler compiler_;
|
||||
DynamicLibrary library_;
|
||||
std::string temp_dir_;
|
||||
std::string last_error_;
|
||||
std::vector<char> cubin_;
|
||||
};
|
||||
} // namespace hostjit
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace hostjit
|
||||
{
|
||||
class DynamicLibrary
|
||||
{
|
||||
public:
|
||||
DynamicLibrary();
|
||||
~DynamicLibrary();
|
||||
|
||||
// Disable copy
|
||||
DynamicLibrary(const DynamicLibrary&) = delete;
|
||||
DynamicLibrary& operator=(const DynamicLibrary&) = delete;
|
||||
|
||||
// Enable move
|
||||
DynamicLibrary(DynamicLibrary&& other) noexcept;
|
||||
DynamicLibrary& operator=(DynamicLibrary&& other) noexcept;
|
||||
|
||||
// Load a shared library
|
||||
bool load(const std::string& library_path);
|
||||
|
||||
// Get a symbol (function or variable) by name
|
||||
void* getSymbol(const std::string& symbol_name);
|
||||
|
||||
// Template helper to get function pointers with type safety
|
||||
template <typename FuncType>
|
||||
FuncType getFunction(const std::string& name)
|
||||
{
|
||||
return reinterpret_cast<FuncType>(getSymbol(name));
|
||||
}
|
||||
|
||||
// Check if library is loaded
|
||||
bool isLoaded() const;
|
||||
|
||||
// Get the last error message
|
||||
std::string getLastError() const;
|
||||
|
||||
// Unload the library
|
||||
void unload();
|
||||
|
||||
private:
|
||||
void* handle_;
|
||||
std::string last_error_;
|
||||
};
|
||||
} // namespace hostjit
|
||||
192
cccl_upstream/c/parallel.v2/src/hostjit/jit_compiler.cpp
Normal file
192
cccl_upstream/c/parallel.v2/src/hostjit/jit_compiler.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <process.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace hostjit
|
||||
{
|
||||
JITCompiler::JITCompiler()
|
||||
: config_(detectDefaultConfig())
|
||||
{}
|
||||
|
||||
JITCompiler::JITCompiler(const CompilerConfig& config)
|
||||
: config_(config)
|
||||
{}
|
||||
|
||||
JITCompiler::~JITCompiler()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
bool JITCompiler::compile(const std::string& source_code)
|
||||
{
|
||||
std::string config_error;
|
||||
if (!validateConfig(config_, &config_error))
|
||||
{
|
||||
last_error_ = "Configuration error: " + config_error;
|
||||
return false;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
temp_dir_ = createTempDirectory();
|
||||
if (temp_dir_.empty())
|
||||
{
|
||||
last_error_ = "Failed to create temporary directory";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string obj_path = temp_dir_ + "/cuda_code.o";
|
||||
auto compile_result = compiler_.compileToObject(source_code, obj_path, config_);
|
||||
|
||||
if (!compile_result.success)
|
||||
{
|
||||
last_error_ = "Compilation failed:\n" + compile_result.diagnostics;
|
||||
removeTempDirectory();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store the cubin for later inspection
|
||||
cubin_ = std::move(compile_result.cubin);
|
||||
|
||||
if (config_.verbose)
|
||||
{
|
||||
std::cout << "Compilation diagnostics:\n" << compile_result.diagnostics << "\n";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string lib_path = temp_dir_ + "/cuda_code.dll";
|
||||
#else
|
||||
std::string lib_path = temp_dir_ + "/libcuda_code.so";
|
||||
#endif
|
||||
auto link_result = compiler_.linkToSharedLibrary({obj_path}, lib_path, config_);
|
||||
|
||||
if (!link_result.success)
|
||||
{
|
||||
last_error_ = "Linking failed:\n" + link_result.diagnostics;
|
||||
removeTempDirectory();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config_.verbose)
|
||||
{
|
||||
std::cout << "Linking diagnostics:\n" << link_result.diagnostics << "\n";
|
||||
}
|
||||
|
||||
if (!library_.load(lib_path))
|
||||
{
|
||||
last_error_ = "Failed to load library: " + library_.getLastError();
|
||||
removeTempDirectory();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config_.verbose)
|
||||
{
|
||||
std::cout << "Successfully loaded library: " << lib_path << "\n";
|
||||
}
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void JITCompiler::cleanup()
|
||||
{
|
||||
library_.unload();
|
||||
|
||||
if (!config_.keep_artifacts)
|
||||
{
|
||||
removeTempDirectory();
|
||||
}
|
||||
|
||||
last_error_.clear();
|
||||
}
|
||||
|
||||
std::string JITCompiler::createTempDirectory()
|
||||
{
|
||||
std::filesystem::path base_tmp_dir;
|
||||
|
||||
#ifdef _WIN32
|
||||
const char* tmp_dir = std::getenv("TEMP");
|
||||
if (!tmp_dir)
|
||||
{
|
||||
tmp_dir = std::getenv("TMP");
|
||||
}
|
||||
if (tmp_dir)
|
||||
{
|
||||
base_tmp_dir = tmp_dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
base_tmp_dir = std::filesystem::temp_directory_path();
|
||||
}
|
||||
#else
|
||||
const char* tmp_dir = std::getenv("TMPDIR");
|
||||
if (tmp_dir)
|
||||
{
|
||||
base_tmp_dir = tmp_dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
base_tmp_dir = "/tmp";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_int_distribution<> dis(0, 999999);
|
||||
|
||||
#ifdef _WIN32
|
||||
int pid = _getpid();
|
||||
#else
|
||||
int pid = getpid();
|
||||
#endif
|
||||
|
||||
for (int attempt = 0; attempt < 10; ++attempt)
|
||||
{
|
||||
std::string dir_name = "hostjit_" + std::to_string(pid) + "_" + std::to_string(dis(gen));
|
||||
std::filesystem::path full_path = base_tmp_dir / dir_name;
|
||||
|
||||
std::error_code ec;
|
||||
if (std::filesystem::create_directories(full_path, ec) && !ec)
|
||||
{
|
||||
return full_path.string();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void JITCompiler::removeTempDirectory()
|
||||
{
|
||||
if (temp_dir_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (std::filesystem::exists(temp_dir_))
|
||||
{
|
||||
std::filesystem::remove_all(temp_dir_);
|
||||
}
|
||||
}
|
||||
catch (const std::filesystem::filesystem_error& e)
|
||||
{
|
||||
if (config_.verbose)
|
||||
{
|
||||
std::cerr << "Warning: Failed to remove temporary directory: " << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
temp_dir_.clear();
|
||||
}
|
||||
} // namespace hostjit
|
||||
210
cccl_upstream/c/parallel.v2/src/hostjit/loader.cpp
Normal file
210
cccl_upstream/c/parallel.v2/src/hostjit/loader.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
#include <hostjit/loader.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace hostjit
|
||||
{
|
||||
#ifdef _WIN32
|
||||
namespace
|
||||
{
|
||||
// Run C++ static constructors in a DLL loaded with /NOENTRY /NODEFAULTLIB.
|
||||
// The compiler places CUDA fatbin registration in the .CRT$XCU section.
|
||||
// Without CRT startup, these never run, so we walk the merged .CRT section
|
||||
// in the PE and call each non-null function pointer.
|
||||
void runStaticInitializers(HMODULE module)
|
||||
{
|
||||
auto base = reinterpret_cast<const unsigned char*>(module);
|
||||
auto dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(base);
|
||||
auto nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(base + dos->e_lfanew);
|
||||
auto sec = IMAGE_FIRST_SECTION(nt);
|
||||
|
||||
for (WORD i = 0; i < nt->FileHeader.NumberOfSections; ++i, ++sec)
|
||||
{
|
||||
if (memcmp(sec->Name, ".CRT", 4) == 0)
|
||||
{
|
||||
using InitFunc = void(__cdecl*)();
|
||||
auto funcs = reinterpret_cast<InitFunc*>(const_cast<unsigned char*>(base) + sec->VirtualAddress);
|
||||
size_t count = sec->SizeOfRawData / sizeof(InitFunc);
|
||||
for (size_t j = 0; j < count; ++j)
|
||||
{
|
||||
if (funcs[j])
|
||||
{
|
||||
funcs[j]();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string getWindowsError()
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
LPSTR buffer = nullptr;
|
||||
DWORD size = FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr,
|
||||
error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<LPSTR>(&buffer),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
std::string message;
|
||||
if (size > 0 && buffer)
|
||||
{
|
||||
message = std::string(buffer, size);
|
||||
while (!message.empty() && (message.back() == '\n' || message.back() == '\r'))
|
||||
{
|
||||
message.pop_back();
|
||||
}
|
||||
LocalFree(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
message = "Unknown error (code: " + std::to_string(error) + ")";
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
} // anonymous namespace
|
||||
#endif
|
||||
|
||||
DynamicLibrary::DynamicLibrary()
|
||||
: handle_(nullptr)
|
||||
{}
|
||||
|
||||
DynamicLibrary::~DynamicLibrary()
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
||||
DynamicLibrary::DynamicLibrary(DynamicLibrary&& other) noexcept
|
||||
: handle_(other.handle_)
|
||||
, last_error_(std::move(other.last_error_))
|
||||
{
|
||||
other.handle_ = nullptr;
|
||||
}
|
||||
|
||||
DynamicLibrary& DynamicLibrary::operator=(DynamicLibrary&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
unload();
|
||||
handle_ = other.handle_;
|
||||
last_error_ = std::move(other.last_error_);
|
||||
other.handle_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DynamicLibrary::load(const std::string& library_path)
|
||||
{
|
||||
unload();
|
||||
|
||||
#ifdef _WIN32
|
||||
SetLastError(0);
|
||||
handle_ = static_cast<void*>(LoadLibraryA(library_path.c_str()));
|
||||
|
||||
if (!handle_)
|
||||
{
|
||||
last_error_ = getWindowsError();
|
||||
if (last_error_.empty())
|
||||
{
|
||||
last_error_ = "Unknown LoadLibrary error";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The DLL is linked with /NOENTRY (no CRT startup), so C++ static
|
||||
// constructors (e.g. CUDA fatbin registration) haven't run yet.
|
||||
runStaticInitializers(static_cast<HMODULE>(handle_));
|
||||
#else
|
||||
dlerror();
|
||||
handle_ = dlopen(library_path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
|
||||
if (!handle_)
|
||||
{
|
||||
const char* error = dlerror();
|
||||
last_error_ = error ? error : "Unknown dlopen error";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
last_error_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void* DynamicLibrary::getSymbol(const std::string& symbol_name)
|
||||
{
|
||||
if (!handle_)
|
||||
{
|
||||
last_error_ = "Library not loaded";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
SetLastError(0);
|
||||
void* symbol = reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle_), symbol_name.c_str()));
|
||||
|
||||
if (!symbol)
|
||||
{
|
||||
last_error_ = getWindowsError();
|
||||
if (last_error_.empty())
|
||||
{
|
||||
last_error_ = "Symbol not found: " + symbol_name;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
dlerror();
|
||||
void* symbol = dlsym(handle_, symbol_name.c_str());
|
||||
|
||||
const char* error = dlerror();
|
||||
if (error)
|
||||
{
|
||||
last_error_ = error;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
last_error_.clear();
|
||||
return symbol;
|
||||
}
|
||||
|
||||
bool DynamicLibrary::isLoaded() const
|
||||
{
|
||||
return handle_ != nullptr;
|
||||
}
|
||||
|
||||
std::string DynamicLibrary::getLastError() const
|
||||
{
|
||||
return last_error_;
|
||||
}
|
||||
|
||||
void DynamicLibrary::unload()
|
||||
{
|
||||
if (handle_)
|
||||
{
|
||||
// Intentionally do NOT unload (dlclose / FreeLibrary) a compiled JIT module. See #9367.
|
||||
//
|
||||
// Each JIT .so is built by Clang with the classic fatbin embedding (-fcuda-include-gpubinary),
|
||||
// which emits a module ctor (__cuda_module_ctor -> __cudaRegisterFatBinary)
|
||||
// in .init_array but NO matching unregister dtor (.fini_array / __cudaUnregisterFatBinary).
|
||||
// Unloading such a module unmaps its fatbin while the CUDA runtime still holds a pointer to it;
|
||||
// that dangling registration corrupts the runtime's module table, so a later module's kernel
|
||||
// launch silently no-ops.
|
||||
handle_ = nullptr;
|
||||
}
|
||||
last_error_.clear();
|
||||
}
|
||||
} // namespace hostjit
|
||||
222
cccl_upstream/c/parallel.v2/src/merge_sort.cu
Normal file
222
cccl_upstream/c/parallel.v2/src/merge_sort.cu
Normal file
@@ -0,0 +1,222 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <cccl/c/merge_sort.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// Keys-only: (temp, temp_bytes, in_keys, out_keys, num_items, cmp_state, stream)
|
||||
using keys_fn_t = int (*)(void*, size_t*, void*, void*, unsigned long long, void*, void*);
|
||||
// Key-value pairs: (temp, temp_bytes, in_keys, in_items, out_keys, out_items, num_items, cmp_state, stream)
|
||||
using pairs_fn_t = int (*)(void*, size_t*, void*, void*, void*, void*, unsigned long long, void*, void*);
|
||||
|
||||
static bool is_null_items(cccl_iterator_t it)
|
||||
{
|
||||
return it.type == CCCL_POINTER && it.state == nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Build
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_merge_sort_build_ex(
|
||||
cccl_device_merge_sort_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in_keys,
|
||||
cccl_iterator_t d_in_items,
|
||||
cccl_iterator_t d_out_keys,
|
||||
cccl_iterator_t d_out_items,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (d_out_keys.type == CCCL_ITERATOR || d_out_items.type == CCCL_ITERATOR)
|
||||
{
|
||||
fprintf(stderr, "\nERROR in cccl_device_merge_sort_build(): merge sort output cannot be an iterator\n");
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
const bool has_items = !is_null_items(d_in_items);
|
||||
|
||||
CubCallResult result = [&] {
|
||||
if (has_items)
|
||||
{
|
||||
return CubCall::from("cub/device/device_merge_sort.cuh")
|
||||
.run("cub::DeviceMergeSort::SortPairsCopy")
|
||||
.name("cccl_jit_merge_sort")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(d_in_keys),
|
||||
in(d_in_items),
|
||||
out(d_out_keys),
|
||||
out(d_out_items),
|
||||
num_items,
|
||||
cmp(op),
|
||||
stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CubCall::from("cub/device/device_merge_sort.cuh")
|
||||
.run("cub::DeviceMergeSort::SortKeysCopy")
|
||||
.name("cccl_jit_merge_sort")
|
||||
.with(temp_storage, temp_bytes, in(d_in_keys), out(d_out_keys), num_items, cmp(op), stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
}
|
||||
}();
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->sort_fn = result.fn_ptr;
|
||||
build_ptr->keys_only = has_items ? 0 : 1;
|
||||
build_ptr->key_type = d_in_keys.value_type;
|
||||
build_ptr->item_type = d_in_items.value_type;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_merge_sort_build(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_merge_sort_build(
|
||||
cccl_device_merge_sort_build_result_t* build,
|
||||
cccl_iterator_t d_in_keys,
|
||||
cccl_iterator_t d_in_items,
|
||||
cccl_iterator_t d_out_keys,
|
||||
cccl_iterator_t d_out_items,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_merge_sort_build_ex(
|
||||
build,
|
||||
d_in_keys,
|
||||
d_in_items,
|
||||
d_out_keys,
|
||||
d_out_items,
|
||||
op,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_merge_sort(
|
||||
cccl_device_merge_sort_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in_keys,
|
||||
cccl_iterator_t d_in_items,
|
||||
cccl_iterator_t d_out_keys,
|
||||
cccl_iterator_t d_out_items,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.sort_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
int status;
|
||||
// Dispatch to the correct function arity. build.keys_only was set at
|
||||
// build time, so we don't have to re-derive the pairs-vs-keys decision
|
||||
// from the iterator arguments here (and they must match the build for
|
||||
// the function-pointer types to be valid).
|
||||
if (!build.keys_only)
|
||||
{
|
||||
// Pairs build: (temp, temp_bytes, in_keys, in_items, out_keys, out_items, num_items, cmp_state, stream)
|
||||
auto fn = reinterpret_cast<pairs_fn_t>(build.sort_fn);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in_keys.state,
|
||||
d_in_items.state,
|
||||
d_out_keys.state,
|
||||
d_out_items.state,
|
||||
num_items,
|
||||
op.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keys-only build: (temp, temp_bytes, in_keys, out_keys, num_items, cmp_state, stream)
|
||||
auto fn = reinterpret_cast<keys_fn_t>(build.sort_fn);
|
||||
status =
|
||||
fn(d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in_keys.state,
|
||||
d_out_keys.state,
|
||||
num_items,
|
||||
op.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_merge_sort(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_merge_sort_cleanup(cccl_device_merge_sort_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->sort_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_merge_sort_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
356
cccl_upstream/c/parallel.v2/src/radix_sort.cu
Normal file
356
cccl_upstream/c/parallel.v2/src/radix_sort.cu
Normal file
@@ -0,0 +1,356 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <cccl/c/radix_sort.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
static bool is_null_it(cccl_iterator_t it)
|
||||
{
|
||||
return it.type == CCCL_POINTER && it.state == nullptr;
|
||||
}
|
||||
|
||||
static bool is_null_op(cccl_op_t op)
|
||||
{
|
||||
return op.name == nullptr || op.name[0] == '\0';
|
||||
}
|
||||
|
||||
// Two JIT wrappers are produced by CubCall per build:
|
||||
//
|
||||
// COPY variant — wraps cub::DeviceRadixSort::Sort{Keys,Pairs}{,Descending}'s
|
||||
// copy-overload. Result is always in *_out; selector is implicitly 0. Used
|
||||
// when the caller invokes the run-time API with is_overwrite_okay=false.
|
||||
// keys-only: fn(temp, temp_bytes, keys_in, keys_out, num_items,
|
||||
// &begin_bit, &end_bit, stream)
|
||||
// pairs: fn(temp, temp_bytes, keys_in, keys_out, values_in, values_out,
|
||||
// num_items, &begin_bit, &end_bit, stream)
|
||||
//
|
||||
// DOUBLE-BUFFER (overwrite) variant — wraps the DoubleBuffer overload.
|
||||
// Constructs cub::DoubleBuffer<KeyT>(keys_in, keys_out) (and ValueT for
|
||||
// pairs), runs the sort, then writes the buffer's `selector` (0 or 1) to a
|
||||
// host-provided int*. Result may live in either keys_in or keys_out depending
|
||||
// on the number of CUB passes — the selector tells the caller which.
|
||||
// keys-only: fn(temp, temp_bytes, keys_in, keys_out, num_items,
|
||||
// &begin_bit, &end_bit, selector_out, stream)
|
||||
// pairs: fn(temp, temp_bytes, keys_in, keys_out, values_in, values_out,
|
||||
// num_items, &begin_bit, &end_bit, selector_out, stream)
|
||||
//
|
||||
// begin_bit/end_bit go through CubCall::typed_scalar (host-pointer + memcpy
|
||||
// onto the stack inside the JIT wrapper).
|
||||
//
|
||||
// Decomposer: only identity (null decomposer) is supported.
|
||||
using radix_sort_keys_fn_t = int (*)(void*, size_t*, void*, void*, unsigned long long, void*, void*, void*);
|
||||
using radix_sort_pairs_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, void*, void*, unsigned long long, void*, void*, void*);
|
||||
using radix_sort_keys_overwrite_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, unsigned long long, void*, void*, void*, void*);
|
||||
using radix_sort_pairs_overwrite_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, void*, void*, unsigned long long, void*, void*, void*, void*);
|
||||
|
||||
// Type info for the begin_bit/end_bit int scalars passed to CubCall.
|
||||
static constexpr cccl_type_info k_int_type{sizeof(int), alignof(int), CCCL_INT32};
|
||||
|
||||
CUresult cccl_device_radix_sort_build_ex(
|
||||
cccl_device_radix_sort_build_result_t* build_ptr,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t input_keys_it,
|
||||
cccl_iterator_t input_values_it,
|
||||
cccl_op_t decomposer,
|
||||
const char* /*decomposer_return_type*/,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (!is_null_op(decomposer))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"\nERROR in cccl_device_radix_sort_build(): custom radix decomposers are not supported "
|
||||
"in the HostJIT path. Use standard integer/float key types.\n");
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
const bool keys_only = is_null_it(input_values_it);
|
||||
const bool ascending = (sort_order == CCCL_ASCENDING);
|
||||
|
||||
// CUB writes to caller-provided device pointers at run time. Build needs
|
||||
// an iterator descriptor for the outputs; synthesize raw-pointer ones with
|
||||
// the same value_type as the matching input.
|
||||
cccl_iterator_t output_keys_it = input_keys_it;
|
||||
output_keys_it.type = CCCL_POINTER;
|
||||
output_keys_it.state = nullptr;
|
||||
cccl_iterator_t output_values_it{};
|
||||
output_values_it.type = CCCL_POINTER;
|
||||
output_values_it.state = nullptr;
|
||||
output_values_it.value_type = input_values_it.value_type;
|
||||
|
||||
const char* cub_algo;
|
||||
if (keys_only)
|
||||
{
|
||||
cub_algo = ascending ? "cub::DeviceRadixSort::SortKeys" : "cub::DeviceRadixSort::SortKeysDescending";
|
||||
}
|
||||
else
|
||||
{
|
||||
cub_algo = ascending ? "cub::DeviceRadixSort::SortPairs" : "cub::DeviceRadixSort::SortPairsDescending";
|
||||
}
|
||||
|
||||
auto cb_copy = [&] {
|
||||
if (keys_only)
|
||||
{
|
||||
return CubCall::from("cub/device/device_radix_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_radix_sort")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(input_keys_it),
|
||||
out(output_keys_it),
|
||||
num_items,
|
||||
typed_scalar(k_int_type, "begin_bit"),
|
||||
typed_scalar(k_int_type, "end_bit"),
|
||||
stream);
|
||||
}
|
||||
return CubCall::from("cub/device/device_radix_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_radix_sort")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(input_keys_it),
|
||||
out(output_keys_it),
|
||||
in(input_values_it),
|
||||
out(output_values_it),
|
||||
num_items,
|
||||
typed_scalar(k_int_type, "begin_bit"),
|
||||
typed_scalar(k_int_type, "end_bit"),
|
||||
stream);
|
||||
}();
|
||||
|
||||
auto cb_overwrite = [&] {
|
||||
if (keys_only)
|
||||
{
|
||||
return CubCall::from("cub/device/device_radix_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_radix_sort_overwrite")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
double_buffer(input_keys_it, output_keys_it, "d_keys_buffer"),
|
||||
num_items,
|
||||
typed_scalar(k_int_type, "begin_bit"),
|
||||
typed_scalar(k_int_type, "end_bit"),
|
||||
selector_out("d_keys_buffer"),
|
||||
stream);
|
||||
}
|
||||
return CubCall::from("cub/device/device_radix_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_radix_sort_overwrite")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
double_buffer(input_keys_it, output_keys_it, "d_keys_buffer"),
|
||||
double_buffer(input_values_it, output_values_it, "d_values_buffer"),
|
||||
num_items,
|
||||
typed_scalar(k_int_type, "begin_bit"),
|
||||
typed_scalar(k_int_type, "end_bit"),
|
||||
selector_out("d_keys_buffer"),
|
||||
stream);
|
||||
}();
|
||||
|
||||
auto result =
|
||||
CubCall::compile({cb_copy, cb_overwrite}, cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->sort_fn = result.fn_ptrs[0];
|
||||
build_ptr->sort_fn_overwrite = result.fn_ptrs[1];
|
||||
build_ptr->key_type = input_keys_it.value_type;
|
||||
build_ptr->value_type = input_values_it.value_type;
|
||||
build_ptr->order = sort_order;
|
||||
build_ptr->keys_only = keys_only ? 1 : 0;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_radix_sort_build(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_radix_sort_build(
|
||||
cccl_device_radix_sort_build_result_t* build,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t input_keys_it,
|
||||
cccl_iterator_t input_values_it,
|
||||
cccl_op_t decomposer,
|
||||
const char* decomposer_return_type,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_radix_sort_build_ex(
|
||||
build,
|
||||
sort_order,
|
||||
input_keys_it,
|
||||
input_values_it,
|
||||
decomposer,
|
||||
decomposer_return_type,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CUresult cccl_device_radix_sort(
|
||||
cccl_device_radix_sort_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_op_t /*decomposer*/,
|
||||
uint64_t num_items,
|
||||
int begin_bit,
|
||||
int end_bit,
|
||||
bool is_overwrite_okay,
|
||||
int* selector,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
// Dispatch on is_overwrite_okay: the copy variant always lands the result
|
||||
// in d_keys_out (selector = 0); the DoubleBuffer variant may land it in
|
||||
// either buffer and reports which via its `selector` member, captured here
|
||||
// by passing a pointer for the wrapper to write into.
|
||||
int status;
|
||||
int local_selector = 0;
|
||||
if (is_overwrite_okay)
|
||||
{
|
||||
if (!build.sort_fn_overwrite)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
if (build.keys_only)
|
||||
{
|
||||
auto fn = reinterpret_cast<radix_sort_keys_overwrite_fn_t>(build.sort_fn_overwrite);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
&begin_bit,
|
||||
&end_bit,
|
||||
&local_selector,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fn = reinterpret_cast<radix_sort_pairs_overwrite_fn_t>(build.sort_fn_overwrite);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
d_values_in.state,
|
||||
d_values_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
&begin_bit,
|
||||
&end_bit,
|
||||
&local_selector,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!build.sort_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
if (build.keys_only)
|
||||
{
|
||||
auto fn = reinterpret_cast<radix_sort_keys_fn_t>(build.sort_fn);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
&begin_bit,
|
||||
&end_bit,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fn = reinterpret_cast<radix_sort_pairs_fn_t>(build.sort_fn);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
d_values_in.state,
|
||||
d_values_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
&begin_bit,
|
||||
&end_bit,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
local_selector = 0;
|
||||
}
|
||||
|
||||
if (selector)
|
||||
{
|
||||
*selector = local_selector;
|
||||
}
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_radix_sort(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_radix_sort_cleanup(cccl_device_radix_sort_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->sort_fn = nullptr;
|
||||
build_ptr->sort_fn_overwrite = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_radix_sort_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
167
cccl_upstream/c/parallel.v2/src/reduce.cu
Normal file
167
cccl_upstream/c/parallel.v2/src/reduce.cu
Normal file
@@ -0,0 +1,167 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include <cccl/c/reduce.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// (temp_storage, temp_bytes, d_in, d_out, num_items, op_state, init_state, stream)
|
||||
using reduce_fn_t = int (*)(void*, size_t*, void*, void*, unsigned long long, void*, void*, void*);
|
||||
|
||||
CUresult cccl_device_reduce_build_ex(
|
||||
cccl_device_reduce_build_result_t* build,
|
||||
cccl_iterator_t input_it,
|
||||
cccl_iterator_t output_it,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
cccl_determinism_t determinism,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* build_config)
|
||||
try
|
||||
{
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(build_config, cub_path, thrust_path);
|
||||
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_reduce.cuh")
|
||||
.run("cub::DeviceReduce::Reduce")
|
||||
.name("cccl_jit_reduce")
|
||||
.with(temp_storage, temp_bytes, in(input_it), out(output_it), num_items, op, init, stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build->payload, build->payload_size);
|
||||
build->jit_compiler = result.compiler;
|
||||
build->reduce_fn = reinterpret_cast<void*>(result.fn_ptr);
|
||||
build->accumulator_size = init.type.size;
|
||||
build->determinism = determinism;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_reduce_build(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_reduce(
|
||||
cccl_device_reduce_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.reduce_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
auto reduce_fn = reinterpret_cast<reduce_fn_t>(build.reduce_fn);
|
||||
|
||||
// Parameter order matches CubCall::with() order: ..., num_items, op.state, init.state, stream
|
||||
const int status = reduce_fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in.state,
|
||||
d_out.state,
|
||||
num_items,
|
||||
op.state,
|
||||
init.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_reduce(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_reduce_nondeterministic(
|
||||
cccl_device_reduce_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream)
|
||||
{
|
||||
return cccl_device_reduce(build, d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, op, init, stream);
|
||||
}
|
||||
|
||||
CUresult cccl_device_reduce_cleanup(cccl_device_reduce_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->reduce_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_reduce_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_reduce_build(
|
||||
cccl_device_reduce_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
cccl_determinism_t determinism,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_reduce_build_ex(
|
||||
build,
|
||||
d_in,
|
||||
d_out,
|
||||
op,
|
||||
init,
|
||||
determinism,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
319
cccl_upstream/c/parallel.v2/src/scan.cu
Normal file
319
cccl_upstream/c/parallel.v2/src/scan.cu
Normal file
@@ -0,0 +1,319 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <cccl/c/scan.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// Variants with an init value (value or future): 8 args
|
||||
// (temp, temp_bytes, d_in, d_out, op_state, init_ptr, num_items, stream)
|
||||
using scan_init_fn_t = int (*)(void*, size_t*, void*, void*, void*, void*, unsigned long long, void*);
|
||||
|
||||
// InclusiveScan without init: 7 args
|
||||
// (temp, temp_bytes, d_in, d_out, op_state, num_items, stream)
|
||||
using scan_no_init_fn_t = int (*)(void*, size_t*, void*, void*, void*, unsigned long long, void*);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Build
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_scan_build_ex(
|
||||
cccl_device_scan_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
cccl_type_info init_type,
|
||||
bool force_inclusive,
|
||||
cccl_init_kind_t init_kind,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
CubCallResult result = [&] {
|
||||
auto base = CubCall::from("cub/device/device_scan.cuh").name("cccl_jit_scan");
|
||||
|
||||
if (init_kind == CCCL_NO_INIT)
|
||||
{
|
||||
// cub::DeviceScan::InclusiveScan(temp, temp_bytes, in, out, op, num_items, stream)
|
||||
return base.run("cub::DeviceScan::InclusiveScan")
|
||||
.with(temp_storage, temp_bytes, in(d_in), out(d_out), op, num_items, stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
}
|
||||
else if (init_kind == CCCL_VALUE_INIT)
|
||||
{
|
||||
// ExclusiveScan or InclusiveScanInit with a value init (memcpy'd from void*)
|
||||
const char* fn = force_inclusive ? "cub::DeviceScan::InclusiveScanInit" : "cub::DeviceScan::ExclusiveScan";
|
||||
cccl_value_t init_val{init_type, nullptr}; // state=nullptr; passed at run time
|
||||
return base.run(fn)
|
||||
.with(temp_storage, temp_bytes, in(d_in), out(d_out), op, init_val, num_items, stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
}
|
||||
else // CCCL_FUTURE_VALUE_INIT
|
||||
{
|
||||
// ExclusiveScan or InclusiveScanInit with cub::FutureValue<accum_t>(ptr)
|
||||
const char* fn = force_inclusive ? "cub::DeviceScan::InclusiveScanInit" : "cub::DeviceScan::ExclusiveScan";
|
||||
return base.run(fn)
|
||||
.with(temp_storage, temp_bytes, in(d_in), out(d_out), op, future_val(init_type), num_items, stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
}
|
||||
}();
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->scan_fn = result.fn_ptr;
|
||||
build_ptr->force_inclusive = force_inclusive;
|
||||
build_ptr->init_kind = init_kind;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_scan_build_ex(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_scan_build(
|
||||
cccl_device_scan_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
cccl_type_info init_type,
|
||||
bool force_inclusive,
|
||||
cccl_init_kind_t init_kind,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_scan_build_ex(
|
||||
build_ptr,
|
||||
d_in,
|
||||
d_out,
|
||||
op,
|
||||
init_type,
|
||||
force_inclusive,
|
||||
init_kind,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static CUresult call_scan_init(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
void* init_ptr, // value state or device pointer for FutureValue
|
||||
CUstream stream)
|
||||
{
|
||||
if (!build.scan_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
// Guard against ABI mismatch: this path uses the 8-arg scan_init_fn_t
|
||||
// (with init pointer). Calling it with a build result compiled for
|
||||
// CCCL_NO_INIT (7-arg scan_no_init_fn_t) would be undefined behaviour.
|
||||
if (build.init_kind == CCCL_NO_INIT)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
auto fn = reinterpret_cast<scan_init_fn_t>(build.scan_fn);
|
||||
const int status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in.state,
|
||||
d_out.state,
|
||||
op.state,
|
||||
init_ptr,
|
||||
(unsigned long long) num_items,
|
||||
reinterpret_cast<void*>(stream));
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_exclusive_scan(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
return call_scan_init(build, d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, op, init.state, stream);
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_exclusive_scan(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_inclusive_scan(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
return call_scan_init(build, d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, op, init.state, stream);
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_inclusive_scan(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_exclusive_scan_future_value(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_iterator_t init,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
// init.state is the device pointer — passed as void* and wrapped in
|
||||
// FutureValue<accum_t> inside the compiled CUDA function.
|
||||
return call_scan_init(build, d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, op, init.state, stream);
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_exclusive_scan_future_value(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_inclusive_scan_future_value(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
cccl_iterator_t init,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
return call_scan_init(build, d_temp_storage, temp_storage_bytes, d_in, d_out, num_items, op, init.state, stream);
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_inclusive_scan_future_value(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_inclusive_scan_no_init(
|
||||
cccl_device_scan_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.scan_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
// Guard against ABI mismatch: this path uses the 7-arg scan_no_init_fn_t.
|
||||
// A build result compiled with an init value stores an 8-arg scan_init_fn_t;
|
||||
// casting it here would be undefined behaviour.
|
||||
if (build.init_kind != CCCL_NO_INIT)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
auto fn = reinterpret_cast<scan_no_init_fn_t>(build.scan_fn);
|
||||
const int status =
|
||||
fn(d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in.state,
|
||||
d_out.state,
|
||||
op.state,
|
||||
(unsigned long long) num_items,
|
||||
reinterpret_cast<void*>(stream));
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_inclusive_scan_no_init(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_scan_cleanup(cccl_device_scan_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->scan_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_scan_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
169
cccl_upstream/c/parallel.v2/src/segmented_reduce.cu
Normal file
169
cccl_upstream/c/parallel.v2/src/segmented_reduce.cu
Normal file
@@ -0,0 +1,169 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <cccl/c/segmented_reduce.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// (temp_storage, temp_bytes, d_in, d_out, num_segments, begin_offsets, end_offsets, op, init, stream)
|
||||
using segmented_reduce_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, unsigned long long, void*, void*, void*, void*, void*);
|
||||
|
||||
CUresult cccl_device_segmented_reduce_build_ex(
|
||||
cccl_device_segmented_reduce_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_iterator_t start_offset_it,
|
||||
cccl_iterator_t end_offset_it,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* build_config)
|
||||
try
|
||||
{
|
||||
const std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
|
||||
const std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(build_config, cub_path, thrust_path);
|
||||
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_segmented_reduce.cuh")
|
||||
.run("cub::DeviceSegmentedReduce::Reduce")
|
||||
.name("cccl_jit_segmented_reduce")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(d_in),
|
||||
out(d_out),
|
||||
num_items,
|
||||
in(start_offset_it),
|
||||
in(end_offset_it),
|
||||
op,
|
||||
init,
|
||||
stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build->payload, build->payload_size);
|
||||
build->jit_compiler = result.compiler;
|
||||
build->segmented_reduce_fn = reinterpret_cast<void*>(result.fn_ptr);
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_segmented_reduce_build(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_segmented_reduce(
|
||||
cccl_device_segmented_reduce_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_segments,
|
||||
cccl_iterator_t start_offset,
|
||||
cccl_iterator_t end_offset,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.segmented_reduce_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
auto segmented_reduce_fn = reinterpret_cast<segmented_reduce_fn_t>(build.segmented_reduce_fn);
|
||||
|
||||
// Parameter order matches CubCall::with() order:
|
||||
// temp_storage, temp_bytes, d_in, d_out, num_items, begin_offsets, end_offsets, op, init, stream
|
||||
const int status = segmented_reduce_fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in.state,
|
||||
d_out.state,
|
||||
num_segments,
|
||||
start_offset.state,
|
||||
end_offset.state,
|
||||
op.state,
|
||||
init.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_segmented_reduce(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_segmented_reduce_build(
|
||||
cccl_device_segmented_reduce_build_result_t* build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
cccl_op_t op,
|
||||
cccl_value_t init,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_segmented_reduce_build_ex(
|
||||
build,
|
||||
d_in,
|
||||
d_out,
|
||||
begin_offset_in,
|
||||
end_offset_in,
|
||||
op,
|
||||
init,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CUresult cccl_device_segmented_reduce_cleanup(cccl_device_segmented_reduce_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->segmented_reduce_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_segmented_reduce_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
336
cccl_upstream/c/parallel.v2/src/segmented_sort.cu
Normal file
336
cccl_upstream/c/parallel.v2/src/segmented_sort.cu
Normal file
@@ -0,0 +1,336 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include <cccl/c/segmented_sort.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
static bool is_null_it(cccl_iterator_t it)
|
||||
{
|
||||
return it.type == CCCL_POINTER && it.state == nullptr;
|
||||
}
|
||||
|
||||
// Two JIT wrappers per build, one per cub::DeviceSegmentedSort overload:
|
||||
//
|
||||
// COPY variant — result always lands in d_keys_out (and d_values_out for
|
||||
// pairs); selector implicitly 0. Used when is_overwrite_okay=false.
|
||||
// keys-only: fn(temp, temp_bytes, keys_in, keys_out, num_items, num_segments,
|
||||
// begin_offsets, end_offsets, stream)
|
||||
// pairs: fn(temp, temp_bytes, keys_in, keys_out, values_in, values_out,
|
||||
// num_items, num_segments, begin_offsets, end_offsets, stream)
|
||||
//
|
||||
// DOUBLE-BUFFER (overwrite) variant — constructs cub::DoubleBuffer locals
|
||||
// from the in/out pointers, runs the DoubleBuffer overload, writes the
|
||||
// buffer's selector to a host-provided int*.
|
||||
// keys-only: fn(..., num_segments, begin_offsets, end_offsets, selector_out, stream)
|
||||
// pairs: fn(..., num_segments, begin_offsets, end_offsets, selector_out, stream)
|
||||
using segmented_sort_keys_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, unsigned long long, unsigned long long, void*, void*, void*);
|
||||
using segmented_sort_pairs_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, void*, void*, unsigned long long, unsigned long long, void*, void*, void*);
|
||||
using segmented_sort_keys_overwrite_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, unsigned long long, unsigned long long, void*, void*, void*, void*);
|
||||
using segmented_sort_pairs_overwrite_fn_t = int (*)(
|
||||
void*, size_t*, void*, void*, void*, void*, unsigned long long, unsigned long long, void*, void*, void*, void*);
|
||||
|
||||
CUresult cccl_device_segmented_sort_build_ex(
|
||||
cccl_device_segmented_sort_build_result_t* build_ptr,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
const bool keys_only = is_null_it(d_values_in);
|
||||
const bool ascending = (sort_order == CCCL_ASCENDING);
|
||||
|
||||
// DeviceSegmentedSort writes to caller-provided device pointers at run time.
|
||||
// Build needs an iterator descriptor for the outputs; synthesize raw-pointer
|
||||
// ones with the same value_type as the matching inputs.
|
||||
cccl_iterator_t d_keys_out = d_keys_in;
|
||||
d_keys_out.type = CCCL_POINTER;
|
||||
d_keys_out.state = nullptr;
|
||||
cccl_iterator_t d_values_out{};
|
||||
d_values_out.type = CCCL_POINTER;
|
||||
d_values_out.state = nullptr;
|
||||
d_values_out.value_type = d_values_in.value_type;
|
||||
|
||||
const char* cub_algo;
|
||||
if (keys_only)
|
||||
{
|
||||
cub_algo = ascending ? "cub::DeviceSegmentedSort::SortKeys" : "cub::DeviceSegmentedSort::SortKeysDescending";
|
||||
}
|
||||
else
|
||||
{
|
||||
cub_algo = ascending ? "cub::DeviceSegmentedSort::SortPairs" : "cub::DeviceSegmentedSort::SortPairsDescending";
|
||||
}
|
||||
|
||||
auto cb_copy = [&] {
|
||||
if (keys_only)
|
||||
{
|
||||
return CubCall::from("cub/device/device_segmented_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_segmented_sort")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(d_keys_in),
|
||||
out(d_keys_out),
|
||||
num_items,
|
||||
num_segments,
|
||||
in(begin_offset_in),
|
||||
in(end_offset_in),
|
||||
stream);
|
||||
}
|
||||
return CubCall::from("cub/device/device_segmented_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_segmented_sort")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(d_keys_in),
|
||||
out(d_keys_out),
|
||||
in(d_values_in),
|
||||
out(d_values_out),
|
||||
num_items,
|
||||
num_segments,
|
||||
in(begin_offset_in),
|
||||
in(end_offset_in),
|
||||
stream);
|
||||
}();
|
||||
|
||||
auto cb_overwrite = [&] {
|
||||
if (keys_only)
|
||||
{
|
||||
return CubCall::from("cub/device/device_segmented_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_segmented_sort_overwrite")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
double_buffer(d_keys_in, d_keys_out, "d_keys_buffer"),
|
||||
num_items,
|
||||
num_segments,
|
||||
in(begin_offset_in),
|
||||
in(end_offset_in),
|
||||
selector_out("d_keys_buffer"),
|
||||
stream);
|
||||
}
|
||||
return CubCall::from("cub/device/device_segmented_sort.cuh")
|
||||
.run(cub_algo)
|
||||
.name("cccl_jit_segmented_sort_overwrite")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
double_buffer(d_keys_in, d_keys_out, "d_keys_buffer"),
|
||||
double_buffer(d_values_in, d_values_out, "d_values_buffer"),
|
||||
num_items,
|
||||
num_segments,
|
||||
in(begin_offset_in),
|
||||
in(end_offset_in),
|
||||
selector_out("d_keys_buffer"),
|
||||
stream);
|
||||
}();
|
||||
|
||||
auto result =
|
||||
CubCall::compile({cb_copy, cb_overwrite}, cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->sort_fn = result.fn_ptrs[0];
|
||||
build_ptr->sort_fn_overwrite = result.fn_ptrs[1];
|
||||
build_ptr->key_type = d_keys_in.value_type;
|
||||
build_ptr->value_type = d_values_in.value_type;
|
||||
build_ptr->order = sort_order;
|
||||
build_ptr->keys_only = keys_only ? 1 : 0;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_segmented_sort_build(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_segmented_sort_build(
|
||||
cccl_device_segmented_sort_build_result_t* build,
|
||||
cccl_sort_order_t sort_order,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t begin_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_segmented_sort_build_ex(
|
||||
build,
|
||||
sort_order,
|
||||
d_keys_in,
|
||||
d_values_in,
|
||||
begin_offset_in,
|
||||
end_offset_in,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
CUresult cccl_device_segmented_sort(
|
||||
cccl_device_segmented_sort_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_values_out,
|
||||
uint64_t num_items,
|
||||
uint64_t num_segments,
|
||||
cccl_iterator_t start_offset_in,
|
||||
cccl_iterator_t end_offset_in,
|
||||
bool is_overwrite_okay,
|
||||
int* selector,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
// Dispatch on is_overwrite_okay (see analogous comment in radix_sort.cu).
|
||||
int status;
|
||||
int local_selector = 0;
|
||||
if (is_overwrite_okay)
|
||||
{
|
||||
if (!build.sort_fn_overwrite)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
if (build.keys_only)
|
||||
{
|
||||
auto fn = reinterpret_cast<segmented_sort_keys_overwrite_fn_t>(build.sort_fn_overwrite);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
static_cast<unsigned long long>(num_segments),
|
||||
start_offset_in.state,
|
||||
end_offset_in.state,
|
||||
&local_selector,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fn = reinterpret_cast<segmented_sort_pairs_overwrite_fn_t>(build.sort_fn_overwrite);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
d_values_in.state,
|
||||
d_values_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
static_cast<unsigned long long>(num_segments),
|
||||
start_offset_in.state,
|
||||
end_offset_in.state,
|
||||
&local_selector,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!build.sort_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
if (build.keys_only)
|
||||
{
|
||||
auto fn = reinterpret_cast<segmented_sort_keys_fn_t>(build.sort_fn);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
static_cast<unsigned long long>(num_segments),
|
||||
start_offset_in.state,
|
||||
end_offset_in.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto fn = reinterpret_cast<segmented_sort_pairs_fn_t>(build.sort_fn);
|
||||
status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_keys_out.state,
|
||||
d_values_in.state,
|
||||
d_values_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
static_cast<unsigned long long>(num_segments),
|
||||
start_offset_in.state,
|
||||
end_offset_in.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
}
|
||||
local_selector = 0;
|
||||
}
|
||||
|
||||
if (selector)
|
||||
{
|
||||
*selector = local_selector;
|
||||
}
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_segmented_sort(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_segmented_sort_cleanup(cccl_device_segmented_sort_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->sort_fn = nullptr;
|
||||
build_ptr->sort_fn_overwrite = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_segmented_sort_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
193
cccl_upstream/c/parallel.v2/src/three_way_partition.cu
Normal file
193
cccl_upstream/c/parallel.v2/src/three_way_partition.cu
Normal file
@@ -0,0 +1,193 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <cccl/c/three_way_partition.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// CUB DevicePartition::If (three-way) generated signature:
|
||||
// (temp, bytes, d_in, first_out, second_out, unselected_out, num_selected_out,
|
||||
// num_items, first_op_state, second_op_state, stream)
|
||||
using three_way_partition_fn_t =
|
||||
int (*)(void*, size_t*, void*, void*, void*, void*, void*, unsigned long long, void*, void*, void*);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Build
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_three_way_partition_build_ex(
|
||||
cccl_device_three_way_partition_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_first_part_out,
|
||||
cccl_iterator_t d_second_part_out,
|
||||
cccl_iterator_t d_unselected_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t select_first_part_op,
|
||||
cccl_op_t select_second_part_op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
// DevicePartition::If (three-way):
|
||||
// (temp, bytes, d_in, d_first_part_out, d_second_part_out, d_unselected_out,
|
||||
// d_num_selected_out, num_items, select_first_op, select_second_op, stream)
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_partition.cuh")
|
||||
.run("cub::DevicePartition::If")
|
||||
.name("cccl_jit_three_way_partition")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(d_in),
|
||||
out(d_first_part_out),
|
||||
out(d_second_part_out),
|
||||
out(d_unselected_out),
|
||||
out(d_num_selected_out),
|
||||
num_items,
|
||||
pred(select_first_part_op, d_in.value_type),
|
||||
pred(select_second_part_op, d_in.value_type),
|
||||
stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->three_way_partition_fn = result.fn_ptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_three_way_partition_build_ex(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_three_way_partition_build(
|
||||
cccl_device_three_way_partition_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_first_part_out,
|
||||
cccl_iterator_t d_second_part_out,
|
||||
cccl_iterator_t d_unselected_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t select_first_part_op,
|
||||
cccl_op_t select_second_part_op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_three_way_partition_build_ex(
|
||||
build_ptr,
|
||||
d_in,
|
||||
d_first_part_out,
|
||||
d_second_part_out,
|
||||
d_unselected_out,
|
||||
d_num_selected_out,
|
||||
select_first_part_op,
|
||||
select_second_part_op,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_three_way_partition(
|
||||
cccl_device_three_way_partition_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_first_part_out,
|
||||
cccl_iterator_t d_second_part_out,
|
||||
cccl_iterator_t d_unselected_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t select_first_part_op,
|
||||
cccl_op_t select_second_part_op,
|
||||
uint64_t num_items,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.three_way_partition_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
auto fn = reinterpret_cast<three_way_partition_fn_t>(build.three_way_partition_fn);
|
||||
const int status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_in.state,
|
||||
d_first_part_out.state,
|
||||
d_second_part_out.state,
|
||||
d_unselected_out.state,
|
||||
d_num_selected_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
select_first_part_op.state,
|
||||
select_second_part_op.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_three_way_partition(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_three_way_partition_cleanup(cccl_device_three_way_partition_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->three_way_partition_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_three_way_partition_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
283
cccl_upstream/c/parallel.v2/src/transform.cu
Normal file
283
cccl_upstream/c/parallel.v2/src/transform.cu
Normal file
@@ -0,0 +1,283 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cuda/std/version>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <cccl/c/transform.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <util/build_utils.h>
|
||||
#include <util/first_call_gate.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// (d_in, d_out, num_items, op_state, stream)
|
||||
using unary_transform_fn_t = int (*)(void*, void*, unsigned long long, void*, void*);
|
||||
// (d_in1, d_in2, d_out, num_items, op_state, stream)
|
||||
using binary_transform_fn_t = int (*)(void*, void*, void*, unsigned long long, void*, void*);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Build
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_unary_transform_build_ex(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
#if CCCL_OS(WINDOWS)
|
||||
build_ptr->first_call_state = nullptr;
|
||||
#endif
|
||||
const std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
const std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* const cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* const ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
#if CCCL_OS(WINDOWS)
|
||||
auto first_call_state = std::make_unique<cccl::detail::first_call_gate>();
|
||||
#endif
|
||||
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_transform.cuh")
|
||||
.run("cub::DeviceTransform::Transform")
|
||||
.name("cccl_jit_unary_transform")
|
||||
.with(in(d_in), out(d_out), num_items, unary_op(op, d_in.value_type, d_out.value_type), stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
#if CCCL_OS(WINDOWS)
|
||||
build_ptr->first_call_state = first_call_state.release();
|
||||
#endif
|
||||
build_ptr->transform_fn = result.fn_ptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_unary_transform_build_ex(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_binary_transform_build_ex(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in1,
|
||||
cccl_iterator_t d_in2,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
#if CCCL_OS(WINDOWS)
|
||||
build_ptr->first_call_state = nullptr;
|
||||
#endif
|
||||
const std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
const std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* const cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* const ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
#if CCCL_OS(WINDOWS)
|
||||
auto first_call_state = std::make_unique<cccl::detail::first_call_gate>();
|
||||
#endif
|
||||
|
||||
// Use the output type as the accumulator type (same as the previous raw JIT
|
||||
// implementation) so the binary op functor uses the correct result type.
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_transform.cuh")
|
||||
.run("cub::DeviceTransform::Transform")
|
||||
.name("cccl_jit_binary_transform")
|
||||
.use_tuple_inputs()
|
||||
.with(force_accum_type(d_out.value_type), in(d_in1), in(d_in2), out(d_out), num_items, op, stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
#if CCCL_OS(WINDOWS)
|
||||
build_ptr->first_call_state = first_call_state.release();
|
||||
#endif
|
||||
build_ptr->transform_fn = result.fn_ptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_binary_transform_build_ex(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Non-ex wrappers (call _ex with nullptr config)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_unary_transform_build(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_unary_transform_build_ex(
|
||||
build_ptr, d_in, d_out, op, cc_major, cc_minor, cub_path, thrust_path, libcudacxx_path, ctk_path, nullptr);
|
||||
}
|
||||
|
||||
CUresult cccl_device_binary_transform_build(
|
||||
cccl_device_transform_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_in1,
|
||||
cccl_iterator_t d_in2,
|
||||
cccl_iterator_t d_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_binary_transform_build_ex(
|
||||
build_ptr, d_in1, d_in2, d_out, op, cc_major, cc_minor, cub_path, thrust_path, libcudacxx_path, ctk_path, nullptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Runtime functions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_unary_transform(
|
||||
cccl_device_transform_build_result_t build,
|
||||
cccl_iterator_t d_in,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.transform_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
const auto fn = reinterpret_cast<unary_transform_fn_t>(build.transform_fn);
|
||||
#if CCCL_OS(WINDOWS)
|
||||
if (!build.first_call_state)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
const auto invoke = [&] {
|
||||
return fn(d_in.state, d_out.state, num_items, op.state, reinterpret_cast<void*>(stream));
|
||||
};
|
||||
// Empty calls return before CUB initializes its static launch configuration,
|
||||
// so they must not complete the first-call gate.
|
||||
const int status =
|
||||
num_items == 0 ? invoke() : static_cast<cccl::detail::first_call_gate*>(build.first_call_state)->invoke(invoke);
|
||||
#else
|
||||
const int status = fn(d_in.state, d_out.state, num_items, op.state, reinterpret_cast<void*>(stream));
|
||||
#endif
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_unary_transform(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_binary_transform(
|
||||
cccl_device_transform_build_result_t build,
|
||||
cccl_iterator_t d_in1,
|
||||
cccl_iterator_t d_in2,
|
||||
cccl_iterator_t d_out,
|
||||
uint64_t num_items,
|
||||
cccl_op_t op,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.transform_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
const auto fn = reinterpret_cast<binary_transform_fn_t>(build.transform_fn);
|
||||
#if CCCL_OS(WINDOWS)
|
||||
if (!build.first_call_state)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
const auto invoke = [&] {
|
||||
return fn(d_in1.state, d_in2.state, d_out.state, num_items, op.state, reinterpret_cast<void*>(stream));
|
||||
};
|
||||
// Empty calls return before CUB initializes its static launch configuration,
|
||||
// so they must not complete the first-call gate.
|
||||
const int status =
|
||||
num_items == 0 ? invoke() : static_cast<cccl::detail::first_call_gate*>(build.first_call_state)->invoke(invoke);
|
||||
#else
|
||||
const int status = fn(d_in1.state, d_in2.state, d_out.state, num_items, op.state, reinterpret_cast<void*>(stream));
|
||||
#endif
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_binary_transform(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_transform_cleanup(cccl_device_transform_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
#if CCCL_OS(WINDOWS)
|
||||
delete static_cast<cccl::detail::first_call_gate*>(build_ptr->first_call_state);
|
||||
build_ptr->first_call_state = nullptr;
|
||||
#endif
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->transform_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_transform_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
184
cccl_upstream/c/parallel.v2/src/unique_by_key.cu
Normal file
184
cccl_upstream/c/parallel.v2/src/unique_by_key.cu
Normal file
@@ -0,0 +1,184 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <cccl/c/unique_by_key.h>
|
||||
#include <hostjit/codegen/cub_call.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
#include <util/build_utils.h>
|
||||
|
||||
using namespace hostjit::codegen;
|
||||
|
||||
// CUB DeviceSelect::UniqueByKey generated signature:
|
||||
// (temp, bytes, keys_in, values_in, keys_out, values_out, num_selected_out, num_items, cmp_state, stream)
|
||||
using unique_by_key_fn_t = int (*)(void*, size_t*, void*, void*, void*, void*, void*, unsigned long long, void*, void*);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Build
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_unique_by_key_build_ex(
|
||||
cccl_device_unique_by_key_build_result_t* build_ptr,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path,
|
||||
cccl_build_config* config)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
std::string cccl_include_str = cccl::detail::parse_cccl_include_path(libcudacxx_path);
|
||||
std::string ctk_root_str = cccl::detail::parse_ctk_root(ctk_path);
|
||||
const char* cccl_include_path = cccl_include_str.empty() ? nullptr : cccl_include_str.c_str();
|
||||
const char* ctk_root = ctk_root_str.empty() ? nullptr : ctk_root_str.c_str();
|
||||
cccl::detail::MergedBuildConfig merged(config, cub_path, thrust_path);
|
||||
|
||||
// DeviceSelect::UniqueByKey(temp, bytes, keys_in, values_in, keys_out, values_out,
|
||||
// num_selected_out, num_items, equality_op, stream)
|
||||
auto result =
|
||||
CubCall::from("cub/device/device_select.cuh")
|
||||
.run("cub::DeviceSelect::UniqueByKey")
|
||||
.name("cccl_jit_unique_by_key")
|
||||
.with(temp_storage,
|
||||
temp_bytes,
|
||||
in(d_keys_in),
|
||||
in(d_values_in),
|
||||
out(d_keys_out),
|
||||
out(d_values_out),
|
||||
out(d_num_selected_out),
|
||||
num_items,
|
||||
cmp(op),
|
||||
stream)
|
||||
.compile(cc_major, cc_minor, merged.get(), ctk_root, cccl_include_path);
|
||||
|
||||
build_ptr->cc = cc_major * 10 + cc_minor;
|
||||
cccl::detail::copy_cubin(result.cubin, build_ptr->payload, build_ptr->payload_size);
|
||||
build_ptr->jit_compiler = result.compiler;
|
||||
build_ptr->unique_by_key_fn = result.fn_ptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_unique_by_key_build_ex(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
CUresult cccl_device_unique_by_key_build(
|
||||
cccl_device_unique_by_key_build_result_t* build,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t op,
|
||||
int cc_major,
|
||||
int cc_minor,
|
||||
const char* cub_path,
|
||||
const char* thrust_path,
|
||||
const char* libcudacxx_path,
|
||||
const char* ctk_path)
|
||||
{
|
||||
return cccl_device_unique_by_key_build_ex(
|
||||
build,
|
||||
d_keys_in,
|
||||
d_values_in,
|
||||
d_keys_out,
|
||||
d_values_out,
|
||||
d_num_selected_out,
|
||||
op,
|
||||
cc_major,
|
||||
cc_minor,
|
||||
cub_path,
|
||||
thrust_path,
|
||||
libcudacxx_path,
|
||||
ctk_path,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_unique_by_key(
|
||||
cccl_device_unique_by_key_build_result_t build,
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
cccl_iterator_t d_keys_in,
|
||||
cccl_iterator_t d_values_in,
|
||||
cccl_iterator_t d_keys_out,
|
||||
cccl_iterator_t d_values_out,
|
||||
cccl_iterator_t d_num_selected_out,
|
||||
cccl_op_t op,
|
||||
uint64_t num_items,
|
||||
CUstream stream)
|
||||
try
|
||||
{
|
||||
if (!build.unique_by_key_fn)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
auto fn = reinterpret_cast<unique_by_key_fn_t>(build.unique_by_key_fn);
|
||||
const int status = fn(
|
||||
d_temp_storage,
|
||||
temp_storage_bytes,
|
||||
d_keys_in.state,
|
||||
d_values_in.state,
|
||||
d_keys_out.state,
|
||||
d_values_out.state,
|
||||
d_num_selected_out.state,
|
||||
static_cast<unsigned long long>(num_items),
|
||||
op.state,
|
||||
reinterpret_cast<void*>(stream));
|
||||
|
||||
return (status == 0) ? CUDA_SUCCESS : CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_unique_by_key(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
CUresult cccl_device_unique_by_key_cleanup(cccl_device_unique_by_key_build_result_t* build_ptr)
|
||||
try
|
||||
{
|
||||
if (build_ptr == nullptr)
|
||||
{
|
||||
return CUDA_ERROR_INVALID_VALUE;
|
||||
}
|
||||
|
||||
cccl::detail::release_jit_artifacts(build_ptr);
|
||||
build_ptr->unique_by_key_fn = nullptr;
|
||||
|
||||
return CUDA_SUCCESS;
|
||||
}
|
||||
catch (const std::exception& exc)
|
||||
{
|
||||
fprintf(stderr, "\nEXCEPTION in cccl_device_unique_by_key_cleanup(): %s\n", exc.what());
|
||||
return CUDA_ERROR_UNKNOWN;
|
||||
}
|
||||
187
cccl_upstream/c/parallel.v2/src/util/build_utils.h
Normal file
187
cccl_upstream/c/parallel.v2/src/util/build_utils.h
Normal file
@@ -0,0 +1,187 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA C++ Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <cccl/c/types.h>
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
namespace cccl::detail
|
||||
{
|
||||
// Strip a leading "-I" prefix from `s`. Centralizes the prefix-handling used
|
||||
// by every entry point that accepts an `-I`-prefixed include path from the
|
||||
// Python layer (which speaks compile-flag syntax) and converts it to a bare
|
||||
// filesystem path (which hostjit's CompilerConfig wants).
|
||||
//
|
||||
// Returns an empty string for null / empty input — every caller treats that
|
||||
// as "no path supplied" so the bare check stays in one place.
|
||||
inline std::string strip_dash_i_prefix(const char* s)
|
||||
{
|
||||
if (!s || s[0] == '\0')
|
||||
{
|
||||
return {};
|
||||
}
|
||||
std::string_view sv{s};
|
||||
if (sv.starts_with("-I"))
|
||||
{
|
||||
sv.remove_prefix(2);
|
||||
}
|
||||
return std::string{sv};
|
||||
}
|
||||
|
||||
// Parse path arguments from the Python layer for use with hostjit.
|
||||
// Returns the bare CCCL include path (strips "-I" prefix if present).
|
||||
inline std::string parse_cccl_include_path(const char* libcudacxx_path)
|
||||
{
|
||||
return strip_dash_i_prefix(libcudacxx_path);
|
||||
}
|
||||
|
||||
// Returns the CTK root directory (strips "-I" prefix and "/include" suffix if
|
||||
// present, then walks up to the directory that contains `nvvm/libdevice/`).
|
||||
// Works for both the flat `/usr/local/cuda` layout and the
|
||||
// `/usr/local/cuda/targets/<arch>/include` layout (and any other arrangement)
|
||||
// because it locates the toolkit root by its `nvvm/libdevice/libdevice.10.bc`
|
||||
// marker rather than by hard-coded directory structure.
|
||||
inline std::string parse_ctk_root(const char* ctk_path)
|
||||
{
|
||||
std::string p = strip_dash_i_prefix(ctk_path);
|
||||
if (p.empty())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
std::filesystem::path fp(p);
|
||||
if (fp.filename() == "include")
|
||||
{
|
||||
fp = fp.parent_path();
|
||||
}
|
||||
for (auto candidate = fp; candidate.has_parent_path() && candidate != candidate.parent_path();
|
||||
candidate = candidate.parent_path())
|
||||
{
|
||||
if (std::filesystem::exists(candidate / "nvvm" / "libdevice"))
|
||||
{
|
||||
return candidate.string();
|
||||
}
|
||||
}
|
||||
return fp.string();
|
||||
}
|
||||
|
||||
// In source-tree (dev) builds, cub/ and thrust/ live at sibling paths to
|
||||
// libcudacxx/include rather than under a single CCCL_INCLUDE_PATH. The test
|
||||
// harness passes them as `-I`-prefixed strings; hostjit's
|
||||
// `internal-isystem` plumbing only honors a single `cccl_include_path` for
|
||||
// libcudacxx/cub/thrust, so push the bare cub/thrust paths into
|
||||
// `include_paths` (`-I <path>`) instead.
|
||||
inline void
|
||||
add_extra_cub_thrust_includes(hostjit::CompilerConfig& jit_config, const char* cub_path, const char* thrust_path)
|
||||
{
|
||||
auto add_if_dir = [&](const std::string& p) {
|
||||
if (!p.empty() && std::filesystem::exists(p))
|
||||
{
|
||||
jit_config.include_paths.push_back(p);
|
||||
}
|
||||
};
|
||||
add_if_dir(strip_dash_i_prefix(cub_path));
|
||||
add_if_dir(strip_dash_i_prefix(thrust_path));
|
||||
}
|
||||
|
||||
// RAII helper for merging cub_path / thrust_path (`-I`-prefixed) into a
|
||||
// `cccl_build_config*`'s `extra_include_dirs` before passing to
|
||||
// `CubCall::compile()`. The merged config and the strings it points into are
|
||||
// kept alive for the lifetime of this object.
|
||||
//
|
||||
// Usage:
|
||||
// MergedBuildConfig merged(build_config, cub_path, thrust_path);
|
||||
// ... .compile(cc_major, cc_minor, merged.get(), ctk_root, ccl_inc);
|
||||
class MergedBuildConfig
|
||||
{
|
||||
public:
|
||||
MergedBuildConfig(const cccl_build_config* base, const char* cub_path, const char* thrust_path)
|
||||
{
|
||||
if (base)
|
||||
{
|
||||
merged_ = *base;
|
||||
}
|
||||
// We append at most two paths (cub + thrust). Reserve up front so the
|
||||
// owned_strs_/ptrs_ vectors don't reallocate — important because we
|
||||
// capture pointers into owned_strs_ for `extra_include_dirs`.
|
||||
owned_strs_.reserve(2);
|
||||
ptrs_.reserve(merged_.num_extra_include_dirs + 2);
|
||||
|
||||
for (size_t i = 0; i < merged_.num_extra_include_dirs; ++i)
|
||||
{
|
||||
ptrs_.push_back(merged_.extra_include_dirs[i]);
|
||||
}
|
||||
auto add = [&](const char* p) {
|
||||
auto s = strip_dash_i_prefix(p);
|
||||
if (!s.empty())
|
||||
{
|
||||
owned_strs_.push_back(std::move(s));
|
||||
}
|
||||
};
|
||||
add(cub_path);
|
||||
add(thrust_path);
|
||||
for (auto& s : owned_strs_)
|
||||
{
|
||||
ptrs_.push_back(s.c_str());
|
||||
}
|
||||
merged_.extra_include_dirs = ptrs_.data();
|
||||
merged_.num_extra_include_dirs = ptrs_.size();
|
||||
}
|
||||
|
||||
cccl_build_config* get()
|
||||
{
|
||||
return &merged_;
|
||||
}
|
||||
|
||||
private:
|
||||
cccl_build_config merged_{};
|
||||
std::vector<std::string> owned_strs_;
|
||||
std::vector<const char*> ptrs_;
|
||||
};
|
||||
|
||||
// Copy cubin data into a heap-allocated buffer the caller owns. Plain `new[]`
|
||||
// — memcpy is noexcept so there's no exception path between the allocation
|
||||
// and the assignment to out_cubin. The caller eventually frees via
|
||||
// release_jit_artifacts() (or delete[] on out_cubin).
|
||||
inline void copy_cubin(const std::vector<char>& cubin, void*& out_cubin, size_t& out_size)
|
||||
{
|
||||
if (cubin.empty())
|
||||
{
|
||||
out_cubin = nullptr;
|
||||
out_size = 0;
|
||||
return;
|
||||
}
|
||||
auto* buf = new char[cubin.size()];
|
||||
std::memcpy(buf, cubin.data(), cubin.size());
|
||||
out_cubin = buf;
|
||||
out_size = cubin.size();
|
||||
}
|
||||
|
||||
// Free the JIT compiler and cubin buffer common to every build_result_t in
|
||||
// c/parallel.v2/. Algorithm-specific fields (X_fn, determinism, etc.) get
|
||||
// nulled by the caller after this. Template'd over the build_result type so
|
||||
// each algorithm header doesn't need to include this one transitively.
|
||||
template <typename BuildResult>
|
||||
void release_jit_artifacts(BuildResult* build_ptr)
|
||||
{
|
||||
delete static_cast<hostjit::JITCompiler*>(build_ptr->jit_compiler);
|
||||
build_ptr->jit_compiler = nullptr;
|
||||
delete[] static_cast<char*>(build_ptr->payload);
|
||||
build_ptr->payload = nullptr;
|
||||
build_ptr->payload_size = 0;
|
||||
}
|
||||
} // namespace cccl::detail
|
||||
53
cccl_upstream/c/parallel.v2/src/util/first_call_gate.h
Normal file
53
cccl_upstream/c/parallel.v2/src/util/first_call_gate.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of CUDA Experimental in CUDA Core Compute 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
namespace cccl::detail
|
||||
{
|
||||
// Serializes the first successful invocation of generated CUB code that
|
||||
// lazily initializes a function-local static. Windows HostJIT translation
|
||||
// units use -fno-threadsafe-statics because the required CRT support is not
|
||||
// available. Callers bypass the gate for empty work; after initialization,
|
||||
// the atomic fast path invokes the generated function without locking.
|
||||
class first_call_gate
|
||||
{
|
||||
public:
|
||||
template <typename Invocation>
|
||||
int invoke(Invocation&& invocation)
|
||||
{
|
||||
if (complete.load(std::memory_order_acquire))
|
||||
{
|
||||
return invocation();
|
||||
}
|
||||
|
||||
std::unique_lock lock(mutex);
|
||||
if (complete.load(std::memory_order_relaxed))
|
||||
{
|
||||
lock.unlock();
|
||||
return invocation();
|
||||
}
|
||||
|
||||
const int status = invocation();
|
||||
if (status == 0)
|
||||
{
|
||||
complete.store(true, std::memory_order_release);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<bool> complete{false};
|
||||
std::mutex mutex;
|
||||
};
|
||||
} // namespace cccl::detail
|
||||
69
cccl_upstream/c/parallel.v2/test/CMakeLists.txt
Normal file
69
cccl_upstream/c/parallel.v2/test/CMakeLists.txt
Normal file
@@ -0,0 +1,69 @@
|
||||
cccl_get_c2h()
|
||||
|
||||
# v2 reuses the same test sources as v1 from c/parallel/test/, compiled with
|
||||
# CCCL_C_PARALLEL_V2 defined. The shared source files contain a small number
|
||||
# of #ifdef CCCL_C_PARALLEL_V2 branches that select v2's backend (hostjit
|
||||
# bitcode vs nvrtc LTO-IR) and skip v1-only test variants.
|
||||
set(v1_test_dir "${CMAKE_CURRENT_SOURCE_DIR}/../../parallel/test")
|
||||
|
||||
function(cccl_c_parallel_v2_add_test target_name_var source)
|
||||
get_filename_component(target_name "${source}" NAME_WE)
|
||||
string(
|
||||
REGEX REPLACE
|
||||
"test_([^.]*)"
|
||||
"cccl.c.parallel.v2.test.\\1"
|
||||
target_name
|
||||
"${target_name}"
|
||||
)
|
||||
set(${target_name_var} ${target_name} PARENT_SCOPE)
|
||||
|
||||
add_executable(${target_name} "${source}")
|
||||
cccl_configure_target(${target_name} DIALECT 20)
|
||||
|
||||
set_target_properties(${target_name} PROPERTIES CUDA_RUNTIME_LIBRARY STATIC)
|
||||
target_link_libraries(
|
||||
${target_name}
|
||||
PRIVATE
|
||||
cccl.compiler_interface
|
||||
cccl.c.parallel.v2
|
||||
cccl.c.parallel.v2.hostjit_lib
|
||||
CUDA::cudart_static
|
||||
CUDA::nvrtc
|
||||
cccl.c2h.main
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
${target_name}
|
||||
PRIVATE
|
||||
"${v1_test_dir}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../src/hostjit/include"
|
||||
)
|
||||
|
||||
list(GET CUDAToolkit_INCLUDE_DIRS 0 CUDA_FIRST_INCLUDE_DIR)
|
||||
target_compile_definitions(
|
||||
${target_name}
|
||||
PRIVATE
|
||||
CCCL_C_PARALLEL_V2=1
|
||||
TEST_CUB_PATH="-I${CCCL_SOURCE_DIR}/cub"
|
||||
TEST_THRUST_PATH="-I${CCCL_SOURCE_DIR}/thrust"
|
||||
TEST_LIBCUDACXX_PATH="-I${CCCL_SOURCE_DIR}/libcudacxx/include"
|
||||
TEST_CTK_PATH="-I${CUDA_FIRST_INCLUDE_DIR}"
|
||||
TEST_INCLUDE_PATH="${v1_test_dir}"
|
||||
)
|
||||
|
||||
add_test(NAME ${target_name} COMMAND ${target_name})
|
||||
endfunction()
|
||||
|
||||
file(
|
||||
GLOB test_srcs
|
||||
RELATIVE "${v1_test_dir}"
|
||||
CONFIGURE_DEPENDS
|
||||
"${v1_test_dir}/*.cu"
|
||||
"${v1_test_dir}/*.cpp"
|
||||
)
|
||||
|
||||
foreach (test_src IN LISTS test_srcs)
|
||||
cccl_c_parallel_v2_add_test(test_target "${v1_test_dir}/${test_src}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(freestanding)
|
||||
36
cccl_upstream/c/parallel.v2/test/freestanding/CMakeLists.txt
Normal file
36
cccl_upstream/c/parallel.v2/test/freestanding/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
||||
cccl_get_c2h()
|
||||
|
||||
function(cccl_c_parallel_v2_add_freestanding_test target_name_var source)
|
||||
get_filename_component(target_name "${source}" NAME_WE)
|
||||
string(
|
||||
REGEX REPLACE
|
||||
"test_([^.]*)"
|
||||
"cccl.c.parallel.v2.test.freestanding.\\1"
|
||||
target_name
|
||||
"${target_name}"
|
||||
)
|
||||
set(target_name_var ${target_name} PARENT_SCOPE)
|
||||
|
||||
cccl_add_executable(
|
||||
${target_name}
|
||||
ADD_CTEST
|
||||
NO_METATARGETS
|
||||
DIALECT 20
|
||||
SOURCES "${source}"
|
||||
)
|
||||
target_link_libraries(
|
||||
${target_name}
|
||||
PRIVATE cccl.c.parallel.v2.hostjit_lib CUDA::cudart
|
||||
)
|
||||
endfunction()
|
||||
|
||||
file(
|
||||
GLOB freestanding_srcs
|
||||
RELATIVE "${CMAKE_CURRENT_LIST_DIR}"
|
||||
CONFIGURE_DEPENDS
|
||||
*.cpp
|
||||
)
|
||||
|
||||
foreach (freestanding_src IN LISTS freestanding_srcs)
|
||||
cccl_c_parallel_v2_add_freestanding_test(test_target "${freestanding_src}")
|
||||
endforeach()
|
||||
@@ -0,0 +1,69 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
static const char* k_source = R"(
|
||||
|
||||
#include <cuda/std/initializer_list>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
__global__ void device_kernel(int* ptr)
|
||||
{
|
||||
::cuda::std::initializer_list<::std::size_t> meow {42ull, 1337ull};
|
||||
*ptr = static_cast<int>(::std::move(*meow.begin()));
|
||||
}
|
||||
|
||||
extern "C" _CCCL_VISIBILITY_EXPORT void host_entry(int* ptr)
|
||||
{
|
||||
device_kernel<<<1, 1>>>(ptr);
|
||||
}
|
||||
)";
|
||||
|
||||
int main()
|
||||
{
|
||||
// Detect Clang/CUDA configuration from the build environment
|
||||
auto config = hostjit::detectDefaultConfig();
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(k_source))
|
||||
{
|
||||
std::fprintf(stderr, "HostJIT compilation failed:\n%s\n", compiler.getLastError().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto host_fn = compiler.getFunction<void (*)(int*)>("host_entry");
|
||||
if (!host_fn)
|
||||
{
|
||||
std::fprintf(stderr, "Symbol 'host_entry' not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int* d_ptr = nullptr;
|
||||
cudaMalloc(&d_ptr, sizeof(int));
|
||||
|
||||
host_fn(d_ptr);
|
||||
cudaDeviceSynchronize();
|
||||
|
||||
int result = 0;
|
||||
cudaMemcpy(&result, d_ptr, sizeof(int), cudaMemcpyDeviceToHost);
|
||||
cudaFree(d_ptr);
|
||||
|
||||
assert(result == 42 && "device kernel did not write expected value");
|
||||
std::printf("freestanding compiler test passed (result=%d)\n", result);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
static const char* k_source = R"(
|
||||
#include <cuda_runtime.h>
|
||||
#include <cuda/std/version>
|
||||
|
||||
__global__ void device_kernel(int* ptr)
|
||||
{
|
||||
*ptr = 42;
|
||||
}
|
||||
|
||||
extern "C" _CCCL_VISIBILITY_EXPORT void host_entry(int* ptr)
|
||||
{
|
||||
device_kernel<<<1, 1>>>(ptr);
|
||||
}
|
||||
)";
|
||||
|
||||
int main()
|
||||
{
|
||||
// Detect Clang/CUDA configuration from the build environment
|
||||
auto config = hostjit::detectDefaultConfig();
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(k_source))
|
||||
{
|
||||
std::fprintf(stderr, "HostJIT compilation failed:\n%s\n", compiler.getLastError().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto host_fn = compiler.getFunction<void (*)(int*)>("host_entry");
|
||||
if (!host_fn)
|
||||
{
|
||||
std::fprintf(stderr, "Symbol 'host_entry' not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int* d_ptr = nullptr;
|
||||
cudaMalloc(&d_ptr, sizeof(int));
|
||||
|
||||
host_fn(d_ptr);
|
||||
cudaDeviceSynchronize();
|
||||
|
||||
int result = 0;
|
||||
cudaMemcpy(&result, d_ptr, sizeof(int), cudaMemcpyDeviceToHost);
|
||||
cudaFree(d_ptr);
|
||||
|
||||
assert(result == 42 && "device kernel did not write expected value");
|
||||
std::printf("freestanding compiler test passed (result=%d)\n", result);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
static const char* cuda_source = R"(
|
||||
#include <cuda_runtime.h>
|
||||
#include <cub/device/device_adjacent_difference.cuh>
|
||||
#include <cuda/std/functional>
|
||||
|
||||
extern "C" _CCCL_VISIBILITY_EXPORT int adjacentDifference(
|
||||
const int* d_input, int* d_output, int num_items) {
|
||||
void* d_temp_storage = nullptr;
|
||||
size_t temp_storage_bytes = 0;
|
||||
|
||||
// Query temp storage
|
||||
cudaError_t err = cub::DeviceAdjacentDifference::SubtractLeftCopy(
|
||||
d_temp_storage, temp_storage_bytes, d_input, d_output, num_items,
|
||||
cuda::std::minus<int>{});
|
||||
if (err != cudaSuccess) return -1;
|
||||
|
||||
// Allocate temp storage
|
||||
err = cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
if (err != cudaSuccess) return -2;
|
||||
|
||||
// Run adjacent difference
|
||||
err = cub::DeviceAdjacentDifference::SubtractLeftCopy(
|
||||
d_temp_storage, temp_storage_bytes, d_input, d_output, num_items,
|
||||
cuda::std::minus<int>{});
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_temp_storage);
|
||||
return -3;
|
||||
}
|
||||
|
||||
err = cudaDeviceSynchronize();
|
||||
cudaFree(d_temp_storage);
|
||||
return (err == cudaSuccess) ? 0 : -4;
|
||||
}
|
||||
)";
|
||||
|
||||
int main()
|
||||
{
|
||||
// Detect Clang/CUDA configuration from the build environment
|
||||
auto config = hostjit::detectDefaultConfig();
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(cuda_source))
|
||||
{
|
||||
std::fprintf(stderr, "HostJIT compilation failed:\n%s\n", compiler.getLastError().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto adjacentDiff = compiler.getFunction<int (*)(const int*, int*, int)>("adjacentDifference");
|
||||
if (!adjacentDiff)
|
||||
{
|
||||
std::cerr << "Failed to get function: " << compiler.getLastError() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Prepare test data: [1, 2, 3, 4, 5, ...]
|
||||
const int N = 8;
|
||||
std::vector<int> h_input(N);
|
||||
std::iota(h_input.begin(), h_input.end(), 1);
|
||||
|
||||
// Expected: SubtractLeft produces [d[0], d[1]-d[0], d[2]-d[1], ...]
|
||||
// For input [1,2,3,4,5,6,7,8]: output [1,1,1,1,1,1,1,1]
|
||||
std::vector<int> expected(N);
|
||||
expected[0] = h_input[0];
|
||||
for (int i = 1; i < N; i++)
|
||||
{
|
||||
expected[i] = h_input[i] - h_input[i - 1];
|
||||
}
|
||||
|
||||
int *d_input, *d_output;
|
||||
CUDA_CHECK(cudaMalloc(&d_input, N * sizeof(int)));
|
||||
CUDA_CHECK(cudaMalloc(&d_output, N * sizeof(int)));
|
||||
CUDA_CHECK(cudaMemcpy(d_input, h_input.data(), N * sizeof(int), cudaMemcpyHostToDevice));
|
||||
|
||||
std::cout << "Computing adjacent differences of " << N << " integers...\n";
|
||||
int status = adjacentDiff(d_input, d_output, N);
|
||||
if (status != 0)
|
||||
{
|
||||
std::cerr << "adjacentDifference failed with status: " << status << "\n";
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
CUDA_CHECK(cudaFree(d_output));
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<int> h_output(N);
|
||||
CUDA_CHECK(cudaMemcpy(h_output.data(), d_output, N * sizeof(int), cudaMemcpyDeviceToHost));
|
||||
|
||||
std::cout << "Input: [";
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
std::cout << (i ? ", " : "") << h_input[i];
|
||||
}
|
||||
std::cout << "]\n";
|
||||
|
||||
std::cout << "Output: [";
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
std::cout << (i ? ", " : "") << h_output[i];
|
||||
}
|
||||
std::cout << "]\n";
|
||||
|
||||
std::cout << "Expected: [";
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
std::cout << (i ? ", " : "") << expected[i];
|
||||
}
|
||||
std::cout << "]\n";
|
||||
|
||||
bool success = (h_output == expected);
|
||||
if (success)
|
||||
{
|
||||
std::cout << "Results verified successfully!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Mismatch!\n";
|
||||
}
|
||||
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
CUDA_CHECK(cudaFree(d_output));
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
// LLVM IR for an add operator with alwaysinline attribute
|
||||
// This IR defines a function: float user_op(float a, float b) { return a + b; }
|
||||
const char* user_op_llvm_ir = R"(
|
||||
target datalayout = "e-p6:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64"
|
||||
target triple = "nvptx64-nvidia-cuda"
|
||||
|
||||
define float @user_op(float %a, float %b) alwaysinline {
|
||||
entry:
|
||||
%result = fadd float %a, %b
|
||||
ret float %result
|
||||
}
|
||||
)";
|
||||
|
||||
// Write LLVM IR text to a file (parseIRFile in the compiler handles both .ll and .bc)
|
||||
bool writeIRFile(const std::string& llvm_ir, const std::string& output_path)
|
||||
{
|
||||
std::ofstream ir_file(output_path);
|
||||
if (!ir_file)
|
||||
{
|
||||
std::cerr << "Failed to write LLVM IR file\n";
|
||||
return false;
|
||||
}
|
||||
ir_file << llvm_ir;
|
||||
ir_file.close();
|
||||
std::cout << "Generated IR file: " << output_path << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* cuda_source = R"(
|
||||
#include <cuda_runtime.h>
|
||||
#include <cub/device/device_reduce.cuh>
|
||||
|
||||
// External declaration - resolved from linked bitcode
|
||||
extern "C" __device__ float user_op(float a, float b);
|
||||
|
||||
// Functor wrapping the external function
|
||||
struct UserOp {
|
||||
__device__ __forceinline__
|
||||
float operator()(float a, float b) const {
|
||||
return user_op(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" _CCCL_VISIBILITY_EXPORT int computeReduce(float* d_input, int num_items, float* result) {
|
||||
float* d_output = nullptr;
|
||||
void* d_temp_storage = nullptr;
|
||||
size_t temp_storage_bytes = 0;
|
||||
float init = HOSTJIT_REDUCE_INIT;
|
||||
UserOp op;
|
||||
|
||||
cudaError_t err = cudaMalloc(&d_output, sizeof(float));
|
||||
if (err != cudaSuccess) return -1;
|
||||
|
||||
err = cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,
|
||||
d_input, d_output, num_items, op, init);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_output);
|
||||
return -2;
|
||||
}
|
||||
|
||||
err = cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_output);
|
||||
return -3;
|
||||
}
|
||||
|
||||
err = cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,
|
||||
d_input, d_output, num_items, op, init);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_temp_storage);
|
||||
cudaFree(d_output);
|
||||
return -4;
|
||||
}
|
||||
|
||||
err = cudaDeviceSynchronize();
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_temp_storage);
|
||||
cudaFree(d_output);
|
||||
return -5;
|
||||
}
|
||||
|
||||
err = cudaMemcpy(result, d_output, sizeof(float), cudaMemcpyDeviceToHost);
|
||||
cudaFree(d_temp_storage);
|
||||
cudaFree(d_output);
|
||||
|
||||
return (err == cudaSuccess) ? 0 : -6;
|
||||
}
|
||||
)";
|
||||
|
||||
int main()
|
||||
{
|
||||
// Step 1: Write LLVM IR file with the custom operator
|
||||
std::string ir_path = (std::filesystem::temp_directory_path() / "user_op.ll").string();
|
||||
if (!writeIRFile(user_op_llvm_ir, ir_path))
|
||||
{
|
||||
std::cerr << "Failed to write IR file\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto config = hostjit::detectDefaultConfig();
|
||||
config.device_bitcode_files.push_back(ir_path);
|
||||
config.macro_definitions["HOSTJIT_REDUCE_INIT"] = "0.0f";
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(cuda_source))
|
||||
{
|
||||
std::cerr << "Failed to get function: " << compiler.getLastError() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto computeReduce = compiler.getFunction<int (*)(float*, int, float*)>("computeReduce");
|
||||
if (!computeReduce)
|
||||
{
|
||||
std::cerr << "Failed to get function: " << compiler.getLastError() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
const int N = 1024;
|
||||
std::vector<float> h_input(N);
|
||||
|
||||
// Initialize with values 1.0 to N
|
||||
std::iota(h_input.begin(), h_input.end(), 1.0f);
|
||||
const float expected = static_cast<float>(N) * (N + 1) / 2.0f; // Sum of 1..N
|
||||
|
||||
// Allocate device memory
|
||||
float* d_input;
|
||||
CUDA_CHECK(cudaMalloc(&d_input, N * sizeof(float)));
|
||||
|
||||
// Copy data to device
|
||||
CUDA_CHECK(cudaMemcpy(d_input, h_input.data(), N * sizeof(float), cudaMemcpyHostToDevice));
|
||||
|
||||
float result = -1.0f;
|
||||
int status = computeReduce(d_input, N, &result);
|
||||
if (status != 0)
|
||||
{
|
||||
std::cerr << "computeReduce failed with status: " << status << "\n";
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify result
|
||||
std::cout << "\nResult: " << result << " (expected: " << expected << ")\n";
|
||||
bool success = std::abs(result - expected) < 0.01f;
|
||||
if (success)
|
||||
{
|
||||
std::cout << "\n*** SUCCESS: Custom operator was inlined and executed correctly! ***\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "\nMismatch! Got " << result << " but expected " << expected << "\n";
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
static const char* cuda_source = R"(
|
||||
#include <cuda_runtime.h>
|
||||
#include <cub/device/device_reduce.cuh>
|
||||
|
||||
// External declaration - resolved from linked bitcode
|
||||
extern "C" __device__ float user_op(float a, float b);
|
||||
|
||||
// Functor wrapping the external function
|
||||
struct UserOp {
|
||||
__device__ __forceinline__
|
||||
float operator()(float a, float b) const {
|
||||
return ::cuda::std::min(a, b);
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" _CCCL_VISIBILITY_EXPORT int computeReduce(float* d_input, int num_items, float* result) {
|
||||
float* d_output = nullptr;
|
||||
void* d_temp_storage = nullptr;
|
||||
size_t temp_storage_bytes = 0;
|
||||
float init = 1e38f;
|
||||
UserOp op;
|
||||
|
||||
cudaError_t err = cudaMalloc(&d_output, sizeof(float));
|
||||
if (err != cudaSuccess) return -1;
|
||||
|
||||
err = cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,
|
||||
d_input, d_output, num_items, op, init);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_output);
|
||||
return -2;
|
||||
}
|
||||
|
||||
err = cudaMalloc(&d_temp_storage, temp_storage_bytes);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_output);
|
||||
return -3;
|
||||
}
|
||||
|
||||
err = cub::DeviceReduce::Reduce(d_temp_storage, temp_storage_bytes,
|
||||
d_input, d_output, num_items, op, init);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_temp_storage);
|
||||
cudaFree(d_output);
|
||||
return -4;
|
||||
}
|
||||
|
||||
err = cudaDeviceSynchronize();
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_temp_storage);
|
||||
cudaFree(d_output);
|
||||
return -5;
|
||||
}
|
||||
|
||||
err = cudaMemcpy(result, d_output, sizeof(float), cudaMemcpyDeviceToHost);
|
||||
cudaFree(d_temp_storage);
|
||||
cudaFree(d_output);
|
||||
|
||||
return (err == cudaSuccess) ? 0 : -6;
|
||||
}
|
||||
)";
|
||||
|
||||
int main()
|
||||
{
|
||||
// Detect Clang/CUDA configuration from the build environment
|
||||
auto config = hostjit::detectDefaultConfig();
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(cuda_source))
|
||||
{
|
||||
std::cerr << "Failed to get function: " << compiler.getLastError() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto computeReduce = compiler.getFunction<int (*)(float*, int, float*)>("computeReduce");
|
||||
if (!computeReduce)
|
||||
{
|
||||
std::cerr << "Failed to get function: " << compiler.getLastError() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
const int N = 1024;
|
||||
std::vector<float> h_input(N);
|
||||
|
||||
// Initialize with values 1.0 to N
|
||||
std::iota(h_input.begin(), h_input.end(), 1.0f);
|
||||
|
||||
const float expected = 1.0f; // Minimum value in the array
|
||||
|
||||
// Allocate device memory
|
||||
float* d_input;
|
||||
CUDA_CHECK(cudaMalloc(&d_input, N * sizeof(float)));
|
||||
|
||||
// Copy data to device
|
||||
CUDA_CHECK(cudaMemcpy(d_input, h_input.data(), N * sizeof(float), cudaMemcpyHostToDevice));
|
||||
|
||||
float result = -1.0f;
|
||||
int status = computeReduce(d_input, N, &result);
|
||||
if (status != 0)
|
||||
{
|
||||
std::cerr << "computeReduce failed with status: " << status << "\n";
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify result
|
||||
std::cout << "\nResult: " << result << " (expected: " << expected << ")\n";
|
||||
bool success = std::abs(result - expected) < 0.01f;
|
||||
if (success)
|
||||
{
|
||||
std::cout << "\n*** SUCCESS: Custom operator was inlined and executed correctly! ***\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "\nMismatch! Got " << result << " but expected " << expected << "\n";
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
static const char* cuda_source = R"(
|
||||
#include <cuda_runtime.h>
|
||||
#include <cub/device/device_reduce.cuh>
|
||||
#include <cuda/std/functional>
|
||||
#include <cuda/__execution/determinism.h>
|
||||
#include <cuda/__execution/require.h>
|
||||
|
||||
extern "C" _CCCL_VISIBILITY_EXPORT int computeSumDeterministic(float* d_input, int num_items, float* result) {
|
||||
float* d_output = nullptr;
|
||||
|
||||
// Allocate output
|
||||
cudaError_t err = cudaMalloc(&d_output, sizeof(float));
|
||||
if (err != cudaSuccess) return -1;
|
||||
|
||||
// Run deterministic sum reduction with gpu_to_gpu determinism
|
||||
auto env = cuda::execution::require(cuda::execution::determinism::gpu_to_gpu);
|
||||
err = cub::DeviceReduce::Sum(d_input, d_output, num_items, env);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_output);
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Synchronize
|
||||
err = cudaDeviceSynchronize();
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_output);
|
||||
return -3;
|
||||
}
|
||||
|
||||
// Copy result back
|
||||
err = cudaMemcpy(result, d_output, sizeof(float), cudaMemcpyDeviceToHost);
|
||||
if (err != cudaSuccess) {
|
||||
cudaFree(d_output);
|
||||
return -4;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
cudaFree(d_output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
)";
|
||||
|
||||
int main()
|
||||
{
|
||||
// Detect Clang/CUDA configuration from the build environment
|
||||
auto config = hostjit::detectDefaultConfig();
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(cuda_source))
|
||||
{
|
||||
std::fprintf(stderr, "HostJIT compilation failed:\n%s\n", compiler.getLastError().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto computeSumDeterministic = compiler.getFunction<int (*)(float*, int, float*)>("computeSumDeterministic");
|
||||
if (!computeSumDeterministic)
|
||||
{
|
||||
std::cerr << "Failed to get function: " << compiler.getLastError() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Prepare test data
|
||||
const int N = 1024;
|
||||
std::vector<float> h_input(N);
|
||||
|
||||
// Initialize with values 1.0 to N
|
||||
std::iota(h_input.begin(), h_input.end(), 1.0f);
|
||||
|
||||
// Expected sum: N*(N+1)/2
|
||||
float expected_sum = static_cast<float>(N) * (N + 1) / 2;
|
||||
|
||||
// Allocate device memory
|
||||
float* d_input;
|
||||
CUDA_CHECK(cudaMalloc(&d_input, N * sizeof(float)));
|
||||
|
||||
// Copy data to device
|
||||
CUDA_CHECK(cudaMemcpy(d_input, h_input.data(), N * sizeof(float), cudaMemcpyHostToDevice));
|
||||
|
||||
// Call the JIT-compiled function
|
||||
std::cout << "Computing deterministic sum of " << N << " floats using CUB...\n";
|
||||
float result = 0;
|
||||
int status = computeSumDeterministic(d_input, N, &result);
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
std::cerr << "computeSumDeterministic failed with status: " << status << "\n";
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify result
|
||||
std::cout << "Result: " << result << " (expected: " << expected_sum << ")\n";
|
||||
|
||||
// Verify determinism: run multiple times and check results are identical
|
||||
const int num_runs = 10;
|
||||
bool deterministic = true;
|
||||
for (int i = 0; i < num_runs; ++i)
|
||||
{
|
||||
float run_result = 0;
|
||||
status = computeSumDeterministic(d_input, N, &run_result);
|
||||
if (status != 0)
|
||||
{
|
||||
std::cerr << "Run " << i << " failed with status: " << status << "\n";
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
return 1;
|
||||
}
|
||||
if (run_result != result)
|
||||
{
|
||||
std::cerr << "Run " << i << " produced different result: " << run_result << " vs " << result << "\n";
|
||||
deterministic = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = (result == expected_sum) && deterministic;
|
||||
if (success)
|
||||
{
|
||||
std::cout << "Results verified successfully! (" << num_runs << " runs produced identical results)\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result != expected_sum)
|
||||
{
|
||||
std::cerr << "Mismatch! Got " << result << " but expected " << expected_sum << "\n";
|
||||
}
|
||||
if (!deterministic)
|
||||
{
|
||||
std::cerr << "Non-deterministic results detected!\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
CUDA_CHECK(cudaFree(d_input));
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Repro harness: feeds a CUDA source string to v1's hostjit and reports
|
||||
// whether compilation succeeds. If a path is passed via argv[1] or
|
||||
// $REPRO_SOURCE_FILE, that file's contents are compiled instead of the
|
||||
// built-in minimal source. Used to test whether v2's actual CubCall-generated
|
||||
// host_input.cu compiles under v1's hostjit infrastructure.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
static const char* default_source = R"(
|
||||
#include <cuda_runtime.h>
|
||||
#include <cub/device/device_reduce.cuh>
|
||||
|
||||
using in_0_it_t = int*;
|
||||
using out_0_it_t = unsigned long long*;
|
||||
|
||||
struct Op_0 {
|
||||
__device__ __forceinline__
|
||||
unsigned long long operator()(unsigned long long a, unsigned long long b) const {
|
||||
return a + b;
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" __attribute__((visibility("default"))) int cccl_jit_reduce(
|
||||
void* d_temp_storage,
|
||||
size_t* temp_storage_bytes,
|
||||
void* d_in_state,
|
||||
void* d_out_state,
|
||||
unsigned long long num_items,
|
||||
void* /*op_state*/,
|
||||
void* init_state)
|
||||
{
|
||||
in_0_it_t d_in = static_cast<in_0_it_t>(d_in_state);
|
||||
out_0_it_t d_out = static_cast<out_0_it_t>(d_out_state);
|
||||
unsigned long long init = *static_cast<unsigned long long*>(init_state);
|
||||
Op_0 op;
|
||||
cudaError_t err = cub::DeviceReduce::Reduce<in_0_it_t, out_0_it_t, Op_0, int, unsigned long long>(
|
||||
d_temp_storage, *temp_storage_bytes, d_in, d_out,
|
||||
static_cast<int>(num_items), op, init);
|
||||
return err == cudaSuccess ? 0 : -1;
|
||||
}
|
||||
)";
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::string source_str;
|
||||
std::string source_path;
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
source_path = argv[1];
|
||||
}
|
||||
else if (const char* env = std::getenv("REPRO_SOURCE_FILE"))
|
||||
{
|
||||
source_path = env;
|
||||
}
|
||||
|
||||
if (!source_path.empty())
|
||||
{
|
||||
std::ifstream f(source_path);
|
||||
if (!f)
|
||||
{
|
||||
std::cerr << "Failed to open: " << source_path << std::endl;
|
||||
return 2;
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << f.rdbuf();
|
||||
source_str = ss.str();
|
||||
std::cerr << "Loaded " << source_str.size() << " bytes from " << source_path << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
source_str = default_source;
|
||||
std::cerr << "Using built-in default source." << std::endl;
|
||||
}
|
||||
|
||||
hostjit::CompilerConfig config = hostjit::detectDefaultConfig();
|
||||
config.sm_version = 80;
|
||||
config.verbose = false;
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(source_str))
|
||||
{
|
||||
std::cerr << "JIT compilation FAILED:\n" << compiler.getLastError() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "JIT compilation succeeded." << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include <hostjit/config.hpp>
|
||||
#include <hostjit/jit_compiler.hpp>
|
||||
|
||||
static const char* k_source = R"(
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
#include <cuda/std/version>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
__global__ void device_kernel(int* ptr)
|
||||
{
|
||||
::std::initializer_list<::std::size_t> meow {42ull, 1337ull};
|
||||
*ptr = static_cast<int>(::std::move(*meow.begin()));
|
||||
}
|
||||
|
||||
extern "C" _CCCL_VISIBILITY_EXPORT void host_entry(int* ptr)
|
||||
{
|
||||
device_kernel<<<1, 1>>>(ptr);
|
||||
}
|
||||
)";
|
||||
|
||||
int main()
|
||||
{
|
||||
// Detect Clang/CUDA configuration from the build environment
|
||||
auto config = hostjit::detectDefaultConfig();
|
||||
|
||||
hostjit::JITCompiler compiler(config);
|
||||
if (!compiler.compile(k_source))
|
||||
{
|
||||
std::fprintf(stderr, "HostJIT compilation failed:\n%s\n", compiler.getLastError().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto host_fn = compiler.getFunction<void (*)(int*)>("host_entry");
|
||||
if (!host_fn)
|
||||
{
|
||||
std::fprintf(stderr, "Symbol 'host_entry' not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int* d_ptr = nullptr;
|
||||
cudaMalloc(&d_ptr, sizeof(int));
|
||||
|
||||
host_fn(d_ptr);
|
||||
cudaDeviceSynchronize();
|
||||
|
||||
int result = 0;
|
||||
cudaMemcpy(&result, d_ptr, sizeof(int), cudaMemcpyDeviceToHost);
|
||||
cudaFree(d_ptr);
|
||||
|
||||
assert(result == 42 && "device kernel did not write expected value");
|
||||
std::printf("freestanding compiler test passed (result=%d)\n", result);
|
||||
return 0;
|
||||
}
|
||||
30
cccl_upstream/c/parallel.v2/test/freestanding/test_util.h
Normal file
30
cccl_upstream/c/parallel.v2/test/freestanding/test_util.h
Normal file
@@ -0,0 +1,30 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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) 2026 NVIDIA CORPORATION & AFFILIATES.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef C_PARALLEL_FREESTANDING_TEST_UTIL_H
|
||||
#define C_PARALLEL_FREESTANDING_TEST_UTIL_H
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#define CUDA_CHECK(call) \
|
||||
do \
|
||||
{ \
|
||||
cudaError_t err = call; \
|
||||
if (err != cudaSuccess) \
|
||||
{ \
|
||||
std::cerr << "CUDA error at " << __FILE__ << ":" << __LINE__ << ": " << cudaGetErrorString(err) << "\n"; \
|
||||
return 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif // C_PARALLEL_FREESTANDING_TEST_UTIL_H
|
||||
Reference in New Issue
Block a user