CUDA 13.2 samples update (#432)

- Added Python samples for CUDA Python 1.0 release
- Renamed top-level `Samples` directory to `cpp` to accommodate Python samples.
This commit is contained in:
Dheemanth
2026-05-13 15:13:18 -07:00
committed by GitHub
parent a4526d5229
commit aeab82ff30
2072 changed files with 15077 additions and 1970 deletions

View File

@@ -0,0 +1,144 @@
# Copyright (c) 1993-2023, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
cmake_minimum_required(VERSION 3.10.0)
project(libnvvm-samples)
enable_testing()
#set_property(GLOBAL PROPERTY ALLOW_DUPLICATE_CUSTOM_TARGETS TRUE)
set(CMAKE_C_STANDARD 11)
# Initialize the CUDA Toolkit search using CUDA_HOME if the user specified it.
if (DEFINED ENV{CUDA_HOME})
if (${CMAKE_VERSION} VERSION_LESS 3.18.0)
set(CUDA_TOOLKIT_ROOT_DIR "$ENV{CUDA_HOME}" CACHE PATH "Path to CUDA Toolkit.")
else () # cmake 3.17 introduced FindCUDAToolkit
set(CUDAToolkit_ROOT "$ENV{CUDA_HOME}" CACHE PATH "Path to CUDA Toolkit.")
endif ()
endif ()
# Locate the root directory of the CUDA Toolkit.
# cmake 3.18 introduces the CUDAToolkit_LIBRARY_ROOT variable.
# This will update CUDA_HOME to a value discovered by find_package.
if (${CMAKE_VERSION} VERSION_LESS 3.18.0)
find_package(CUDA REQUIRED)
set(CUDA_HOME "${CUDA_TOOLKIT_ROOT_DIR}")
get_filename_component(CUDA_LIB_ROOT "${CUDA_cudart_static_LIBRARY}" DIRECTORY)
find_library(CUDA_LIB NAMES cuda cuda.lib PATHS "${CUDA_LIB_ROOT}" "${CUDA_LIB_ROOT}/stubs")
include_directories("${CUDA_INCLUDE_DIRS}")
find_library(CUDADEVRT_LIB cudadevrt PATHS "${CUDA_LIB_ROOT}" "${CUDA_LIB_ROOT}/stubs")
else () # Else, we're using cmake versions >= 3.18.
cmake_policy(SET CMP0074 NEW) # Use CUDAToolkit_ROOT as a cmake prefix.
find_package(CUDAToolkit REQUIRED)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CUDA_LIB "${CUDA_cuda_driver_LIBRARY}")
include_directories("${CUDAToolkit_INCLUDE_DIRS}")
get_filename_component(CUDA_HOME "${CUDAToolkit_BIN_DIR}" DIRECTORY)
find_library(CUDADEVRT_LIB cudadevrt PATH "${CUDAToolkit_LIBRARY_DIR}")
endif ()
message(STATUS "Using CUDA_HOME: ${CUDA_HOME}")
message(STATUS "Using CUDA_LIB: ${CUDA_LIB}")
if (("${CUDA_HOME}" STREQUAL "") OR ("${CUDA_LIB}" STREQUAL ""))
message(FATAL_ERROR "Failed to locate paths to the CUDA toolkit and nvcc.")
endif ()
# Find the nvvm directory in the toolkit.
find_file(LIBNVVM_HOME nvvm PATHS "$ENV{LIBNVVM_HOME}" "${CUDA_HOME}")
message(STATUS "Using LIBNVVM_HOME: ${LIBNVVM_HOME}")
# Find libNVVM and nvvm.h.
# (Linux: nvvm/lib64, windows: nvvm/lib/x64)
find_library(NVVM_LIB nvvm PATHS "${LIBNVVM_HOME}/lib64" "${LIBNVVM_HOME}/lib/x64")
find_file(NVVM_H nvvm.h PATH "${LIBNVVM_HOME}/include")
get_filename_component(NVVM_INCLUDE_DIR ${NVVM_H} DIRECTORY)
include_directories(${NVVM_INCLUDE_DIR})
message(STATUS "Using libnvvm header: ${NVVM_H}")
message(STATUS "Using libnvvm header path: ${NVVM_INCLUDE_DIR}")
message(STATUS "Using libnvvm library: ${NVVM_LIB}")
# Set the rpath to libnvvm.
find_path(LIBNVVM_RPATH lib lib64 PATHS "$ENV{LIBNVVM_HOME}" "${CUDA_HOME}")
if(DEFINED RUNTIME_LIBNVVM_PATH)
message(STATUS "Using RUNTIME_LIBNVVM_PATH: ${RUNTIME_LIBNVVM_PATH}")
set(LIBNVVM_RPATH ${RUNTIME_LIBNVVM_PATH})
else()
get_filename_component(LIBNVVM_RPATH ${NVVM_LIB} DIRECTORY)
endif()
set(CMAKE_INSTALL_RPATH "${LIBNVVM_RPATH}")
message(STATUS "Using rpath: ${CMAKE_INSTALL_RPATH}")
# On Windows, locate the nvvm.dll so we can install it later.
if (WIN32)
find_file(NVVM_DLL nvvm64_40_0.dll PATHS "${LIBNVVM_HOME}/bin" "${LIBNVVM_HOME}/bin/x64/")
if (NOT NVVM_DLL)
message(FATAL_ERROR "Found nvvm .h/.lib, but not .dll")
endif()
file(COPY ${NVVM_DLL} DESTINATION "${CMAKE_BINARY_DIR}")
endif (WIN32)
add_definitions(-DLIBDEVICE_MAJOR_VERSION=1)
add_definitions(-DLIBDEVICE_MINOR_VERSION=0)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/common/include")
# If you wish to build the cuda-c-linking sample and have the LLVM dependencies
# met, then set the ENABLE_CUDA_C_LINKING_SAMPLE variable. This variable can be
# set locally or by adding "-DENABLE_CUDA_C_LINKING_SAMPLE=1" to your cmake
# invocation. See the note about "cuda-c-linking" in README.md.
if (ENABLE_CUDA_C_LINKING_SAMPLE)
# Include the LLVM dev package which is required to build cuda-c-linking.
find_package(LLVM CONFIG PATHS "$ENV{LLVM_HOME}")
if (LLVM_FOUND)
add_subdirectory(cuda-c-linking)
else ()
message(STATUS "Skipping the build of the cuda-c-linking sample: Failed to locate the LLVM package.")
endif ()
else ()
message(STATUS "Skipping the build of the cuda-c-linking sample.")
endif ()
# Samples
add_subdirectory(cuda-shared-memory)
add_subdirectory(device-side-launch)
add_subdirectory(simple)
add_subdirectory(syscalls)
add_subdirectory(ptxgen)
add_subdirectory(uvmlite)
# Include InstallSamples to set CUDA_SAMPLES_INSTALL_DIR
# libNVVM samples will use this variable for installation
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake")
include(InstallSamples)
# Install nvvm DLL to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin (for standalone libNVVM build)
if (WIN32 AND NVVM_DLL)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(FILES ${NVVM_DLL} DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
else()
install(FILES ${NVVM_DLL} DESTINATION bin)
endif()
endif()

100
cpp/7_libNVVM/README.md Normal file
View File

@@ -0,0 +1,100 @@
libNVVM and NVVM IR Samples
===========================
Introduction
------------
The following samples illustrate the use of libNVVM and NVVM IR. Running and
testing these samples requires an NVIDIA driver compatible with the CUDA
Toolkit being used for compilation.
- cuda-shared-memory - A directory containing NVVM IR programs that demonstrate
CUDA 'shared' memory usage.
- cuda-c-linking - Builds an NVVM IR program using the LLVM IR build APIs. It
will link the generated PTX with a PTX generated by nvcc, and launch the
linked program on GPU using CUDA driver APIs.
- device-side-launch - Demonstrates launching a kernel within a kernel (CUDA
dynamic parallelism).
- ptxgen -A standalone NVVM IR program to PTX compiler. It will link the
libDevice library with the input NVVM IR program, verify the IR for
conformance to the NVVM IR specification, and then generate PTX.
- simple - Reads in a NVVM IR program from a file, compiles it to PTX, and
launches the program on GPU using CUDA driver APIs.
- syscalls - A directory containing NVVM IR programs that demonstrate the use of
device side malloc/free/vprintf functions.
- uvmlite - Demonstrates the use of unified virtual memory.
Steps for Building the Samples
------------------------------
The following environment variables can be used to control the build
process for the samples. If not specified, CUDA_HOME will be derived by
looking for nvcc in your PATH. CMake will try to automatically
identify all of these paths.
- CUDA_HOME : The directory where the CUDA toolkit is installed,
e.g., /usr/local/cuda.
- LIBNVVM_HOME : The directory where libNVVM components are located.
e.g., $CUDA_HOME/nvvm.
- LLVM_HOME : This should point to the install directory if you built llvm
locally. This is only required for building the cuda-c-linking
sample (see the cuda-c-linking note below).
After setting the environment variables and adding the path to the CMake tool
via the PATH environment variable, sample script utils/build.sh (for Linux) or
utils/build.bat (for Windows) may be executed. This script will use build
directory "build" to build the samples, and then install them in the "install"
directory.
If you chose to build using Visual Studio and its integrated CMake support,
then simply run "Build All" and "Install libnvvm-samples." The installed
samples will be copied to out/install/\<build architecture\>/bin/
Alternatively, we provide a Makefile that will automatically build these
samples on Linux as part of the toplevel cuda-samples build. Windows users
should build manually via utils/built.bat or Visual Studio's CMake integration.
To build and run the libNVVM samples with CUDA Toolkits 13.0 or newer, user-mode
driver 580 and newer, old kernel-mode drivers (version 550 or earlier),you must
update the CMake invocation in utils/build.sh or build.bat by adding
`CMAKE_PREFIX_PATH` with a stubs path as follows:
```
-DCMAKE_PREFIX_PATH=/usr/local/cuda/lib64/stubs/
```
A Note About the cuda-c-linking Sample
--------------------------------------
This sample requires a development package (or locally-built) LLVM library
between versions 7 to 14 inclusive. LLVM 15 defaults to using opaque pointers,
which are not supported in libNVVM for pre-Blackwell architectures.
The LLVM_HOME environment variable is required for users who wish to build the
cuda-c-linking sample and have a locally built copy of LLVM that they wish to
use. That sample requires the development package of LLVM with the LLVM header
files and libraries.
If the LLVM dependencies are met, the user can enable the building of this
sample by setting the CMake variable "ENABLE_CUDA_C_LINKING_SAMPLE" from either
the command line invocation of CMake or by modifying the CMakeLists.txt in this
directory.
Windows users should download LLVM 14 sources from llvm.org and build+install
LLVM locally. Using the llvm.org provided Windows installer lacks some of
the required components the cuda-c-linking sample depends on.
For Ubuntu users, the "llvm-dev" package contains the LLVM headers and libraries
this sample requires, the user should not have to explicitly define an LLVM_HOME
in this case.
Windows users will want to build this sample using the same CMake build mode
as they built LLVM with. For instance if they built LLVM in Release mode,
then this sample should also be built in Release mode. The utils/build.bat can
be updated to reflect this: Add "-DCMAKE_BUILD_TYPE=Release" to the CMake
invocation.

View File

@@ -0,0 +1,122 @@
// Copyright (c) 1993-2023, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef DDSWRITER_H
#define DDSWRITER_H
#include <fstream>
typedef int DWORD;
/// DDS File Structures
struct DDSPixelFormat
{
DWORD Size;
DWORD Flags;
DWORD FourCC;
DWORD RGBBitCount;
DWORD RBitMask;
DWORD GBitMask;
DWORD BBitMask;
DWORD ABitMask;
};
struct DDSHeader
{
DWORD Size;
DWORD Flags;
DWORD Height;
DWORD Width;
DWORD PitchOrLinearSize;
DWORD Depth;
DWORD MipMapCount;
DWORD Reserved1[11];
DDSPixelFormat PixelFormat;
DWORD Caps;
DWORD Caps2;
DWORD Caps3;
DWORD Caps4;
DWORD Reserved2;
};
#define DDPF_ALPHAPIXELS 0x1
#define DDPF_RGB 0x40
#define DDSD_CAPS 0x1
#define DDSD_HEIGHT 0x2
#define DDSD_WIDTH 0x4
#define DDSD_PIXELFORMAT 0x1000
#define DDSCAPS_TEXTURE 0x1000
/// WriteDDS - Writes image data to a .dds file. The data is expected to be
/// (R, G, B, A) tuples of 32-bit floating-point data of dimensions
/// width X height. The floating-point data should be normalized to [0,1] and
/// will be scaled to a [0, 255] 8-bit value.
void writeDDS(const char *filename, const float *data, unsigned width, unsigned height)
{
// Write out the result as a .dds file
// This is a quick and dirty DDS writer
DDSHeader header;
memset(&header, 0, sizeof(DDSHeader));
header.Size = sizeof(DDSHeader);
header.Flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
header.Height = height;
header.Width = width;
header.PitchOrLinearSize = width * 4;
header.Caps = DDSCAPS_TEXTURE;
header.PixelFormat.Size = sizeof(DDSPixelFormat);
header.PixelFormat.Flags = DDPF_RGB | DDPF_ALPHAPIXELS;
header.PixelFormat.RGBBitCount = 32;
header.PixelFormat.ABitMask = 0xFF000000;
header.PixelFormat.RBitMask = 0x00FF0000;
header.PixelFormat.GBitMask = 0x0000FF00;
header.PixelFormat.BBitMask = 0x000000FF;
std::ofstream str(filename, std::ios::binary);
int magic = 0x20534444;
str.write((const char *)&magic, 4);
str.write((const char *)&header, sizeof(header));
for (unsigned j = 0; j < height; ++j) {
for (unsigned i = 0; i < width; ++i) {
unsigned char r, g, b, a;
r = (unsigned char)(data[j * width * 4 + i * 4 + 0] * 255.0);
g = (unsigned char)(data[j * width * 4 + i * 4 + 1] * 255.0);
b = (unsigned char)(data[j * width * 4 + i * 4 + 2] * 255.0);
a = (unsigned char)(data[j * width * 4 + i * 4 + 3] * 255.0);
str.write((const char *)&b, 1);
str.write((const char *)&g, 1);
str.write((const char *)&r, 1);
str.write((const char *)&a, 1);
}
}
str.close();
}
#endif

View File

@@ -0,0 +1,98 @@
# Copyright (c) 1993-2025, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
message(STATUS "Found LLVM Version ${LLVM_PACKAGE_VERSION}")
if (LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL "15" OR
LLVM_PACKAGE_VERSION VERSION_LESS "7")
message(STATUS "The cuda-c-linking sample is expected to build with "
"LLVM development libraries v7 to v14, opaque pointers are "
"not supported in libNVVM for pre-Blackwell architectures.")
return()
endif ()
add_executable(cuda-c-linking cuda-c-linking.cpp)
add_test(NAME cuda-c-linking
COMMAND cuda-c-linking
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
target_link_libraries(cuda-c-linking ${NVVM_LIB} ${CUDA_LIB})
# See https://llvm.org/docs/CMake.html#developing-llvm-passes-out-of-source
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS_LIST})
include_directories(${LLVM_INCLUDE_DIRS})
llvm_map_components_to_libnames(llvm_libs core support)
target_link_libraries(cuda-c-linking ${llvm_libs})
if (UNIX)
set_target_properties(cuda-c-linking PROPERTIES
LINK_FLAGS "-Wl,-rpath,${LIBNVVM_RPATH}")
endif()
##############################
### Math Lib
##############################
### The math library can be built to support a number of GPU platforms using the
### common compute_75 architecture.
### For a collection of specific architecutres, the compiler options
### "-gencode=compute_XX,code=sm_XX"... can be used multiple times in the same
### nvcc invocation.
### The result is bundled into a system library using the 'nvcc -lib' feature.
### >> nvcc -m64 -arch=compute_75 -dc math-funcs.cu -o math-funcs64.o
### >> nvcc -m64 -lib math-funcs64.o -o libmathfuncs64.a
enable_language(CUDA)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -dc") # -dc: Build for device only.
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.18.0)
set(CMAKE_CUDA_ARCHITECTURES 75)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Wno-deprecated-gpu-targets")
endif ()
if (WIN32)
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0)
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0)
set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0)
endif ()
add_library(mathfuncs64 STATIC math-funcs.cu)
set_target_properties(mathfuncs64 PROPERTIES PREFIX "lib"
OUTPUT_NAME "mathfuncs64"
SUFFIX ".a" CUDA_SEPERABLE_COMPILATION ON)
# Install to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin (for standalone libNVVM build)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(TARGETS cuda-c-linking mathfuncs64 DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
else()
install(TARGETS cuda-c-linking mathfuncs64 DESTINATION bin)
endif()
if (WIN32)
add_custom_command(
TARGET cuda-c-linking
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_BINARY_DIR}/nvvm64_40_0.dll" "$<TARGET_FILE_DIR:cuda-c-linking>"
)
endif ()

View File

@@ -0,0 +1,49 @@
Introduction
============
This sample demonstrates linking a libnvvm-generated module with an existing
CUDA C library. The LLVM C++ API is used to generate an LLVM IR module that
conforms to the NVVM IR specification and contains a call to an externally-
defined function, and this module is compiled to PTX with libnvvm. The JIT
linker (part of the CUDA Driver API) is then used to assemble the PTX and link
it with the math library, creating a linked CUBIN image. This image is then
executed on the first CUDA device on the system.
Files
-----
- cuda-c-linking.cpp - Main source file demonstrating the generated of a
PTX file using libnvvm and linking it with a CUDA C
device library
- math-funcs - CUDA C device library source file
- CMakeLists.txt - CMake build script
Building
--------
This sample is optionally built as part of the libnvvm samples from the CUDA
samples tree. Please see the README file at the root of the libnvvm samples
for build instructions.
Usage
-----
Once built, the sample can be executed by running the "cuda-c-linking" binary.
Linux:
$ cd $SAMPLES_INSTALL_DIR
$ ./cuda-c-linking
Windows:
$ cd %SAMPLES_INSTALL_DIR%
$ cuda-c-linking.exe
For inspection purposes, the following command-line options are available:
- -save-ptx - Write generated PTX kernel to cuda-c-linking.kernel.ptx
- -save-ir - Write generated LLVM IR to cuda-c-linking.kernel.ll
- -save-cubin - Write linked CUBIN image to cuda-c-linking.linked.cubin

View File

@@ -0,0 +1,325 @@
// Copyright (c) 1993-2025, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#include <cuda.h>
#include <llvm/ADT/StringExtras.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/Program.h>
#include <llvm/Support/raw_ostream.h>
#include <memory>
#include <nvvm.h>
#include <string>
#include "DDSWriter.h"
static_assert(sizeof(void *) == 8, "Only 64bit targets are supported.");
using namespace llvm;
static cl::opt<bool> SaveCubin("save-cubin", cl::desc("Write linker cubin to disk"), cl::init(false));
static cl::opt<bool> SaveIR("save-ir", cl::desc("Write LLVM IR to disk"), cl::init(false));
static cl::opt<bool> SavePTX("save-ptx", cl::desc("Write PTX to disk"), cl::init(false));
// Width and height of the output image.
const unsigned width = 1024;
const unsigned height = 512;
// If 'err' is non-zero, emit an error message and exit.
#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)
static void __checkCudaErrors(CUresult err, const char *filename, int line)
{
assert(filename);
if (CUDA_SUCCESS != err) {
const char *ename = NULL;
const CUresult res = cuGetErrorName(err, &ename);
fprintf(stderr,
"CUDA API Error %04d: \"%s\" from file <%s>, "
"line %i.\n",
err,
((CUDA_SUCCESS == res) ? ename : "Unknown"),
filename,
line);
exit(err);
}
}
// Verify that the NVVM result code is success, or terminate otherwise.
void checkNVVMCall(nvvmResult res)
{
if (res != NVVM_SUCCESS) {
errs() << "libnvvm call failed\n";
exit(res);
}
}
/// generateModule - Generate and LLVM IR module that calls an
/// externally-defined function
std::unique_ptr<Module> generateModule(LLVMContext &context)
{
// Create the module and setup the layout and triple.
auto mod = std::make_unique<Module>("nvvm-module", context);
mod->setDataLayout("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-"
"f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:"
"64");
mod->setTargetTriple("nvptx64-nvidia-cuda");
// Get pointers to some commonly-used types.
Type *voidTy = Type::getVoidTy(context);
Type *floatTy = Type::getFloatTy(context);
Type *i32Ty = Type::getInt32Ty(context);
Type *floatGenericPtrTy = PointerType::get(floatTy, /* address space */ 0);
// void @mandelbrot(float*)
Type *mandelbrotParamTys[] = {floatGenericPtrTy};
FunctionType *mandelbrotTy = FunctionType::get(voidTy, mandelbrotParamTys, false);
FunctionCallee mandelbrotFunc = mod->getOrInsertFunction("mandelbrot", mandelbrotTy);
// Kernel argument types.
Type *paramTys[] = {floatGenericPtrTy};
// Kernel function type.
FunctionType *funcTy = FunctionType::get(voidTy, paramTys, false);
// Kernel function.
Function *func = Function::Create(funcTy, GlobalValue::ExternalLinkage, "kernel", *mod);
func->arg_begin()->setName("ptr");
// 'entry' basic block in kernel function.
BasicBlock *entry = BasicBlock::Create(context, "entry", func);
// Build the entry block.
IRBuilder<> builder(entry);
builder.CreateCall(mandelbrotFunc, func->arg_begin());
builder.CreateRetVoid();
// Create kernel metadata.
Metadata *mdVals[] = {ValueAsMetadata::get(func),
MDString::get(context, "kernel"),
ConstantAsMetadata::get(ConstantInt::getTrue(context))};
MDNode *kernelMD = MDNode::get(context, mdVals);
NamedMDNode *nvvmAnnot = mod->getOrInsertNamedMetadata("nvvm.annotations");
nvvmAnnot->addOperand(kernelMD);
// Set the NVVM IR version to 2.0.
auto *two = ConstantInt::get(Type::getInt32Ty(context), 2);
auto *zero = ConstantInt::get(Type::getInt32Ty(context), 0);
auto *versionMD = MDNode::get(context, {ConstantAsMetadata::get(two), ConstantAsMetadata::get(zero)});
NamedMDNode *nvvmIRVersion = mod->getOrInsertNamedMetadata("nvvmir.version");
nvvmIRVersion->addOperand(versionMD);
return mod;
}
// Use libNVVM to compile an NVVM IR module to PTX.
std::string generatePtx(const std::string &module, int devMajor, int devMinor, const char *moduleName)
{
assert(moduleName);
// libNVVM initialization.
nvvmProgram compileUnit;
checkNVVMCall(nvvmCreateProgram(&compileUnit));
// Create a libNVVM compilation unit from the NVVM IR.
checkNVVMCall(nvvmAddModuleToProgram(compileUnit, module.c_str(), module.size(), moduleName));
std::string computeArg = "-arch=compute_";
computeArg += utostr(devMajor);
computeArg += utostr(devMinor);
// Compile the NVVM IR into PTX.
const char *options[] = {computeArg.c_str()};
nvvmResult res = nvvmCompileProgram(compileUnit, 1, options);
if (res != NVVM_SUCCESS) {
errs() << "nvvmCompileProgram failed!\n";
size_t logSize;
nvvmGetProgramLogSize(compileUnit, &logSize);
char *msg = new char[logSize];
nvvmGetProgramLog(compileUnit, msg);
errs() << msg << "\n";
delete[] msg;
exit(EXIT_FAILURE);
}
// Get the result PTX size and source.
size_t ptxSize = 0;
checkNVVMCall(nvvmGetCompiledResultSize(compileUnit, &ptxSize));
char *ptx = new char[ptxSize];
checkNVVMCall(nvvmGetCompiledResult(compileUnit, ptx));
// Clean-up libNVVM.
checkNVVMCall(nvvmDestroyProgram(&compileUnit));
return std::string(ptx);
}
int main(int argc, char **argv)
{
cl::ParseCommandLineOptions(argc, argv, "cuda-c-linking");
// Locate the pre-built library.
std::string libpath0 = sys::fs::getMainExecutable(argv[0], (void *)main);
SmallString<256> libpath(libpath0);
const char *mathlibFile = "libmathfuncs64.a";
sys::path::remove_filename(libpath);
sys::path::append(libpath, mathlibFile);
if (!sys::fs::exists(libpath.c_str())) {
errs() << "Unable to locate math library, expected at " << libpath << '\n';
return EXIT_FAILURE;
}
outs() << "Using math library: " << libpath.str() << "\n";
// Initialize CUDA and obtain device 0.
checkCudaErrors(cuInit(0));
int nDevices;
checkCudaErrors(cuDeviceGetCount(&nDevices));
if (nDevices == 0) {
errs() << "Failed to locate any CUDA compute devices.\n";
exit(EXIT_FAILURE);
}
CUdevice device;
checkCudaErrors(cuDeviceGet(&device, 0));
char name[128];
checkCudaErrors(cuDeviceGetName(name, 128, device));
outs() << "Using CUDA Device [0]: " << name << "\n";
int devMajor = 0, devMinor = 0;
checkCudaErrors(cuDeviceGetAttribute(&devMajor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device));
checkCudaErrors(cuDeviceGetAttribute(&devMinor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, device));
outs() << "Device Compute Capability: " << devMajor << "." << devMinor << "\n";
if (devMajor < 7 && devMinor < 5) {
errs() << "ERROR: Device 0 is not sm_75 or later.\n";
return 1;
}
// Generate the IR module
LLVMContext ctx;
std::string moduleStr;
auto module = generateModule(ctx);
if (SaveIR) {
std::error_code err;
raw_fd_ostream out("cuda-c-linking.kernel.ll", err);
out << *(module.get());
}
// Write the module to a string.
{
llvm::raw_string_ostream str(moduleStr);
str << *module.get();
}
// Generate PTX.
std::string ptx = generatePtx(moduleStr, devMajor, devMinor, module->getModuleIdentifier().c_str());
if (SavePTX) {
std::error_code err;
raw_fd_ostream out("cuda-c-linking.kernel.ptx", err);
out << ptx;
}
// Create the CUDA context.
CUcontext context;
checkCudaErrors(cuCtxCreate(&context, NULL, 0, device));
// Create a JIT linker and generate the result CUBIN.
CUlinkState linker;
char linkerInfo[1024]{};
char linkerErrors[1024]{};
CUjit_option linkerOptions[] = {CU_JIT_INFO_LOG_BUFFER,
CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES,
CU_JIT_ERROR_LOG_BUFFER,
CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES,
CU_JIT_LOG_VERBOSE};
void *linkerOptionValues[] = {linkerInfo,
reinterpret_cast<void *>(1024),
linkerErrors,
reinterpret_cast<void *>(1024),
reinterpret_cast<void *>(1)};
checkCudaErrors(cuLinkCreate(5, linkerOptions, linkerOptionValues, &linker));
checkCudaErrors(
cuLinkAddData(linker, CU_JIT_INPUT_PTX, (void *)ptx.c_str(), ptx.size(), "<compiled-ptx>", 0, NULL, NULL));
checkCudaErrors(cuLinkAddFile(linker, CU_JIT_INPUT_LIBRARY, libpath.c_str(), 0, NULL, NULL));
void *cubin;
size_t cubinSize;
checkCudaErrors(cuLinkComplete(linker, &cubin, &cubinSize));
outs() << "Linker Log:\n" << linkerInfo << "\n" << linkerErrors << "\n";
if (SaveCubin) {
std::error_code err;
raw_fd_ostream out("cuda-c-linking.linked.cubin", err, sys::fs::OF_None);
out.write(reinterpret_cast<char *>(cubin), cubinSize);
}
// Create a module and load the cubin into it.
CUmodule cudaModule;
checkCudaErrors(cuModuleLoadDataEx(&cudaModule, cubin, 0, 0, 0));
// Now that the CUBIN is loaded, we can release the linker.
checkCudaErrors(cuLinkDestroy(linker));
// Get kernel function.
CUfunction function;
checkCudaErrors(cuModuleGetFunction(&function, cudaModule, "kernel"));
// Device data.
CUdeviceptr devBuffer;
checkCudaErrors(cuMemAlloc(&devBuffer, sizeof(float) * width * height * 4));
float *data = new float[width * height * 4];
// Each thread will generate one pixel, and we'll subdivide the problem into
// 16x16 chunks.
const unsigned blockSizeX = 16;
const unsigned blockSizeY = 16;
const unsigned blockSizeZ = 1;
const unsigned gridSizeX = width / 16;
const unsigned gridSizeY = height / 16;
const unsigned gridSizeZ = 1;
// Execute the kernel.
outs() << "Launching kernel\n";
void *params[] = {&devBuffer};
checkCudaErrors(cuLaunchKernel(
function, gridSizeX, gridSizeY, gridSizeZ, blockSizeX, blockSizeY, blockSizeZ, 0, NULL, params, NULL));
// Retrieve the result data from the device.
checkCudaErrors(cuMemcpyDtoH(&data[0], devBuffer, sizeof(float) * width * height * 4));
writeDDS("mandelbrot.dds", data, width, height);
outs() << "Output saved to mandelbrot.dds\n";
// Cleanup.
delete[] data;
checkCudaErrors(cuMemFree(devBuffer));
checkCudaErrors(cuModuleUnload(cudaModule));
checkCudaErrors(cuCtxDestroy(context));
return 0;
}

View File

@@ -0,0 +1,84 @@
// Copyright (c) 1993-2023, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Simple implementation of Mandelbrot set from Wikipedia
// http://en.wikipedia.org/wiki/Mandelbrot_set
// Note that this kernel is meant to be a simple, straight-forward
// implementation, and so may not represent optimized GPU code.
extern "C" __device__ void mandelbrot(float *Data)
{
// Which pixel am I?
unsigned DataX = blockIdx.x * blockDim.x + threadIdx.x;
unsigned DataY = blockIdx.y * blockDim.y + threadIdx.y;
unsigned Width = gridDim.x * blockDim.x;
unsigned Height = gridDim.y * blockDim.y;
float R, G, B, A;
// Scale coordinates to (-2.5, 1) and (-1, 1)
float NormX = (float)DataX / (float)Width;
NormX *= 3.5f;
NormX -= 2.5f;
float NormY = (float)DataY / (float)Height;
NormY *= 2.0f;
NormY -= 1.0f;
float X0 = NormX;
float Y0 = NormY;
float X = 0.0f;
float Y = 0.0f;
unsigned Iter = 0;
unsigned MaxIter = 1000;
// Iterate
while (X * X + Y * Y < 4.0f && Iter < MaxIter) {
float XTemp = X * X - Y * Y + X0;
Y = 2.0f * X * Y + Y0;
X = XTemp;
Iter++;
}
unsigned ColorG = Iter % 50;
unsigned ColorB = Iter % 25;
R = 0.0f;
G = (float)ColorG / 50.0f;
B = (float)ColorB / 25.0f;
A = 1.0f;
Data[DataY * Width * 4 + DataX * 4 + 0] = R;
Data[DataY * Width * 4 + DataX * 4 + 1] = G;
Data[DataY * Width * 4 + DataX * 4 + 2] = B;
Data[DataY * Width * 4 + DataX * 4 + 3] = A;
}

View File

@@ -0,0 +1,57 @@
# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
add_custom_target(clobber)
add_custom_target(testrun)
add_test(NAME test-cuda-shared-memory-shared_memory
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/ptxgen" "${CMAKE_CURRENT_SOURCE_DIR}/shared_memory.ll"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
add_test(NAME test-cuda-shared-memory-extern_shared_memory
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/ptxgen" "${CMAKE_CURRENT_SOURCE_DIR}/extern_shared_memory.ll"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
set_tests_properties(test-cuda-shared-memory-shared_memory
test-cuda-shared-memory-extern_shared_memory
PROPERTIES FIXTURES_REQUIRED PTXGENTEST)
# Install to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin/cuda-shared-memory (for standalone libNVVM build)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(FILES shared_memory.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR}/cuda-shared-memory)
install(FILES extern_shared_memory.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR}/cuda-shared-memory)
else()
install(FILES shared_memory.ll DESTINATION bin/cuda-shared-memory)
install(FILES extern_shared_memory.ll DESTINATION bin/cuda-shared-memory)
endif()
file(COPY shared_memory.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY extern_shared_memory.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
# Copy the .ll files to the folder of executable file for full testing
file(COPY shared_memory.ll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/cuda-shared-memory)
file(COPY extern_shared_memory.ll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/cuda-shared-memory)

View File

@@ -0,0 +1,58 @@
; Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
; This NVVM IR program shows how to use unsize external shared memory.
; What it does is similar to the following CUDA C code.
;
; extern __shared__ int a[];
;
; __global__ void foo()
; {
; int *p1 = a;
; float *p2 = (float *)&a[10];
;
; *p1 = 10;
; *p2 = 21.0f;
; }
;
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
@a = external addrspace(3) global [0 x i32]
define void @foo() {
entry:
store i32 10, i32 addrspace(3)* getelementptr inbounds ([0 x i32], [0 x i32] addrspace(3)* @a, i64 0, i64 0), align 4
store float 2.100000e+01, float addrspace(3)* bitcast (i32 addrspace(3)* getelementptr inbounds ([0 x i32], [0 x i32] addrspace(3)* @a, i64 0, i64 10) to float addrspace(3)*), align 4
ret void
}
!nvvm.annotations = !{!0}
!0 = !{void ()* @foo, !"kernel", i32 1}
!nvvmir.version = !{!1}
!1 = !{i32 2, i32 0}

View File

@@ -0,0 +1,58 @@
; Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
; This NVVM IR program shows how to use shared memory.
; What it does is similar to the following CUDA C code.
;
;
; __shared__ int a;
; __shared__ int b[10];
;
; __global__ void foo()
; {
; a = 1;
; b[5] = 2;
; }
;
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
@a = internal addrspace(3) global i32 undef, align 4
@b = internal addrspace(3) global [10 x i32] undef, align 4
define void @foo() {
entry:
store i32 1, i32 addrspace(3)* @a, align 4
store i32 2, i32 addrspace(3)* getelementptr inbounds ([10 x i32], [10 x i32] addrspace(3)* @b, i64 0, i64 5), align 4
ret void
}
!nvvm.annotations = !{!0}
!0 = !{void ()* @foo, !"kernel", i32 1}
!nvvmir.version = !{!1}
!1 = !{i32 2, i32 0}

View File

@@ -0,0 +1,69 @@
# Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(CMAKE_INSTALL_RPATH ${LIBNVVM_HOME})
set(CMAKE_INCLUDE_CURRENT_DIR YES)
set_property(SOURCE dsl.c
PROPERTY COMPILE_DEFINITIONS LIBCUDADEVRT="${CUDADEVRT_LIB}")
add_executable(dsl dsl.c)
add_test(NAME device-side-launch COMMAND dsl
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
target_link_libraries(dsl ${NVVM_LIB} ${CUDA_LIB})
if (WIN32)
set_target_properties(dsl PROPERTIES COMPILE_FLAGS "/wd4996")
else (WIN32)
set_target_properties(dsl PROPERTIES LINK_FLAGS "-Wl,-rpath,${LIBNVVM_RPATH}")
endif (WIN32)
# Install to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin (for standalone libNVVM build)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(TARGETS dsl DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
install(FILES dsl-gpu64.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
else()
install(TARGETS dsl DESTINATION bin)
install(FILES dsl-gpu64.ll DESTINATION bin)
endif()
# 'dsl' will load dsl-gpu64.ll from the current working directory. That
# .ll file should be present where tests are executed (the build directory).
add_custom_command(
TARGET dsl
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/dsl-gpu64.ll" "$<TARGET_FILE_DIR:dsl>"
)
if (WIN32)
add_custom_command(
TARGET dsl
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_BINARY_DIR}/nvvm64_40_0.dll" "$<TARGET_FILE_DIR:dsl>"
)
endif ()

View File

@@ -0,0 +1,80 @@
Device-Side Launch From NVVM IR
===============================
This document is for the programming language and compiler implementers who
target NVVM IR and plan to support Dynamic Parallelism in their langauge.
It provides the low-level details related to supporting kernel launches at
the NVVM IR level.
This document assumes the CUDA runtime is used. The method for device-side
launch using the OpenCL runtime is similar but different.
This document is written after the "Device-Side Launch from PTX"
section from CUDA C Programming Guide
(http://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-side-launch-from-ptx).
Kernel Launch APIs
------------------
Device-side kernel launches can be implemented using the following two APIs
in an NVVM IR program: cudaLaunchDevice() and cudaGetParameterBuffer().
cudaLaunchDevice() launches the specified kernel with the parameter buffer
that is obtained by calling cudaGetParameterBuffer() and filled with the
parameters to the launched kernel. The parameter buffer can be NULL, i.e.,
no need to invoke cudaGetParameterBuffer(), if the launched kernel does not
take any parameters.
cudaLaunchDevice
----------------
At the NVVM IR level, cudaLaunchDeviceV2() needs to be declared in the
form shown below before it is used.
; NVVM IR level declaration of cudaLaunchDeviceV2
declare i32 @cudaLaunchDeviceV2(i8*, %struct.CUstream_st*)
The CUDA-level declaration below is mapped to one of the aftorementioned NVVM
IR level declarations and is found in the system header file
cuda_device_runtime_api.h. The function is defined in the cudadevrt system
library, which must be linked with a program in order to use device-side
kernel launch functionality.
extern __device__ __cudart_builtin__ cudaError_t CUDARTAPI
cudaLaunchDeviceV2(void *parameterBuffer, cudaStream_t stream);
The first parameter is a pointer to the parameter buffer, and the
second parameter is the stream associated with the launch. The layout
of the parameter buffer is explained in "Parameter Buffer Layout"
below.
cudaGetParameterBuffer
----------------------
cudaGetParameterBufferV2() needs to be declared at the NVVM IR level
before it's used. The NVVM IR level declaration must be in the form
given below:
; NVVM IR level declaration of cudaGetParameterBufferV2
declare i8* @cudaGetParameterBufferV2(i8*, %struct.dim3, %struct.dim3, i32)
The following CUDA-level declaration of cudaGetParameterBufferV2() is
mapped to the aforementioned NVVM IR level declaration:
extern __device__ __cudart_builtin__ void * CUDARTAPI
cudaGetParameterBufferV2(void *func, dim3 gridDimension,
dim3 blockDimension,
unsigned int sharedMemSize);
The first parameter is a pointer to the kernel to be launched, and the
other parameters specify the launch configuration, i.e., as grid
dimension, block dimension, and shared memory size.
Parameter Buffer Layout
-----------------------
Parameter reordering in the parameter buffer is prohibited, and each individual
parameter placed in the parameter buffer is required to be aligned. That is,
each parameter must be placed at the n-th byte in the parameter buffer, where n
is the smallest multiple of the parameter size that is greater than the offset
of the last byte taken by the preceding parameter. The maximum size of the
parameter buffer is 4KB.

View File

@@ -0,0 +1,103 @@
; Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
; This NVVM IR program shows how to call cudaGetParameterBuffer and cudaLaunchDevice functions.
; What it does is similar to the following CUDA C code.
;
; __global__ void kernel(int depth)
; {
; if (threadIdx.x == 0) {
; printf("kernel launched, depth = %d\n", depth);
; }
;
; __syncthreads();
;
; if (++depth > 3)
; return;
;
; kernel<<<1,1>>>(depth);
;
; }
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
%struct.dim3 = type { i32, i32, i32 }
%struct.CUstream_st = type opaque
@"$str" = private addrspace(1) constant [29 x i8] c"kernel launched, depth = %d\0A\00"
define void @kernel(i32 %depth) {
entry:
%tmp31 = alloca i32, align 8
%gen2local = addrspacecast i32* %tmp31 to i32 addrspace(5)*
%tmp31.sub = bitcast i32* %tmp31 to i8*
%0 = call i32 @llvm.nvvm.read.ptx.sreg.tid.x()
%cmp = icmp eq i32 %0, 0
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
%1 = addrspacecast i8 addrspace(1)* getelementptr inbounds ([29 x i8], [29 x i8] addrspace(1)* @"$str", i64 0, i64 0) to i8 addrspace(0)*
store i32 %depth, i32 addrspace(5)* %gen2local, align 8
%call = call i32 @vprintf(i8* %1, i8* %tmp31.sub)
br label %if.end
if.end: ; preds = %if.then, %entry
call void @llvm.cuda.syncthreads()
%inc = add nsw i32 %depth, 1
%cmp5 = icmp sgt i32 %inc, 3
br i1 %cmp5, label %return, label %if.end7
if.end7: ; preds = %if.end
%call15 = call i8* @cudaGetParameterBufferV2(i8* bitcast (void (i32)* @kernel to i8*), %struct.dim3 { i32 1, i32 1, i32 1 }, %struct.dim3 { i32 1, i32 1, i32 1 }, i32 0)
%tobool = icmp eq i8* %call15, null
br i1 %tobool, label %return, label %cond.true
cond.true: ; preds = %if.end7
%conv = bitcast i8* %call15 to i32*
store i32 %inc, i32* %conv, align 4
%call20 = call i32 @cudaLaunchDeviceV2(i8* %call15, %struct.CUstream_st* null)
br label %return
return: ; preds = %cond.true, %if.end7, %if.end
ret void
}
declare i32 @llvm.nvvm.read.ptx.sreg.tid.x() nounwind readnone
declare i32 @vprintf(i8* nocapture, i8*) nounwind
declare void @llvm.cuda.syncthreads() nounwind
declare i8* @cudaGetParameterBufferV2(i8*, %struct.dim3, %struct.dim3, i32)
declare i32 @cudaLaunchDeviceV2(i8*, %struct.CUstream_st*)
!nvvm.annotations = !{!0}
!0 = !{void (i32)* @kernel, !"kernel", i32 1}
!nvvmir.version = !{!1}
!1 = !{i32 2, i32 0}

View File

@@ -0,0 +1,260 @@
// Copyright (c) 2014-2025, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <builtin_types.h>
#include <cuda.h>
#include <math.h>
#include <nvvm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
// The full path to the libcudadevrt.a is determined by the build environment.
const char *_libCudaDevRt = LIBCUDADEVRT;
static const char *getLibCudaDevRtName(void)
{
// Check that the library exists.
FILE *fh = fopen(_libCudaDevRt, "rb");
if (fh == NULL) {
fprintf(stderr, "Error locating the libcudadevrt runtime: %s\n", _libCudaDevRt);
exit(EXIT_FAILURE);
}
fclose(fh);
return _libCudaDevRt;
}
// If 'err' is non-zero, emit an error message and exit.
#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)
static void __checkCudaErrors(CUresult err, const char *filename, int line)
{
assert(filename);
if (CUDA_SUCCESS != err) {
const char *ename = NULL;
const CUresult res = cuGetErrorName(err, &ename);
fprintf(stderr,
"CUDA API Error %04d: \"%s\" from file <%s>, "
"line %i.\n",
err,
((CUDA_SUCCESS == res) ? ename : "Unknown"),
filename,
line);
exit(err);
}
}
static char *loadProgramSource(const char *filename, size_t *size)
{
assert(filename && size);
char *source = NULL;
*size = 0;
FILE *fh = fopen(filename, "rb");
if (fh) {
struct stat statbuf;
stat(filename, &statbuf);
source = (char *)malloc(statbuf.st_size + 1);
if (source) {
fread(source, statbuf.st_size, 1, fh);
source[statbuf.st_size] = 0;
*size = statbuf.st_size + 1;
}
}
else {
fprintf(stderr, "Error reading file %s\n", filename);
exit(EXIT_FAILURE);
}
return source;
}
// Compile the NVVM IR into PTX.
static char *generatePTX(const char *ll, size_t size, const char *filename, int devMajor, int devMinor)
{
assert(ll && filename);
// Create a program instance for libNVVM.
nvvmProgram program;
nvvmResult result = nvvmCreateProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmCreateProgram: Failed\n");
exit(EXIT_FAILURE);
}
// Add the NVVM IR as a module to our libNVVM program.
result = nvvmAddModuleToProgram(program, ll, size, filename);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmAddModuleToProgram: Failed\n");
exit(EXIT_FAILURE);
}
// Dynamically construct the compute capability.
char arch[32] = {0};
snprintf(arch, sizeof(arch) - 1, "-arch=compute_%d%d", devMajor, devMinor);
// Compile the IR into PTX.
const char *options[] = {arch};
result = nvvmCompileProgram(program, 1, options);
if (result != NVVM_SUCCESS) {
char *Msg = NULL;
size_t LogSize;
fprintf(stderr, "nvvmCompileProgram: Failed\n");
nvvmGetProgramLogSize(program, &LogSize);
Msg = (char *)malloc(LogSize);
nvvmGetProgramLog(program, Msg);
fprintf(stderr, "%s\n", Msg);
free(Msg);
exit(EXIT_FAILURE);
}
size_t ptxSize = 0;
result = nvvmGetCompiledResultSize(program, &ptxSize);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmGetCompiledResultSize: Failed\n");
exit(EXIT_FAILURE);
}
char *ptx = malloc(ptxSize);
assert(ptx);
result = nvvmGetCompiledResult(program, ptx);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmGetCompiledResult: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
result = nvvmDestroyProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmDestroyProgram: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
return ptx;
}
// Return the device compute capability in major and minor.
static CUdevice cudaDeviceInit(int *major, int *minor)
{
assert(major && minor);
// Count the number of CUDA compute capable devices..
CUresult err = cuInit(0);
int deviceCount = 0;
if (CUDA_SUCCESS == err)
checkCudaErrors(cuDeviceGetCount(&deviceCount));
if (deviceCount == 0) {
fprintf(stderr, "cudaDeviceInit error: no devices supporting CUDA\n");
exit(EXIT_FAILURE);
}
// Get the first device discovered (device 0) and print its name.
CUdevice cuDevice = 0;
checkCudaErrors(cuDeviceGet(&cuDevice, 0));
char name[128] = {0};
checkCudaErrors(cuDeviceGetName(name, sizeof(name), cuDevice));
printf("Using CUDA Device [0]: %s\n", name);
// Get and test the compute capability.
checkCudaErrors(cuDeviceGetAttribute(major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice));
checkCudaErrors(cuDeviceGetAttribute(minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice));
printf("compute capability = %d.%d\n", *major, *minor);
if (*major < 7 && *minor < 5) {
fprintf(stderr, "Device 0 is not sm_75 or later\n");
exit(EXIT_FAILURE);
}
return cuDevice;
}
static CUresult buildKernel(CUcontext *phContext, CUdevice *phDevice, CUmodule *phModule, CUfunction *phKernel)
{
assert(phContext && phDevice && phModule && phKernel);
// Initialize CUDA and obtain the device's compute capability.
int major = 0, minor = 0;
*phDevice = cudaDeviceInit(&major, &minor);
checkCudaErrors(cuCtxCreate(phContext, NULL, 0, *phDevice));
// Get the NVVM IR from file.
size_t size = 0;
const char *filename = "dsl-gpu64.ll";
char *ll = loadProgramSource(filename, &size);
fprintf(stdout, "NVVM IR ll file loaded\n");
// Use libNVVM to generate PTX.
char *ptx = generatePTX(ll, size, filename, major, minor);
fprintf(stdout, "PTX generated:\n");
fprintf(stdout, "%s\n", ptx);
// Create a context and link the PTX and device library.
const char *libCudaDevRtName = getLibCudaDevRtName();
void *cubin = NULL;
size_t cubinSize = 0;
CUlinkState linkState;
checkCudaErrors(cuLinkCreate(0, NULL, NULL, &linkState));
checkCudaErrors(cuLinkAddData(linkState, CU_JIT_INPUT_PTX, (void *)ptx, strlen(ptx) + 1, 0, 0, 0, 0));
checkCudaErrors(cuLinkAddFile(linkState, CU_JIT_INPUT_LIBRARY, libCudaDevRtName, 0, NULL, NULL));
checkCudaErrors(cuLinkComplete(linkState, &cubin, &cubinSize));
// Load the linked binary.
checkCudaErrors(cuModuleLoadData(phModule, cubin));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "kernel"));
checkCudaErrors(cuLinkDestroy(linkState));
free(ll);
free(ptx);
return CUDA_SUCCESS;
}
int main(int argc, char **argv)
{
const unsigned int nThreads = 1;
const unsigned int nBlocks = 1;
// Initialize the device and get a handle to the kernel.
CUcontext hContext = 0;
CUdevice hDevice = 0;
CUmodule hModule = 0;
CUfunction hKernel = 0;
checkCudaErrors(buildKernel(&hContext, &hDevice, &hModule, &hKernel));
// Launch the kernel.
int depth = 0;
void *params[] = {&depth};
checkCudaErrors(cuLaunchKernel(hKernel, nBlocks, 1, 1, nThreads, 1, 1, 0, NULL, params, NULL));
if (hModule) {
checkCudaErrors(cuModuleUnload(hModule));
hModule = 0;
}
if (hContext) {
checkCudaErrors(cuCtxDestroy(hContext));
hContext = 0;
}
return 0;
}

View File

@@ -0,0 +1,74 @@
# Copyright (c) 1993-2023, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
add_executable(ptxgen ptxgen.c)
add_test(NAME ptxgenTest
COMMAND ptxgen "${CMAKE_CURRENT_SOURCE_DIR}/test.ll"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
set_tests_properties(ptxgenTest PROPERTIES FIXTURES_SETUP PTXGENTEST)
target_link_libraries(ptxgen ${NVVM_LIB})
if (DEFINED RUNTIME_LIBNVVM_PATH)
get_filename_component(_LIBNVVM_HOME "${RUNTIME_LIBNVVM_PATH}" DIRECTORY)
else ()
set(_LIBNVVM_HOME "${LIBNVVM_HOME}")
endif ()
if (WIN32)
set_target_properties(ptxgen PROPERTIES
COMPILE_FLAGS "/wd4996 -DLIBNVVM_HOME=\"${LIBNVVM_HOME}\"")
else (WIN32)
set_target_properties(ptxgen PROPERTIES
COMPILE_FLAGS "-DLIBNVVM_HOME=\"${_LIBNVVM_HOME}\""
LINK_FLAGS "-Wl,-rpath,${LIBNVVM_RPATH}")
endif (WIN32)
# Install to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin (for standalone libNVVM build)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(TARGETS ptxgen DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
install(FILES test.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
else()
install(TARGETS ptxgen DESTINATION bin)
install(FILES test.ll DESTINATION bin)
endif()
add_custom_command(
TARGET ptxgen
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/test.ll" "$<TARGET_FILE_DIR:ptxgen>"
)
if (WIN32)
add_custom_command(
TARGET ptxgen
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_BINARY_DIR}/nvvm64_40_0.dll" "$<TARGET_FILE_DIR:ptxgen>"
)
endif ()

View File

@@ -0,0 +1,27 @@
Introduction
============
ptxgen is a simple IR compiler that generates PTX code in stdout with the
input NVVM IR files. It generates warnings, errors, and other messages in
stderr. When multiple input files are given, it links them into a single
module before the compilation, and generates a single PTX module.
ptxgen always links the libDevice library with the input NVVM IR program.
Before compiling the input IR, ptxgen will verify the IR for conformance
to the NVVM IR specification.
Usage
-----
The command-line options, except for the program name and the input file
names, are directly passed to nvvmCompileProgram without modification.
Each input NVVM IR file can be either in the bitcode representation or
in the text representation. Input file names and command-line options can be
interleaved.
For example,
$ ptxgen a.ll -arch=compute_75 b.bc
links a.ll and b.bc, and generates PTX code for the compute_75 architecture.

View File

@@ -0,0 +1,277 @@
// Copyright (c) 1993-2023, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <nvvm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
/* Two levels of indirection to stringify LIBDEVICE_MAJOR_VERSION and
* LIBDEVICE_MINOR_VERSION correctly. */
#define getLibDeviceName() _getLibDeviceName(LIBDEVICE_MAJOR_VERSION, LIBDEVICE_MINOR_VERSION)
#define _getLibDeviceName(MAJOR, MINOR) __getLibDeviceName(MAJOR, MINOR)
#define __getLibDeviceName(MAJOR, MINOR) ("/libdevice/libdevice." #MAJOR #MINOR ".bc")
#define getLibnvvmHome() _getLibnvvmHome(LIBNVVM_HOME)
#define _getLibnvvmHome(NVVM_HOME) __getLibnvvmHome(NVVM_HOME)
#define __getLibnvvmHome(NVVM_HOME) (#NVVM_HOME)
typedef enum {
PTXGEN_SUCCESS = 0x0000,
PTXGEN_FILE_IO_ERROR = 0x0001,
PTXGEN_BAD_ALLOC_ERROR = 0x0002,
PTXGEN_LIBNVVM_COMPILATION_ERROR = 0x0004,
PTXGEN_LIBNVVM_ERROR = 0x0008,
PTXGEN_INVALID_USAGE = 0x0010,
PTXGEN_LIBNVVM_HOME_UNDEFINED = 0x0020,
PTXGEN_LIBNVVM_VERIFICATION_ERROR = 0x0040
} PTXGenStatus;
typedef enum { PTXGEN_INPUT_PROGRAM, PTXGEN_INPUT_LIBDEVICE } PTXGENInput;
static PTXGenStatus getLibDevicePath(char **buffer)
{
assert(buffer);
const char *libnvvmPath = getLibnvvmHome();
if (libnvvmPath == NULL) {
fprintf(stderr, "The environment variable LIBNVVM_HOME undefined\n");
return PTXGEN_LIBNVVM_HOME_UNDEFINED;
}
const char *libdevice = getLibDeviceName();
*buffer = malloc(strlen(libnvvmPath) + strlen(libdevice) + 1);
if (*buffer == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
return PTXGEN_BAD_ALLOC_ERROR;
}
// Concatenate libnvvmPath with libdevice.
*buffer = strcat(strcpy(*buffer, libnvvmPath), libdevice);
return PTXGEN_SUCCESS;
}
static PTXGenStatus addFileToProgram(const char *filename, nvvmProgram prog, PTXGENInput inputType)
{
assert(filename);
// Open the input file.
FILE *f = fopen(filename, "rb");
if (f == NULL) {
fprintf(stderr, "Failed to open %s\n", filename);
return PTXGEN_FILE_IO_ERROR;
}
// Allocate a buffer for the input.
struct stat fileStat;
fstat(fileno(f), &fileStat);
char *buffer = malloc(fileStat.st_size);
if (buffer == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
return PTXGEN_BAD_ALLOC_ERROR;
}
const size_t size = fread(buffer, 1, fileStat.st_size, f);
if (ferror(f)) {
fprintf(stderr, "Failed to read %s\n", filename);
fclose(f);
free(buffer);
return PTXGEN_FILE_IO_ERROR;
}
fclose(f);
// Add the input to the libNVVM program instance.
nvvmResult result;
if (inputType == PTXGEN_INPUT_LIBDEVICE)
result = nvvmLazyAddModuleToProgram(prog, buffer, size, filename);
else
result = nvvmAddModuleToProgram(prog, buffer, size, filename);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "Failed to add the module %s to the compilation unit\n", filename);
free(buffer);
return PTXGEN_LIBNVVM_ERROR;
}
free(buffer);
return PTXGEN_SUCCESS;
}
// Read the nvvmProgram compilation log.
static PTXGenStatus dumpCompilationLog(nvvmProgram prog)
{
size_t logSize;
PTXGenStatus status = PTXGEN_SUCCESS;
if (nvvmGetProgramLogSize(prog, &logSize) != NVVM_SUCCESS) {
fprintf(stderr, "Failed to get the compilation log size\n");
status = PTXGEN_LIBNVVM_ERROR;
}
else {
char *log = malloc(logSize);
if (log == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
status = PTXGEN_BAD_ALLOC_ERROR;
}
else if (nvvmGetProgramLog(prog, log) != NVVM_SUCCESS) {
fprintf(stderr, "Failed to get the compilation log\n");
status = PTXGEN_LIBNVVM_ERROR;
}
else {
fprintf(stderr, "%s\n", log);
}
free(log);
}
return status;
}
static PTXGenStatus
generatePTX(unsigned numOptions, const char **options, unsigned numFilenames, const char **filenames)
{
// Create the compilation unit (the libNVVM program instance).
nvvmProgram prog;
if (nvvmCreateProgram(&prog) != NVVM_SUCCESS) {
fprintf(stderr, "Failed to create the compilation unit\n");
return PTXGEN_LIBNVVM_ERROR;
}
// Add the module to the compilation unit.
for (unsigned i = 0; i < numFilenames; ++i) {
PTXGenStatus status = addFileToProgram(filenames[i], prog, PTXGEN_INPUT_PROGRAM);
if (status != PTXGEN_SUCCESS) {
nvvmDestroyProgram(&prog);
return status;
}
}
// Verify the compilation unit.
if (nvvmVerifyProgram(prog, numOptions, options) != NVVM_SUCCESS) {
fprintf(stderr, "Failed to verify the compilation unit\n");
return PTXGEN_LIBNVVM_VERIFICATION_ERROR;
}
// Add libdevice to the libNVVM program instance.
char *libDeviceName;
PTXGenStatus status = getLibDevicePath(&libDeviceName);
if (status != PTXGEN_SUCCESS) {
nvvmDestroyProgram(&prog);
return status;
}
status = addFileToProgram(libDeviceName, prog, PTXGEN_INPUT_LIBDEVICE);
free(libDeviceName);
if (status != PTXGEN_SUCCESS) {
nvvmDestroyProgram(&prog);
return status;
}
// Display the compilation log.
status |= dumpCompilationLog(prog);
if (status & PTXGEN_LIBNVVM_VERIFICATION_ERROR) {
nvvmDestroyProgram(&prog);
return status;
}
// Compile the compilation unit.
if (nvvmCompileProgram(prog, numOptions, options) != NVVM_SUCCESS) {
fprintf(stderr, "Failed to generate PTX from the compilation unit\n");
status |= PTXGEN_LIBNVVM_COMPILATION_ERROR;
}
else {
size_t ptxSize;
if (nvvmGetCompiledResultSize(prog, &ptxSize) != NVVM_SUCCESS) {
fprintf(stderr, "Failed to get the PTX output size\n");
status |= PTXGEN_LIBNVVM_ERROR;
}
else {
char *ptx = malloc(ptxSize);
if (ptx == NULL) {
fprintf(stderr, "Failed to allocate memory\n");
status |= PTXGEN_BAD_ALLOC_ERROR;
}
else if (nvvmGetCompiledResult(prog, ptx) != NVVM_SUCCESS) {
fprintf(stderr, "Failed to get the PTX output\n");
status |= PTXGEN_LIBNVVM_ERROR;
}
else {
fprintf(stdout, "%s\n", ptx);
}
free(ptx);
}
}
status |= dumpCompilationLog(prog);
// Release the resources.
nvvmDestroyProgram(&prog);
return status;
}
static void showUsage(void)
{
fprintf(stderr,
"Usage: ptxgen [OPTION]... [FILE]...\n"
" [FILE] could be a .bc file or a .ll file\n");
}
int main(int argc, char *argv[])
{
PTXGenStatus status = PTXGEN_SUCCESS;
if (argc == 1) {
showUsage();
return PTXGEN_INVALID_USAGE;
}
// Extract libNVVM options and the input file names.
unsigned numOptions = 0, numFilenames = 0;
const char **options = malloc((argc - 1) * sizeof(char *));
const char **filenames = malloc((argc - 1) * sizeof(char *));
assert(options && filenames);
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
options[numOptions] = argv[i];
++numOptions;
}
else {
filenames[numFilenames] = argv[i];
++numFilenames;
}
}
if (numFilenames == 0) {
showUsage();
status = PTXGEN_INVALID_USAGE;
}
else {
// Use libNVVM to generate PTX.
status = generatePTX(numOptions, options, numFilenames, filenames);
}
free(options);
free(filenames);
return status;
}

View File

@@ -0,0 +1,39 @@
; Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
define void @_Z9testEmptyv() {
entry:
ret void
}
!nvvmir.version = !{!0}
!nvvm.annotations = !{!1}
!0 = !{i32 2, i32 0}
!1 = !{void ()* @_Z9testEmptyv, !"kernel", i32 1}

View File

@@ -0,0 +1,68 @@
# Copyright (c) 1993-2023, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(CMAKE_INSTALL_RPATH ${LIBNVVM_HOME})
set(CMAKE_INCLUDE_CURRENT_DIR YES)
add_executable(simple simple.c)
add_test(NAME simple COMMAND simple WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
target_link_libraries(simple ${NVVM_LIB} ${CUDA_LIB})
if (WIN32)
set_target_properties(simple PROPERTIES COMPILE_FLAGS "/wd4996")
else (WIN32)
set_target_properties(simple PROPERTIES
LINK_FLAGS "-Wl,-rpath,${LIBNVVM_RPATH}")
endif (WIN32)
# Install to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin (for standalone libNVVM build)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(TARGETS simple DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
install(FILES simple-gpu64.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
else()
install(TARGETS simple DESTINATION bin)
install(FILES simple-gpu64.ll DESTINATION bin)
endif()
# 'simple' will load simple-gpu64.ll from the current working directory. That
# .ll file should be present where tests are executed (the build directory).
add_custom_command(
TARGET simple
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/simple-gpu64.ll" "$<TARGET_FILE_DIR:simple>"
)
if (WIN32)
add_custom_command(
TARGET simple
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_BINARY_DIR}/nvvm64_40_0.dll" "$<TARGET_FILE_DIR:simple>"
)
endif ()

View File

@@ -0,0 +1,6 @@
Introduction
============
simple is a C program that illustrates the use of libNVVM.
simple converts the simple-gpu64.ll NVVM-IR file into PTX via the libNVVM API
and forwards that PTX to the CUDA driver for GPU execution.

View File

@@ -0,0 +1,61 @@
; Copyright (c) 1993-2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
define i32 @ave(i32 %a, i32 %b) {
entry:
%add = add nsw i32 %a, %b
%div = sdiv i32 %add, 2
ret i32 %div
}
define void @simple(i32* %data) {
entry:
%0 = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.x()
%1 = call i32 @llvm.nvvm.read.ptx.sreg.ntid.x()
%mul = mul i32 %0, %1
%2 = call i32 @llvm.nvvm.read.ptx.sreg.tid.x()
%add = add i32 %mul, %2
%call = call i32 @ave(i32 %add, i32 %add)
%idxprom = sext i32 %add to i64
%arrayidx = getelementptr inbounds i32, i32* %data, i64 %idxprom
store i32 %call, i32* %arrayidx, align 4
ret void
}
declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.x() nounwind readnone
declare i32 @llvm.nvvm.read.ptx.sreg.ntid.x() nounwind readnone
declare i32 @llvm.nvvm.read.ptx.sreg.tid.x() nounwind readnone
!nvvm.annotations = !{!1}
!1 = !{void (i32*)* @simple, !"kernel", i32 1}
!nvvmir.version = !{!2}
!2 = !{i32 2, i32 0, i32 3, i32 1}

View File

@@ -0,0 +1,251 @@
// Copyright (c) 1993-2025, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <builtin_types.h>
#include <cuda.h>
#include <math.h>
#include <nvvm.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
// If 'err' is non-zero, emit an error message and exit.
#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)
static void __checkCudaErrors(CUresult err, const char *filename, int line)
{
assert(filename);
if (CUDA_SUCCESS != err) {
const char *ename = NULL;
const CUresult res = cuGetErrorName(err, &ename);
fprintf(stderr,
"CUDA API Error %04d: \"%s\" from file <%s>, "
"line %i.\n",
err,
((CUDA_SUCCESS == res) ? ename : "Unknown"),
filename,
line);
exit(err);
}
}
// Return a CUDA capable device or exit if one cannot be found.
static CUdevice cudaDeviceInit(int *devMajor, int *devMinor)
{
assert(devMajor && devMinor);
CUresult err = cuInit(0);
int deviceCount = 0;
if (CUDA_SUCCESS == err)
checkCudaErrors(cuDeviceGetCount(&deviceCount));
if (deviceCount == 0) {
fprintf(stderr, "cudaDeviceInit error: no devices supporting CUDA\n");
exit(EXIT_FAILURE);
}
// Locate a CUDA supporting device and its name.
CUdevice cuDevice = 0;
checkCudaErrors(cuDeviceGet(&cuDevice, 0));
char name[128];
cuDeviceGetName(name, sizeof(name), cuDevice);
printf("Using CUDA Device [0]: %s\n", name);
// Obtain the device's compute capability.
checkCudaErrors(cuDeviceGetAttribute(devMajor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice));
checkCudaErrors(cuDeviceGetAttribute(devMinor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice));
if (*devMajor < 7 && *devMinor < 5) {
fprintf(stderr, "Device 0 is not sm_75 or later\n");
exit(EXIT_FAILURE);
}
return cuDevice;
}
static CUresult
initCUDA(CUcontext *phContext, CUdevice *phDevice, CUmodule *phModule, CUfunction *phKernel, const char *ptx)
{
assert(phContext && phDevice && phModule && phKernel && ptx);
// Create a CUDA context on the device.
checkCudaErrors(cuCtxCreate(phContext, NULL, 0, *phDevice));
// Load the PTX.
checkCudaErrors(cuModuleLoadDataEx(phModule, ptx, 0, 0, 0));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "simple"));
return CUDA_SUCCESS;
}
static char *loadProgramSource(const char *filename, size_t *size)
{
assert(filename && size);
char *source = NULL;
*size = 0;
FILE *fh = fopen(filename, "rb");
if (fh) {
struct stat statbuf;
stat(filename, &statbuf);
source = malloc(statbuf.st_size + 1);
assert(source);
fread(source, statbuf.st_size, 1, fh);
source[statbuf.st_size] = 0;
*size = statbuf.st_size + 1;
}
else {
fprintf(stderr, "Error reading file %s\n", filename);
exit(EXIT_FAILURE);
}
return source;
}
static char *generatePTX(const char *ir, size_t size, const char *filename, int devMajor, int devMinor)
{
assert(ir && filename);
// Create a program instance for use with libNVVM.
nvvmProgram program;
nvvmResult result = nvvmCreateProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmCreateProgram: Failed\n");
exit(EXIT_FAILURE);
}
// Add the NVVM IR to the program instance.
result = nvvmAddModuleToProgram(program, ir, size, filename);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmAddModuleToProgram: Failed\n");
exit(EXIT_FAILURE);
}
// Dynamically construct the compute capability.
char arch[32] = {0};
snprintf(arch, sizeof(arch) - 1, "-arch=compute_%d%d", devMajor, devMinor);
// Compile the IR into PTX.
const char *options[] = {arch};
result = nvvmCompileProgram(program, 1, options);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmCompileProgram: Failed\n");
size_t logSize;
nvvmGetProgramLogSize(program, &logSize);
char *msg = malloc(logSize);
assert(msg);
nvvmGetProgramLog(program, msg);
fprintf(stderr, "%s\n", msg);
free(msg);
exit(EXIT_FAILURE);
}
// Obrain the resulting PTX.
size_t ptxSize;
result = nvvmGetCompiledResultSize(program, &ptxSize);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmGetCompiledResultSize: Failed\n");
exit(EXIT_FAILURE);
}
char *ptx = malloc(ptxSize);
assert(ptx);
result = nvvmGetCompiledResult(program, ptx);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmGetCompiledResult: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
// Cleanup the libNVVM program instance.
result = nvvmDestroyProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmDestroyProgram: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
return ptx;
}
int main(int argc, char **argv)
{
const unsigned int nThreads = 32;
const unsigned int nBlocks = 1;
const size_t memSize = nThreads * nBlocks * sizeof(int);
const char *filename = "simple-gpu64.ll";
// Retrieve the NVVM IR from filename and create the kernel parameters.
size_t size = 0;
char *ir = loadProgramSource(filename, &size);
fprintf(stdout, "NVVM IR (.ll) file loaded\n");
// Initialize the device and obtain the compute capability.
int devMajor = 0, devMinor = 0;
CUdevice hDevice = cudaDeviceInit(&devMajor, &devMinor);
// Use libNVVM to generate PTX from the NVVM IR.
char *ptx = generatePTX(ir, size, filename, devMajor, devMinor);
fprintf(stdout, "PTX generated:\n");
fprintf(stdout, "%s\n", ptx);
// Initialize the device and get a handle to the kernel.
CUcontext hContext = 0;
CUmodule hModule = 0;
CUfunction hKernel = 0;
checkCudaErrors(initCUDA(&hContext, &hDevice, &hModule, &hKernel, ptx));
// Allocate memory on the host and device.
int *hData = malloc(memSize);
if (!hData) {
fprintf(stderr, "Could not allocate host memory\n");
exit(EXIT_FAILURE);
}
CUdeviceptr dData = 0;
checkCudaErrors(cuMemAlloc(&dData, memSize));
// Launch the kernel.
void *params[] = {&dData};
checkCudaErrors(cuLaunchKernel(hKernel, nBlocks, 1, 1, nThreads, 1, 1, 0, NULL, params, NULL));
fprintf(stdout, "CUDA kernel launched\n");
// Copy the result back to the host.
checkCudaErrors(cuMemcpyDtoH(hData, dData, memSize));
// Print the result.
for (unsigned i = 0; i < nBlocks * nThreads; i++)
fprintf(stdout, "%d ", hData[i]);
fprintf(stdout, "\n");
// Cleanup.
if (dData)
checkCudaErrors(cuMemFree(dData));
if (hModule)
checkCudaErrors(cuModuleUnload(hModule));
if (hContext)
checkCudaErrors(cuCtxDestroy(hContext));
free(hData);
free(ir);
free(ptx);
return 0;
}

View File

@@ -0,0 +1,54 @@
# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
add_test(NAME test-syscalls-malloc-free
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/ptxgen" "${CMAKE_CURRENT_SOURCE_DIR}/malloc-free.ll"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
add_test(NAME test-syscalls-vprintf
COMMAND "${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/ptxgen" "${CMAKE_CURRENT_SOURCE_DIR}/vprintf.ll"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
set_tests_properties(test-syscalls-vprintf test-syscalls-malloc-free
PROPERTIES FIXTURES_REQUIRED PTXGENTEST)
# Install to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin/syscalls (for standalone libNVVM build)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(FILES malloc-free.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR}/syscalls)
install(FILES vprintf.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR}/syscalls)
else()
install(FILES malloc-free.ll DESTINATION bin/syscalls)
install(FILES vprintf.ll DESTINATION bin/syscalls)
endif()
file(COPY malloc-free.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY vprintf.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
# Copy the .ll files to the folder of executable file for full testing
file(COPY malloc-free.ll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/syscalls)
file(COPY vprintf.ll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../ptxgen/syscalls)

View File

@@ -0,0 +1,60 @@
; Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
; This NVVM IR program shows how to call the malloc and free functions.
; What it does is similar to the following CUDA C code.
;
; __device__ int *p;
;
; __global__ void foo()
; {
; p = (int *)malloc(128);
; free(p);
; }
;
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
@p = internal addrspace(1) global i32* null, align 8
define void @foo() {
entry:
%call = tail call i8* @malloc(i64 128)
%conv = bitcast i8* %call to i32*
store i32* %conv, i32* addrspace(1)* @p, align 8
tail call void @free(i8* %call)
ret void
}
declare noalias i8* @malloc(i64) nounwind
declare void @free(i8* nocapture) nounwind
!nvvm.annotations = !{!0}
!0 = !{void ()* @foo, !"kernel", i32 1}
!nvvmir.version = !{!1}
!1 = !{i32 2, i32 0}

View File

@@ -0,0 +1,92 @@
; Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
; This NVVM IR programs shows how to call the vprintf function.
; What it does is similar to the following CUDA C code.
;
; __global__ void foo(char c, short s, int i, long l,
; float f, double d, char* p)
; {
; printf("Hello world from %c %hi %d %ld %f %f %s\n", c, s, i, l, f, d, p);
; }
;
; There is no direct printf() support. In order to use
; vprintf(), a local buffer is allocated. Integer types that
; are shorter than int need to be extended to int and float
; needs to be extended to double before being pushed into
; the local buffer.
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
@"$str" = private addrspace(4) constant [41 x i8] c"Hello world from %c %hi %d %ld %f %f %s\0A\00"
define void @foo(i8 signext %c, i16 signext %s, i32 %i, i64 %l, float %f, double %d, i8* %p) {
entry:
%tmp = alloca [12 x i32], align 8
%tmp2 = getelementptr inbounds [12 x i32], [12 x i32]* %tmp, i64 0, i64 0
%gen2local = addrspacecast i32* %tmp2 to i32 addrspace(5)*
%conv = sext i8 %c to i32
store i32 %conv, i32 addrspace(5)* %gen2local, align 8
%conv2 = sext i16 %s to i32
%getElem11 = getelementptr i32, i32 addrspace(5)* %gen2local, i64 1
store i32 %conv2, i32 addrspace(5)* %getElem11, align 4
%getElem12 = getelementptr i32, i32 addrspace(5)* %gen2local, i64 2
store i32 %i, i32 addrspace(5)* %getElem12, align 8
%getElem13 = getelementptr i32, i32 addrspace(5)* %gen2local, i64 4
%bitCast = bitcast i32 addrspace(5)* %getElem13 to i64 addrspace(5)*
store i64 %l, i64 addrspace(5)* %bitCast, align 8
%getElem14 = getelementptr i32, i32 addrspace(5)* %gen2local, i64 6
%conv6 = fpext float %f to double
%bitCast15 = bitcast i32 addrspace(5)* %getElem14 to double addrspace(5)*
store double %conv6, double addrspace(5)* %bitCast15, align 8
%getElem16 = getelementptr i32, i32 addrspace(5)* %gen2local, i64 8
%bitCast17 = bitcast i32 addrspace(5)* %getElem16 to double addrspace(5)*
store double %d, double addrspace(5)* %bitCast17, align 8
%getElem18 = getelementptr i32, i32 addrspace(5)* %gen2local, i64 10
%bitCast19 = bitcast i32 addrspace(5)* %getElem18 to i8* addrspace(5)*
store i8* %p, i8* addrspace(5)* %bitCast19, align 8
%0 = addrspacecast i8 addrspace(4)* getelementptr inbounds ([41 x i8], [41 x i8] addrspace(4)* @"$str", i64 0, i64 0) to i8*
%1 = bitcast [12 x i32]* %tmp to i8*
%call = call i32 @vprintf(i8* %0, i8* %1)
ret void
}
declare i32 @vprintf(i8* nocapture, i8*) nounwind
!nvvm.annotations = !{!0}
!0 = !{void (i8, i16, i32, i64, float, double, i8*)* @foo, !"kernel", i32 1}
!nvvmir.version = !{!1}
!1 = !{i32 2, i32 0}

4
cpp/7_libNVVM/utils/build.bat Executable file
View File

@@ -0,0 +1,4 @@
mkdir build
cd build
cmake.exe -DCMAKE_INSTALL_PREFIX=..\install -G "NMake Makefiles" ..
nmake && nmake test && nmake install && cd ..

6
cpp/7_libNVVM/utils/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/sh
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../install ..
make && make test && make install

View File

@@ -0,0 +1,72 @@
# Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of NVIDIA CORPORATION nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(CMAKE_INSTALL_RPATH ${LIBNVVM_HOME})
set(CMAKE_INCLUDE_CURRENT_DIR YES)
set_property(SOURCE uvmlite.c
PROPERTY COMPILE_DEFINITIONS LIBCUDADEVRT="${CUDADEVRT_LIB}")
add_executable(uvmlite uvmlite.c)
add_test(NAME uvmlite COMMAND uvmlite WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
target_link_libraries(uvmlite ${NVVM_LIB} ${CUDA_LIB})
if (WIN32)
set (LIB_ARCH_SUFFIX "/x64")
set_target_properties(uvmlite PROPERTIES COMPILE_FLAGS "/wd4996")
else (WIN32)
set (LIB_ARCH_SUFFIX "64")
set_target_properties(uvmlite PROPERTIES
LINK_FLAGS "-Wl,-rpath,${LIBNVVM_RPATH}")
endif ()
# Install to CUDA_SAMPLES_INSTALL_DIR if defined (for unified installation),
# otherwise install to bin (for standalone libNVVM build)
if(DEFINED CUDA_SAMPLES_INSTALL_DIR)
install(TARGETS uvmlite DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
install(FILES uvmlite64.ll DESTINATION ${CUDA_SAMPLES_INSTALL_DIR})
else()
install(TARGETS uvmlite DESTINATION bin)
install(FILES uvmlite64.ll DESTINATION bin)
endif()
# 'uvmlite' will load uvmlite64.ll from the current working directory. That
# .ll file should be present where tests are executed (the build directory).
add_custom_command(
TARGET uvmlite
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/uvmlite64.ll" "$<TARGET_FILE_DIR:uvmlite>"
)
if (WIN32)
add_custom_command(
TARGET uvmlite
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_BINARY_DIR}/nvvm64_40_0.dll" "$<TARGET_FILE_DIR:uvmlite>"
)
endif ()

View File

@@ -0,0 +1,59 @@
Unified Virtual Memory Lite (UVM-lite) From NVVM IR
===================================================
This document is for the programming language and compiler
implementers who target NVVM IR and plan to support Unified Virtual
Memory Lite (UVM-lite) in their language. It provides the low-level
details related to supporting kernel launches at the NVVM IR level.
This document assumes the CUDA runtime is used. For the limits and
restrictions, please refer to the official CUDA documents.
Allocating a variable in the unified virtual memory environment.
----------------------------------------------------------------
In a system that supports unified virtual memory environment, a
variable can be allocated at a location where host and other devices
in the system can reference the variable directly. We call such a
variable a managed variable and say that the variable has the
managed attribute or is managed. The attribute can be specified
using a metadata.
@xxx = internal addrspace(1) global i32 10, align 4
...
!1 = !{i32 addrspace(1)* @xxx, !"managed", i32 1}
A global variable, e.g., @xxx, can be defined and used as usual, but
here we have a metadata that specifies the managed attributes. (Note
that the attribute can only be used with variables in the global
address space.)
Accessing a managed variable in the host
----------------------------------------
To access a managed variable defined in the NVVM IR code, we should
retrieve a device pointer first, which can be done using cuModuleGetGlobal().
CUdeviceptr devp_xxx; // device pointer to xxx
size_t size_xxx; // size of xxx
result = cuModuleGetGlobal(&devp_xxx, &size_xxx, hModule, "xxx");
Whether or not the pointer points to managed memory may be queried
by calling cuPointerGetAttribute() with the pointer attribute
CU_POINTER_ATTRIBUTE_IS_MANAGED.
unsigned int attrVal;
result = cuPointerGetAttribute(&attrVal, CU_POINTER_ATTRIBUTE_IS_MANAGED, devp_xxx);
// result will be 1 if the pointer is managed or zero otherwise.
It is safe to use cuPointerGetAttribute to get the host pointers,
since the device pointers are opaque.
void *host_ptr_xxx;
int *p_xxx;
result = cuPointerGetAttribute(&host_ptr_xxx, CU_POINTER_ATTRIBUTE_HOST_POINTER, devp_xxx);
p_xxx = (int *)devp_xxx;
*p_xxx += 1; // read & write without explicit copying

View File

@@ -0,0 +1,320 @@
// Copyright (c) 2014-2025, NVIDIA CORPORATION. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of NVIDIA CORPORATION nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <builtin_types.h>
#include <cuda.h>
#include <math.h>
#include <nvvm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#define ERROR_IF(expr) \
if (expr) { \
fprintf(stderr, "Failed check at %s:%d\n", __FILE__, __LINE__); \
exit(EXIT_FAILURE); \
}
// If 'err' is non-zero, emit an error message and exit.
#define checkCudaErrors(err) __checkCudaErrors(err, __FILE__, __LINE__)
static void __checkCudaErrors(CUresult err, const char *filename, int line)
{
assert(filename);
if (CUDA_SUCCESS != err) {
const char *ename = NULL;
const CUresult res = cuGetErrorName(err, &ename);
fprintf(stderr,
"CUDA API Error %04d: \"%s\" from file <%s>, "
"line %i.\n",
err,
((CUDA_SUCCESS == res) ? ename : "Unknown"),
filename,
line);
exit(err);
}
}
// Compile the NVVM IR into PTX.
static char *generatePTX(const char *ll, size_t size, const char *filename, int devMajor, int devMinor)
{
assert(ll && filename);
// Create a program instance for libNVVM.
nvvmProgram program;
nvvmResult result = nvvmCreateProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmCreateProgram: Failed\n");
exit(EXIT_FAILURE);
}
// Add the NVVM IR as a module to our libNVVM program.
result = nvvmAddModuleToProgram(program, ll, size, filename);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmAddModuleToProgram: Failed\n");
exit(EXIT_FAILURE);
}
// Dynamically construct the compute capability.
char arch[32] = {0};
snprintf(arch, sizeof(arch) - 1, "-arch=compute_%d%d", devMajor, devMinor);
// Compile the IR into PTX.
const char *options[] = {arch};
result = nvvmCompileProgram(program, 1, options);
if (result != NVVM_SUCCESS) {
char *Msg = NULL;
size_t LogSize;
fprintf(stderr, "nvvmCompileProgram: Failed\n");
nvvmGetProgramLogSize(program, &LogSize);
Msg = (char *)malloc(LogSize);
nvvmGetProgramLog(program, Msg);
fprintf(stderr, "%s\n", Msg);
free(Msg);
exit(EXIT_FAILURE);
}
size_t ptxSize = 0;
result = nvvmGetCompiledResultSize(program, &ptxSize);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmGetCompiledResultSize: Failed\n");
exit(EXIT_FAILURE);
}
char *ptx = malloc(ptxSize);
assert(ptx);
result = nvvmGetCompiledResult(program, ptx);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmGetCompiledResult: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
result = nvvmDestroyProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmDestroyProgram: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
return ptx;
}
static char *loadProgramSource(const char *filename, size_t *size)
{
assert(filename && size);
*size = 0;
char *source = NULL;
FILE *fh = fopen(filename, "rb");
if (fh) {
struct stat statbuf;
stat(filename, &statbuf);
source = (char *)malloc(statbuf.st_size + 1);
if (source) {
fread(source, statbuf.st_size, 1, fh);
source[statbuf.st_size] = 0;
*size = statbuf.st_size + 1;
}
}
else {
fprintf(stderr, "Error reading file %s\n", filename);
exit(EXIT_FAILURE);
}
return source;
}
// Return the device compute capability in major and minor.
static CUdevice cudaDeviceInit(int *major, int *minor)
{
assert(major && minor);
// Count the number of CUDA compute capable devices..
CUresult err = cuInit(0);
int deviceCount = 0;
if (CUDA_SUCCESS == err)
checkCudaErrors(cuDeviceGetCount(&deviceCount));
if (deviceCount == 0) {
fprintf(stderr, "cudaDeviceInit error: no devices supporting CUDA\n");
exit(EXIT_FAILURE);
}
// Get the first device discovered (device 0) and print its name.
CUdevice cuDevice = 0;
checkCudaErrors(cuDeviceGet(&cuDevice, 0));
char name[128] = {0};
checkCudaErrors(cuDeviceGetName(name, sizeof(name), cuDevice));
printf("Using CUDA Device [0]: %s\n", name);
// Get and test the compute capability.
checkCudaErrors(cuDeviceGetAttribute(major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice));
checkCudaErrors(cuDeviceGetAttribute(minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice));
printf("compute capability = %d.%d\n", *major, *minor);
if (*major < 7 && *minor < 5) {
fprintf(stderr, "Device 0 is not sm_75 or later\n");
exit(EXIT_FAILURE);
}
// Check if managed memory is supported.
int supportsUvm = 0;
checkCudaErrors(cuDeviceGetAttribute(&supportsUvm, CU_DEVICE_ATTRIBUTE_MANAGED_MEMORY, cuDevice));
if (!supportsUvm) {
printf("This device does not support managed memory.");
exit(EXIT_SUCCESS);
}
// Check if unified addressing is supported (host and device share same
// the address space).
int supportsUva = 0;
checkCudaErrors(cuDeviceGetAttribute(&supportsUva, CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING, cuDevice));
if (!supportsUva) {
printf("This device does not support a unified address space.");
exit(EXIT_SUCCESS);
}
return cuDevice;
}
static CUresult buildKernel(CUcontext *phContext, CUdevice *phDevice, CUmodule *phModule, CUfunction *phKernel)
{
assert(phContext && phDevice && phModule && phKernel);
// Initialize CUDA and obtain the device's compute capability.
int major = 0, minor = 0;
*phDevice = cudaDeviceInit(&major, &minor);
// Create a context on the device.
checkCudaErrors(cuCtxCreate(phContext, NULL, 0, *phDevice));
// Get the NVVM IR from file.
size_t size = 0;
const char *filename = "uvmlite64.ll";
char *ll = loadProgramSource(filename, &size);
fprintf(stdout, "NVVM IR ll file loaded\n");
// Use libNVVM to generate PTX.
char *ptx = generatePTX(ll, size, filename, major, minor);
fprintf(stdout, "PTX generated:\n");
fprintf(stdout, "%s\n", ptx);
// Load module from PTX.
checkCudaErrors(cuModuleLoadDataEx(phModule, ptx, 0, NULL, NULL));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "test_kernel"));
free(ll);
free(ptx);
return CUDA_SUCCESS;
}
int main(void)
{
const unsigned int nThreads = 1;
const unsigned int nBlocks = 1;
// Pointers to the variables in the managed memory.
// See uvmlite64.ll for their definition.
CUdeviceptr devp_xxx, devp_yyy;
size_t size_xxx, size_yyy;
int *p_xxx, *p_yyy;
// Initialize the device and get a handle to the kernel
CUcontext hContext = 0;
CUdevice hDevice = 0;
CUmodule hModule = 0;
CUfunction hKernel = 0;
checkCudaErrors(buildKernel(&hContext, &hDevice, &hModule, &hKernel));
// Whether or not a device supports unified addressing may be queried by
// calling cuDeviceGetAttribute() with the deivce attribute
// CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING.
{
int attrVal;
checkCudaErrors(cuDeviceGetAttribute(&attrVal, CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING, hDevice));
ERROR_IF(attrVal != 1);
}
// Get the address of the variable xxx, yyy in the managed memory.
checkCudaErrors(cuModuleGetGlobal(&devp_xxx, &size_xxx, hModule, "xxx"));
checkCudaErrors(cuModuleGetGlobal(&devp_yyy, &size_yyy, hModule, "yyy"));
// Whether or not the pointer points to managed memory may be queried by
// calling cuPointerGetAttribute() with the pointer attribute
// CU_POINTER_ATTRIBUTE_IS_MANAGED.
{
unsigned int attrVal;
checkCudaErrors(cuPointerGetAttribute(&attrVal, CU_POINTER_ATTRIBUTE_IS_MANAGED, devp_xxx));
ERROR_IF(attrVal != 1);
checkCudaErrors(cuPointerGetAttribute(&attrVal, CU_POINTER_ATTRIBUTE_IS_MANAGED, devp_yyy));
ERROR_IF(attrVal != 1);
}
// Since CUdeviceptr is opaque, it is safe to use cuPointerGetAttribute to get
// the host pointers.
{
void *host_ptr_xxx, *host_ptr_yyy;
checkCudaErrors(cuPointerGetAttribute(&host_ptr_xxx, CU_POINTER_ATTRIBUTE_HOST_POINTER, devp_xxx));
checkCudaErrors(cuPointerGetAttribute(&host_ptr_yyy, CU_POINTER_ATTRIBUTE_HOST_POINTER, devp_yyy));
p_xxx = (int *)host_ptr_xxx;
p_yyy = (int *)host_ptr_yyy;
}
printf("The initial value of xxx initialized by the device = %d\n", *p_xxx);
printf("The initial value of yyy initialized by the device = %d\n", *p_yyy);
ERROR_IF(*p_xxx != 10);
ERROR_IF(*p_yyy != 100);
// The host adds 1 and 11 to xxx and yyy.
*p_xxx += 1;
*p_yyy += 11;
printf("The host added 1 and 11 to xxx and yyy.\n");
// Launch the kernel with the following parameters.
{
void *params[] = {(void *)&devp_xxx};
checkCudaErrors(cuLaunchKernel(hKernel, nBlocks, 1, 1, nThreads, 1, 1, 0, NULL, params, NULL));
}
checkCudaErrors(cuCtxSynchronize());
printf("kernel added 20 and 30 to xxx and yyy, respectively.\n");
printf("The final value checked in the host: xxx = %d, yyy = %d\n", *p_xxx, *p_yyy);
if (hModule) {
checkCudaErrors(cuModuleUnload(hModule));
hModule = 0;
}
if (hContext) {
checkCudaErrors(cuCtxDestroy(hContext));
hContext = 0;
}
return 0;
}

View File

@@ -0,0 +1,61 @@
; Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of NVIDIA CORPORATION nor the names of its
; contributors may be used to endorse or promote products derived
; from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
; PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
; EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
; PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
; OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-i128:128:128-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64"
target triple = "nvptx64-nvidia-cuda"
; the initial value of xxx is 10
@xxx = internal addrspace(1) global i32 10, align 4
; the initial value of yyy is 100
@yyy = internal addrspace(1) global i32 100, align 4
@llvm.used = appending global [3 x i8*] [i8* bitcast (i8* addrspacecast (i32 addrspace(1)* @xxx to i8*) to i8*), i8* bitcast (i8* addrspacecast (i32 addrspace(1)* @yyy to i8*) to i8*), i8* bitcast (void (i32*)* @test_kernel to i8*)], section "llvm.metadata"
; %ptr can be in the managed space, and its address can be directly used in the host and device.
; See the uvmlite.c, which passes the device pointer of xxx as the kernel parameter.
; This kernel also directly accesses @yyy, which is also managed.
define void @test_kernel(i32* nocapture %ptr) nounwind alwaysinline {
; *%ptr = *%ptr + 20
%gen2other = addrspacecast i32* %ptr to i32 addrspace(1)*
%tmp1 = load i32, i32 addrspace(1)* %gen2other, align 4
%add = add nsw i32 %tmp1, 20
store i32 %add, i32 addrspace(1)* %gen2other, align 4
; @yyy = @yyy + 30
%tmp2 = load i32, i32 addrspace(1)* @yyy, align 4
%add3 = add nsw i32 %tmp2, 30
store i32 %add3, i32 addrspace(1)* @yyy, align 4
ret void
}
!nvvm.annotations = !{!7, !8, !9}
!nvvmir.version = !{!6}
!6 = !{i32 2, i32 0}
!7 = !{i32 addrspace(1)* @xxx, !"managed", i32 1}
!8 = !{i32 addrspace(1)* @yyy, !"managed", i32 1}
!9 = !{void (i32*)* @test_kernel, !"kernel", i32 1}