[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:
1
cccl_upstream/libcudacxx/test/utils/nvidia/getsm/.gitignore
vendored
Normal file
1
cccl_upstream/libcudacxx/test/utils/nvidia/getsm/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
build
|
||||
@@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
project(getsm CUDA)
|
||||
|
||||
add_executable(getsm main.cu)
|
||||
|
||||
add_custom_target(get-sm ALL COMMAND getsm)
|
||||
25
cccl_upstream/libcudacxx/test/utils/nvidia/getsm/main.cu
Normal file
25
cccl_upstream/libcudacxx/test/utils/nvidia/getsm/main.cu
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define CUDA_CALL(...) \
|
||||
do \
|
||||
{ \
|
||||
cudaError_t err = __VA_ARGS__; \
|
||||
if (err != cudaSuccess) \
|
||||
{ \
|
||||
printf("CUDA ERROR: %s: %s\n", cudaGetErrorName(err), cudaGetErrorString(err)); \
|
||||
return err; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
int main()
|
||||
{
|
||||
int selected_device;
|
||||
CUDA_CALL(cudaGetDevice(&selected_device));
|
||||
|
||||
cudaDeviceProp device_prop;
|
||||
CUDA_CALL(cudaGetDeviceProperties(&device_prop, selected_device));
|
||||
|
||||
FILE* output = fopen("sm", "w");
|
||||
fprintf(output, "%d%d\n", device_prop.major, device_prop.minor);
|
||||
fclose(output);
|
||||
}
|
||||
@@ -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)
|
||||
55
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/README.md
Normal file
55
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/README.md
Normal 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
|
||||
```
|
||||
327
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/nvrtcc.cpp
Normal file
327
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/nvrtcc.cpp
Normal 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;
|
||||
}
|
||||
118
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/nvrtcc_build.h
Normal file
118
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/nvrtcc_build.h
Normal 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;
|
||||
}
|
||||
127
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/nvrtcc_common.h
Normal file
127
cccl_upstream/libcudacxx/test/utils/nvidia/nvrtc/nvrtcc_common.h
Normal 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";
|
||||
@@ -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));
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define WINDOWS_STUFF
|
||||
# include "platform.win.h"
|
||||
#else
|
||||
# include "platform.linux.h"
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
static void platform_exec(char const* process, char** args, size_t)
|
||||
{
|
||||
execvp(process, args);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user