mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-13 00:08:26 +08:00
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:
144
cpp/7_libNVVM/CMakeLists.txt
Normal file
144
cpp/7_libNVVM/CMakeLists.txt
Normal 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
100
cpp/7_libNVVM/README.md
Normal 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.
|
||||
122
cpp/7_libNVVM/common/include/DDSWriter.h
Normal file
122
cpp/7_libNVVM/common/include/DDSWriter.h
Normal 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
|
||||
98
cpp/7_libNVVM/cuda-c-linking/CMakeLists.txt
Normal file
98
cpp/7_libNVVM/cuda-c-linking/CMakeLists.txt
Normal 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 ()
|
||||
49
cpp/7_libNVVM/cuda-c-linking/README.md
Normal file
49
cpp/7_libNVVM/cuda-c-linking/README.md
Normal 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
|
||||
325
cpp/7_libNVVM/cuda-c-linking/cuda-c-linking.cpp
Normal file
325
cpp/7_libNVVM/cuda-c-linking/cuda-c-linking.cpp
Normal 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;
|
||||
}
|
||||
84
cpp/7_libNVVM/cuda-c-linking/math-funcs.cu
Normal file
84
cpp/7_libNVVM/cuda-c-linking/math-funcs.cu
Normal 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;
|
||||
}
|
||||
57
cpp/7_libNVVM/cuda-shared-memory/CMakeLists.txt
Normal file
57
cpp/7_libNVVM/cuda-shared-memory/CMakeLists.txt
Normal 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)
|
||||
58
cpp/7_libNVVM/cuda-shared-memory/extern_shared_memory.ll
Normal file
58
cpp/7_libNVVM/cuda-shared-memory/extern_shared_memory.ll
Normal 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}
|
||||
58
cpp/7_libNVVM/cuda-shared-memory/shared_memory.ll
Normal file
58
cpp/7_libNVVM/cuda-shared-memory/shared_memory.ll
Normal 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}
|
||||
69
cpp/7_libNVVM/device-side-launch/CMakeLists.txt
Normal file
69
cpp/7_libNVVM/device-side-launch/CMakeLists.txt
Normal 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 ()
|
||||
80
cpp/7_libNVVM/device-side-launch/README.md
Normal file
80
cpp/7_libNVVM/device-side-launch/README.md
Normal 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.
|
||||
103
cpp/7_libNVVM/device-side-launch/dsl-gpu64.ll
Normal file
103
cpp/7_libNVVM/device-side-launch/dsl-gpu64.ll
Normal 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}
|
||||
260
cpp/7_libNVVM/device-side-launch/dsl.c
Normal file
260
cpp/7_libNVVM/device-side-launch/dsl.c
Normal 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;
|
||||
}
|
||||
74
cpp/7_libNVVM/ptxgen/CMakeLists.txt
Normal file
74
cpp/7_libNVVM/ptxgen/CMakeLists.txt
Normal 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 ()
|
||||
27
cpp/7_libNVVM/ptxgen/README.md
Normal file
27
cpp/7_libNVVM/ptxgen/README.md
Normal 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.
|
||||
277
cpp/7_libNVVM/ptxgen/ptxgen.c
Normal file
277
cpp/7_libNVVM/ptxgen/ptxgen.c
Normal 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;
|
||||
}
|
||||
39
cpp/7_libNVVM/ptxgen/test.ll
Normal file
39
cpp/7_libNVVM/ptxgen/test.ll
Normal 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}
|
||||
68
cpp/7_libNVVM/simple/CMakeLists.txt
Normal file
68
cpp/7_libNVVM/simple/CMakeLists.txt
Normal 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 ()
|
||||
6
cpp/7_libNVVM/simple/README.md
Normal file
6
cpp/7_libNVVM/simple/README.md
Normal 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.
|
||||
61
cpp/7_libNVVM/simple/simple-gpu64.ll
Normal file
61
cpp/7_libNVVM/simple/simple-gpu64.ll
Normal 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}
|
||||
251
cpp/7_libNVVM/simple/simple.c
Normal file
251
cpp/7_libNVVM/simple/simple.c
Normal 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;
|
||||
}
|
||||
54
cpp/7_libNVVM/syscalls/CMakeLists.txt
Normal file
54
cpp/7_libNVVM/syscalls/CMakeLists.txt
Normal 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)
|
||||
60
cpp/7_libNVVM/syscalls/malloc-free.ll
Normal file
60
cpp/7_libNVVM/syscalls/malloc-free.ll
Normal 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}
|
||||
92
cpp/7_libNVVM/syscalls/vprintf.ll
Normal file
92
cpp/7_libNVVM/syscalls/vprintf.ll
Normal 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
4
cpp/7_libNVVM/utils/build.bat
Executable 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
6
cpp/7_libNVVM/utils/build.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=../install ..
|
||||
make && make test && make install
|
||||
72
cpp/7_libNVVM/uvmlite/CMakeLists.txt
Normal file
72
cpp/7_libNVVM/uvmlite/CMakeLists.txt
Normal 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 ()
|
||||
59
cpp/7_libNVVM/uvmlite/README.md
Normal file
59
cpp/7_libNVVM/uvmlite/README.md
Normal 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
|
||||
320
cpp/7_libNVVM/uvmlite/uvmlite.c
Normal file
320
cpp/7_libNVVM/uvmlite/uvmlite.c
Normal 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;
|
||||
}
|
||||
61
cpp/7_libNVVM/uvmlite/uvmlite64.ll
Normal file
61
cpp/7_libNVVM/uvmlite/uvmlite64.ll
Normal 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}
|
||||
Reference in New Issue
Block a user