Apply consistent code formatting across the repo. Add clang-format and pre-commit hooks.

This commit is contained in:
Rob Armstrong
2025-03-27 10:30:07 -07:00
parent 2cd58fbc9a
commit ceab6e8bcc
782 changed files with 107230 additions and 106548 deletions

View File

@@ -32,40 +32,42 @@
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 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;
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 DDPF_RGB 0x40
#define DDSD_CAPS 0x1
#define DDSD_HEIGHT 0x2
#define DDSD_WIDTH 0x4
#define DDSD_CAPS 0x1
#define DDSD_HEIGHT 0x2
#define DDSD_WIDTH 0x4
#define DDSD_PIXELFORMAT 0x1000
#define DDSCAPS_TEXTURE 0x1000
@@ -75,46 +77,46 @@ struct DDSHeader {
/// (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));
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.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;
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);
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();
str.close();
}
#endif

View File

@@ -24,6 +24,7 @@
// (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>
@@ -34,10 +35,8 @@
#include <llvm/Support/Path.h>
#include <llvm/Support/Program.h>
#include <llvm/Support/raw_ostream.h>
#include <nvvm.h>
#include <cassert>
#include <memory>
#include <nvvm.h>
#include <string>
#include "DDSWriter.h"
@@ -45,289 +44,282 @@
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));
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 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);
}
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);
}
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");
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);
// 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);
// 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 argument types.
Type *paramTys[] = {floatGenericPtrTy};
// Kernel function type.
FunctionType *funcTy = FunctionType::get(voidTy, paramTys, false);
// 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");
// 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);
// '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();
// 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);
// 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);
// 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;
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);
std::string generatePtx(const std::string &module, int devMajor, int devMinor, const char *moduleName)
{
assert(moduleName);
// libNVVM initialization.
nvvmProgram compileUnit;
checkNVVMCall(nvvmCreateProgram(&compileUnit));
// 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);
// 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);
}
// 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));
// 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));
// Clean-up libNVVM.
checkNVVMCall(nvvmDestroyProgram(&compileUnit));
return std::string(ptx);
return std::string(ptx);
}
int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, "cuda-c-linking");
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);
// 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;
}
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";
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));
// 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";
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 < 5) {
errs() << "ERROR: Device 0 is not sm_50 or later.\n";
return 1;
}
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 < 5) {
errs() << "ERROR: Device 0 is not sm_50 or later.\n";
return 1;
}
// Generate the IR module
LLVMContext ctx;
std::string moduleStr;
auto module = generateModule(ctx);
// 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());
}
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();
}
// 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;
}
// 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, 0, device));
// Create the CUDA context.
CUcontext context;
checkCudaErrors(cuCtxCreate(&context, 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 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));
// 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));
// Now that the CUBIN is loaded, we can release the linker.
checkCudaErrors(cuLinkDestroy(linker));
// Get kernel function.
CUfunction function;
checkCudaErrors(cuModuleGetFunction(&function, cudaModule, "kernel"));
// 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];
// 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;
// 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));
// 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));
// 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";
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));
// Cleanup.
delete[] data;
checkCudaErrors(cuMemFree(devBuffer));
checkCudaErrors(cuModuleUnload(cudaModule));
checkCudaErrors(cuCtxDestroy(context));
return 0;
return 0;
}

View File

@@ -29,57 +29,56 @@
// 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) {
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;
// 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;
float R, G, B, A;
// Scale coordinates to (-2.5, 1) and (-1, 1)
// Scale coordinates to (-2.5, 1) and (-1, 1)
float NormX = (float)DataX / (float)Width;
NormX *= 3.5f;
NormX -= 2.5f;
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 NormY = (float)DataY / (float)Height;
NormY *= 2.0f;
NormY -= 1.0f;
float X0 = NormX;
float Y0 = NormY;
float X0 = NormX;
float Y0 = NormY;
float X = 0.0f;
float Y = 0.0f;
float X = 0.0f;
float Y = 0.0f;
unsigned Iter = 0;
unsigned MaxIter = 1000;
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;
// 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;
X = XTemp;
Iter++;
}
Iter++;
}
unsigned ColorG = Iter % 50;
unsigned ColorB = Iter % 25;
unsigned ColorG = Iter % 50;
unsigned ColorB = Iter % 25;
R = 0.0f;
G = (float)ColorG / 50.0f;
B = (float)ColorB / 25.0f;
A = 1.0f;
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;
Data[DataY * Width * 4 + DataX * 4 + 0] = R;
Data[DataY * Width * 4 + DataX * 4 + 1] = G;
Data[DataY * Width * 4 + DataX * 4 + 2] = B;
Data[DataY * Width * 4 + DataX * 4 + 3] = A;
}

View File

@@ -38,6 +38,6 @@ add_test(NAME test-cuda-shared-memory-extern_shared_memory
set_tests_properties(test-cuda-shared-memory-shared_memory
test-cuda-shared-memory-extern_shared_memory
PROPERTIES FIXTURES_REQUIRED PTXGENTEST)
file(COPY shared_memory.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY extern_shared_memory.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")

View File

@@ -16,7 +16,7 @@ section from CUDA C Programming Guide
Kernel Launch APIs
------------------
Device-side kernel launches can be implemented using the following two 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
@@ -34,8 +34,8 @@ form shown below before it is used.
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
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.
@@ -60,21 +60,21 @@ given below:
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,
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.
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,
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
is the smallest multiple of the parameter size that is greater than the offset
of the last byte taken by the preceding parameter. The maximum size of the
parameter buffer is 4KB.

View File

@@ -37,221 +37,224 @@
// 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;
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 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;
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;
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);
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);
}
// 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);
}
// 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);
// 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);
}
// 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);
}
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);
}
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);
}
result = nvvmDestroyProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmDestroyProgram: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
return ptx;
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);
}
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 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 < 5) {
fprintf(stderr, "Device 0 is not sm_50 or later\n");
exit(EXIT_FAILURE);
}
return cuDevice;
// 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 < 5) {
fprintf(stderr, "Device 0 is not sm_50 or later\n");
exit(EXIT_FAILURE);
}
return cuDevice;
}
static CUresult buildKernel(CUcontext *phContext, CUdevice *phDevice,
CUmodule *phModule, CUfunction *phKernel) {
assert(phContext && phDevice && phModule && phKernel);
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, 0, *phDevice));
// Initialize CUDA and obtain the device's compute capability.
int major = 0, minor = 0;
*phDevice = cudaDeviceInit(&major, &minor);
checkCudaErrors(cuCtxCreate(phContext, 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");
// 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);
// 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));
// 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));
// Load the linked binary.
checkCudaErrors(cuModuleLoadData(phModule, cubin));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "kernel"));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "kernel"));
checkCudaErrors(cuLinkDestroy(linkState));
free(ll);
free(ptx);
return CUDA_SUCCESS;
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;
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));
// 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));
// 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;
}
if (hModule) {
checkCudaErrors(cuModuleUnload(hModule));
hModule = 0;
}
if (hContext) {
checkCudaErrors(cuCtxDestroy(hContext));
hContext = 0;
}
return 0;
return 0;
}

View File

@@ -33,235 +33,245 @@
/* 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 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() _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
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);
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 *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;
}
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);
// Concatenate libnvvmPath with libdevice.
*buffer = strcat(strcpy(*buffer, libnvvmPath), libdevice);
return PTXGEN_SUCCESS;
return PTXGEN_SUCCESS;
}
static PTXGenStatus addFileToProgram(const char *filename, nvvmProgram prog,
PTXGENInput inputType) {
assert(filename);
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;
}
// 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);
// 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);
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;
}
// 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;
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);
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;
}
free(log);
}
return status;
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;
}
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);
// 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;
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 = addFileToProgram(libDeviceName, prog, PTXGEN_INPUT_LIBDEVICE);
free(libDeviceName);
if (status != PTXGEN_SUCCESS) {
nvvmDestroyProgram(&prog);
return status;
}
}
status |= dumpCompilationLog(prog);
// Display the compilation log.
status |= dumpCompilationLog(prog);
if (status & PTXGEN_LIBNVVM_VERIFICATION_ERROR) {
nvvmDestroyProgram(&prog);
return status;
}
// Release the resources.
nvvmDestroyProgram(&prog);
// 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);
}
}
return status;
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");
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;
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 (argc == 1) {
showUsage();
return PTXGEN_INVALID_USAGE;
}
}
if (numFilenames == 0) {
showUsage();
status = PTXGEN_INVALID_USAGE;
} else {
// Use libNVVM to generate PTX.
status = generatePTX(numOptions, options, numFilenames, filenames);
}
// 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;
}
}
free(options);
free(filenames);
return status;
if (numFilenames == 0) {
showUsage();
status = PTXGEN_INVALID_USAGE;
}
else {
// Use libNVVM to generate PTX.
status = generatePTX(numOptions, options, numFilenames, filenames);
}
free(options);
free(filenames);
return status;
}

View File

@@ -35,212 +35,217 @@
// 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 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);
}
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);
// 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));
if (*devMajor < 5) {
fprintf(stderr, "Device 0 is not sm_50 or later\n");
exit(EXIT_FAILURE);
}
checkCudaErrors(cuDeviceGetAttribute(
devMinor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice));
// Obtain the device's compute capability.
checkCudaErrors(cuDeviceGetAttribute(devMajor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice));
if (*devMajor < 5) {
fprintf(stderr, "Device 0 is not sm_50 or later\n");
exit(EXIT_FAILURE);
}
checkCudaErrors(cuDeviceGetAttribute(devMinor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice));
return cuDevice;
return cuDevice;
}
static CUresult initCUDA(CUcontext *phContext, CUdevice *phDevice,
CUmodule *phModule, CUfunction *phKernel,
const char *ptx) {
assert(phContext && phDevice && phModule && phKernel && ptx);
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, 0, *phDevice));
// Create a CUDA context on the device.
checkCudaErrors(cuCtxCreate(phContext, 0, *phDevice));
// Load the PTX.
checkCudaErrors(cuModuleLoadDataEx(phModule, ptx, 0, 0, 0));
// Load the PTX.
checkCudaErrors(cuModuleLoadDataEx(phModule, ptx, 0, 0, 0));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "simple"));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "simple"));
return CUDA_SUCCESS;
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 *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);
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);
}
// 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);
}
// 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);
// 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);
}
// 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");
// 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);
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;
return 0;
}

View File

@@ -35,6 +35,6 @@ add_test(NAME test-syscalls-vprintf
set_tests_properties(test-syscalls-vprintf test-syscalls-malloc-free
PROPERTIES FIXTURES_REQUIRED PTXGENTEST)
file(COPY malloc-free.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
file(COPY vprintf.ll DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")

View File

@@ -31,7 +31,7 @@ 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().

View File

@@ -34,290 +34,287 @@
#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); \
}
#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);
}
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);
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);
}
// 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);
}
// 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);
// 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);
}
// 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);
}
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);
}
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);
}
result = nvvmDestroyProgram(&program);
if (result != NVVM_SUCCESS) {
fprintf(stderr, "nvvmDestroyProgram: Failed\n");
free(ptx);
exit(EXIT_FAILURE);
}
return ptx;
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;
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;
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);
}
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 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 < 5) {
fprintf(stderr, "Device 0 is not sm_50 or later\n");
exit(EXIT_FAILURE);
}
// 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 < 5) {
fprintf(stderr, "Device 0 is not sm_50 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 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);
}
// 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;
return cuDevice;
}
static CUresult buildKernel(CUcontext *phContext, CUdevice *phDevice,
CUmodule *phModule, CUfunction *phKernel) {
assert(phContext && phDevice && phModule && phKernel);
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);
// 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, 0, *phDevice));
// Create a context on the device.
checkCudaErrors(cuCtxCreate(phContext, 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");
// 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);
// 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));
// Load module from PTX.
checkCudaErrors(cuModuleLoadDataEx(phModule, ptx, 0, NULL, NULL));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "test_kernel"));
// Locate the kernel entry point.
checkCudaErrors(cuModuleGetFunction(phKernel, *phModule, "test_kernel"));
free(ll);
free(ptx);
return CUDA_SUCCESS;
free(ll);
free(ptx);
return CUDA_SUCCESS;
}
int main(void) {
const unsigned int nThreads = 1;
const unsigned int nBlocks = 1;
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;
// 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));
// 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);
}
// 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"));
// 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;
// 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);
}
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;
// 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));
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;
}
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);
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);
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;
// 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");
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());
// 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);
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;
}
if (hModule) {
checkCudaErrors(cuModuleUnload(hModule));
hModule = 0;
}
if (hContext) {
checkCudaErrors(cuCtxDestroy(hContext));
hContext = 0;
}
return 0;
return 0;
}