clover: Add a function internalizer pass before LTO v2
authorTom Stellard <thomas.stellard@amd.com>
Thu, 21 Jun 2012 17:55:15 +0000 (13:55 -0400)
committerTom Stellard <thomas.stellard@amd.com>
Fri, 29 Jun 2012 18:46:18 +0000 (18:46 +0000)
The function internalizer pass marks non-kernel functions as internal,
which enables optimizations like function inlining and global dead-code
elimination.

v2:
  - Pass vector arguments by const reference

src/gallium/state_trackers/clover/llvm/invocation.cpp

index 27276bc6a19e955e72e5b8aa5fc12e51e96017b5..be15e960b18ad90441f35a02cfbddd8a49a09201 100644 (file)
@@ -36,6 +36,7 @@
 #include <llvm/Support/MemoryBuffer.h>
 #include <llvm/Support/PathV1.h>
 #include <llvm/Target/TargetData.h>
+#include <llvm/Transforms/IPO.h>
 #include <llvm/Transforms/IPO/PassManagerBuilder.h>
 
 #include "pipe/p_state.h"
@@ -134,7 +135,18 @@ namespace {
    }
 
    void
-   link(llvm::Module *mod, const std::string &triple) {
+   find_kernels(llvm::Module *mod, std::vector<llvm::Function *> &kernels) {
+      const llvm::NamedMDNode *kernel_node =
+                                 mod->getNamedMetadata("opencl.kernels");
+      for (unsigned i = 0; i < kernel_node->getNumOperands(); ++i) {
+         kernels.push_back(llvm::dyn_cast<llvm::Function>(
+                                    kernel_node->getOperand(i)->getOperand(0)));
+      }
+   }
+
+   void
+   link(llvm::Module *mod, const std::string &triple,
+        const std::vector<llvm::Function *> &kernels) {
 
       llvm::PassManager PM;
       llvm::PassManagerBuilder Builder;
@@ -145,14 +157,38 @@ namespace {
       linker.LinkInFile(llvm::sys::Path(LIBCLC_PATH + triple + "/lib/builtins.bc"), isNative);
       mod = linker.releaseModule();
 
+      // Add a function internalizer pass.
+      //
+      // By default, the function internalizer pass will look for a function
+      // called "main" and then mark all other functions as internal.  Marking
+      // functions as internal enables the optimizer to perform optimizations
+      // like function inlining and global dead-code elimination.
+      //
+      // When there is no "main" function in a module, the internalize pass will
+      // treat the module like a library, and it won't internalize any functions.
+      // Since there is no "main" function in our kernels, we need to tell
+      // the internalizer pass that this module is not a library by passing a
+      // list of kernel functions to the internalizer.  The internalizer will
+      // treat the functions in the list as "main" functions and internalize
+      // all of the other functions.
+      std::vector<const char*> export_list;
+      for (std::vector<llvm::Function *>::const_iterator I = kernels.begin(),
+                                                         E = kernels.end();
+                                                         I != E; ++I) {
+         llvm::Function *kernel = *I;
+         export_list.push_back(kernel->getName().data());
+      }
+      PM.add(llvm::createInternalizePass(export_list));
+
       // Run link time optimizations
-      Builder.populateLTOPassManager(PM, false, true);
       Builder.OptLevel = 2;
+      Builder.populateLTOPassManager(PM, false, true);
       PM.run(*mod);
    }
 
    module
-   build_module_llvm(llvm::Module *mod) {
+   build_module_llvm(llvm::Module *mod,
+                     const std::vector<llvm::Function *> &kernels) {
 
       module m;
       struct pipe_llvm_program_header header;
@@ -163,15 +199,14 @@ namespace {
       llvm::WriteBitcodeToFile(mod, bitcode_ostream);
       bitcode_ostream.flush();
 
+      llvm::Function *kernel_func;
       std::string kernel_name;
       compat::vector<module::argument> args;
-      const llvm::NamedMDNode *kernel_node =
-                                 mod->getNamedMetadata("opencl.kernels");
+
       // XXX: Support more than one kernel
-      assert(kernel_node->getNumOperands() <= 1);
+      assert(kernels.size() == 1);
 
-      llvm::Function *kernel_func = llvm::dyn_cast<llvm::Function>(
-                                   kernel_node->getOperand(0)->getOperand(0));
+      kernel_func = kernels[0];
       kernel_name = kernel_func->getName();
 
       for (llvm::Function::arg_iterator I = kernel_func->arg_begin(),
@@ -219,9 +254,13 @@ clover::compile_program_llvm(const compat::string &source,
                              enum pipe_shader_ir ir,
                              const compat::string &triple) {
 
+   std::vector<llvm::Function *> kernels;
+
    llvm::Module *mod = compile(source, "cl_input", triple);
 
-   link(mod, triple);
+   find_kernels(mod, kernels);
+
+   link(mod, triple, kernels);
 
    // Build the clover::module
    switch (ir) {
@@ -230,6 +269,6 @@ clover::compile_program_llvm(const compat::string &source,
          assert(0);
          return module();
       default:
-         return build_module_llvm(mod);
+         return build_module_llvm(mod, kernels);
    }
 }