diff --git a/examples_ptx/ptxgen/Makefile b/examples_ptx/ptxgen/Makefile index f1aad5b6..48b3b7ea 100644 --- a/examples_ptx/ptxgen/Makefile +++ b/examples_ptx/ptxgen/Makefile @@ -3,12 +3,12 @@ CUDA60 = /usr/local/cuda-6.0 all: ptxgen55 ptxgen60 -ptxgen60: ptxgen.c - gcc -O3 -o ptxgen60 ptxgen.c -L$(CUDA60)/nvvm/lib64 -lnvvm -I$(CUDA60)/nvvm/include -I$(CUDA60)/include -DLIBDEVICE_MAJOR_VERSION=1 -DLIBDEVICE_MINOR_VERSION=0 -DLIBNVVM_HOME=$(CUDA60)/nvvm -Wl,-rpath,$(CUDA60)/nvvm/lib64 +ptxgen60: ptxgen.cpp + g++ -O3 -o ptxgen60 ptxgen.cpp -L$(CUDA60)/nvvm/lib64 -lnvvm -I$(CUDA60)/nvvm/include -I$(CUDA60)/include -DLIBDEVICE_MAJOR_VERSION=1 -DLIBDEVICE_MINOR_VERSION=0 -DLIBNVVM_HOME=$(CUDA60)/nvvm -Wl,-rpath,$(CUDA60)/nvvm/lib64 -ptxgen55: ptxgen.c - gcc -O3 -o ptxgen55 ptxgen.c -L$(CUDA55)/nvvm/lib64 -lnvvm -I$(CUDA55)/nvvm/include -I$(CUDA55)/include -DLIBDEVICE_MAJOR_VERSION=1 -DLIBDEVICE_MINOR_VERSION=0 -DLIBNVVM_HOME=$(CUDA55)/nvvm -Wl,-rpath,$(CUDA55)/nvvm/lib64 +ptxgen55: ptxgen.cpp + g++ -O3 -o ptxgen55 ptxgen.cpp -L$(CUDA55)/nvvm/lib64 -lnvvm -I$(CUDA55)/nvvm/include -I$(CUDA55)/include -DLIBDEVICE_MAJOR_VERSION=1 -DLIBDEVICE_MINOR_VERSION=0 -DLIBNVVM_HOME=$(CUDA55)/nvvm -Wl,-rpath,$(CUDA55)/nvvm/lib64 clean: diff --git a/examples_ptx/ptxgen/ptxgen.cpp b/examples_ptx/ptxgen/ptxgen.cpp new file mode 100644 index 00000000..09f43b8d --- /dev/null +++ b/examples_ptx/ptxgen/ptxgen.cpp @@ -0,0 +1,300 @@ +/* + * Copyright 1993-2012 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#include + +#include +#include +#include + +/* Two levels of indirection to stringify LIBDEVICE_MAJOR_VERSION and + * LIBDEVICE_MINOR_VERSION correctly. */ +#define getLibDeviceNameForArch(ARCH) \ + _getLibDeviceNameForArch(ARCH, \ + LIBDEVICE_MAJOR_VERSION, \ + LIBDEVICE_MINOR_VERSION) +#define _getLibDeviceNameForArch(ARCH, MAJOR, MINOR) \ + __getLibDeviceNameForArch(ARCH, MAJOR, MINOR) +#define __getLibDeviceNameForArch(ARCH, MAJOR, MINOR) \ + ("/libdevice/libdevice.compute_" #ARCH "." #MAJOR #MINOR ".bc") + +#define getLibnvvmHome _getLibnvvmHome(LIBNVVM_HOME) +#define _getLibnvvmHome(NVVM_HOME) __getLibnvvmHome(NVVM_HOME) +#define __getLibnvvmHome(NVVM_HOME) (#NVVM_HOME) + +typedef struct stat Stat; + +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 +}; +#define PTXGENStatus int + +static PTXGENStatus getLibDeviceName(int computeArch, char **buffer) +{ + const char *libnvvmPath = getLibnvvmHome; + const char *libdevice = NULL; + + if (libnvvmPath == NULL) { + fprintf(stderr, "The environment variable LIBNVVM_HOME undefined\n"); + return PTXGEN_LIBNVVM_HOME_UNDEFINED; + } + + /* Use libdevice for compute_20, if the target is not compute_20, compute_30, + * or compute_35. */ + switch (computeArch) { + default: + libdevice = getLibDeviceNameForArch(20); + break; + case 30: + libdevice = getLibDeviceNameForArch(30); + break; + case 35: + libdevice = getLibDeviceNameForArch(35); + break; + } + + *buffer = (char *) malloc(strlen(libnvvmPath) + strlen(libdevice) + 1); + if (*buffer == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + return PTXGEN_BAD_ALLOC_ERROR; + } + + /* Concatenate libnvvmPath and name. */ + *buffer = strcat(strcpy(*buffer, libnvvmPath), libdevice); + + return PTXGEN_SUCCESS; +} + +static PTXGENStatus addFileToProgram(const char *filename, nvvmProgram prog) +{ + char *buffer; + size_t size; + Stat fileStat; + + /* 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 buffer for the input. */ + fstat(fileno(f), &fileStat); + buffer = (char *) malloc(fileStat.st_size); + if (buffer == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + return PTXGEN_BAD_ALLOC_ERROR; + } + 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); + + if (nvvmAddModuleToProgram(prog, buffer, size, filename) != 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; +} + +static PTXGENStatus generatePTX(int numOptions, const char **options, + int numFilenames, const char **filenames, + int computeArch) +{ + PTXGENStatus status; + nvvmProgram prog; + char *libDeviceName; + int i; + + /* Create the compiliation unit. */ + if (nvvmCreateProgram(&prog) != NVVM_SUCCESS) { + fprintf(stderr, "Failed to create the compilation unit\n"); + return PTXGEN_LIBNVVM_ERROR; + } + + /* Add libdevice. */ + status = getLibDeviceName(computeArch, &libDeviceName); + if (status != PTXGEN_SUCCESS) { + nvvmDestroyProgram(&prog); + return status; + } + status = addFileToProgram(libDeviceName, prog); + free(libDeviceName); + if (status != PTXGEN_SUCCESS) { + nvvmDestroyProgram(&prog); + return status; + } + + /* Add the module to the compilation unit. */ + for (i = 0; i < numFilenames; ++i) { + status = addFileToProgram(filenames[i], prog); + 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"); + status |= PTXGEN_LIBNVVM_VERIFICATION_ERROR; + } + + /* Print warnings and errors. */ + { + size_t logSize; + char *log; + if (nvvmGetProgramLogSize(prog, &logSize) != NVVM_SUCCESS) { + fprintf(stderr, "Failed to get the compilation log size\n"); + status |= PTXGEN_LIBNVVM_ERROR; + } else { + log = (char *) 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); + } + } + + 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; + char *ptx; + if (nvvmGetCompiledResultSize(prog, &ptxSize) != NVVM_SUCCESS) { + fprintf(stderr, "Failed to get the PTX output size\n"); + status |= PTXGEN_LIBNVVM_ERROR; + } else { + ptx = (char *) 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); + } + } + + /* Print warnings and errors. */ + { + size_t logSize; + char *log; + if (nvvmGetProgramLogSize(prog, &logSize) != NVVM_SUCCESS) { + fprintf(stderr, "Failed to get the compilation log size\n"); + status |= PTXGEN_LIBNVVM_ERROR; + } else { + log = (char *) 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); + } + } + + /* Release the resources. */ + nvvmDestroyProgram(&prog); + + return PTXGEN_SUCCESS; +} + +static void showUsage() +{ + 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 numOptions = 0; + char **options = NULL; + int numFilenames = 0; + char **filenames = NULL; + int computeArch = 35; + int i; + + /* Process the command-line arguments to extract the libnvvm options and the + * input file names. */ + if (argc == 1) { + showUsage(); + return PTXGEN_INVALID_USAGE; + } + + options = (char **) malloc((argc ) * sizeof (char *)); + filenames = (char **) malloc((argc - 1) * sizeof (char *)); + + for (i = 1; i < argc; ++i) { + if (argv[i][0] == '-') { + options[numOptions] = argv[i]; + ++numOptions; + } else { + filenames[numFilenames] = argv[i]; + ++numFilenames; + } + } + static char sm_35opt[] = "-arch=compute_35"; + options[numOptions] = sm_35opt; + numOptions++; + + if (numFilenames == 0) { + /* If no input filename is found, then show the usage. */ + showUsage(); + status = PTXGEN_INVALID_USAGE; + } else { + /* Run libnvvm to generate PTX. */ + status = generatePTX(numOptions, (const char **) options, + numFilenames, (const char **) filenames, + computeArch); + } + + free(options); + free(filenames); + return status; +}