[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:
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