[Beignet] [PATCH 3/3] Modify the PCH using in the building process.

junyan.he at inbox.com junyan.he at inbox.com
Thu Dec 12 23:31:03 PST 2013


From: Junyan He <junyan.he at linux.intel.com>

The source building process will first find the
PCH version based on the CL option and then do
the conformance check. If passed, the PCH will
be used and if failed, it will fallback to building
from scratch.

Signed-off-by: Junyan He <junyan.he at linux.intel.com>
---
 backend/src/backend/program.cpp | 206 ++++++++++++++++++++++++++++------------
 1 file changed, 147 insertions(+), 59 deletions(-)

diff --git a/backend/src/backend/program.cpp b/backend/src/backend/program.cpp
index fc9b03c..6c1a652 100644
--- a/backend/src/backend/program.cpp
+++ b/backend/src/backend/program.cpp
@@ -30,6 +30,7 @@
 #include "ir/liveness.hpp"
 #include "ir/value.hpp"
 #include "ir/unit.hpp"
+#include "clang/Serialization/ASTReader.h"
 #include "llvm/llvm_to_gen.hpp"
 #include "llvm/Config/config.h"
 #include "llvm/Support/Threading.h"
@@ -464,13 +465,25 @@ namespace gbe {
     GBE_SAFE_DELETE(program);
   }
 
+  extern std::string ocl_stdlib_str;
   BVAR(OCL_OUTPUT_BUILD_LOG, false);
-  static bool buildModuleFromSource(const char* input, const char* output, std::string options,
-                                    size_t stringSize, char *err, size_t *errSize) {
+
+  static bool createClangInstance(FILE *clFile, const char *input,
+          const char *source, const char* pch_name,
+          std::vector<std::string>& cl_opts, std::vector<std::string>& define_opts,
+          clang::CompilerInstance& Clang, std::string& ErrorString,
+          clang::DiagnosticsEngine*& Diags_ptr) {
     // Arguments to pass to the clang frontend
     vector<const char *> args;
     bool bOpt = true;
     bool bFastMath = false;
+    bool pch_valid = false;
+
+    std::string options;
+    for (auto it = std::begin(cl_opts); it != std::end(cl_opts); ++it) {
+      options += *it;
+      options += " ";
+    }
 
     vector<std::string> useless; //hold substrings to avoid c_str free
     size_t start = 0, end = 0;
@@ -478,10 +491,9 @@ namespace gbe {
        clang unsupport options:
        -cl-denorms-are-zero, -cl-strict-aliasing
        -cl-no-signed-zeros, -cl-fp32-correctly-rounded-divide-sqrt
-       all support options, refer to clang/include/clang/Driver/Options.inc
-    */
+       all support options, refer to clang/include/clang/Driver/Options.inc */
     const std::string unsupportedOptions("-cl-denorms-are-zero, -cl-strict-aliasing,"
-                                         "-cl-no-signed-zeros, -cl-fp32-correctly-rounded-divide-sqrt");
+            "-cl-no-signed-zeros, -cl-fp32-correctly-rounded-divide-sqrt");
     while (end != std::string::npos) {
       end = options.find(' ', start);
       std::string str = options.substr(start, end - start);
@@ -507,11 +519,11 @@ namespace gbe {
     // FIXME as we don't support function call currently, we may encounter
     // build problem with -O0 as we rely on always inline all functions option. 
     if(bOpt)
-      args.push_back("-O2");
+        args.push_back("-O2");
     else
-      args.push_back("-O1");
+        args.push_back("-O1");
     if(bFastMath)
-      args.push_back("-D __FAST_RELAXED_MATH__=1");
+        args.push_back("-D __FAST_RELAXED_MATH__=1");
 #if LLVM_VERSION_MINOR <= 2
     args.push_back("-triple");
     args.push_back("nvptx");
@@ -524,7 +536,6 @@ namespace gbe {
     args.push_back(input);
 
     // The compiler invocation needs a DiagnosticsEngine so it can report problems
-    std::string ErrorString;
     llvm::raw_string_ostream ErrorInfo(ErrorString);
     llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts = new clang::DiagnosticOptions();
     DiagOpts->ShowCarets = false;
@@ -533,26 +544,26 @@ namespace gbe {
     args.push_back("ptx32");
 
     clang::TextDiagnosticPrinter *DiagClient =
-                             new clang::TextDiagnosticPrinter(ErrorInfo, *DiagOpts)
-    llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
-    clang::DiagnosticsEngine Diags(DiagID, DiagClient);
+        new clang::TextDiagnosticPrinter(ErrorInfo, *DiagOpts)
+        llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
+    clang::DiagnosticsEngine *Diags = new clang::DiagnosticsEngine(DiagID, DiagClient);
 #else
     args.push_back("-ffp-contract=off");
 
     clang::TextDiagnosticPrinter *DiagClient =
-                             new clang::TextDiagnosticPrinter(ErrorInfo, &*DiagOpts);
+        new clang::TextDiagnosticPrinter(ErrorInfo, &*DiagOpts);
     llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID(new clang::DiagnosticIDs());
-    clang::DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
+    clang::DiagnosticsEngine *Diags = new clang::DiagnosticsEngine(DiagID, &*DiagOpts, DiagClient);
 #endif /* LLVM_VERSION_MINOR <= 1 */
     // Create the compiler invocation
     llvm::OwningPtr<clang::CompilerInvocation> CI(new clang::CompilerInvocation);
     clang::CompilerInvocation::CreateFromArgs(*CI,
-                                              &args[0],
-                                              &args[0] + args.size(),
-                                              Diags);
+            &args[0],
+            &args[0] + args.size(),
+            *Diags);
+
+    Diags_ptr = Diags;
 
-    // Create the compiler instance
-    clang::CompilerInstance Clang;
     Clang.setInvocation(CI.take());
     // Get ready to report problems
 #if LLVM_VERSION_MINOR <= 2
@@ -569,8 +580,40 @@ namespace gbe {
     clang::LangOptions & lang_opts = Clang.getLangOpts();
     lang_opts.OpenCL = 1;
 
-    clang::PreprocessorOptions prep_opt = Clang.getPreprocessorOpts();
-    prep_opt.DisablePCHValidation = 1;
+    clang::PreprocessorOptions& prep_opt = Clang.getPreprocessorOpts();
+    /* Check the PCH conformance here. */
+    if (pch_name) {
+      Clang.createFileManager();
+      clang::FileManager& FileMgr = Clang.getFileManager();
+      if (clang::ASTReader::isAcceptableASTFile(std::string(pch_name), FileMgr,
+                  Clang.getLangOpts(),
+                  Clang.getTargetOpts(),
+                  Clang.getPreprocessorOpts())) {
+          prep_opt.ImplicitPCHInclude = std::string(pch_name);
+	  prep_opt.Includes.push_back(std::string(pch_name));
+          /* Need not to check again. */
+	  prep_opt.DisablePCHValidation = 1;
+          pch_valid = true;
+      }
+    }
+
+    if (!pch_valid) {
+      fwrite(ocl_stdlib_str.c_str(), strlen(ocl_stdlib_str.c_str()), 1, clFile);
+    }
+
+    // Write the source to the cl file
+    fwrite(source, strlen(source), 1, clFile);
+
+    /* Add back the defines. */
+    for (auto it = std::begin(define_opts); it != std::end(define_opts); ++it) {
+       prep_opt.addMacroDef(*it);
+    }
+
+    return true;
+  }
+
+  static bool buildModuleFromSource(clang::CompilerInstance& Clang, std::string& ErrorString,
+          const char* output, size_t stringSize, char *err, size_t *errSize) {
 
     //llvm flags need command line parsing to take effect
     if (!Clang.getFrontendOpts().LLVMArgs.empty()) {
@@ -585,6 +628,8 @@ namespace gbe {
       delete [] Args;
     }
 
+    clang::PreprocessorOptions prep_opt = Clang.getPreprocessorOpts();
+
     // Create an action and make the compiler instance carry it out
     llvm::OwningPtr<clang::CodeGenAction> Act(new clang::EmitLLVMOnlyAction());
     auto retVal = Clang.ExecuteAction(*Act);
@@ -630,8 +675,6 @@ namespace gbe {
     return true;
   }
 
-  extern std::string ocl_stdlib_str;
-
   BVAR(OCL_USE_PCH, true);
   static gbe_program programNewFromSource(const char *source,
                                           size_t stringSize,
@@ -639,6 +682,7 @@ namespace gbe {
                                           char *err,
                                           size_t *errSize)
   {
+
     char clStr[L_tmpnam+1], llStr[L_tmpnam+1];
     const std::string clName = std::string(tmpnam_r(clStr)) + ".cl"; /* unsafe! */
     const std::string llName = std::string(tmpnam_r(llStr)) + ".ll"; /* unsafe! */
@@ -648,16 +692,17 @@ namespace gbe {
     FILE *clFile = fopen(clName.c_str(), "w");
     FATAL_IF(clFile == NULL, "Failed to open temporary file");
 
+    std::string PCH_suffix;
     bool usePCH = true;
     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.
+       We just use the most general build opt to build the PCH header file.
+       Beyond veresion check, 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
@@ -680,29 +725,68 @@ namespace gbe {
        -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. */
+       because the -D define will make the conformance check fail, we will filter
+       the defines out before check and set it back later.  */
+
+    std::vector<std::string> cl_opts;
+    std::vector<std::string> define_opts;
 
     if(options) {
       char *p;
+      char *tmp_c = new char[strlen(options) + 1];
+      memcpy(tmp_c, options, strlen(options) + 1);
+      p = ::strtok(tmp_c, " ");
+      while (p != NULL)
+      {
+        if (*p == '-' && *(p + 1) == 'D') {
+          if (strlen(p) == 2) {
+            p = strtok(NULL, " ");
+            if (*p == '-') {// wrong parameter?
+              p = strtok(NULL, " ");
+              continue;
+            }
+          } else {
+            p += 2;
+          }
+
+          define_opts.push_back(std::string(p));
+        } else {
+          cl_opts.push_back(std::string(p));
+        }
+
+	p = strtok(NULL, " ");
+      }
+      delete[] tmp_c;
+
       const char * incompatible_opts[] = {
           "-cl-single-precision-constant",
-//        "-cl-denorms-are-zero",
-          "-cl-std=",
+          "-cl-std=CL1.1"
       };
+
+      bool has_suffix = false;
+      for (auto it = std::begin(cl_opts); it != std::end(cl_opts); it++) {
+        for (unsigned int i = 0; i < sizeof(incompatible_opts)/sizeof(char *); i++ ) {
+          if (*it == std::string(incompatible_opts[i])) {
+            if (has_suffix == false) {
+              PCH_suffix = incompatible_opts[i];
+              has_suffix = true;
+            } else {
+              usePCH = false;
+              PCH_suffix = "";
+              break;
+            }
+          }
+        }
+
+        if (!usePCH)
+          break;
+      }
+
       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]);
@@ -712,34 +796,38 @@ namespace gbe {
           }
         }
       }
-
-      clOpt += options;
     }
 
     std::string dirs = PCH_OBJECT_DIR;
     std::istringstream idirs(dirs);
 
-    while (getline(idirs, pchHeaderName, ';')) {
-      if(access(pchHeaderName.c_str(), R_OK) == 0) {
-        findPCH = true;
-        break;
+    if (usePCH) {
+      while (getline(idirs, pchHeaderName, ';')) {
+        if(pchHeaderName.find(PCH_suffix) != std::string::npos &&
+            access(pchHeaderName.c_str(), R_OK) == 0) {
+          findPCH = true;
+          break;
+        }
       }
     }
 
-    if (usePCH && findPCH) {
-      clOpt += " -include-pch ";
-      clOpt += pchHeaderName;
-      clOpt += " ";
-    } else
-      fwrite(ocl_stdlib_str.c_str(), strlen(ocl_stdlib_str.c_str()), 1, clFile);
-
-    // Write the source to the cl file
-    fwrite(source, strlen(source), 1, clFile);
+    // Create the compiler instance
+    std::string ErrorString;
+    clang::CompilerInstance Clang;
+    clang::DiagnosticsEngine* Diags;
+    if (!createClangInstance(clFile, clName.c_str(),
+           source, findPCH ? pchHeaderName.c_str() : NULL,
+           cl_opts, define_opts, Clang, ErrorString, Diags)) {
+      remove(llName.c_str());
+      remove(clName.c_str());
+      delete Diags;
+      fclose(clFile);
+      return NULL;
+    }
     fclose(clFile);
 
     gbe_program p;
-    if (buildModuleFromSource(clName.c_str(), llName.c_str(), clOpt.c_str(),
-                              stringSize, err, errSize)) {
+    if (buildModuleFromSource(Clang, ErrorString, llName.c_str(), stringSize, err, errSize)) {
     // Now build the program from llvm
       static std::mutex gbe_mutex;
       gbe_mutex.lock();
@@ -750,8 +838,7 @@ namespace gbe {
         err += *errSize;
         clangErrSize = *errSize;
       }
-      p = gbe_program_new_from_llvm(llName.c_str(), stringSize,
-                                    err, errSize);
+      p = gbe_program_new_from_llvm(llName.c_str(), stringSize, err, errSize);
       if (err != NULL)
         *errSize += clangErrSize;
       gbe_mutex.unlock();
@@ -759,6 +846,7 @@ namespace gbe {
     } else
       p = NULL;
     remove(clName.c_str());
+    delete Diags;
     return p;
   }
 
-- 
1.8.3.2



More information about the Beignet mailing list