gollvm: transition Go compilation step to new driver scheme

Convert over the Go => assembler compilation step to work with the new
driver infrastructure. This creates a new Tool subclass for the Go
compilation, which incorporates all of the previously written code for
invoking gofrontend and the LLVM back end.

Change-Id: Ie4ed4745fc7c765c066a0883bdb7ad261163fbe4
Reviewed-on: https://go-review.googlesource.com/110622
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/driver-main/llvm-goc.cpp b/driver-main/llvm-goc.cpp
index 2856c8f..4ee29b1 100644
--- a/driver-main/llvm-goc.cpp
+++ b/driver-main/llvm-goc.cpp
@@ -154,866 +154,6 @@
   return true;
 }
 
-static std::unique_ptr<ToolOutputFile>
-GetOutputStream(std::string outFileName, bool binary)
-{
-  // Open the file.
-  std::error_code EC;
-  sys::fs::OpenFlags OpenFlags = sys::fs::F_None;
-  if (!binary)
-    OpenFlags |= sys::fs::F_Text;
-  auto FDOut = llvm::make_unique<ToolOutputFile>(outFileName, EC,
-                                                OpenFlags);
-  if (EC) {
-    errs() << "error opening " << outFileName << ": "
-           << EC.message() << '\n';
-    return nullptr;
-  }
-
-  return FDOut;
-}
-
-class BEDiagnosticHandler : public DiagnosticHandler {
-  bool *error_;
- public:
-  BEDiagnosticHandler(bool *errorPtr)
-      : error_(errorPtr) {}
-  bool handleDiagnostics(const DiagnosticInfo &DI) override {
-    if (DI.getSeverity() == DS_Error)
-      *error_ = true;
-    if (auto *Remark = dyn_cast<DiagnosticInfoOptimizationBase>(&DI))
-      if (!Remark->isEnabled())
-        return true;
-    DiagnosticPrinterRawOStream DP(errs());
-    errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": ";
-    DI.print(DP);
-    errs() << "\n";
-    return true;
-  }
-};
-
-// Are we in "assemble" mode (all inputs are .s files) or "compile" mode
-// (all inputs are *.go files)?
-enum CompileMode {
-  UnknownCompileMode,
-  CompileAssemblyMode,
-  CompileGoMode
-};
-
-// Helper class for managing the overall Go compilation process.
-
-class CompilationOrchestrator {
- public:
-  CompilationOrchestrator(const char *argvZero,
-                          opt::InputArgList &args,
-                          opt::OptTable &opts);
-  ~CompilationOrchestrator();
-
-  // Various stages in the setup/execution of the compilation.  The
-  // convention here is to return false if there was a fatal error, true
-  // otherwise.
-  bool inspectCommandLine();
-  bool preamble();
-  bool initBridge();
-  bool resolveInputOutput();
-  bool invokeFrontEnd();
-  bool invokeBridge();
-  bool invokeBackEnd();
-  bool invokeAssembler();
-
-  // Exit code to return if there was an error in one of the steps above.
-  int errorReturnCode() const { return errorReturnCode_; }
-
-  // Temporary: return asm output file.
-  const std::string &asmOutFile() const { return asmOutFileName_; }
-
- private:
-  Triple triple_;
-  llvm::LLVMContext context_;
-  const char *progname_;
-  CodeGenOpt::Level cgolvl_;
-  unsigned olvl_;
-  int errorReturnCode_;
-  opt::OptTable &opts_;
-  opt::InputArgList &args_;
-  std::unique_ptr<Llvm_backend> bridge_;
-  std::unique_ptr<TargetMachine> target_;
-  std::unique_ptr<Llvm_linemap> linemap_;
-  std::unique_ptr<llvm::Module> module_;
-  std::vector<std::string> inputFileNames_;
-  std::string outFileName_;
-  std::string asmOutFileName_;
-  std::unique_ptr<ToolOutputFile> asmout_;
-  std::unique_ptr<ToolOutputFile> out_;
-  std::unique_ptr<TargetLibraryInfoImpl> tlii_;
-  CompileMode compileMode_;
-  bool hasError_;
-  bool tmpCreated_;
-
-  CompileMode compileMode() const { return compileMode_; }
-  bool setCompileMode(CompileMode mode) {
-    bool mixedModeError = false;
-    if (compileMode_ == UnknownCompileMode)
-      compileMode_ = mode;
-    else if (compileMode_ != mode)
-      mixedModeError = true;
-    return mixedModeError;
-  }
-
-  bool phaseSuccessful() { errorReturnCode_++; return true; }
-
-  void createPasses(legacy::PassManager &MPM,
-                    legacy::FunctionPassManager &FPM);
-  TargetMachine::CodeGenFileType getOutputFileType();
-  template<typename IT>
-  llvm::Optional<IT> getLastArgAsInteger(gollvm::options::ID id,
-                                         IT defaultValue);
-  PICLevel::Level getPicLevel();
-  llvm::Optional<llvm::Reloc::Model> reconcileRelocModel();
-  bool reconcileOptionPair(gollvm::options::ID yesOption,
-                           gollvm::options::ID noOption,
-                           bool defaultVal);
-  llvm::Optional<llvm::FPOpFusion::FPOpFusionMode> getFPOpFusionMode();
-  std::string firstFileBase();
-};
-
-CompilationOrchestrator::CompilationOrchestrator(const char *argvZero,
-                                                 opt::InputArgList &args,
-                                                 opt::OptTable &opts)
-    : progname_(argvZero), cgolvl_(CodeGenOpt::Default),
-      olvl_(2), errorReturnCode_(1), opts_(opts), args_(args),
-      compileMode_(UnknownCompileMode),
-      hasError_(false), tmpCreated_(false)
-{
-  InitializeAllTargets();
-  InitializeAllTargetMCs();
-  InitializeAllAsmPrinters();
-  InitializeAllAsmParsers();
-}
-
-CompilationOrchestrator::~CompilationOrchestrator()
-{
-  if (tmpCreated_)
-    sys::fs::remove(asmOutFileName_);
-}
-
-TargetMachine::CodeGenFileType CompilationOrchestrator::getOutputFileType()
-{
-  TargetMachine::CodeGenFileType ft;
-  if (args_.hasArg(gollvm::options::OPT_S) ||
-      args_.hasArg(gollvm::options::OPT_emit_llvm))
-    ft = TargetMachine::CGFT_AssemblyFile;
-  else
-    ft = TargetMachine::CGFT_ObjectFile;
-  return ft;
-}
-
-template<typename IT>
-llvm::Optional<IT>
-CompilationOrchestrator::getLastArgAsInteger(gollvm::options::ID id,
-                                             IT defaultValue)
-{
-  IT result = defaultValue;
-  opt::Arg *arg = args_.getLastArg(id);
-  if (arg != nullptr) {
-    if (llvm::StringRef(arg->getValue()).getAsInteger(10, result)) {
-      errs() << progname_ << ": invalid argument '"
-             << arg->getValue() << "' to '"
-             << arg->getAsString(args_) << "' option\n";
-      return None;
-    }
-  }
-  return result;
-}
-
-// Return any settings from the -fPIC/-fpic options, if present. The
-// intent of the code below is to support "rightmost on the command
-// line wins" (compatible with clang and other compilers), so if you
-// specify "-fPIC -fpic" you get small PIC, whereas "-fPIC -fpic
-// -fPIC" this will give you large PIC.
-PICLevel::Level CompilationOrchestrator::getPicLevel()
-{
-  opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_fpic,
-                                   gollvm::options::OPT_fno_pic,
-                                   gollvm::options::OPT_fPIC,
-                                   gollvm::options::OPT_fno_PIC);
-  if (arg == nullptr)
-    return PICLevel::NotPIC;
-  if (arg->getOption().matches(gollvm::options::OPT_fpic))
-    return PICLevel::SmallPIC;
-  else if (arg->getOption().matches(gollvm::options::OPT_fPIC))
-    return PICLevel::BigPIC;
-  return PICLevel::NotPIC;
-}
-
-// Given a pair of llvm::opt options (presumably corresponding to
-// -fXXX and -fno-XXX boolean flags), select the correct value for the
-// option depending on the relative position of the options on the
-// command line (rightmost wins). For example, given -fblah -fno-blah
-// -fblah, we want the same semantics as a single -fblah.
-
-bool
-CompilationOrchestrator::reconcileOptionPair(gollvm::options::ID yesOption,
-                                             gollvm::options::ID noOption,
-                                             bool defaultVal)
-{
-  opt::Arg *arg = args_.getLastArg(yesOption, noOption);
-  if (arg == nullptr)
-    return defaultVal;
-  if (arg->getOption().matches(yesOption))
-    return true;
-  assert(arg->getOption().matches(noOption));
-  return false;
-}
-
-llvm::Optional<llvm::Reloc::Model>
-CompilationOrchestrator::reconcileRelocModel()
-{
-  auto picLevel = getPicLevel();
-  if (picLevel != llvm::PICLevel::NotPIC) {
-    Reloc::Model R = Reloc::PIC_;
-    return R;
-  }
-  return None;
-}
-
-llvm::Optional<llvm::FPOpFusion::FPOpFusionMode>
-CompilationOrchestrator::getFPOpFusionMode()
-{
-  opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_ffp_contract_EQ);
-  llvm::FPOpFusion::FPOpFusionMode res = FPOpFusion::Standard;
-  if (arg != nullptr) {
-    std::string val(arg->getValue());
-    if (val == "off")
-      res = FPOpFusion::Strict;
-    else if (val == "on")
-      res = FPOpFusion::Standard;
-    else if (val == "fast")
-      res = FPOpFusion::Fast;
-    else {
-      errs() << progname_ << ": invalid argument '"
-             << arg->getValue() << "' to '"
-             << arg->getAsString(args_) << "' option\n";
-      return None;
-    }
-  }
-  return res;
-}
-
-std::string CompilationOrchestrator::firstFileBase()
-{
-  std::string firstFile = inputFileNames_[0];
-  size_t dotindex = firstFile.find_last_of(".");
-  assert(dotindex != std::string::npos);
-  return firstFile.substr(0, dotindex);
-}
-
-bool CompilationOrchestrator::inspectCommandLine()
-{
-  // Collect input file names
-  for (opt::Arg *arg : args_) {
-    if (arg->getOption().getKind() == opt::Option::InputClass) {
-      const char *val = arg->getValue();
-      inputFileNames_.push_back(val);
-    }
-  }
-
-  // Set triple.
-  if (const opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_target_EQ))
-    triple_ = Triple(Triple::normalize(arg->getValue()));
-  else
-    triple_ = Triple(sys::getDefaultTargetTriple());
-
-  // Support -march
-  std::string archStr;
-  opt::Arg *archarg = args_.getLastArg(gollvm::options::OPT_march_EQ);
-  if (archarg != nullptr) {
-    std::string val(archarg->getValue());
-    if (val == "native")
-      archStr = sys::getHostCPUName();
-    else
-      archStr = archarg->getValue();
-    triple_.setArchName(archStr);
-  }
-
-  return phaseSuccessful();
-}
-
-bool CompilationOrchestrator::preamble()
-{
-  // Get the target specific parser.
-  std::string Error;
-  const Target *TheTarget =
-      TargetRegistry::lookupTarget("", triple_, Error);
-  if (!TheTarget) {
-    errs() << progname_ << ": " << Error;
-    return false;
-  }
-
-  // optimization level
-  opt::Arg *oarg = args_.getLastArg(gollvm::options::OPT_O_Group);
-  if (oarg != nullptr) {
-    StringRef lev(oarg->getValue());
-    if (lev.size() != 1) {
-      errs() << progname_ << ": invalid argument to -O flag: "
-             << lev << "\n";
-      return false;
-    }
-    switch (lev[0]) {
-      case '0':
-        olvl_ = 0;
-        cgolvl_ = CodeGenOpt::None;
-        break;
-      case '1':
-        olvl_ = 1;
-        cgolvl_ = CodeGenOpt::Less;
-        break;
-      case 's': // TODO: -Os same as -O for now.
-      case '2':
-        olvl_ = 2;
-        cgolvl_ = CodeGenOpt::Default;
-        break;
-      case '3':
-        olvl_ = 3;
-        cgolvl_ = CodeGenOpt::Aggressive;
-        break;
-      default:
-        errs() << progname_ << ": invalid optimization level.\n";
-        return false;
-    }
-  }
-
-  go_no_warn = args_.hasArg(gollvm::options::OPT_w);
-
-  TargetOptions Options;
-
-  // FIXME: turn off integrated assembler for now.
-  Options.DisableIntegratedAS = true;
-
-  // FIXME: this hard-wires on the equivalent of -ffunction-sections
-  // and -fdata-sections, since there doesn't seem to be a high-level
-  // hook for selecting a separate section for a specific variable or
-  // function (other than forcing it into a comdat, which is not
-  // always what we want).
-  Options.FunctionSections = true;
-  Options.DataSections = true;
-  Options.UniqueSectionNames = true;
-
-  // FIXME: this needs to be dependent on target triple
-  Options.EABIVersion = llvm::EABI::Default;
-
-  // init array use
-  Options.UseInitArray =
-      reconcileOptionPair(gollvm::options::OPT_fuse_init_array,
-                          gollvm::options::OPT_fno_use_init_array,
-                          true);
-
-  // FP trapping mode
-  Options.NoTrappingFPMath =
-      reconcileOptionPair(gollvm::options::OPT_ftrapping_math,
-                          gollvm::options::OPT_fno_trapping_math,
-                          true);
-
-
-  // The -fno-math-errno option is essentially a no-op when compiling
-  // Go code, but -fmath-errno does not make sense, since 'errno' is
-  // not exposed in any meaningful way as part of the math package.
-  // Allow users to set -fno-math-errno for compatibility reasons, but
-  // issue an error if -fmath-errno is set.
-  bool mathErrno = reconcileOptionPair(gollvm::options::OPT_fmath_errno,
-                                       gollvm::options::OPT_fno_math_errno,
-                                       false);
-  if (mathErrno) {
-    errs() << "error: -fmath-errno unsupported for Go code\n";
-    return false;
-  }
-
-  // FP contract settings.
-  auto dofuse = getFPOpFusionMode();
-  if (!dofuse)
-    return false;
-  Options.AllowFPOpFusion = *dofuse;
-
-  // Support -mcpu
-  std::string cpuStr;
-  opt::Arg *cpuarg = args_.getLastArg(gollvm::options::OPT_mcpu_EQ);
-  if (cpuarg != nullptr) {
-    std::string val(cpuarg->getValue());
-    if (val == "native")
-      cpuStr = sys::getHostCPUName();
-    else
-      cpuStr = cpuarg->getValue();
-  }
-
-  // Features
-  SubtargetFeatures features;
-  features.getDefaultSubtargetFeatures(triple_);
-  std::string featStr = features.getString();
-
-  // Create target machine
-  Optional<llvm::CodeModel::Model> CM = None;
-  target_.reset(
-      TheTarget->createTargetMachine(triple_.getTriple(), cpuStr, featStr,
-                                     Options, reconcileRelocModel(),
-                                     CM, cgolvl_));
-  assert(target_.get() && "Could not allocate target machine!");
-
-  return phaseSuccessful();
-}
-
-// This helper performs the various initial steps needed to set up the
-// compilation, including prepping the LLVM context, creating an LLVM
-// module, creating the bridge itself (Llvm_backend object) and
-// setting up the Go frontend via a call to go_create_gogo(). At the
-// end of this routine things should be ready to kick off the front end.
-
-bool CompilationOrchestrator::initBridge()
-{
-  // Set up the LLVM context
-  context_.setDiagnosticHandler(
-      llvm::make_unique<BEDiagnosticHandler>(&this->hasError_));
-
-  // Construct linemap and module
-  linemap_.reset(new Llvm_linemap());
-  module_.reset(new llvm::Module("gomodule", context_));
-
-  // Add the target data from the target machine, if it exists
-  module_->setTargetTriple(triple_.getTriple());
-  module_->setDataLayout(target_->createDataLayout());
-  module_->setPICLevel(getPicLevel());
-
-  // Now construct Llvm_backend helper.
-  bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get()));
-
-  // Honor inline, tracelevel cmd line options
-  llvm::Optional<unsigned> tl =
-      getLastArgAsInteger(gollvm::options::OPT_tracelevel_EQ, 0u);
-  if (!tl)
-    return false;
-  bridge_->setTraceLevel(*tl);
-  bridge_->setNoInline(args_.hasArg(gollvm::options::OPT_fno_inline));
-
-  // Support -fgo-dump-ast
-  if (args_.hasArg(gollvm::options::OPT_fgo_dump_ast))
-    go_enable_dump("ast");
-
-  // Populate 'args' struct with various bits of information needed by
-  // the front end, then pass it to the front end via go_create_gogo().
-  struct go_create_gogo_args args;
-  unsigned bpi = target_->getPointerSize(0) * 8;
-  args.int_type_size = bpi;
-  args.pointer_size = bpi;
-  opt::Arg *pkpa = args_.getLastArg(gollvm::options::OPT_fgo_pkgpath_EQ);
-  args.pkgpath = (pkpa == nullptr ? NULL : pkpa->getValue());
-  opt::Arg *pkpp = args_.getLastArg(gollvm::options::OPT_fgo_prefix_EQ);
-  args.prefix = (pkpp == nullptr ? NULL : pkpp->getValue());
-  opt::Arg *relimp =
-      args_.getLastArg(gollvm::options::OPT_fgo_relative_import_path_EQ);
-  args.relative_import_path =
-      (relimp == nullptr ? NULL : relimp->getValue());
-  opt::Arg *chdr =
-      args_.getLastArg(gollvm::options::OPT_fgo_c_header_EQ);
-  args.c_header = (chdr == nullptr ? NULL : chdr->getValue());
-  args.check_divide_by_zero =
-      reconcileOptionPair(gollvm::options::OPT_fgo_check_divide_zero,
-                          gollvm::options::OPT_fno_go_check_divide_zero,
-                          true);
-  args.check_divide_overflow =
-        reconcileOptionPair(gollvm::options::OPT_fgo_check_divide_overflow,
-                            gollvm::options::OPT_fno_go_check_divide_overflow,
-                            true);
-  args.compiling_runtime =
-      args_.hasArg(gollvm::options::OPT_fgo_compiling_runtime);
-  llvm::Optional<int> del =
-      getLastArgAsInteger(gollvm::options::OPT_fgo_debug_escape_EQ, 0);
-  if (!del)
-    return false;
-  args.debug_escape_level = *del;
-  opt::Arg *hasharg =
-      args_.getLastArg(gollvm::options::OPT_fgo_debug_escape_hash_EQ);
-  args.debug_escape_hash = (hasharg != nullptr ? hasharg->getValue() : NULL);
-  args.nil_check_size_threshold = -1;
-  args.linemap = linemap_.get();
-  args.backend = bridge_.get();
-  go_create_gogo (&args);
-
-  /* The default precision for floating point numbers.  This is used
-     for floating point constants with abstract type.  This may
-     eventually be controllable by a command line option.  */
-  mpfr_set_default_prec (256);
-
-  // Escape analysis
-  bool enableEscapeAnalysis =
-      reconcileOptionPair(gollvm::options::OPT_fgo_optimize_allocs,
-                          gollvm::options::OPT_fno_go_optimize_allocs,
-                          true);
-  go_enable_optimize("allocs", enableEscapeAnalysis ? 1 : 0);
-
-  // Include dirs
-  std::vector<std::string> incargs =
-      args_.getAllArgValues(gollvm::options::OPT_I);
-  for (auto dir : incargs) {
-    if (sys::fs::is_directory(dir))
-      go_add_search_path(dir.c_str());
-  }
-
-  // Library dirs
-  // TODO: add version, architecture dirs
-  std::vector<std::string> libargs =
-      args_.getAllArgValues(gollvm::options::OPT_L);
-  for (auto dir : libargs)
-    if (sys::fs::is_directory(dir))
-      go_add_search_path(dir.c_str());
-
-  return phaseSuccessful();
-}
-
-bool CompilationOrchestrator::resolveInputOutput()
-{
-  // What 'mode' are we operating in? If all inputs are *.s files,
-  // then we're in assemble mode; if all inputs are *.go files, we're
-  // in compile mode. No support for a mix of *.s and *.go files.
-  bool mixedModeError = false;
-  if (inputFileNames_.size() == 0) {
-    errs() << "error: no input files supplied.\n";
-    return false;
-  }
-  for (auto &fn : inputFileNames_) {
-    // Check for existence of input file.
-    if (!sys::fs::exists(fn)) {
-      errs() << "error: input file '"
-             << fn << "' does not exist\n";
-      return false;
-    }
-    size_t dotindex = fn.find_last_of(".");
-    if (dotindex == std::string::npos) {
-      errs() << "error: malformed input file '"
-             << fn << "' (no extension)\n";
-      return false;
-    }
-    std::string extension = fn.substr(dotindex);
-    if (extension.compare(".s") == 0) {
-      mixedModeError = setCompileMode(CompileAssemblyMode);
-      if (mixedModeError)
-        break;
-    } else if (extension.compare(".go") == 0) {
-      mixedModeError = setCompileMode(CompileGoMode);
-      if (mixedModeError)
-        break;
-    } else {
-      errs() << "error: unknown input file '"
-             << fn << "' (extension must be '.s' or '.go')\n";
-      return false;
-    }
-  }
-  if (mixedModeError) {
-    errs() << "error: mixing of assembler (*.s) and Go (*.go) "
-           << "source files not supported.\n";
-    return false;
-  }
-  if (args_.hasArg(gollvm::options::OPT_S) &&
-      compileMode_ == CompileAssemblyMode) {
-    errs() << "error: -S option not valid if input files "
-           << "are assembly source.\n";
-    return false;
-  }
-
-  // Decide where we're going to send the output for this compilation.
-  // If the "-o" option was not specified, use the basename of the
-  // first input argument.
-  TargetMachine::CodeGenFileType ft = getOutputFileType();
-  opt::Arg *outFileArg = args_.getLastArg(gollvm::options::OPT_o);
-  if (outFileArg) {
-    outFileName_ = outFileArg->getValue();
-  } else {
-    outFileName_ = firstFileBase();
-    if (ft == TargetMachine::CGFT_AssemblyFile) {
-      if (args_.hasArg(gollvm::options::OPT_emit_llvm)) {
-        if (args_.hasArg(gollvm::options::OPT_S)) {
-          outFileName_ += ".ll";
-        } else {
-          outFileName_ += ".bc";
-        }
-      } else {
-        outFileName_ += ".s";
-      }
-    } else {
-      outFileName_ += ".o";
-    }
-  }
-
-  // If we're not compiling to assembly, then we need an intermediate
-  // output file into which we'll emit assembly code.
-  if (ft != TargetMachine::CGFT_AssemblyFile) {
-    if (args_.hasArg(gollvm::options::OPT_save_temps)) {
-      asmOutFileName_ = firstFileBase();
-      asmOutFileName_ += ".s";
-    } else {
-      llvm::SmallString<128> tempFileName;
-      std::error_code tfcEC =
-          llvm::sys::fs::createTemporaryFile("asm", "s", tempFileName);
-      if (tfcEC) {
-        errs() << tfcEC.message() << "\n";
-        return false;
-      }
-      tmpCreated_ = true;
-      sys::RemoveFileOnSignal(tempFileName);
-      asmOutFileName_ = std::string(tempFileName.c_str());
-    }
-  } else {
-    asmOutFileName_ = outFileName_;
-  }
-
-  // Open the assembler output file
-  asmout_ = GetOutputStream(asmOutFileName_, /*text*/ false);
-  if (!asmout_)
-    return false;
-
-  // Open object file as well if needed
-  if (ft != TargetMachine::CGFT_AssemblyFile) {
-    out_ = GetOutputStream(outFileName_, /*binary*/ true);
-    if (!out_)
-      return false;
-  }
-
-  return phaseSuccessful();
-}
-
-bool CompilationOrchestrator::invokeFrontEnd()
-{
-  // If we're in "assemble" mode, no need to invoke the compiler
-  if (compileMode_ == CompileAssemblyMode)
-    return true;
-
-  // Collect the input files and kick off the front end
-  // Kick off the front end
-  unsigned nfiles = inputFileNames_.size();
-  std::unique_ptr<const char *> filenames(new const char *[nfiles]);
-  const char **fns = filenames.get();
-  unsigned idx = 0;
-  for (auto &fn : inputFileNames_)
-    fns[idx++] = fn.c_str();
-  go_parse_input_files(fns, nfiles, false, true);
-  if (!args_.hasArg(gollvm::options::OPT_nobackend))
-    go_write_globals();
-  if (args_.hasArg(gollvm::options::OPT_dump_ir))
-    bridge_->dumpModule();
-  if (!args_.hasArg(gollvm::options::OPT_noverify) && !go_be_saw_errors())
-    bridge_->verifyModule();
-  llvm::Optional<unsigned> tl =
-      getLastArgAsInteger(gollvm::options::OPT_tracelevel_EQ, 0u);
-  if (*tl)
-    std::cerr << "linemap stats:" << linemap_->statistics() << "\n";
-
-  // Delete the bridge at this point. In the case that there were
-  // errors, this will help clean up any unreachable llvm Instructions
-  // (which would otherwise trigger asserts); in the non-error case it
-  // will help to free up bridge-related memory prior to kicking off
-  // the pass manager.
-  bridge_.reset(nullptr);
-
-  // Early exit at this point if we've seen errors
-  if (go_be_saw_errors())
-    return false;
-
-  return phaseSuccessful();
-}
-
-void CompilationOrchestrator::createPasses(legacy::PassManager &MPM,
-                                           legacy::FunctionPassManager &FPM)
-{
-  if (args_.hasArg(gollvm::options::OPT_disable_llvm_passes))
-    return;
-
-  // FIXME: support LTO, ThinLTO, PGO
-
-  PassManagerBuilder pmb;
-
-  // Configure the inliner
-  if (args_.hasArg(gollvm::options::OPT_fno_inline) || olvl_ == 0) {
-    // Nothing here at the moment. There is go:noinline, but no equivalent
-    // of go:alwaysinline.
-  } else {
-    bool disableInlineHotCallSite = false; // for autofdo, not yet supported
-    pmb.Inliner =
-        createFunctionInliningPass(olvl_, 2, disableInlineHotCallSite);
-  }
-
-  pmb.OptLevel = olvl_;
-  pmb.SizeLevel = 0; // TODO: decide on right value here
-  pmb.PrepareForThinLTO = false;
-  pmb.PrepareForLTO = false;
-
-  FPM.add(new TargetLibraryInfoWrapperPass(*tlii_));
-  if (! args_.hasArg(gollvm::options::OPT_noverify))
-    FPM.add(createVerifierPass());
-
-  pmb.populateFunctionPassManager(FPM);
-  pmb.populateModulePassManager(MPM);
-}
-
-bool CompilationOrchestrator::invokeBackEnd()
-{
-  // If we're in "assemble" mode, no need to invoke the compiler
-  if (compileMode_ == CompileAssemblyMode)
-    return true;
-
-  tlii_.reset(new TargetLibraryInfoImpl(triple_));
-
-  legacy::PassManager modulePasses;
-  legacy::FunctionPassManager functionPasses(module_.get());
-
-  // Set up module and function passes
-  if (!args_.hasArg(gollvm::options::OPT_disable_llvm_passes)) {
-    modulePasses.add(
-        createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
-    functionPasses.add(
-        createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
-    createPasses(modulePasses, functionPasses);
-  }
-
-  // Add passes to emit bitcode or LLVM IR as appropriate. Here we mimic
-  // clang behavior, which is to emit bitcode when "-emit-llvm" is specified
-  // but an LLVM IR dump of "-S -emit-llvm" is used.
-  raw_pwrite_stream *OS = &asmout_->os();
-  if (args_.hasArg(gollvm::options::OPT_emit_llvm)) {
-    bool bitcode = !args_.hasArg(gollvm::options::OPT_S);
-    bool preserveUseLists =
-        reconcileOptionPair(gollvm::options::OPT_emit_llvm_uselists,
-                            gollvm::options::OPT_no_emit_llvm_uselists,
-                            false);
-    modulePasses.add(bitcode ?
-                     createBitcodeWriterPass(*OS, preserveUseLists) :
-                     createPrintModulePass(*OS, "", preserveUseLists));
-  }
-
-  // Set up codegen passes
-  legacy::PassManager codeGenPasses;
-  if (!args_.hasArg(gollvm::options::OPT_disable_llvm_passes)) {
-    codeGenPasses.add(
-        createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
-
-    // Codegen setup
-    codeGenPasses.add(new TargetLibraryInfoWrapperPass(*tlii_));
-    bool noverify = args_.hasArg(gollvm::options::OPT_noverify);
-    TargetMachine::CodeGenFileType ft = TargetMachine::CGFT_AssemblyFile;
-    if (target_->addPassesToEmitFile(codeGenPasses, *OS, ft,
-                                     /*DisableVerify=*/ noverify)) {
-      errs() << "error: unable to interface with target\n";
-      return false;
-    }
-  }
-
-  // Here we go... first function passes
-  functionPasses.doInitialization();
-  for (Function &F : *module_.get())
-    if (!F.isDeclaration())
-      functionPasses.run(F);
-  functionPasses.doFinalization();
-
-  // ... then module passes
-  modulePasses.run(*module_.get());
-
-  // ... and finally code generation
-  if (args_.hasArg(gollvm::options::OPT_disable_llvm_passes))
-    codeGenPasses.run(*module_.get());
-
-  if (hasError_)
-    return false;
-
-  // If -v is in effect, print something to show the effect of the
-  // compilation. This is in some sense a fiction, because the top
-  // level driver is not invoking a tool to perform the compile, but
-  // there is an expectation with compilers that if you take the
-  // "-v" output and then execute each command shown by hand, you'll
-  // get the same effect as the original command that produced the
-  // "-v" output.
-  if (args_.hasArg(gollvm::options::OPT_v)) {
-    errs() << progname_ << " -S";
-    for (auto arg : args_) {
-      if (arg->getOption().matches(gollvm::options::OPT_v) ||
-          arg->getOption().matches(gollvm::options::OPT_c) ||
-          arg->getOption().matches(gollvm::options::OPT_o) ||
-          arg->getOption().matches(gollvm::options::OPT_save_temps))
-        continue;
-      errs() << " " << arg->getAsString(args_);
-    }
-    errs() << " -o " << asmOutFileName_ << "\n";
-  }
-
-  // Keep the resulting output file if -S or -save-temps are in effect.
-  if (getOutputFileType() == TargetMachine::CGFT_AssemblyFile ||
-      args_.hasArg(gollvm::options::OPT_save_temps))
-    asmout_->keep();
-
-  return phaseSuccessful();
-}
-
-bool CompilationOrchestrator::invokeAssembler()
-{
-  if (getOutputFileType() == TargetMachine::CGFT_AssemblyFile)
-    return true;
-
-  ArrayRef<StringRef> searchpaths;
-  auto aspath = sys::findProgramByName("as", searchpaths);
-  if (! aspath ) {
-    errs() << "error: unable to locate path for 'as'" << "\n";
-    return false;
-  }
-
-  // Note: ArgStringList is effectively a vector of "const char *".
-  opt::ArgStringList asmcmd;
-  asmcmd.push_back("as");
-  if (compileMode_ == CompileAssemblyMode) {
-    for (auto &fn : inputFileNames_)
-      asmcmd.push_back(fn.c_str());
-  } else {
-    asmcmd.push_back(asmOutFileName_.c_str());
-  }
-  asmcmd.push_back("-o");
-  asmcmd.push_back(outFileName_.c_str());
-  args_.AddAllArgValues(asmcmd,
-                        gollvm::options::OPT_Wa_COMMA,
-                        gollvm::options::OPT_Xassembler);
-  opt::Arg *gzarg = args_.getLastArg(gollvm::options::OPT_gz,
-                                     gollvm::options::OPT_gz_EQ);
-  std::string cds;
-  if (gzarg != nullptr) {
-    if (gzarg->getOption().matches(gollvm::options::OPT_gz)) {
-      asmcmd.push_back("-compress-debug-sections");
-    } else {
-      cds = "-compress-debug-sections=";
-      cds += gzarg->getValue();
-      asmcmd.push_back(cds.c_str());
-    }
-  }
-  asmcmd.push_back(nullptr);
-
-  if (args_.hasArg(gollvm::options::OPT_v)) {
-    bool first = true;
-    for (auto arg : asmcmd) {
-      errs() << (first ? "" : " ") << arg;
-      first = false;
-    }
-    errs() << "\n";
-  }
-
-  std::string errMsg;
-  bool rval = true;
-  int rc = sys::ExecuteAndWait(*aspath, asmcmd.data(),
-                               /*env=*/nullptr, /*Redirects*/{},
-                               /*secondsToWait=*/0,
-                               /*memoryLimit=*/0, &errMsg);
-  if (rc != 0) {
-    errs() << errMsg << "\n";
-    rval = false;
-  } else {
-    out_->keep();
-  }
-
-  return (rval ? phaseSuccessful() : false);
-}
-
 int main(int argc, char **argv)
 {
   // Print a stack trace if we signal out.
@@ -1028,32 +168,6 @@
   if (!clp.parseCommandLine(argc, argv))
     return 1;
 
-  CompilationOrchestrator orchestrator(argv[0], clp.args(), *opts.get());
-
-  // Collect input files from the command line, resolve target.
-  if (!orchestrator.inspectCommandLine())
-    return orchestrator.errorReturnCode();
-
-  // Initialize target
-  if (!orchestrator.preamble())
-    return orchestrator.errorReturnCode();
-
-  // Set up the bridge
-  if (!orchestrator.initBridge())
-    return orchestrator.errorReturnCode();
-
-  // Determine output file
-  if (!orchestrator.resolveInputOutput())
-    return orchestrator.errorReturnCode();
-
-  // Invoke front end
-  if (! orchestrator.invokeFrontEnd())
-    return orchestrator.errorReturnCode();
-
-  // Invoke back end
-  if (! orchestrator.invokeBackEnd())
-    return orchestrator.errorReturnCode();
-
   // Create driver.
   Driver driver(clp.args(), opts.get(), argv[0]);
 
@@ -1065,7 +179,7 @@
   // Build compilation; construct actions for this compile.
   std::unique_ptr<Compilation> compilation =
       driver.buildCompilation(*toolchain);
-  if (!driver.buildActions(*compilation, orchestrator.asmOutFile()))
+  if (!driver.buildActions(*compilation))
     return 2;
 
   // Process the action list. This will carry out actions that don't
diff --git a/driver/CMakeLists.txt b/driver/CMakeLists.txt
index 8b05fc3..d96d067 100644
--- a/driver/CMakeLists.txt
+++ b/driver/CMakeLists.txt
@@ -20,6 +20,7 @@
   Artifact.cpp
   Command.cpp
   Compilation.cpp
+  CompileGo.cpp
   Driver.cpp
   GccUtils.cpp
   GnuTools.cpp
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
new file mode 100644
index 0000000..a3bf0de
--- /dev/null
+++ b/driver/CompileGo.cpp
@@ -0,0 +1,624 @@
+//===-- CompileGo.cpp ------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Gollvm driver helper class "CompileGo" methods.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CompileGo.h"
+
+#include "go-llvm-linemap.h"
+#include "go-llvm-diagnostics.h"
+#include "go-llvm.h"
+#include "go-c.h"
+#include "mpfr.h"
+#include "GollvmOptions.h"
+
+#include "Action.h"
+#include "Artifact.h"
+#include "Driver.h"
+#include "ToolChain.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/Bitcode/BitcodeWriterPass.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/IRPrintingPasses.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/MC/SubtargetFeature.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/PassManagerBuilder.h"
+
+using namespace llvm;
+
+namespace gollvm {
+namespace driver {
+
+class CompileGoImpl {
+ public:
+  CompileGoImpl(ToolChain &tc);
+
+  // Perform compilation.
+  bool performAction(Compilation &compilation,
+                     const Action &jobAction,
+                     const ArtifactList &inputArtifacts,
+                     const Artifact &output);
+
+ private:
+  Triple triple_;
+  const ToolChain &toolchain_;
+  Driver &driver_;
+  LLVMContext context_;
+  const char *progname_;
+  opt::InputArgList &args_;
+  CodeGenOpt::Level cgolvl_;
+  unsigned olvl_;
+  bool hasError_;
+  std::unique_ptr<Llvm_backend> bridge_;
+  std::unique_ptr<TargetMachine> target_;
+  std::unique_ptr<Llvm_linemap> linemap_;
+  std::unique_ptr<Module> module_;
+  std::vector<std::string> inputFileNames_;
+  std::string asmOutFileName_;
+  std::unique_ptr<ToolOutputFile> asmout_;
+  std::unique_ptr<TargetLibraryInfoImpl> tlii_;
+
+  void createPasses(legacy::PassManager &MPM,
+                    legacy::FunctionPassManager &FPM);
+
+  // This routine emits output for -### and/or -v, then returns TRUE
+  // of the compilation should be stubbed out (-###) or FALSE otherwise.
+  bool preamble(const Artifact &output);
+
+  // The routines below return TRUE for success, FALSE for failure/error/
+  bool setup();
+  bool initBridge();
+  bool invokeFrontEnd();
+  bool invokeBridge();
+  bool invokeBackEnd();
+  bool resolveInputOutput(const Action &jobAction,
+                          const ArtifactList &inputArtifacts,
+                          const Artifact &output);
+};
+
+CompileGoImpl::CompileGoImpl(ToolChain &tc)
+    : triple_(tc.driver().triple()),
+      toolchain_(tc),
+      driver_(tc.driver()),
+      progname_(tc.driver().progname()),
+      args_(tc.driver().args()),
+      cgolvl_(CodeGenOpt::Default),
+      olvl_(2),
+      hasError_(false)
+{
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  InitializeAllAsmPrinters();
+  InitializeAllAsmParsers();
+}
+
+bool CompileGoImpl::performAction(Compilation &compilation,
+                                  const Action &jobAction,
+                                  const ArtifactList &inputArtifacts,
+                                  const Artifact &output)
+{
+  if (preamble(output))
+    return true;
+
+  // Resolve input/output files.
+  if (!resolveInputOutput(jobAction, inputArtifacts, output))
+    return false;
+
+  // Setup
+  if (!setup())
+    return false;
+
+  // Set up the bridge
+  if (!initBridge())
+    return false;
+
+  // Invoke front end
+  if (!invokeFrontEnd())
+    return false;
+
+  // Invoke back end
+  if (!invokeBackEnd())
+    return false;
+
+  return true;
+}
+
+class BEDiagnosticHandler : public DiagnosticHandler {
+  bool *error_;
+ public:
+  BEDiagnosticHandler(bool *errorPtr)
+      : error_(errorPtr) {}
+  bool handleDiagnostics(const DiagnosticInfo &DI) override {
+    if (DI.getSeverity() == DS_Error)
+      *error_ = true;
+    if (auto *Remark = dyn_cast<DiagnosticInfoOptimizationBase>(&DI))
+      if (!Remark->isEnabled())
+        return true;
+    DiagnosticPrinterRawOStream DP(errs());
+    errs() << LLVMContext::getDiagnosticMessagePrefix(DI.getSeverity()) << ": ";
+    DI.print(DP);
+    errs() << "\n";
+    return true;
+  }
+};
+
+bool CompileGoImpl::preamble(const Artifact &output)
+{
+  // If -v is in effect, print something to show the effect of the
+  // compilation. This is in some sense a fiction, because the top
+  // level driver is not invoking an external tool to perform the
+  // compile, but there is an expectation with compilers that if you
+  // take the "-v" output and then execute each command shown by hand,
+  // you'll get the same effect as the original command that produced
+  // the "-v" output.
+  bool hashHashHash = args_.hasArg(gollvm::options::OPT__HASH_HASH_HASH);
+  const char *qu = (hashHashHash ? "\"" : "");
+  if (args_.hasArg(gollvm::options::OPT_v) || hashHashHash) {
+    errs() << " " << progname_ << " " << qu << "-S" << qu;
+    for (auto arg : args_) {
+      if (arg->getOption().matches(gollvm::options::OPT_v) ||
+          arg->getOption().matches(gollvm::options::OPT_c) ||
+          arg->getOption().matches(gollvm::options::OPT_o) ||
+          arg->getOption().matches(gollvm::options::OPT__HASH_HASH_HASH) ||
+          arg->getOption().matches(gollvm::options::OPT_save_temps))
+        continue;
+      errs() << " " << qu << arg->getAsString(args_) << qu;
+    }
+    errs() << " " << qu << "-o" << qu << " " << qu << output.file() << qu
+           << "\n";
+  }
+
+  return hashHashHash;
+}
+
+bool CompileGoImpl::resolveInputOutput(const Action &jobAction,
+                                       const ArtifactList &inputArtifacts,
+                                       const Artifact &output)
+{
+  // Collect input files
+  for (auto inp : inputArtifacts)
+    inputFileNames_.push_back(inp->file());
+  assert(! inputFileNames_.empty());
+  asmOutFileName_ = output.file();
+
+  // Open output file.
+  std::error_code EC;
+  sys::fs::OpenFlags OpenFlags = sys::fs::F_Text;
+  auto FDOut = llvm::make_unique<ToolOutputFile>(asmOutFileName_, EC,
+                                                 OpenFlags);
+  if (EC) {
+    errs() << progname_ << ": error opening " << asmOutFileName_ << ": "
+           << EC.message() << '\n';
+    return false;
+  }
+
+  asmout_.reset(FDOut.release());
+  asmout_->keep();
+  return true;
+}
+
+bool CompileGoImpl::setup()
+{
+  // Set triple.
+  triple_ = driver_.triple();
+
+  // Get the target specific parser.
+  std::string Error;
+  const Target *TheTarget =
+      TargetRegistry::lookupTarget("", triple_, Error);
+  if (!TheTarget) {
+    errs() << progname_ << Error;
+    return false;
+  }
+
+  // optimization level
+  opt::Arg *oarg = args_.getLastArg(gollvm::options::OPT_O_Group);
+  if (oarg != nullptr) {
+    StringRef lev(oarg->getValue());
+    if (lev.size() != 1) {
+      errs() << progname_ << ": invalid argument to -O flag: "
+             << lev << "\n";
+      return false;
+    }
+    switch (lev[0]) {
+      case '0':
+        olvl_ = 0;
+        cgolvl_ = CodeGenOpt::None;
+        break;
+      case '1':
+        olvl_ = 1;
+        cgolvl_ = CodeGenOpt::Less;
+        break;
+      case 's': // TODO: -Os same as -O for now.
+      case '2':
+        olvl_ = 2;
+        cgolvl_ = CodeGenOpt::Default;
+        break;
+      case '3':
+        olvl_ = 3;
+        cgolvl_ = CodeGenOpt::Aggressive;
+        break;
+      default:
+        errs() << progname_ << ": invalid optimization level.\n";
+        return false;
+    }
+  }
+
+  go_no_warn = args_.hasArg(gollvm::options::OPT_w);
+
+  TargetOptions Options;
+
+  // FIXME: turn off integrated assembler for now.
+  Options.DisableIntegratedAS = true;
+
+  // FIXME: this hard-wires on the equivalent of -ffunction-sections
+  // and -fdata-sections, since there doesn't seem to be a high-level
+  // hook for selecting a separate section for a specific variable or
+  // function (other than forcing it into a comdat, which is not
+  // always what we want).
+  Options.FunctionSections = true;
+  Options.DataSections = true;
+  Options.UniqueSectionNames = true;
+
+  // FIXME: this needs to be dependent on target triple
+  Options.EABIVersion = llvm::EABI::Default;
+
+  // init array use
+  Options.UseInitArray =
+      driver_.reconcileOptionPair(gollvm::options::OPT_fuse_init_array,
+                                  gollvm::options::OPT_fno_use_init_array,
+                                  true);
+
+  // FP trapping mode
+  Options.NoTrappingFPMath =
+      driver_.reconcileOptionPair(gollvm::options::OPT_ftrapping_math,
+                                  gollvm::options::OPT_fno_trapping_math,
+                                  false);
+
+
+  // The -fno-math-errno option is essentially a no-op when compiling
+  // Go code, but -fmath-errno does not make sense, since 'errno' is
+  // not exposed in any meaningful way as part of the math package.
+  // Allow users to set -fno-math-errno for compatibility reasons, but
+  // issue an error if -fmath-errno is set.
+  bool mathErrno =
+      driver_.reconcileOptionPair(gollvm::options::OPT_fmath_errno,
+                                  gollvm::options::OPT_fno_math_errno,
+                                  false);
+  if (mathErrno) {
+    errs() << "error: -fmath-errno unsupported for Go code\n";
+    return false;
+  }
+
+  // FP contract settings.
+  auto dofuse = driver_.getFPOpFusionMode();
+  if (!dofuse)
+    return false;
+  Options.AllowFPOpFusion = *dofuse;
+
+  // Support -mcpu
+  std::string cpuStr;
+  opt::Arg *cpuarg = args_.getLastArg(gollvm::options::OPT_mcpu_EQ);
+  if (cpuarg != nullptr) {
+    std::string val(cpuarg->getValue());
+    if (val == "native")
+      cpuStr = sys::getHostCPUName();
+    else
+      cpuStr = cpuarg->getValue();
+  }
+
+  // Features.
+  // FIXME: incorporate command line flags.
+  SubtargetFeatures features;
+  features.getDefaultSubtargetFeatures(triple_);
+  std::string featStr = features.getString();
+
+  // Create target machine
+  Optional<llvm::CodeModel::Model> CM = None;
+  target_.reset(
+      TheTarget->createTargetMachine(triple_.getTriple(), cpuStr, featStr,
+                                     Options, driver_.reconcileRelocModel(),
+                                     CM, cgolvl_));
+  assert(target_.get() && "Could not allocate target machine!");
+
+  return true;
+}
+
+// This helper performs the various initial steps needed to set up the
+// compilation, including prepping the LLVM context, creating an LLVM
+// module, creating the bridge itself (Llvm_backend object) and
+// setting up the Go frontend via a call to go_create_gogo(). At the
+// end of this routine things should be ready to kick off the front end.
+
+bool CompileGoImpl::initBridge()
+{
+  // Set up the LLVM context
+  context_.setDiagnosticHandler(
+      llvm::make_unique<BEDiagnosticHandler>(&this->hasError_));
+
+  // Construct linemap and module
+  linemap_.reset(new Llvm_linemap());
+  module_.reset(new llvm::Module("gomodule", context_));
+
+  // Add the target data from the target machine, if it exists
+  module_->setTargetTriple(triple_.getTriple());
+  module_->setDataLayout(target_->createDataLayout());
+  module_->setPICLevel(driver_.getPicLevel());
+
+  // Now construct Llvm_backend helper.
+  bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get()));
+
+  // Honor inline, tracelevel cmd line options
+  llvm::Optional<unsigned> tl =
+      driver_.getLastArgAsInteger(gollvm::options::OPT_tracelevel_EQ, 0u);
+  if (!tl)
+    return false;
+  bridge_->setTraceLevel(*tl);
+  bridge_->setNoInline(args_.hasArg(gollvm::options::OPT_fno_inline));
+
+  // Support -fgo-dump-ast
+  if (args_.hasArg(gollvm::options::OPT_fgo_dump_ast))
+    go_enable_dump("ast");
+
+  // Populate 'args' struct with various bits of information needed by
+  // the front end, then pass it to the front end via go_create_gogo().
+  struct go_create_gogo_args args;
+  unsigned bpi = target_->getPointerSize(0) * 8;
+  args.int_type_size = bpi;
+  args.pointer_size = bpi;
+  opt::Arg *pkpa = args_.getLastArg(gollvm::options::OPT_fgo_pkgpath_EQ);
+  args.pkgpath = (pkpa == nullptr ? NULL : pkpa->getValue());
+  opt::Arg *pkpp = args_.getLastArg(gollvm::options::OPT_fgo_prefix_EQ);
+  args.prefix = (pkpp == nullptr ? NULL : pkpp->getValue());
+  opt::Arg *relimp =
+      args_.getLastArg(gollvm::options::OPT_fgo_relative_import_path_EQ);
+  args.relative_import_path =
+      (relimp == nullptr ? NULL : relimp->getValue());
+  opt::Arg *chdr =
+      args_.getLastArg(gollvm::options::OPT_fgo_c_header_EQ);
+  args.c_header = (chdr == nullptr ? NULL : chdr->getValue());
+  args.check_divide_by_zero =
+      driver_.reconcileOptionPair(gollvm::options::OPT_fgo_check_divide_zero,
+                                  gollvm::options::OPT_fno_go_check_divide_zero,
+                                  true);
+  args.check_divide_overflow =
+      driver_.reconcileOptionPair(gollvm::options::OPT_fgo_check_divide_overflow,
+                                  gollvm::options::OPT_fno_go_check_divide_overflow,
+                                  true);
+  args.compiling_runtime =
+      args_.hasArg(gollvm::options::OPT_fgo_compiling_runtime);
+  llvm::Optional<int> del =
+      driver_.getLastArgAsInteger(gollvm::options::OPT_fgo_debug_escape_EQ, 0);
+  if (!del)
+    return false;
+  args.debug_escape_level = *del;
+  opt::Arg *hasharg =
+      args_.getLastArg(gollvm::options::OPT_fgo_debug_escape_hash_EQ);
+  args.debug_escape_hash = (hasharg != nullptr ? hasharg->getValue() : NULL);
+  args.nil_check_size_threshold = -1;
+  args.linemap = linemap_.get();
+  args.backend = bridge_.get();
+  go_create_gogo (&args);
+
+  /* The default precision for floating point numbers.  This is used
+     for floating point constants with abstract type.  This may
+     eventually be controllable by a command line option.  */
+  mpfr_set_default_prec (256);
+
+  // Escape analysis
+  bool enableEscapeAnalysis =
+      driver_.reconcileOptionPair(gollvm::options::OPT_fgo_optimize_allocs,
+                                  gollvm::options::OPT_fno_go_optimize_allocs,
+                                  true);
+  go_enable_optimize("allocs", enableEscapeAnalysis ? 1 : 0);
+
+  // Include dirs
+  std::vector<std::string> incargs =
+      args_.getAllArgValues(gollvm::options::OPT_I);
+  for (auto &dir : incargs) {
+    if (sys::fs::is_directory(dir))
+      go_add_search_path(dir.c_str());
+  }
+
+  // Library dirs
+  // TODO: add version, architecture dirs
+  std::vector<std::string> libargs =
+      args_.getAllArgValues(gollvm::options::OPT_L);
+  for (auto &dir : libargs) {
+    struct stat st;
+    if (stat (dir.c_str(), &st) == 0 && S_ISDIR (st.st_mode))
+      go_add_search_path(dir.c_str());
+  }
+
+  return true;
+}
+
+bool CompileGoImpl::invokeFrontEnd()
+{
+  // Collect the input files and kick off the front end
+  // Kick off the front end
+  unsigned nfiles = inputFileNames_.size();
+  std::unique_ptr<const char *> filenames(new const char *[nfiles]);
+  const char **fns = filenames.get();
+  unsigned idx = 0;
+  for (auto &fn : inputFileNames_)
+    fns[idx++] = fn.c_str();
+  go_parse_input_files(fns, nfiles, false, true);
+  if (!args_.hasArg(gollvm::options::OPT_nobackend))
+    go_write_globals();
+  if (args_.hasArg(gollvm::options::OPT_dump_ir))
+    bridge_->dumpModule();
+  if (!args_.hasArg(gollvm::options::OPT_noverify) && !go_be_saw_errors())
+    bridge_->verifyModule();
+  llvm::Optional<unsigned> tl =
+      driver_.getLastArgAsInteger(gollvm::options::OPT_tracelevel_EQ, 0u);
+  if (*tl)
+    std::cerr << "linemap stats:" << linemap_->statistics() << "\n";
+
+  // Delete the bridge at this point. In the case that there were
+  // errors, this will help clean up any unreachable LLVM Instructions
+  // (which would otherwise trigger asserts); in the non-error case it
+  // will help to free up bridge-related memory prior to kicking off
+  // the pass manager.
+  bridge_.reset(nullptr);
+
+  // Early exit at this point if we've seen errors
+  if (go_be_saw_errors())
+    return false;
+
+  return true;
+}
+
+void CompileGoImpl::createPasses(legacy::PassManager &MPM,
+                                           legacy::FunctionPassManager &FPM)
+{
+  if (args_.hasArg(gollvm::options::OPT_disable_llvm_passes))
+    return;
+
+  // FIXME: support LTO, ThinLTO, PGO
+
+  PassManagerBuilder pmb;
+
+  // Configure the inliner
+  if (args_.hasArg(gollvm::options::OPT_fno_inline) || olvl_ == 0) {
+    // Nothing here at the moment. There is go:noinline, but no equivalent
+    // of go:alwaysinline.
+  } else {
+    bool disableInlineHotCallSite = false; // for autofdo, not yet supported
+    pmb.Inliner =
+        createFunctionInliningPass(olvl_, 2, disableInlineHotCallSite);
+  }
+
+  pmb.OptLevel = olvl_;
+  pmb.SizeLevel = 0; // TODO: decide on right value here
+  pmb.PrepareForThinLTO = false;
+  pmb.PrepareForLTO = false;
+
+  FPM.add(new TargetLibraryInfoWrapperPass(*tlii_));
+  if (! args_.hasArg(gollvm::options::OPT_noverify))
+    FPM.add(createVerifierPass());
+
+  pmb.populateFunctionPassManager(FPM);
+  pmb.populateModulePassManager(MPM);
+}
+
+bool CompileGoImpl::invokeBackEnd()
+{
+  tlii_.reset(new TargetLibraryInfoImpl(triple_));
+
+  // Set up module and function passes
+  legacy::PassManager modulePasses;
+  modulePasses.add(
+      createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
+  legacy::FunctionPassManager functionPasses(module_.get());
+  functionPasses.add(
+      createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
+  createPasses(modulePasses, functionPasses);
+
+  // Add passes to emit bitcode or LLVM IR as appropriate. Here we mimic
+  // clang behavior, which is to emit bitcode when "-emit-llvm" is specified
+  // but an LLVM IR dump of "-S -emit-llvm" is used.
+  raw_pwrite_stream *OS = &asmout_->os();
+  if (args_.hasArg(gollvm::options::OPT_emit_llvm)) {
+    bool bitcode = !args_.hasArg(gollvm::options::OPT_S);
+    bool preserveUseLists =
+        driver_.reconcileOptionPair(gollvm::options::OPT_emit_llvm_uselists,
+                                    gollvm::options::OPT_no_emit_llvm_uselists,
+                                    false);
+    modulePasses.add(bitcode ?
+                     createBitcodeWriterPass(*OS, preserveUseLists) :
+                     createPrintModulePass(*OS, "", preserveUseLists));
+  }
+
+  // Set up codegen passes
+  legacy::PassManager codeGenPasses;
+  codeGenPasses.add(
+      createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis()));
+
+  // Codegen setup
+  codeGenPasses.add(new TargetLibraryInfoWrapperPass(*tlii_));
+  bool noverify = args_.hasArg(gollvm::options::OPT_noverify);
+  TargetMachine::CodeGenFileType ft = TargetMachine::CGFT_AssemblyFile;
+  if (target_->addPassesToEmitFile(codeGenPasses, *OS, ft,
+                                   /*DisableVerify=*/ noverify)) {
+    errs() << "error: unable to interface with target\n";
+    return false;
+  }
+
+  // Here we go... first function passes
+  functionPasses.doInitialization();
+  for (Function &F : *module_.get())
+    if (!F.isDeclaration())
+      functionPasses.run(F);
+  functionPasses.doFinalization();
+
+  // ... then module passes
+  modulePasses.run(*module_.get());
+
+  // ... and finally code generation
+  codeGenPasses.run(*module_.get());
+
+  if (hasError_)
+    return false;
+
+  return true;
+}
+
+//......................................................................
+
+CompileGo::CompileGo(ToolChain &tc)
+    : InternalTool("gocompiler", tc),
+      impl_(new CompileGoImpl(tc))
+{
+}
+
+CompileGo::~CompileGo()
+{
+}
+
+bool CompileGo::performAction(Compilation &compilation,
+                              const Action &jobAction,
+                              const ArtifactList &inputArtifacts,
+                              const Artifact &output)
+{
+  return impl_->performAction(compilation, jobAction, inputArtifacts, output);
+}
+
+} // end namespace driver
+} // end namespace gollvm
diff --git a/driver/CompileGo.h b/driver/CompileGo.h
new file mode 100644
index 0000000..c0e8fa4
--- /dev/null
+++ b/driver/CompileGo.h
@@ -0,0 +1,50 @@
+//===-- CompileGo.h -------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the CompileGo class (helper for driver functionality).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GOLLVM_DRIVER_COMPILEGO_H
+#define GOLLVM_DRIVER_COMPILEGO_H
+
+#include "Tool.h"
+
+namespace gollvm {
+namespace driver {
+
+class ToolChain;
+class Compilation;
+class Action;
+class Artifact;
+class CompileGoImpl;
+
+// Concrete go compiler tool. This tool is used by the driver to carry
+// out "compile" actions, e.g. "compile this set of Go files into
+// assembly".
+
+class CompileGo : public InternalTool {
+ public:
+  CompileGo(ToolChain &tc);
+  ~CompileGo();
+
+  // Perform compilation.
+  bool performAction(Compilation &compilation,
+                     const Action &jobAction,
+                     const ArtifactList &inputArtifacts,
+                     const Artifact &output) override;
+
+ private:
+  std::unique_ptr<CompileGoImpl> impl_;
+};
+
+} // end namespace driver
+} // end namespace gollvm
+
+#endif // GOLLVM_DRIVER_COMPILEGO_H
diff --git a/driver/Driver.cpp b/driver/Driver.cpp
index 71901af..8833208 100644
--- a/driver/Driver.cpp
+++ b/driver/Driver.cpp
@@ -269,8 +269,7 @@
   return result;
 }
 
-bool Driver::buildActions(Compilation &compilation,
-                          const std::string &asmOutFile)
+bool Driver::buildActions(Compilation &compilation)
 {
   inarglist gofiles;
   inarglist asmfiles;
@@ -281,6 +280,13 @@
     if (arg->getOption().getKind() == opt::Option::InputClass) {
       std::string fn(arg->getValue());
 
+      // At the moment the canonical "-" input (stdin) is assumed
+      // to be Go source.
+      if (!strcmp(arg->getValue(), "-")) {
+        gofiles.push_back(arg);
+        continue;
+      }
+
       size_t dotindex = fn.find_last_of(".");
       if (dotindex != std::string::npos) {
         std::string extension = fn.substr(dotindex);
@@ -326,11 +332,6 @@
     compilation.recordAction(gocompact);
     compilation.addAction(gocompact);
 
-    // Temporary: at this point compilation is still being done
-    // by CompilationOrchestrator, so create a dummy output artifact
-    // corresponding to the asm output file.
-    artmap_[gocompact] = compilation.newFileArtifact(asmOutFile.c_str(), false);
-
     // Schedule assemble action now if no -S.
     if (!OPT_S) {
       // Create action
@@ -404,10 +405,6 @@
 
 bool Driver::processAction(Action *act, Compilation &compilation, bool lastAct)
 {
-  // Temporary: Go compilation has already been performed.
-  if (act->type() == Action::A_Compile)
-    return true;
-
   // Select the result file for this action.
   Artifact *result = nullptr;
   if (!lastAct) {
diff --git a/driver/Driver.h b/driver/Driver.h
index 938d442..ff5a24a 100644
--- a/driver/Driver.h
+++ b/driver/Driver.h
@@ -56,9 +56,7 @@
   std::unique_ptr<Compilation> buildCompilation(ToolChain &tc);
 
   // Build actions for compilation. Returns false if error.
-  // TEMPORARY: pass in asm out file from compiled Go.
-  bool buildActions(Compilation &compilation,
-                    const std::string &asmOutFile);
+  bool buildActions(Compilation &compilation);
 
   // Process the action list. This means:
   // - execute any non-dependent actions that don't require the
diff --git a/driver/LinuxToolChain.cpp b/driver/LinuxToolChain.cpp
index 2c18651..187dab0 100644
--- a/driver/LinuxToolChain.cpp
+++ b/driver/LinuxToolChain.cpp
@@ -16,6 +16,7 @@
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/raw_ostream.h"
 
+#include "CompileGo.h"
 #include "Driver.h"
 #include "GnuTools.h"
 #include "Tool.h"
@@ -60,8 +61,7 @@
 
 Tool *Linux::buildCompiler()
 {
-  // implementation to appear in a subsequent patch
-  return nullptr;
+  return new CompileGo(*this);
 }
 
 Tool *Linux::buildAssembler()