From: Luo <xionghu....@intel.com> This API compiles a program's source for all the devices or a specific device in the OpenCL context associated with program. The pre-processor runs before the program sources are compiled.
Signed-off-by: Luo <xionghu....@intel.com> --- backend/src/backend/program.cpp | 162 ++++++++++++++++++++++++++++++++++++++++ backend/src/backend/program.h | 10 +++ src/cl_api.c | 41 ++++++++++ src/cl_gbe_loader.cpp | 4 + src/cl_program.c | 109 +++++++++++++++++++++++++++ src/cl_program.h | 8 +- 6 files changed, 333 insertions(+), 1 deletion(-) diff --git a/backend/src/backend/program.cpp b/backend/src/backend/program.cpp index fab6bce..7cfc07e 100644 --- a/backend/src/backend/program.cpp +++ b/backend/src/backend/program.cpp @@ -842,6 +842,166 @@ namespace gbe { } #endif +#ifdef GBE_COMPILER_AVAILABLE + static gbe_program programCompileFromSource(uint32_t deviceID, + const char *source, + const char *temp_header_path, + size_t stringSize, + const char *options, + char *err, + size_t *errSize) + { + char clStr[] = "/tmp/XXXXXX.cl"; + int clFd = mkstemps(clStr, 3); + const std::string clName = std::string(clStr); + std::string clOpt; + + FILE *clFile = fdopen(clFd, "w"); + FATAL_IF(clFile == NULL, "Failed to open temporary file"); + + bool usePCH = OCL_USE_PCH; + bool findPCH = false; + + /* Because our header file is so big, we want to avoid recompile the header from + scratch. We use the PCH support of Clang to save the huge compiling time. + We just use the most general build opt to build the PCH header file, so if + user pass new build options here, the PCH can not pass the Clang's compitable + validating. Clang will do three kinds of compatible check: Language Option, + Target Option and Preprocessing Option. Other kinds of options such as the + CodeGen options will not affect the AST result, so no need to check. + + According to OpenCL 1.1's spec, the CL build options: + -D name=definition + If the definition is not used in our header, it is compitable + + -cl-single-precision-constant + -cl-denorms-are-zero + -cl-std= + Language options, really affect. + + -cl-opt-disable + -cl-mad-enable + -cl-no-signed-zeros + -cl-unsafe-math-optimizations + -cl-finite-math-only + -cl-fast-relaxed-math + CodeGen options, not affect + + -Werror + -w + Our header should not block the compiling because of warning. + + So we just disable the PCH validation of Clang and do the judgement by ourself. */ + + if(options) { + char *p; + /* FIXME: Though we can disable the pch valid check, and load pch successfully, + but these language opts and pre-defined macro will still generate the diag msg + to the diag engine of the Clang and cause the Clang to report error. + We filter them all here to avoid these. */ + const char * incompatible_opts[] = { + "-cl-single-precision-constant", +// "-cl-denorms-are-zero", + "-cl-fast-relaxed-math", + "-cl-std=", + }; + const char * incompatible_defs[] = { + "GET_FLOAT_WORD", + "__NV_CL_C_VERSION", + "GEN7_SAMPLER_CLAMP_BORDER_WORKAROUND" + }; + + for (unsigned int i = 0; i < sizeof(incompatible_opts)/sizeof(char *); i++ ) { + p = strstr(const_cast<char *>(options), incompatible_opts[i]); + if (p) { + usePCH = false; + break; + } + } + + if (usePCH) { + for (unsigned int i = 0; i < sizeof(incompatible_defs)/sizeof(char *); i++ ) { + p = strstr(const_cast<char *>(options), incompatible_defs[i]); + if (p) { + usePCH = false; + break; + } + } + } + + + clOpt += options; + } + + std::string dirs = OCL_PCH_PATH; + std::istringstream idirs(dirs); + std::string pchFileName; + + while (getline(idirs, pchFileName, ':')) { + if(access(pchFileName.c_str(), R_OK) == 0) { + findPCH = true; + break; + } + } + + if (usePCH && findPCH) { + clOpt += " -include-pch "; + clOpt += pchFileName; + clOpt += " "; + } else + fwrite(ocl_stdlib_str.c_str(), strlen(ocl_stdlib_str.c_str()), 1, clFile); + + if (!OCL_STRICT_CONFORMANCE) { + fwrite(ocl_mathfunc_fastpath_str.c_str(), strlen(ocl_mathfunc_fastpath_str.c_str()), 1, clFile); + } + + //for clCompilerProgram usage. + if(temp_header_path){ + clOpt += " -I "; + clOpt += temp_header_path; + clOpt += " "; + } + + // reset the file number in case we have inserted something into the kernel + std::string resetFileNum = "#line 1\n"; + fwrite(resetFileNum.c_str(), strlen(resetFileNum.c_str()), 1, clFile); + + // Write the source to the cl file + fwrite(source, strlen(source), 1, clFile); + fclose(clFile); + + gbe_program p; + static std::mutex gbe_mutex; + gbe_mutex.lock(); + //FIXME: if use new allocated context to link two modules there would be context mismatch + //for some functions, so we use global context now, need switch to new context later. + llvm::Module * out_module; + llvm::LLVMContext* llvm_ctx = &llvm::getGlobalContext(); + if (buildModuleFromSource(clName.c_str(), &out_module, llvm_ctx, clOpt.c_str(), + stringSize, err, errSize)) { + // Now build the program from llvm + size_t clangErrSize = 0; + if (err != NULL) { + GBE_ASSERT(errSize != NULL); + stringSize -= *errSize; + err += *errSize; + clangErrSize = *errSize; + } + + p = gbe_program_new_gen_program(deviceID, out_module, NULL); + + if (err != NULL) + *errSize += clangErrSize; + if (OCL_OUTPUT_BUILD_LOG && options) + llvm::errs() << options; + } else + p = NULL; + gbe_mutex.unlock(); + remove(clName.c_str()); + return p; + } +#endif + static size_t programGetGlobalConstantSize(gbe_program gbeProgram) { if (gbeProgram == NULL) return 0; const gbe::Program *program = (const gbe::Program*) gbeProgram; @@ -1006,6 +1166,7 @@ namespace gbe { } /* namespace gbe */ GBE_EXPORT_SYMBOL gbe_program_new_from_source_cb *gbe_program_new_from_source = NULL; +GBE_EXPORT_SYMBOL gbe_program_compile_from_source_cb *gbe_program_compile_from_source = NULL; GBE_EXPORT_SYMBOL gbe_program_new_from_binary_cb *gbe_program_new_from_binary = NULL; GBE_EXPORT_SYMBOL gbe_program_serialize_to_binary_cb *gbe_program_serialize_to_binary = NULL; GBE_EXPORT_SYMBOL gbe_program_new_from_llvm_cb *gbe_program_new_from_llvm = NULL; @@ -1048,6 +1209,7 @@ namespace gbe { CallBackInitializer(void) { gbe_program_new_from_source = gbe::programNewFromSource; + gbe_program_compile_from_source = gbe::programCompileFromSource; gbe_program_get_global_constant_size = gbe::programGetGlobalConstantSize; gbe_program_get_global_constant_data = gbe::programGetGlobalConstantData; gbe_program_delete = gbe::programDelete; diff --git a/backend/src/backend/program.h b/backend/src/backend/program.h index 077d0d3..45754c4 100644 --- a/backend/src/backend/program.h +++ b/backend/src/backend/program.h @@ -121,6 +121,16 @@ typedef gbe_program (gbe_program_new_from_source_cb)(uint32_t deviceID, char *err, size_t *err_size); extern gbe_program_new_from_source_cb *gbe_program_new_from_source; +/*! Create a new program from the given source code and compile it (zero terminated string) */ +typedef gbe_program (gbe_program_compile_from_source_cb)(uint32_t deviceID, + const char *source, + const char *temp_header_path, + size_t stringSize, + const char *options, + char *err, + size_t *err_size); +extern gbe_program_compile_from_source_cb *gbe_program_compile_from_source; + /*! Create a new program from the given blob */ typedef gbe_program (gbe_program_new_from_binary_cb)(uint32_t deviceID, const char *binary, size_t size); extern gbe_program_new_from_binary_cb *gbe_program_new_from_binary; diff --git a/src/cl_api.c b/src/cl_api.c index 8598088..790fbff 100644 --- a/src/cl_api.c +++ b/src/cl_api.c @@ -933,6 +933,47 @@ error: } cl_int +clCompileProgram(cl_program program , + cl_uint num_devices , + const cl_device_id * device_list , + const char * options , + cl_uint num_input_headers , + const cl_program * input_headers , + const char ** header_include_names , + void (CL_CALLBACK * pfn_notify )(cl_program, void *), + void * user_data ) +{ + cl_int err = CL_SUCCESS; + CHECK_PROGRAM(program); + INVALID_VALUE_IF (num_devices > 1); + INVALID_VALUE_IF (num_devices == 0 && device_list != NULL); + INVALID_VALUE_IF (num_devices != 0 && device_list == NULL); + INVALID_VALUE_IF (pfn_notify == 0 && user_data != NULL); + INVALID_VALUE_IF (num_input_headers == 0 && input_headers != NULL); + INVALID_VALUE_IF (num_input_headers != 0 && input_headers == NULL); + + /* Everything is easy. We only support one device anyway */ + if (num_devices != 0) { + assert(program->ctx); + INVALID_DEVICE_IF (device_list[0] != program->ctx->device); + } + + /* TODO support create program from binary */ + assert(program->source_type == FROM_LLVM || + program->source_type == FROM_SOURCE || + program->source_type == FROM_BINARY); + if((err = cl_program_compile(program, num_input_headers, input_headers, header_include_names, options)) != CL_SUCCESS) { + goto error; + } + program->is_built = CL_TRUE; + + if (pfn_notify) pfn_notify(program, user_data); + +error: + return err; +} + +cl_int clUnloadCompiler(void) { return CL_SUCCESS; diff --git a/src/cl_gbe_loader.cpp b/src/cl_gbe_loader.cpp index da83c9a..71c1492 100644 --- a/src/cl_gbe_loader.cpp +++ b/src/cl_gbe_loader.cpp @@ -41,6 +41,10 @@ struct GbeLoaderInitializer if (gbe_program_new_from_source == NULL) return; + gbe_program_compile_from_source = *(gbe_program_compile_from_source_cb **)dlsym(dlh, "gbe_program_compile_from_source"); + if (gbe_program_compile_from_source == NULL) + return; + gbe_program_serialize_to_binary = *(gbe_program_serialize_to_binary_cb **)dlsym(dlh, "gbe_program_serialize_to_binary"); if (gbe_program_serialize_to_binary == NULL) return; diff --git a/src/cl_program.c b/src/cl_program.c index 6910330..017935b 100644 --- a/src/cl_program.c +++ b/src/cl_program.c @@ -33,6 +33,9 @@ #include <stdint.h> #include <string.h> #include <assert.h> +#include <unistd.h> +#include <sys/stat.h> +#include <libgen.h> static void cl_program_release_sources(cl_program p) @@ -449,6 +452,112 @@ error: return err; } +LOCAL cl_int +cl_program_compile(cl_program p, + cl_uint num_input_headers, + const cl_program * input_headers, + const char ** header_include_names, + const char* options) +{ + cl_int err = CL_SUCCESS; + int i = 0; + int copyed = 0; + + if (p->ref_n > 1) + return CL_INVALID_OPERATION; + + if (options) { + if(p->build_opts == NULL || strcmp(options, p->build_opts) != 0) { + if(p->build_opts) { + cl_free(p->build_opts); + p->build_opts = NULL; + } + TRY_ALLOC (p->build_opts, cl_calloc(strlen(options) + 1, sizeof(char))); + memcpy(p->build_opts, options, strlen(options)); + + p->source_type = p->source ? FROM_SOURCE : p->binary ? FROM_BINARY : FROM_LLVM; + } + } + + if (options == NULL && p->build_opts) { + p->source_type = p->source ? FROM_SOURCE : p->binary ? FROM_BINARY : FROM_LLVM; + + cl_free(p->build_opts); + p->build_opts = NULL; + } + + char temp_header_path[255] = "/tmp/beignet_header/"; + if (p->source_type == FROM_SOURCE) { + + if (!CompilerSupported()) { + err = CL_COMPILER_NOT_AVAILABLE; + goto error; + } + + //write the headers to /tmp/beignet_header for include. + for (i = 0; i < num_input_headers; i++) { + if(header_include_names[i] == NULL || input_headers[i] == NULL) + continue; + + char temp_path[255]; + strcpy(temp_path, temp_header_path); + strcat(temp_path, header_include_names[i]); + char* dirc = strdup(temp_path); + char* dir = dirname(dirc); + mkdir(dir, 0755); + if(access(dir, R_OK|W_OK) != 0){ + err = CL_COMPILE_PROGRAM_FAILURE; + goto error; + } + free(dirc); + + FILE* pfile = fopen(temp_path, "wb"); + if(pfile){ + fwrite(input_headers[i]->source, strlen(input_headers[i]->source), 1, pfile); + fclose(pfile); + }else{ + err = CL_COMPILE_PROGRAM_FAILURE; + goto error; + } + } + + p->opaque = gbe_program_compile_from_source(p->ctx->device->vendor_id, p->source, temp_header_path, + p->build_log_max_sz, options, p->build_log, &p->build_log_sz); + + system("rm /tmp/beignet_header/* -rf"); + + if (UNLIKELY(p->opaque == NULL)) { + if (p->build_log_sz > 0 && strstr(p->build_log, "error: error reading 'options'")) + err = CL_INVALID_BUILD_OPTIONS; + else + err = CL_BUILD_PROGRAM_FAILURE; + goto error; + } + + /* Create all the kernels */ + TRY (cl_program_load_gen_program, p); + p->source_type = FROM_LLVM; + } + + for (i = 0; i < p->ker_n; i ++) { + const gbe_kernel opaque = gbe_program_get_kernel(p->opaque, i); + p->bin_sz += gbe_kernel_get_code_size(opaque); + } + + TRY_ALLOC (p->bin, cl_calloc(p->bin_sz, sizeof(char))); + for (i = 0; i < p->ker_n; i ++) { + const gbe_kernel opaque = gbe_program_get_kernel(p->opaque, i); + size_t sz = gbe_kernel_get_code_size(opaque); + + memcpy(p->bin + copyed, gbe_kernel_get_code(opaque), sz); + copyed += sz; + } + +error: + p->is_built = 1; + return err; +} + LOCAL cl_kernel cl_program_create_kernel(cl_program p, const char *name, cl_int *errcode_ret) { diff --git a/src/cl_program.h b/src/cl_program.h index b9bf395..a576d37 100644 --- a/src/cl_program.h +++ b/src/cl_program.h @@ -110,6 +110,12 @@ cl_program_create_from_llvm(cl_context context, /* Build the program as specified by OCL */ extern cl_int cl_program_build(cl_program p, const char* options); - +/* Compile the program as specified by OCL */ +extern cl_int +cl_program_compile(cl_program p, + cl_uint num_input_headers, + const cl_program * input_headers, + const char ** header_include_names, + const char* options); #endif /* __CL_PROGRAM_H__ */ -- 1.8.1.2 _______________________________________________ Beignet mailing list Beignet@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/beignet