[Beignet] [PATCH 2/4] add [opencl-1.2] API clCompileProgram.

xionghu.luo at intel.com xionghu.luo at intel.com
Tue May 27 19:52:36 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/gen_program.cpp |   1 -
 backend/src/backend/program.cpp     | 158 +++++++++++++++++++++++++++++++++++-
 backend/src/backend/program.h       |  10 +++
 src/cl_api.c                        |  41 ++++++++++
 src/cl_program.c                    | 103 +++++++++++++++++++++++
 src/cl_program.h                    |   8 +-
 6 files changed, 318 insertions(+), 3 deletions(-)

diff --git a/backend/src/backend/gen_program.cpp b/backend/src/backend/gen_program.cpp
index 74b6fa1..7019060 100644
--- a/backend/src/backend/gen_program.cpp
+++ b/backend/src/backend/gen_program.cpp
@@ -92,7 +92,6 @@ namespace gbe {
     }
 
     if(llvm_ctx){
-      delete (llvm::LLVMContext*)llvm_ctx;
       llvm_ctx = NULL;
     }
   }
diff --git a/backend/src/backend/program.cpp b/backend/src/backend/program.cpp
index 90306cc..18895cd 100644
--- a/backend/src/backend/program.cpp
+++ b/backend/src/backend/program.cpp
@@ -801,7 +801,161 @@ namespace gbe {
     gbe_program p;
     // will delete the module and llvm_ctx in the destructor of GenProgram.
     llvm::Module * out_module;
-    llvm::LLVMContext* llvm_ctx = new llvm::LLVMContext;
+    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_from_llvm(deviceID, NULL, out_module, llvm_ctx, stringSize,
+                                    err, errSize, optLevel);
+      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;
+  }
+
+  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.
+    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
@@ -991,6 +1145,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;
@@ -1031,6 +1186,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 5c72964..8d434ba 100644
--- a/backend/src/backend/program.h
+++ b/backend/src/backend/program.h
@@ -120,6 +120,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 3a77dcd..36b523a 100644
--- a/src/cl_api.c
+++ b/src/cl_api.c
@@ -897,6 +897,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_program.c b/src/cl_program.c
index 7186d36..ed486d0 100644
--- a/src/cl_program.c
+++ b/src/cl_program.c
@@ -32,6 +32,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)
@@ -443,6 +446,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 989bb8d..1e156c7 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