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()