Add API to compile from source string (#244)

* Added new API to compile a program from a string
 * will not generate output files using the options
* modified build system to work as a linked in library
* added ability to specify sampler map as a string
* added ability to output binary to a vector
* Added data structure for descriptor map entries
* New exported header DescriptorMap.h
 * Data structure to represent descriptor map entries
 * ostream operator produces lines of descriptor map file
* Refactored ArgKind enum into exported header
 * added PodUBO
 * Needs to be integrated more cleanly
* Replaced generation of descriptor map to use new data structures
 * vector populated in SPIRVProducerPass
 * compile functions stream into the file

The clspv executable continues to function as before.
diff --git a/lib/Compiler.cpp b/lib/Compiler.cpp
index 178bc9c..e103007 100644
--- a/lib/Compiler.cpp
+++ b/lib/Compiler.cpp
@@ -23,19 +23,25 @@
 #include "llvm/IR/Module.h"
 #include "llvm/IR/Verifier.h"
 #include "llvm/LinkAllPasses.h"
+#include "llvm/Support/Allocator.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/MathExtras.h"
+#include "llvm/Support/StringSaver.h"
 #include "llvm/Support/raw_ostream.h"
 #include "llvm/Transforms/IPO/PassManagerBuilder.h"
 
+#include "clspv/DescriptorMap.h"
 #include "clspv/Option.h"
 #include "clspv/Passes.h"
 #include "clspv/opencl_builtins_header.h"
 
 #include "FrontendPlugin.h"
 
+#include <cassert>
 #include <numeric>
 #include <string>
+#include <sstream>
 
 using namespace clang;
 
@@ -151,8 +157,20 @@
 
 // Populates |SamplerMapEntries| with data from the input sampler map. Returns 0
 // if successful.
-int ParseSamplerMap(llvm::SmallVectorImpl<std::pair<unsigned, std::string>> *SamplerMapEntries) {
-  if (!SamplerMap.empty()) {
+int ParseSamplerMap(const std::string &sampler_map,
+                    llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
+                        *SamplerMapEntries) {
+  std::unique_ptr<llvm::MemoryBuffer> samplerMapBuffer(nullptr);
+  if (!sampler_map.empty()) {
+    // Parse the sampler map from the provided string.
+    samplerMapBuffer = llvm::MemoryBuffer::getMemBuffer(sampler_map);
+
+    if (!SamplerMap.empty()) {
+      llvm::outs() << "Warning: -samplermap is ignored when the sampler map is "
+                      "provided through a string.\n";
+    }
+  } else if (!SamplerMap.empty()) {
+    // Parse the sampler map from the option provided file.
     auto errorOrSamplerMapFile =
         llvm::MemoryBuffer::getFile(SamplerMap.getValue());
 
@@ -163,164 +181,169 @@
       return -1;
     }
 
-    auto samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
-
+    samplerMapBuffer = std::move(errorOrSamplerMapFile.get());
     if (0 == samplerMapBuffer->getBufferSize()) {
       llvm::errs() << "Error: Sampler map was an empty file!\n";
       return -1;
     }
+  }
 
-    llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
+  // No sampler map to parse.
+  if (!samplerMapBuffer || 0 == samplerMapBuffer->getBufferSize())
+    return 0;
 
-    // We need to keep track of the beginning of the current entry.
-    const char *b = samplerMapBuffer->getBufferStart();
-    for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
-      // If we have a separator between declarations.
-      if ((*i == '|') || (*i == ',') || (i == e)) {
-        if (i == b) {
-          llvm::errs() << "Error: Sampler map contained an empty entry!\n";
-          return -1;
-        }
+  llvm::SmallVector<llvm::StringRef, 3> samplerStrings;
 
-        samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
-
-        // And set b the next character after i.
-        b = i + 1;
+  // We need to keep track of the beginning of the current entry.
+  const char *b = samplerMapBuffer->getBufferStart();
+  for (const char *i = b, *e = samplerMapBuffer->getBufferEnd();; i++) {
+    // If we have a separator between declarations.
+    if ((*i == '|') || (*i == ',') || (i == e)) {
+      if (i == b) {
+        llvm::errs() << "Error: Sampler map contained an empty entry!\n";
+        return -1;
       }
 
-      // If we have a separator between declarations within a single sampler.
-      if ((*i == ',') || (i == e)) {
-        enum NormalizedCoords {
-          CLK_NORMALIZED_COORDS_FALSE = 0x00,
-          CLK_NORMALIZED_COORDS_TRUE = 0x01,
-          CLK_NORMALIZED_COORDS_NOT_SET
-        } NormalizedCoord = CLK_NORMALIZED_COORDS_NOT_SET;
+      samplerStrings.push_back(llvm::StringRef(b, i - b).trim());
 
-        enum AddressingModes {
-          CLK_ADDRESS_NONE = 0x00,
-          CLK_ADDRESS_CLAMP_TO_EDGE = 0x02,
-          CLK_ADDRESS_CLAMP = 0x04,
-          CLK_ADDRESS_MIRRORED_REPEAT = 0x08,
-          CLK_ADDRESS_REPEAT = 0x06,
-          CLK_ADDRESS_NOT_SET
-        } AddressingMode = CLK_ADDRESS_NOT_SET;
+      // And set b the next character after i.
+      b = i + 1;
+    }
 
-        enum FilterModes {
-          CLK_FILTER_NEAREST = 0x10,
-          CLK_FILTER_LINEAR = 0x20,
-          CLK_FILTER_NOT_SET
-        } FilterMode = CLK_FILTER_NOT_SET;
+    // If we have a separator between declarations within a single sampler.
+    if ((*i == ',') || (i == e)) {
+      enum NormalizedCoords {
+        CLK_NORMALIZED_COORDS_FALSE = 0x00,
+        CLK_NORMALIZED_COORDS_TRUE = 0x01,
+        CLK_NORMALIZED_COORDS_NOT_SET
+      } NormalizedCoord = CLK_NORMALIZED_COORDS_NOT_SET;
 
-        for (auto str : samplerStrings) {
-          if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
-            if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
-              llvm::errs() << "Error: Sampler map normalized coordinates was "
-                              "previously set!\n";
-              return -1;
-            }
-            NormalizedCoord = CLK_NORMALIZED_COORDS_FALSE;
-          } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
-            if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
-              llvm::errs() << "Error: Sampler map normalized coordinates was "
-                              "previously set!\n";
-              return -1;
-            }
-            NormalizedCoord = CLK_NORMALIZED_COORDS_TRUE;
-          } else if ("CLK_ADDRESS_NONE" == str) {
-            if (CLK_ADDRESS_NOT_SET != AddressingMode) {
-              llvm::errs()
-                  << "Error: Sampler map addressing mode was previously set!\n";
-              return -1;
-            }
-            AddressingMode = CLK_ADDRESS_NONE;
-          } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == str) {
-            if (CLK_ADDRESS_NOT_SET != AddressingMode) {
-              llvm::errs()
-                  << "Error: Sampler map addressing mode was previously set!\n";
-              return -1;
-            }
-            AddressingMode = CLK_ADDRESS_CLAMP_TO_EDGE;
-          } else if ("CLK_ADDRESS_CLAMP" == str) {
-            if (CLK_ADDRESS_NOT_SET != AddressingMode) {
-              llvm::errs()
-                  << "Error: Sampler map addressing mode was previously set!\n";
-              return -1;
-            }
-            AddressingMode = CLK_ADDRESS_CLAMP;
-          } else if ("CLK_ADDRESS_MIRRORED_REPEAT" == str) {
-            if (CLK_ADDRESS_NOT_SET != AddressingMode) {
-              llvm::errs()
-                  << "Error: Sampler map addressing mode was previously set!\n";
-              return -1;
-            }
-            AddressingMode = CLK_ADDRESS_MIRRORED_REPEAT;
-          } else if ("CLK_ADDRESS_REPEAT" == str) {
-            if (CLK_ADDRESS_NOT_SET != AddressingMode) {
-              llvm::errs()
-                  << "Error: Sampler map addressing mode was previously set!\n";
-              return -1;
-            }
-            AddressingMode = CLK_ADDRESS_REPEAT;
-          } else if ("CLK_FILTER_NEAREST" == str) {
-            if (CLK_FILTER_NOT_SET != FilterMode) {
-              llvm::errs()
-                  << "Error: Sampler map filtering mode was previously set!\n";
-              return -1;
-            }
-            FilterMode = CLK_FILTER_NEAREST;
-          } else if ("CLK_FILTER_LINEAR" == str) {
-            if (CLK_FILTER_NOT_SET != FilterMode) {
-              llvm::errs()
-                  << "Error: Sampler map filtering mode was previously set!\n";
-              return -1;
-            }
-            FilterMode = CLK_FILTER_LINEAR;
-          } else {
-            llvm::errs() << "Error: Unknown sampler string '" << str
-                         << "' found!\n";
+      enum AddressingModes {
+        CLK_ADDRESS_NONE = 0x00,
+        CLK_ADDRESS_CLAMP_TO_EDGE = 0x02,
+        CLK_ADDRESS_CLAMP = 0x04,
+        CLK_ADDRESS_MIRRORED_REPEAT = 0x08,
+        CLK_ADDRESS_REPEAT = 0x06,
+        CLK_ADDRESS_NOT_SET
+      } AddressingMode = CLK_ADDRESS_NOT_SET;
+
+      enum FilterModes {
+        CLK_FILTER_NEAREST = 0x10,
+        CLK_FILTER_LINEAR = 0x20,
+        CLK_FILTER_NOT_SET
+      } FilterMode = CLK_FILTER_NOT_SET;
+
+      for (auto str : samplerStrings) {
+        if ("CLK_NORMALIZED_COORDS_FALSE" == str) {
+          if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
+            llvm::errs() << "Error: Sampler map normalized coordinates was "
+                            "previously set!\n";
             return -1;
           }
-        }
-
-        if (CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
-          llvm::errs() << "Error: Sampler map entry did not contain normalized "
-                          "coordinates entry!\n";
+          NormalizedCoord = CLK_NORMALIZED_COORDS_FALSE;
+        } else if ("CLK_NORMALIZED_COORDS_TRUE" == str) {
+          if (CLK_NORMALIZED_COORDS_NOT_SET != NormalizedCoord) {
+            llvm::errs() << "Error: Sampler map normalized coordinates was "
+                            "previously set!\n";
+            return -1;
+          }
+          NormalizedCoord = CLK_NORMALIZED_COORDS_TRUE;
+        } else if ("CLK_ADDRESS_NONE" == str) {
+          if (CLK_ADDRESS_NOT_SET != AddressingMode) {
+            llvm::errs()
+                << "Error: Sampler map addressing mode was previously set!\n";
+            return -1;
+          }
+          AddressingMode = CLK_ADDRESS_NONE;
+        } else if ("CLK_ADDRESS_CLAMP_TO_EDGE" == str) {
+          if (CLK_ADDRESS_NOT_SET != AddressingMode) {
+            llvm::errs()
+                << "Error: Sampler map addressing mode was previously set!\n";
+            return -1;
+          }
+          AddressingMode = CLK_ADDRESS_CLAMP_TO_EDGE;
+        } else if ("CLK_ADDRESS_CLAMP" == str) {
+          if (CLK_ADDRESS_NOT_SET != AddressingMode) {
+            llvm::errs()
+                << "Error: Sampler map addressing mode was previously set!\n";
+            return -1;
+          }
+          AddressingMode = CLK_ADDRESS_CLAMP;
+        } else if ("CLK_ADDRESS_MIRRORED_REPEAT" == str) {
+          if (CLK_ADDRESS_NOT_SET != AddressingMode) {
+            llvm::errs()
+                << "Error: Sampler map addressing mode was previously set!\n";
+            return -1;
+          }
+          AddressingMode = CLK_ADDRESS_MIRRORED_REPEAT;
+        } else if ("CLK_ADDRESS_REPEAT" == str) {
+          if (CLK_ADDRESS_NOT_SET != AddressingMode) {
+            llvm::errs()
+                << "Error: Sampler map addressing mode was previously set!\n";
+            return -1;
+          }
+          AddressingMode = CLK_ADDRESS_REPEAT;
+        } else if ("CLK_FILTER_NEAREST" == str) {
+          if (CLK_FILTER_NOT_SET != FilterMode) {
+            llvm::errs()
+                << "Error: Sampler map filtering mode was previously set!\n";
+            return -1;
+          }
+          FilterMode = CLK_FILTER_NEAREST;
+        } else if ("CLK_FILTER_LINEAR" == str) {
+          if (CLK_FILTER_NOT_SET != FilterMode) {
+            llvm::errs()
+                << "Error: Sampler map filtering mode was previously set!\n";
+            return -1;
+          }
+          FilterMode = CLK_FILTER_LINEAR;
+        } else {
+          llvm::errs() << "Error: Unknown sampler string '" << str
+                       << "' found!\n";
           return -1;
         }
-
-        if (CLK_ADDRESS_NOT_SET == AddressingMode) {
-          llvm::errs() << "Error: Sampler map entry did not contain addressing "
-                          "mode entry!\n";
-          return -1;
-        }
-
-        if (CLK_FILTER_NOT_SET == FilterMode) {
-          llvm::errs()
-              << "Error: Sampler map entry did not contain filer mode entry!\n";
-          return -1;
-        }
-
-        // Generate an equivalent expression in string form.  Sort the
-        // strings to get a canonical ordering.
-        std::sort(samplerStrings.begin(), samplerStrings.end(),
-                  std::less<StringRef>());
-        const auto samplerExpr = std::accumulate(
-            samplerStrings.begin(), samplerStrings.end(), std::string(),
-            [](std::string left, std::string right) {
-              return left + std::string(left.empty() ? "" : "|") + right;
-            });
-
-        SamplerMapEntries->emplace_back(
-            NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
-
-        // And reset the sampler strings for the next sampler in the map.
-        samplerStrings.clear();
       }
 
-      // And lastly, if we are at the end of the file
-      if (i == e) {
-        break;
+      if (CLK_NORMALIZED_COORDS_NOT_SET == NormalizedCoord) {
+        llvm::errs() << "Error: Sampler map entry did not contain normalized "
+                        "coordinates entry!\n";
+        return -1;
       }
+
+      if (CLK_ADDRESS_NOT_SET == AddressingMode) {
+        llvm::errs() << "Error: Sampler map entry did not contain addressing "
+                        "mode entry!\n";
+        return -1;
+      }
+
+      if (CLK_FILTER_NOT_SET == FilterMode) {
+        llvm::errs()
+            << "Error: Sampler map entry did not contain filer mode entry!\n";
+        return -1;
+      }
+
+      // Generate an equivalent expression in string form.  Sort the
+      // strings to get a canonical ordering.
+      std::sort(samplerStrings.begin(), samplerStrings.end(),
+                std::less<StringRef>());
+      const auto samplerExpr = std::accumulate(
+          samplerStrings.begin(), samplerStrings.end(), std::string(),
+          [](std::string left, std::string right) {
+            return left + std::string(left.empty() ? "" : "|") + right;
+          });
+
+      // SamplerMapEntries->push_back(std::make_pair(
+      //    NormalizedCoord | AddressingMode | FilterMode, samplerExpr));
+      SamplerMapEntries->emplace_back(
+          NormalizedCoord | AddressingMode | FilterMode, samplerExpr);
+
+      // And reset the sampler strings for the next sampler in the map.
+      samplerStrings.clear();
+    }
+
+    // And lastly, if we are at the end of the file
+    if (i == e) {
+      break;
     }
   }
 
@@ -331,16 +354,26 @@
 int SetCompilerInstanceOptions(CompilerInstance &instance,
                                const llvm::StringRef &overiddenInputFilename,
                                const clang::FrontendInputFile &kernelFile,
+                               const std::string &program,
                                llvm::raw_string_ostream *diagnosticsStream) {
-  auto errorOrInputFile =
-      llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
+  std::unique_ptr<llvm::MemoryBuffer> memory_buffer(nullptr);
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> errorOrInputFile(nullptr);
+  if (program.empty()) {
+    auto errorOrInputFile =
+        llvm::MemoryBuffer::getFileOrSTDIN(InputFilename.getValue());
 
-  // If there was an error in getting the input file.
-  if (!errorOrInputFile) {
-    llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
-                 << InputFilename.getValue() << "'\n";
-    return -1;
+    // If there was an error in getting the input file.
+    if (!errorOrInputFile) {
+      llvm::errs() << "Error: " << errorOrInputFile.getError().message() << " '"
+                   << InputFilename.getValue() << "'\n";
+      return -1;
+    }
+    memory_buffer.reset(errorOrInputFile.get().release());
+  } else {
+    memory_buffer = llvm::MemoryBuffer::getMemBuffer(program.c_str(),
+                                                     overiddenInputFilename);
   }
+
   if (verify) {
     instance.getDiagnosticOpts().VerifyDiagnostics = true;
   }
@@ -425,8 +458,10 @@
   // Disable generation of lifetime intrinsic.
   instance.getCodeGenOpts().DisableLifetimeMarkers = true;
   instance.getFrontendOpts().Inputs.push_back(kernelFile);
-  instance.getPreprocessorOpts().addRemappedFile(
-      overiddenInputFilename, errorOrInputFile.get().release());
+  // instance.getPreprocessorOpts().addRemappedFile(
+  //    overiddenInputFilename, errorOrInputFile.get().release());
+  instance.getPreprocessorOpts().addRemappedFile(overiddenInputFilename,
+                                                 memory_buffer.release());
 
   struct OpenCLBuiltinMemoryBuffer final : public llvm::MemoryBuffer {
     OpenCLBuiltinMemoryBuffer(const void *data, uint64_t data_length) {
@@ -476,16 +511,30 @@
   return 0;
 }
 
-// Populates |pm| with necessary passes to optimize and legalize the IR. 
-void PopulatePassManager(llvm::legacy::PassManager *pm,
-                         llvm::raw_svector_ostream *binaryStream,
-                         llvm::raw_string_ostream *descriptor_map_out,
-                         llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
-                             *SamplerMapEntries) {
+// Populates |pm| with necessary passes to optimize and legalize the IR.
+int PopulatePassManager(
+    llvm::legacy::PassManager *pm, llvm::raw_svector_ostream *binaryStream,
+    std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries,
+    llvm::SmallVectorImpl<std::pair<unsigned, std::string>>
+        *SamplerMapEntries) {
   llvm::PassManagerBuilder pmBuilder;
 
   switch (OptimizationLevel) {
   case '0':
+  case '1':
+  case '2':
+  case '3':
+  case 's':
+  case 'z':
+    break;
+  default:
+    llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
+                 << " specified!\n";
+    return -1;
+  }
+
+  switch (OptimizationLevel) {
+  case '0':
     pmBuilder.OptLevel = 0;
     break;
   case '1':
@@ -602,8 +651,10 @@
   // anymore so leave this right before SPIR-V generation.
   pm->add(clspv::createUBOTypeTransformPass());
   pm->add(clspv::createSPIRVProducerPass(
-      *binaryStream, *descriptor_map_out, *SamplerMapEntries,
+      *binaryStream, descriptor_map_entries, *SamplerMapEntries,
       OutputAssembly.getValue(), OutputFormat == "c"));
+
+  return 0;
 }
 } // namespace
 
@@ -613,29 +664,16 @@
   // ParseCommandLineOptions with the specific option.
   const int llvmArgc = 2;
   const char *llvmArgv[llvmArgc] = {
-      argv[0], "-simplifycfg-sink-common=false",
+      argv[0],
+      "-simplifycfg-sink-common=false",
   };
 
   llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
 
   llvm::cl::ParseCommandLineOptions(argc, argv);
 
-  switch (OptimizationLevel) {
-  case '0':
-  case '1':
-  case '2':
-  case '3':
-  case 's':
-  case 'z':
-    break;
-  default:
-    llvm::errs() << "Unknown optimization level -O" << OptimizationLevel
-                 << " specified!\n";
-    return -1;
-  }
-
   llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
-  if (auto error = ParseSamplerMap(&SamplerMapEntries))
+  if (auto error = ParseSamplerMap("", &SamplerMapEntries))
     return error;
 
   // if no output file was provided, use a default
@@ -652,8 +690,8 @@
                                       clang::InputKind::OpenCL);
   std::string log;
   llvm::raw_string_ostream diagnosticsStream(log);
-  if (auto error = SetCompilerInstanceOptions(instance, overiddenInputFilename,
-                                              kernelFile, &diagnosticsStream))
+  if (auto error = SetCompilerInstanceOptions(
+          instance, overiddenInputFilename, kernelFile, "", &diagnosticsStream))
     return error;
 
   // Parse.
@@ -697,10 +735,12 @@
   SmallVector<char, 10000> binary;
   llvm::raw_svector_ostream binaryStream(binary);
   std::string descriptor_map;
-  llvm::raw_string_ostream descriptor_map_out(descriptor_map);
   llvm::legacy::PassManager pm;
-  PopulatePassManager(&pm, &binaryStream, &descriptor_map_out,
-                      &SamplerMapEntries);
+  std::vector<version0::DescriptorMapEntry> descriptor_map_entries;
+  if (auto error =
+          PopulatePassManager(&pm, &binaryStream,
+                              &descriptor_map_entries, &SamplerMapEntries))
+    return error;
   pm.run(*module);
 
   // Write outputs
@@ -708,8 +748,6 @@
   // Write the descriptor map, if requested.
   std::error_code error;
   if (!DescriptorMapFilename.empty()) {
-    descriptor_map_out.flush();
-
     llvm::raw_fd_ostream descriptor_map_out_fd(DescriptorMapFilename, error,
                                                llvm::sys::fs::F_RW |
                                                    llvm::sys::fs::F_Text);
@@ -718,7 +756,12 @@
                    << DescriptorMapFilename << "': " << error.message() << '\n';
       return -1;
     }
-    descriptor_map_out_fd << descriptor_map;
+    std::string descriptor_map_string;
+    std::ostringstream str(descriptor_map_string);
+    for (const auto &entry : descriptor_map_entries) {
+      str << entry << "\n";
+    }
+    descriptor_map_out_fd << str.str();
     descriptor_map_out_fd.close();
   }
 
@@ -746,4 +789,114 @@
 
   return 0;
 }
+
+int CompileFromSourceString(const std::string &program,
+                            const std::string &sampler_map,
+                            const std::string &options,
+                            std::vector<uint32_t> *output_binary,
+                            std::vector<clspv::version0::DescriptorMapEntry> *descriptor_map_entries) {
+  // We need to change how one of the called passes works by spoofing
+  // ParseCommandLineOptions with the specific option.
+  const int llvmArgc = 2;
+  const char *llvmArgv[llvmArgc] = {
+      "clspv",
+      "-simplifycfg-sink-common=false",
+  };
+
+  llvm::cl::ParseCommandLineOptions(llvmArgc, llvmArgv);
+
+  llvm::SmallVector<const char *, 20> argv;
+  llvm::BumpPtrAllocator A;
+  llvm::StringSaver Saver(A);
+  argv.push_back(Saver.save("clspv").data());
+  llvm::cl::TokenizeGNUCommandLine(options, Saver, argv);
+  int argc = static_cast<int>(argv.size());
+  llvm::cl::ParseCommandLineOptions(argc, &argv[0]);
+
+  llvm::SmallVector<std::pair<unsigned, std::string>, 8> SamplerMapEntries;
+  if (auto error = ParseSamplerMap(sampler_map, &SamplerMapEntries))
+    return error;
+
+  InputFilename = "source.cl";
+  llvm::StringRef overiddenInputFilename = InputFilename.getValue();
+
+  clang::CompilerInstance instance;
+  clang::FrontendInputFile kernelFile(overiddenInputFilename,
+                                      clang::InputKind::OpenCL);
+  std::string log;
+  llvm::raw_string_ostream diagnosticsStream(log);
+  if (auto error =
+          SetCompilerInstanceOptions(instance, overiddenInputFilename,
+                                     kernelFile, program, &diagnosticsStream))
+    return error;
+
+  // Parse.
+  llvm::LLVMContext context;
+  clang::EmitLLVMOnlyAction action(&context);
+
+  // Prepare the action for processing kernelFile
+  const bool success = action.BeginSourceFile(instance, kernelFile);
+  if (!success) {
+    return -1;
+  }
+
+  action.Execute();
+  action.EndSourceFile();
+
+  clang::DiagnosticConsumer *const consumer =
+      instance.getDiagnostics().getClient();
+  consumer->finish();
+
+  auto num_errors = consumer->getNumErrors();
+  if (num_errors > 0) {
+    llvm::errs() << log << "\n";
+    return -1;
+  }
+
+  if (clspv::Option::ConstantArgsInUniformBuffer() &&
+      !clspv::Option::InlineEntryPoints()) {
+    llvm::errs() << "clspv restriction: -constant-arg-ubo requires "
+                    "-inline-entry-points\n";
+    return -1;
+  }
+
+  llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry();
+  llvm::initializeCore(Registry);
+  llvm::initializeScalarOpts(Registry);
+
+  std::unique_ptr<llvm::Module> module(action.takeModule());
+
+  // Optimize.
+  // Create a memory buffer for temporarily writing the result.
+  SmallVector<char, 10000> binary;
+  llvm::raw_svector_ostream binaryStream(binary);
+  std::string descriptor_map;
+  llvm::legacy::PassManager pm;
+  if (auto error =
+          PopulatePassManager(&pm, &binaryStream,
+                              descriptor_map_entries, &SamplerMapEntries))
+    return error;
+  pm.run(*module);
+
+  // Write outputs
+
+  // Write the descriptor map. This is required.
+  assert(descriptor_map_entries && "Valid descriptor map container is required.");
+  if (!DescriptorMapFilename.empty()) {
+    llvm::errs() << "Warning: -descriptormap is ignored descriptor map container is provided.\n";
+  }
+
+  // Write the resulting binary.
+  // Wait until now to try writing the file so that we only write it on
+  // successful compilation.
+  assert(output_binary && "Valid binary container is required.");
+  if (!OutputFilename.empty()) {
+    llvm::outs()
+        << "Warning: -o is ignored when binary container is provided.\n";
+  }
+  output_binary->resize(binary.size() / 4);
+  memcpy(output_binary->data(), binary.data(), binary.size());
+
+  return 0;
+}
 } // namespace clspv