[Beignet] [PATCH V2 1/3] add [opencl-1.2] API clCompileProgram.
xionghu.luo at intel.com
xionghu.luo at intel.com
Wed Jun 4 19:44:15 PDT 2014
From: Luo <xionghu.luo at 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.luo at intel.com>
---
backend/src/backend/program.cpp | 161 ++++++++++++++++++++++++++++++++++++++++
backend/src/backend/program.h | 10 +++
src/cl_api.c | 41 ++++++++++
src/cl_gbe_loader.cpp | 4 +
src/cl_program.c | 103 +++++++++++++++++++++++++
src/cl_program.h | 8 +-
6 files changed, 326 insertions(+), 1 deletion(-)
diff --git a/backend/src/backend/program.cpp b/backend/src/backend/program.cpp
index fab6bce..5af66fb 100644
--- a/backend/src/backend/program.cpp
+++ b/backend/src/backend/program.cpp
@@ -842,6 +842,165 @@ 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;
+ // will delete the module and llvm_ctx in the destructor of GenProgram.
+ 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
+ static std::mutex gbe_mutex;
+ gbe_mutex.lock();
+ 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;
+ gbe_mutex.unlock();
+ if (OCL_OUTPUT_BUILD_LOG && options)
+ llvm::errs() << options;
+ } else
+ p = NULL;
+ 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 +1165,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 +1208,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..9fd202a 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,106 @@ 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) {
+ //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
More information about the Beignet
mailing list