[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:
EngineX CI
2026-07-30 09:35:51 +00:00
parent b4d01f481e
commit 56fd68e7dd
8871 changed files with 1454674 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
project(nvrtcc CUDA)
cccl_get_cudatoolkit()
add_executable(libcudacxx.nvrtcc nvrtcc.cpp)
set_target_properties(libcudacxx.nvrtcc PROPERTIES OUTPUT_NAME nvrtcc)
target_link_libraries(
libcudacxx.nvrtcc
PUBLIC #
CUDA::nvrtc
CUDA::cudart
CUDA::cuda_driver
)
target_compile_features(libcudacxx.nvrtcc PRIVATE cxx_std_17)

View File

@@ -0,0 +1,55 @@
# NVRTCC
## How to use:
Configure libcudacxx to test with NVRTC and `cmake --build` the project before executing `lit`.
```sh
cmake ... -DLIBCUDACXX_TEST_WITH_NVRTC=ON
cmake --build $BUILD_DIR
lit ... $TEST_DIR
```
## How it works
`nvrtcc` processes incoming arguments matching for flags that modify its behavior, and passes the rest to NVRTC.
It will hopefully filter any that don't apply (gcc warnings and such).
The input file is processed to be compatible with NVRTC similarly to the `nvrtc.sh` scripts during compilation.
The resulting file is then compiled with NVRTC and stored as a fatbin. This is, in effect, a compilation pass for NVRTC.
`.fail.cpp` tests will be analyzable by lit and should work properly.
For execution, the fatbin file is provided to nvrtcc again, but will instead launch the precompiled test unit on the GPU.
```mermaid
flowchart TD;
compile{nvrtcc};
runfat{nvrtcc};
start[Execute lit]
build(Execute nvrtcc)
pass(Pass: Continue)
fail(Fail: Compile time failure)
save[Save fatbin]
load[Load fatbin]
testpass[Pass: No failures]
testfail[Fail: Report driver error]
save --> load;
subgraph Build Pass
start -- lit -sv -Denable_nvrtc=true --> build;
build -- nvrtcc -x cu test.pass.cpp ... --> compile;
compile --> fail;
compile --> pass;
pass --> save;
end
subgraph Test Pass
load -- nvrtcc test.pass.cpp.fatbin --> runfat;
runfat --> testpass
runfat --> testfail
end
```

View File

@@ -0,0 +1,327 @@
//===----------------------------------------------------------------------===//
//
// Part of libcu++, the C++ Standard Library for your entire system,
// 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) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#include <algorithm>
#include <cassert>
#include <deque>
#include <functional>
#include <regex>
#include <set>
#include <string>
#include <vector>
#include <stdio.h>
#include "nvrtcc_build.h"
#include "nvrtcc_run.h"
#include "utils/platform.h"
ArgList nvrtcArguments;
ArgList ignoredArguments;
std::string outputDir;
std::string outputFile;
std::string inputFile;
bool skipOutput = false;
bool building = false;
bool execute = false;
ExecutionConfig executionConfig;
enum ArgProcessorState
{
NORMAL,
GREEDY,
ABORT,
};
// Handlers may increment the iterator of the argument parser if they need multiple arguments
// First argument is incrementable, last argument is the end of the list
using ArgHandler = std::function<ArgProcessorState(const std::smatch&)>;
using ArgPair = std::pair<std::regex, ArgHandler>;
using ArgHandlerMap = std::vector<ArgPair>;
int g_argc;
char** g_argv;
// Ignore PTX arch, only capture output version since PTX only compilation *must* be the same
std::regex real_capture("^.*arch=.*,code=(sm_[0-9]+a?)$");
std::regex virtual_capture("^.*arch=.*,code=(compute_[0-9]+a?)$");
// Arch list is a set of unique pairs of strings and bools
// e.x. { compute_arch, real_or_virtual }
// { "sm_80", true } { "compute_80", false }
using ArchList = std::set<ArchConfig>;
ArchList buildList;
// Input example: arch=compute_80,code=sm_80
static ArchConfig translate_gpu_arch(const std::string& arch)
{
std::smatch real;
std::smatch virt;
std::regex_match(arch, real, real_capture);
std::regex_match(arch, virt, virtual_capture);
// Safe default of "compute_60" in case parsing fails
ArchConfig config = (real.size()) ? ArchConfig{real[1].str(), true}
: (virt.size()) ? ArchConfig{virt[1].str(), false}
: ArchConfig{"compute_60", false};
return config;
}
// Greedy handlers inform the argument processor to expect more arguments
constexpr auto make_greedy_handler = [](char const* match) {
return ArgPair{std::regex(match), [](const std::smatch&) {
return GREEDY;
}};
};
ArgPair argHandlers[] = {
{// Forward all arguments to NVCC
std::regex("^-c$"),
[](const std::smatch&) {
building = true;
// We're compiling, maybe do something useful
return NORMAL; // Unreachable
}},
{// Forward all arguments to NVCC
std::regex("^-E$"),
[](const std::smatch&) {
platform_exec("nvcc", g_argv, g_argc);
return ABORT; // Unreachable
}},
{// Greed input file type flag
make_greedy_handler("^-x$")},
{// Matches for CUDA input type
std::regex("^-x ?cu$"),
[](const std::smatch& match) {
ignoredArguments.emplace_back(match[0].str());
return NORMAL;
}},
{// Matches anything other than CUDA as the CUDA flag is captured before this one
std::regex("^-x ?(.*)$"),
[](const std::smatch&) {
// If we're building with something else just add the default arch
buildList.emplace(translate_gpu_arch(""));
return NORMAL;
}},
{// The include flag is improperly formatted, greed append
make_greedy_handler("^-I$")},
{std::regex("^-I ?(.+)$"),
[](const std::smatch& match) {
nvrtcArguments.emplace_back(match[0].str());
return NORMAL;
}},
{make_greedy_handler("^(-include|-isystem)$")},
{// Matches any force include or system include directories
// Might need to figure out if we need to force include a file manually
std::regex("^-include ?(.+)$"),
[](const std::smatch& match) {
nvrtcArguments.emplace_back("--pre-include=" + match[1].str());
return NORMAL;
}},
{make_greedy_handler("^-o$")},
{// Matches '-o nul' which is used for syntax only testing (i.e. .fail.cpp tests)
std::regex("^-o (?:.*?dev)?.*nul$"),
[](const std::smatch&) {
skipOutput = true;
return NORMAL;
}},
{// Matches '-o object' and obtains the output directory
// \\\\ skip C++ escape, and skip regex escape to match \ on Windows
// The second match grouping catches the name sorta of the file. i.e. test.pass.cpp -> test.pass
std::regex("^-o (.+)[\\\\/]([^\\\\/]+)\\..+$"),
[](const std::smatch& match) {
outputDir = match[1].str();
outputFile = match[2].str();
return NORMAL;
}},
{make_greedy_handler("^-gencode$")},
{// Matches '-gencode=' or '-gencode ...'
std::regex("^-gencode[= ]?(.+)$"),
[](const std::smatch& match) {
buildList.emplace(translate_gpu_arch(match[1].str().data()));
return NORMAL;
}},
{// Matches the many various versions of dialect switch and normalizes it
std::regex("^[-/]std[:=](.+)$"),
[](const std::smatch& match) {
nvrtcArguments.emplace_back("-std=" + match[1].str());
return NORMAL;
}},
{// Matches -G/--device-debug
std::regex("^(?:-G|--device-debug)$"),
[](const std::smatch&) {
nvrtcArguments.emplace_back("-G");
return NORMAL;
}},
{// Matches --device-int128/-device-int128
std::regex("^(?:--device-int128|-device-int128)$"),
[](const std::smatch&) {
nvrtcArguments.emplace_back("-device-int128");
return NORMAL;
}},
#if CUDA_VERSION >= 12080
{// Matches --device-float128/-device-float128
std::regex("^(?:--device-float128|-device-float128)$"),
[](const std::smatch&) {
enable_float128 = true;
return NORMAL;
}},
#endif // CUDA_VERSION >= 12080
{// Matches -D
std::regex("^-D.+$"),
[](const std::smatch& match) {
nvrtcArguments.emplace_back(match[0].str());
return NORMAL;
}},
{// Capture an argument that is just '-'. If no input file is listed input is on stdin
std::regex("^-$"),
[](const std::smatch& match) {
inputFile = match[0].str();
return NORMAL;
}},
{// If an input lists a .gpu file, run that file instead
std::regex("^([^-].*).gpu$"),
[](const std::smatch& match) {
execute = true;
executionConfig = ExecutionConfig{RunConfig{1, 0}, {match[0].str()}};
return NORMAL;
}},
{// If an input is a .exe file, search for other builds and run those
std::regex("^([^-].*).exe$"),
[](const std::smatch& match) {
execute = true;
executionConfig = load_execution_config_from_file(match[1].str() + ".build.yml");
assert(executionConfig.builds.size());
return NORMAL;
}},
{// Capture any argument not starting with '-' as the input file
std::regex("^([^-].+)[\\\\/].+$"),
[](const std::smatch& match) {
inputFile = match[0].str();
// Capture directory of input file as an include path
nvrtcArguments.emplace_back("-I " + match[1].str());
return NORMAL;
}},
{// Throw away remaining arguments
std::regex("^-.+$"),
[](const std::smatch& match) {
ignoredArguments.emplace_back(match[0].str());
return NORMAL;
}},
};
int main(int argc, char** argv)
{
// Greedily take off first arg
g_argc = argc - 1;
g_argv = argv + 1;
ArgProcessorState argState = NORMAL;
// Start by parsing arguments and building the configuration
std::string c_arg{};
for (auto a = g_argv; a < g_argv + g_argc; a++)
{
// If the argument was greedy, we'll retry with an appended argument
c_arg = (argState == GREEDY) ? c_arg + " " + *a : *a;
for (auto& h : argHandlers)
{
auto& regex = h.first;
auto& handler = h.second;
std::smatch matches;
std::regex_match(c_arg, matches, regex);
if (matches.size())
{
argState = handler(matches);
break;
}
}
}
fprintf(stderr, "NVRTCC Configuration:\r\n");
fprintf(stderr, " Output dir: %s\r\n", outputDir.c_str());
fprintf(stderr, " Output file: %s\r\n", outputFile.c_str());
fprintf(stderr, " Input file: %s\r\n", inputFile.c_str());
fprintf(stderr, " Building: %s\r\n", building ? "true" : "false");
fprintf(stderr, " Skipping output: %s\r\n", skipOutput ? "true" : "false");
fprintf(stderr, " Executing: %s\r\n", execute ? "true" : "false");
// Load the input file and execute
if (execute)
{
fprintf(
stderr, "Executing %s with %i threads\r\n", executionConfig.builds[0].c_str(), executionConfig.rc.threadCount);
load_and_run_gpu_code(executionConfig.builds[0], executionConfig.rc);
fprintf(stderr, "Execution Passed\r\n");
return 0;
}
// Linking exits and does nothing
if (!building)
{
return 0;
}
// Rebuild the output file template based on the filename
// Check for nul - do not write files
std::string outputTemplate;
if (outputDir.size() && outputFile.size())
{
outputTemplate = outputDir + "/" + outputFile;
}
else
{
outputTemplate = "temp";
}
// load input test file and prepend fakemain
std::string testCu = program + load_input_file(inputFile);
// Write any needed kernel launch data to file for later
RunConfig runConfig = parse_run_config(testCu);
nvrtcArguments.emplace_back("-DCCCL_ENABLE_ASSERTIONS");
if (!skipOutput)
{
std::ofstream ostr(outputTemplate + ".build.yml");
ostr << "cuda_thread_count: " << runConfig.threadCount << '\n';
ostr << "cuda_block_shmem_size: " << runConfig.shmemSize << '\n';
// Do a build for each arch and add it to the build list
ostr << "builds:\n";
for (const auto& build : buildList)
{
auto gpuCode = nvrtc_build_prog(testCu, build, nvrtcArguments);
std::string gpuCodeFile = outputTemplate + "." + archString(build) + ".gpu";
write_output_file(gpuCode.data(), gpuCode.size(), gpuCodeFile);
ostr << " - ";
ostr << '\'' << gpuCodeFile << '\'';
ostr << '\n';
}
ostr.close();
}
else
{
for (const auto& build : buildList)
{
auto gpuCode = nvrtc_build_prog(testCu, build, nvrtcArguments);
}
}
return 0;
}

View File

@@ -0,0 +1,118 @@
//===----------------------------------------------------------------------===//
//
// Part of libcu++, the C++ Standard Library for your entire system,
// 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) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <cuda.h>
#include <nvrtc.h>
#include <stdio.h>
#include "nvrtcc_common.h"
inline bool enable_float128 = false;
extern std::string inputFile;
// Arch configs are strings and bools determining architecture and ptx/sass compilation
using ArchConfig = std::tuple<std::string, bool>;
constexpr auto archString = [](const ArchConfig& a) -> const auto& {
return std::get<0>(a);
};
constexpr auto archId = [](const ArchConfig& a) -> int {
std::regex pattern("(?:compute_|sm_)(\\d+a?)");
std::smatch match;
if (!std::regex_search(archString(a), match, pattern))
{
fprintf(stderr, "Invalid arch string: %s\n", archString(a).c_str());
exit(1);
}
return std::stoi(match[1].str());
};
constexpr auto isArchReal = [](const ArchConfig& a) -> const auto& {
return std::get<1>(a);
};
using ArgList = std::vector<std::string>;
using GpuProg = std::vector<char>;
// Takes arguments for building a file and returns the path to the output file
GpuProg nvrtc_build_prog(const std::string& testCu, const ArchConfig& config, const ArgList& argList)
{
// Assemble arguments
std::vector<const char*> optList;
// Be careful with lifetimes here
std::for_each(argList.begin(), argList.end(), [&](const auto& it) {
optList.emplace_back(it.c_str());
});
// Use the translated architecture
std::string gpu_arch("--gpu-architecture=" + archString(config));
optList.emplace_back(gpu_arch.c_str());
if (enable_float128)
{
// __float128 is only supported on architectures >= 100
if (archId(config) >= 100)
{
optList.emplace_back("-device-float128");
}
}
fprintf(stderr, "NVRTC opt list:\r\n");
for (const auto& it : optList)
{
fprintf(stderr, " %s\r\n", it);
}
fprintf(stderr, "Compiling program...\r\n");
nvrtcProgram prog;
NVRTC_SAFE_CALL(nvrtcCreateProgram(&prog, testCu.c_str(), inputFile.c_str(), 0, nullptr, nullptr));
nvrtcResult compile_result = nvrtcCompileProgram(prog, optList.size(), optList.data());
fprintf(stderr, "Collecting logs...\r\n");
size_t log_size;
NVRTC_SAFE_CALL(nvrtcGetProgramLogSize(prog, &log_size));
{
std::unique_ptr<char[]> log{new char[log_size]};
NVRTC_SAFE_CALL(nvrtcGetProgramLog(prog, log.get()));
printf("%s\r\n", log.get());
}
if (compile_result != NVRTC_SUCCESS)
{
exit(1);
}
size_t codeSize;
GpuProg code;
if (isArchReal(config))
{
NVRTC_SAFE_CALL(nvrtcGetCUBINSize(prog, &codeSize));
code.resize(codeSize);
NVRTC_SAFE_CALL(nvrtcGetCUBIN(prog, code.data()));
}
else
{
NVRTC_SAFE_CALL(nvrtcGetPTXSize(prog, &codeSize));
code.resize(codeSize);
NVRTC_SAFE_CALL(nvrtcGetPTX(prog, code.data()));
}
NVRTC_SAFE_CALL(nvrtcDestroyProgram(&prog));
return code;
}

View File

@@ -0,0 +1,127 @@
//===----------------------------------------------------------------------===//
//
// Part of libcu++, the C++ Standard Library for your entire system,
// 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) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#pragma once
#include <fstream>
#include <iostream>
#include <iterator>
#include <regex>
#include <string>
#include <vector>
#define NVRTC_SAFE_CALL(x) \
do \
{ \
nvrtcResult result = x; \
if (result != NVRTC_SUCCESS) \
{ \
printf("\nNVRTC ERROR: %s failed with error %s\n", #x, nvrtcGetErrorString(result)); \
exit(1); \
} \
} while (0)
#define CUDA_SAFE_CALL(x) \
do \
{ \
CUresult result = x; \
if (result != CUDA_SUCCESS) \
{ \
const char* msg; \
cuGetErrorName(result, &msg); \
printf("\nCUDA ERROR: %s failed with error %s\n", #x, msg); \
exit(1); \
} \
} while (0)
#define CUDA_API_CALL(x) \
do \
{ \
cudaError_t err = x; \
if (err != cudaSuccess) \
{ \
printf("\nCUDA ERROR: %s: %s\n", cudaGetErrorName(err), cudaGetErrorString(err)); \
exit(1); \
} \
} while (0)
static void write_output_file(const char* data, size_t datasz, const std::string& file)
{
std::ofstream ostr(file, std::ios::binary);
assert(!!ostr);
ostr.write(data, datasz);
ostr.close();
}
static std::string load_input_file(const std::string& file)
{
if (file == "-")
{
return std::string(std::istream_iterator<char>{std::cin}, std::istream_iterator<char>{});
}
else
{
std::ifstream istr(file);
assert(!!istr);
return std::string(std::istreambuf_iterator<char>{istr}, std::istreambuf_iterator<char>{});
}
}
static int parse_int_assignment(const std::string& input, std::string var, int def)
{
auto lineBegin = input.find(var);
auto lineEnd = input.find('\n', lineBegin);
if (lineBegin == std::string::npos || lineEnd == std::string::npos)
{
return def;
}
std::string line(input.begin() + lineBegin, input.begin() + lineEnd);
std::regex varRegex("^" + var + ".*?([0-9]+).*?$");
std::smatch match;
std::regex_match(line, match, varRegex);
if (match.size())
{
return std::stoi(match[1].str(), nullptr);
}
fprintf(stderr, "ERROR: Could not find an integer literal for '%s' on line '%s':\r\n", var.c_str(), line.c_str());
exit(1);
return def;
}
struct RunConfig
{
int threadCount = 1;
int shmemSize = 0;
};
static RunConfig parse_run_config(const std::string& input)
{
return RunConfig{
parse_int_assignment(input, "cuda_thread_count", 1),
parse_int_assignment(input, "cuda_block_shmem_size", 0),
};
}
// Fake main for adapting kernels
static const char* program = R"program(
__host__ __device__ int fake_main(int argc, char ** argv);
#define main fake_main
// extern "C" to stop the name from being mangled
extern "C" __global__ void main_kernel() {
fake_main(0, nullptr);
}
)program";

View File

@@ -0,0 +1,78 @@
//===----------------------------------------------------------------------===//
//
// Part of libcu++, the C++ Standard Library for your entire system,
// 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) 2023 NVIDIA CORPORATION & AFFILIATES.
//
//===----------------------------------------------------------------------===//
#include <cuda.h>
#include <cuda_runtime.h>
#include <nvrtc.h>
#include "nvrtcc_common.h"
struct ExecutionConfig
{
RunConfig rc;
std::vector<std::string> builds;
};
static ExecutionConfig load_execution_config_from_file(const std::string& file)
{
std::vector<std::string> builds;
auto config = load_input_file(file);
std::regex config_regex("^ *- *'(.*gpu)'$");
fprintf(stderr, "Builds found: \r\n");
size_t line_begin = 0;
size_t line_end = config.find('\n');
while (line_end != std::string::npos)
{
// Match any line with a .gpu file
// std::regex cannot handle multiline, so we need to make sure that's not included
std::string line(config.begin() + line_begin, config.begin() + line_end);
std::smatch match;
std::regex_match(line, match, config_regex);
if (match.size())
{
builds.emplace_back(match[1].str());
}
line_begin = line_end + 1;
line_end = config.find('\n', line_begin);
}
return {parse_run_config(config), builds};
}
static void load_and_run_gpu_code(const std::string inputFile, const RunConfig& rc)
{
std::ifstream istr(inputFile, std::ios::binary);
assert(!!istr);
std::vector<char> code(std::istreambuf_iterator<char>{istr}, std::istreambuf_iterator<char>{});
istr.close();
CUdevice cuDevice;
CUcontext context;
CUmodule module;
CUfunction kernel;
CUDA_SAFE_CALL(cuInit(0));
CUDA_SAFE_CALL(cuDeviceGet(&cuDevice, 0));
CUDA_SAFE_CALL(cuDevicePrimaryCtxRetain(&context, cuDevice));
CUDA_SAFE_CALL(cuCtxSetCurrent(context));
CUDA_SAFE_CALL(cuModuleLoadDataEx(&module, code.data(), 0, 0, 0));
CUDA_SAFE_CALL(cuModuleGetFunction(&kernel, module, "main_kernel"));
CUDA_SAFE_CALL(cuLaunchKernel(kernel, 1, 1, 1, rc.threadCount, 1, 1, rc.shmemSize, nullptr, nullptr, 0));
CUDA_API_CALL(cudaGetLastError());
CUDA_API_CALL(cudaDeviceSynchronize());
CUDA_SAFE_CALL(cuModuleUnload(module));
}

View File

@@ -0,0 +1,8 @@
#pragma once
#if defined(_MSC_VER)
# define WINDOWS_STUFF
# include "platform.win.h"
#else
# include "platform.linux.h"
#endif

View File

@@ -0,0 +1,8 @@
#pragma once
#include <unistd.h>
static void platform_exec(char const* process, char** args, size_t)
{
execvp(process, args);
}

View File

@@ -0,0 +1,41 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <string>
#include <windows.h>
static void platform_exec(char const* process, char** args, size_t nargs)
{
std::string cl{};
STARTUPINFOA si{};
PROCESS_INFORMATION pi{};
si.cb = sizeof(si);
cl.append(process);
for (auto iter = args; iter < (args + nargs); iter++)
{
cl.append(" ");
cl.append(*iter);
}
printf("Running command: %s\r\n", cl.data());
bool exec_result =
CreateProcess(nullptr, (LPSTR) cl.data(), nullptr, nullptr, false, false, nullptr, nullptr, &si, &pi);
if (!exec_result)
{
printf("Launch error: %i", GetLastError());
}
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
ExitProcess(0);
}