Enable separate compilation.

Change-Id: I240665cd02706d11d2f8717b3e3276da435134cb
diff --git a/src/compiler_llvm/compilation_unit.cc b/src/compiler_llvm/compilation_unit.cc
index 4eb7fb5..9c1f8d0 100644
--- a/src/compiler_llvm/compilation_unit.cc
+++ b/src/compiler_llvm/compilation_unit.cc
@@ -17,9 +17,11 @@
 #include "compilation_unit.h"
 
 #include "compiled_method.h"
+#include "file.h"
 #include "instruction_set.h"
 #include "ir_builder.h"
 #include "logging.h"
+#include "os.h"
 
 #include "runtime_support_builder_arm.h"
 #include "runtime_support_builder_x86.h"
@@ -45,6 +47,7 @@
 #include <llvm/Support/Debug.h>
 #include <llvm/Support/FormattedStream.h>
 #include <llvm/Support/ManagedStatic.h>
+#include <llvm/Support/MemoryBuffer.h>
 #include <llvm/Support/PassNameParser.h>
 #include <llvm/Support/PluginLoader.h>
 #include <llvm/Support/PrettyStackTrace.h>
@@ -54,12 +57,17 @@
 #include <llvm/Support/TargetSelect.h>
 #include <llvm/Support/ToolOutputFile.h>
 #include <llvm/Support/raw_ostream.h>
+#include <llvm/Support/system_error.h>
 #include <llvm/Target/TargetData.h>
 #include <llvm/Target/TargetLibraryInfo.h>
 #include <llvm/Target/TargetMachine.h>
 #include <llvm/Transforms/IPO.h>
 #include <llvm/Transforms/IPO/PassManagerBuilder.h>
 
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
 #include <string>
 
 namespace {
@@ -156,115 +164,113 @@
   return true;
 }
 
-
-bool CompilationUnit::Materialize() {
-  // Lookup the LLVM target
-  char const* target_triple = NULL;
-  char const* target_attr = NULL;
-
-  switch (insn_set_) {
-  case kThumb2:
-    target_triple = "thumb-none-linux-gnueabi";
-    target_attr = "+thumb2,+neon,+neonfp,+vfp3";
-    break;
-
-  case kArm:
-    target_triple = "armv7-none-linux-gnueabi";
-    target_attr = "+v7,+neon,+neonfp,+vfp3";
-    break;
-
-  case kX86:
-    target_triple = "i386-pc-linux-gnu";
-    target_attr = "";
-    break;
-
-  case kMips:
-    target_triple = "mipsel-unknown-linux";
-    target_attr = "mips32r2";
-    break;
-
-  default:
-    LOG(FATAL) << "Unknown instruction set: " << insn_set_;
+// TODO: Move to scoped_temp_file.h
+class ScopedTempFile {
+ public:
+  ScopedTempFile(const std::string& filename_template)
+      : filename_(filename_template), file_(NULL) {
+    int fd = mkstemp(&filename_[0]);
+    CHECK_NE(-1, fd);
+    file_ = OS::FileFromFd(filename_.c_str(), fd);
   }
 
-  std::string errmsg;
-  llvm::Target const* target =
-    llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
+  ~ScopedTempFile() {
+    delete file_;
+    TEMP_FAILURE_RETRY(unlink(filename_.c_str()));
+  }
 
-  CHECK(target != NULL) << errmsg;
+  int GetFd() {
+    return file_->Fd();
+  }
 
-  // Target options
-  llvm::TargetOptions target_options;
-  target_options.FloatABIType = llvm::FloatABI::Soft;
-  target_options.NoFramePointerElim = true;
-  target_options.NoFramePointerElimNonLeaf = true;
-  target_options.UseSoftFloat = false;
-  target_options.EnableFastISel = true;
+  const std::string &GetName() const {
+    return filename_;
+  }
 
-  // Create the llvm::TargetMachine
-  llvm::TargetMachine* target_machine =
-    target->createTargetMachine(target_triple, "", target_attr, target_options,
-                                llvm::Reloc::Static, llvm::CodeModel::Small,
-                                llvm::CodeGenOpt::Less);
+  bool ReadToString(std::string &buffer) {
+    off_t file_size = file_->Length();
+    if (file_size <= 0) {
+      buffer.clear();
+      return true;
+    }
 
-  CHECK(target_machine != NULL) << "Failed to create target machine";
+    buffer.reserve(file_size);
+    buffer.resize(file_size);
+    return file_->ReadFully(&buffer[0], file_size);
+  }
 
+ private:
+  std::string filename_;
+  File *file_;
+};
 
-  // Add target data
-  llvm::TargetData const* target_data = target_machine->getTargetData();
+bool CompilationUnit::Materialize() {
+  const std::string tmp_file = "/tmp/art-llvm-XXXXXX";
 
-  // PassManager for code generation passes
-  llvm::PassManager pm;
-  pm.add(new llvm::TargetData(*target_data));
+  // Prepare the input
+  ScopedTempFile input(tmp_file);
+  if (input.GetFd() < 0) {
+    PLOG(ERROR) << "Failed to save the module to the file " << tmp_file;
+    return false;
+  }
 
-  // FunctionPassManager for optimization pass
-  llvm::FunctionPassManager fpm(module_);
-  fpm.add(new llvm::TargetData(*target_data));
+  // Write the bitcode to the file
+  if (!WriteBitcodeToFile(input.GetName())) {
+    return false;
+  }
 
-  // Add optimization pass
-  llvm::PassManagerBuilder pm_builder;
-  pm_builder.Inliner = llvm::createAlwaysInlinerPass();
-  pm_builder.OptLevel = 1;
-  pm_builder.DisableSimplifyLibCalls = 1;
-  pm_builder.populateModulePassManager(pm);
-  pm_builder.populateFunctionPassManager(fpm);
+  // Prepare the output
+  ScopedTempFile output(tmp_file);
+  if (output.GetFd() < 0) {
+    PLOG(ERROR) << "Failed to prepare the output file " << tmp_file;
+    return false;
+  }
 
-  // Add passes to emit ELF image
-  {
-    llvm::formatted_raw_ostream formatted_os(
-      *(new llvm::raw_string_ostream(elf_image_)), true);
+  // Fork a process to do the compilation
+  pid_t pid = fork();
+  if (pid == 0) {
+    // change process groups, so we don't get ripped by ProcessManager
+    setpgid(0, 0);
 
-    // Ask the target to add backend passes as necessary.
-    if (target_machine->addPassesToEmitFile(pm,
-                                            formatted_os,
-                                            llvm::TargetMachine::CGFT_ObjectFile,
-                                            true)) {
-      LOG(FATAL) << "Unable to generate ELF for this target";
+    // TODO: Should use exec* family instead of invoking a function.
+    // Forward our compilation request to bcc.
+    exit(static_cast<int>(!MaterializeFile(input.GetFd(), output.GetFd(),
+                                           insn_set_)));
+  } else {
+    if (pid < 0) {
+      LOG(FATAL) << "Failed to fork a process to do the compilation: "
+                 << strerror(errno);
+    }
+
+    // Free the resources
+    context_.reset(NULL);
+    irb_.reset(NULL);
+    module_ = NULL;
+
+    int status;
+
+    // Wait for child to finish
+    pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+    if (got_pid != pid) {
+      PLOG(ERROR) << "waitpid failed: wanted " << pid << ", got " << got_pid;
       return false;
     }
 
-    // Add pass to update the frame_size_in_bytes_
-    pm.add(new ::UpdateFrameSizePass(this));
-
-    // Run the per-function optimization
-    fpm.doInitialization();
-    for (llvm::Module::iterator F = module_->begin(), E = module_->end();
-         F != E; ++F) {
-      fpm.run(*F);
+    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+      LOG(ERROR) << "Failed to compile the bitcode: " << WEXITSTATUS(status);
+      return false;
     }
-    fpm.doFinalization();
+  }
 
-    // Run the code generation passes
-    pm.run(*module_);
+  // Read the result out from the output file
+  TEMP_FAILURE_RETRY(lseek(output.GetFd(), 0, SEEK_SET));
+  if (!output.ReadToString(elf_image_)) {
+    LOG(ERROR) << "Failed to read the result file";
+    return false;
   }
 
   LOG(INFO) << "Compilation Unit: " << elf_idx_ << " (done)";
 
-  // Free the resources
-  context_.reset(NULL);
-  irb_.reset(NULL);
-  module_ = NULL;
-
   return true;
 }
 
@@ -292,6 +298,141 @@
   }
 }
 
+bool CompilationUnit::MaterializeFile(int input_fd, int output_fd,
+                                      InstructionSet insn_set) {
+  // Initialize the LLVM first
+  llvm::InitializeAllTargets();
+  llvm::InitializeAllTargetMCs();
+  llvm::InitializeAllAsmPrinters();
+  llvm::InitializeAllAsmParsers();
+
+  // Read the LLVM module from input_fd
+  llvm::OwningPtr<llvm::MemoryBuffer> memory_buffer;
+  llvm::error_code load_input_err =
+      llvm::MemoryBuffer::getOpenFile(input_fd, "<art-llvm-module>",
+                                      memory_buffer);
+
+  if (load_input_err) {
+    LOG(ERROR) << "Failed to load input for compiler into memory: "
+               << load_input_err.message();
+    return false;
+  }
+
+  // It's safe to use the global context now
+  std::string load_module_errmsg;
+  llvm::Module *module = ParseBitcodeFile(memory_buffer.get(),
+                                          llvm::getGlobalContext(),
+                                          &load_module_errmsg);
+  if (module == NULL) {
+    LOG(ERROR) << "Failed to load LLVM module to compiler: "
+               << load_module_errmsg;
+    return false;
+  }
+
+  // Lookup the LLVM target
+  char const* target_triple = NULL;
+  char const* target_attr = NULL;
+
+  switch (insn_set) {
+  case kThumb2:
+    target_triple = "thumb-none-linux-gnueabi";
+    target_attr = "+thumb2,+neon,+neonfp,+vfp3";
+    break;
+
+  case kArm:
+    target_triple = "armv7-none-linux-gnueabi";
+    // TODO: Fix for Xoom.
+    target_attr = "+v7,+neon,+neonfp,+vfp3";
+    break;
+
+  case kX86:
+    target_triple = "i386-pc-linux-gnu";
+    target_attr = "";
+    break;
+
+  case kMips:
+    target_triple = "mipsel-unknown-linux";
+    target_attr = "mips32r2";
+    break;
+
+  default:
+    LOG(FATAL) << "Unknown instruction set: " << insn_set;
+  }
+
+  std::string errmsg;
+  llvm::Target const* target =
+    llvm::TargetRegistry::lookupTarget(target_triple, errmsg);
+
+  CHECK(target != NULL) << errmsg;
+
+  // Target options
+  llvm::TargetOptions target_options;
+  target_options.FloatABIType = llvm::FloatABI::Soft;
+  target_options.NoFramePointerElim = true;
+  target_options.NoFramePointerElimNonLeaf = true;
+  target_options.UseSoftFloat = false;
+  target_options.EnableFastISel = true;
+
+  // Create the llvm::TargetMachine
+  llvm::TargetMachine* target_machine =
+    target->createTargetMachine(target_triple, "", target_attr, target_options,
+                                llvm::Reloc::Static, llvm::CodeModel::Small,
+                                llvm::CodeGenOpt::Less);
+
+  CHECK(target_machine != NULL) << "Failed to create target machine";
+
+  // Add target data
+  llvm::TargetData const* target_data = target_machine->getTargetData();
+
+  // PassManager for code generation passes
+  llvm::PassManager pm;
+  pm.add(new llvm::TargetData(*target_data));
+
+  // FunctionPassManager for optimization pass
+  llvm::FunctionPassManager fpm(module);
+  fpm.add(new llvm::TargetData(*target_data));
+
+  // Add optimization pass
+  llvm::PassManagerBuilder pm_builder;
+  pm_builder.Inliner = llvm::createAlwaysInlinerPass();
+  pm_builder.OptLevel = 1;
+  pm_builder.DisableSimplifyLibCalls = 1;
+  pm_builder.populateModulePassManager(pm);
+  pm_builder.populateFunctionPassManager(fpm);
+
+  // Add passes to emit ELF image
+  {
+    llvm::formatted_raw_ostream formatted_os(
+      *(new llvm::raw_fd_ostream(output_fd, /* shouldClose */false)), true);
+
+    // Ask the target to add backend passes as necessary.
+    if (target_machine->addPassesToEmitFile(pm,
+                                            formatted_os,
+                                            llvm::TargetMachine::CGFT_ObjectFile,
+                                            true)) {
+      LOG(FATAL) << "Unable to generate ELF for this target";
+      return false;
+    }
+
+    // FIXME: Unable to run the UpdateFrameSizePass pass since it tries to
+    //        update the value reside in the different address space.
+    // Add pass to update the frame_size_in_bytes_
+    //pm.add(new ::UpdateFrameSizePass(this));
+
+    // Run the per-function optimization
+    fpm.doInitialization();
+    for (llvm::Module::iterator F = module->begin(), E = module->end();
+         F != E; ++F) {
+      fpm.run(*F);
+    }
+    fpm.doFinalization();
+
+    // Run the code generation passes
+    pm.run(*module);
+  }
+
+  return true;
+}
 
 } // namespace compiler_llvm
 } // namespace art