| //===-- CompileGo.cpp -----------------------------------------------------===// |
| // |
| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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 "GollvmConfig.h" |
| #include "GollvmPasses.h" |
| |
| #include "Action.h" |
| #include "ArchCpuSetup.h" |
| #include "Artifact.h" |
| #include "CallingConv.h" |
| #include "Driver.h" |
| #include "ToolChain.h" |
| |
| namespace gollvm { namespace arch { |
| #include "ArchCpusAttrs.h" |
| } } |
| |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/Analysis/TargetLibraryInfo.h" |
| #include "llvm/Bitcode/BitcodeWriterPass.h" |
| #include "llvm/CodeGen/MachineModuleInfo.h" |
| #include "llvm/CodeGen/Passes.h" |
| #include "llvm/CodeGen/TargetPassConfig.h" |
| #include "llvm/Config/llvm-config.h" |
| #include "llvm/IR/DiagnosticInfo.h" |
| #include "llvm/IR/DiagnosticPrinter.h" |
| #include "llvm/IR/IRPrintingPasses.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/LLVMRemarkStreamer.h" |
| #include "llvm/IR/LegacyPassManager.h" |
| #include "llvm/IR/Verifier.h" |
| #include "llvm/MC/SubtargetFeature.h" |
| #include "llvm/MC/TargetRegistry.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/Remarks/RemarkStreamer.h" |
| #include "llvm/Remarks/YAMLRemarkSerializer.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Format.h" |
| #include "llvm/Support/Host.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/Regex.h" |
| #include "llvm/Support/Signals.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" |
| #include "llvm/Transforms/Utils.h" |
| |
| #include <sstream> |
| |
| using namespace llvm; |
| |
| namespace gollvm { |
| namespace driver { |
| |
| // For keeping track of info on -Rpass=<regex> and related options. |
| class RemarkCtl { |
| public: |
| std::shared_ptr<llvm::Regex> optimizationRemarkPattern_; |
| std::shared_ptr<llvm::Regex> optimizationRemarkMissedPattern_; |
| std::shared_ptr<llvm::Regex> optimizationRemarkAnalysisPattern_; |
| }; |
| |
| class CompileGoImpl { |
| public: |
| CompileGoImpl(CompileGo &cg, ToolChain &tc, const std::string &executablePath); |
| |
| // Perform compilation. |
| bool performAction(Compilation &compilation, |
| const Action &jobAction, |
| const ArtifactList &inputArtifacts, |
| const Artifact &output); |
| |
| private: |
| CompileGo &cg_; |
| Triple triple_; |
| const ToolChain &toolchain_; |
| Driver &driver_; |
| CallingConvId cconv_; |
| LLVMContext context_; |
| const char *progname_; |
| std::string executablePath_; |
| 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<ToolOutputFile> optRecordFile_; |
| RemarkCtl remarkCtl_; |
| std::unique_ptr<TargetLibraryInfoImpl> tlii_; |
| std::string targetCpuAttr_; |
| std::string targetFeaturesAttr_; |
| std::string sampleProfileFile_; |
| bool enable_gc_; |
| |
| void createPasses(legacy::PassManager &MPM, |
| legacy::FunctionPassManager &FPM); |
| void setupGoSearchPath(); |
| void setCConv(); |
| |
| // The routines below return TRUE for success, FALSE for failure/error/ |
| bool setup(const Action &jobAction); |
| bool initBridge(); |
| bool invokeFrontEnd(); |
| bool invokeBridge(); |
| bool invokeBackEnd(const Action &jobAction); |
| bool resolveInputOutput(const Action &jobAction, |
| const ArtifactList &inputArtifacts, |
| const Artifact &output); |
| |
| // Misc |
| bool enableVectorization(bool slp); |
| }; |
| |
| CompileGoImpl::CompileGoImpl(CompileGo &cg, |
| ToolChain &tc, |
| const std::string &executablePath) |
| : cg_(cg), |
| triple_(tc.driver().triple()), |
| toolchain_(tc), |
| driver_(tc.driver()), |
| cconv_(CallingConvId::MaxID), |
| progname_(tc.driver().progname()), |
| executablePath_(executablePath), |
| args_(tc.driver().args()), |
| cgolvl_(CodeGenOpt::Default), |
| olvl_(2), |
| hasError_(false), |
| enable_gc_(false) |
| { |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| InitializeAllAsmPrinters(); |
| InitializeAllAsmParsers(); |
| } |
| |
| bool CompileGoImpl::performAction(Compilation &compilation, |
| const Action &jobAction, |
| const ArtifactList &inputArtifacts, |
| const Artifact &output) |
| { |
| if (cg_.emitMinusVOrHashHashHash(triple_, output, jobAction)) |
| return true; |
| |
| // Resolve input/output files. |
| if (!resolveInputOutput(jobAction, inputArtifacts, output)) |
| return false; |
| |
| // Setup |
| if (!setup(jobAction)) |
| return false; |
| |
| // Set up the bridge |
| if (!initBridge()) |
| return false; |
| |
| // Invoke front end |
| if (!invokeFrontEnd()) |
| return false; |
| |
| // Invoke back end |
| if (!invokeBackEnd(jobAction)) |
| return false; |
| |
| return true; |
| } |
| |
| class BEDiagnosticHandler : public DiagnosticHandler { |
| bool *error_; |
| public: |
| BEDiagnosticHandler(bool *errorPtr, RemarkCtl &remarkCtl) |
| : error_(errorPtr), r_(remarkCtl) {} |
| 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; |
| } |
| |
| // Overrides of parent class methods. |
| bool isAnalysisRemarkEnabled(StringRef passName) const override { |
| return (r_.optimizationRemarkAnalysisPattern_ && |
| r_.optimizationRemarkAnalysisPattern_->match(passName)); |
| } |
| bool isMissedOptRemarkEnabled(StringRef passName) const override { |
| return (r_.optimizationRemarkMissedPattern_ && |
| r_.optimizationRemarkMissedPattern_->match(passName)); |
| } |
| bool isPassedOptRemarkEnabled(StringRef passName) const override { |
| return (r_.optimizationRemarkPattern_ && |
| r_.optimizationRemarkPattern_->match(passName)); |
| } |
| bool isAnyRemarkEnabled() const override { |
| return (r_.optimizationRemarkPattern_ || |
| r_.optimizationRemarkMissedPattern_ || |
| r_.optimizationRemarkAnalysisPattern_); |
| } |
| |
| private: |
| RemarkCtl &r_; |
| }; |
| |
| 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::OF_Text; |
| auto FDOut = std::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; |
| } |
| |
| static std::shared_ptr<llvm::Regex> |
| generateOptimizationRemarkRegex(opt::ArgList &args, opt::Arg *rpassArg) |
| { |
| llvm::StringRef val = rpassArg->getValue(); |
| std::string regexError; |
| std::shared_ptr<llvm::Regex> pattern = std::make_shared<llvm::Regex>(val); |
| if (!pattern->isValid(regexError)) { |
| errs() << "error: invalid regex for '" |
| << rpassArg->getAsString(args) << "' option: " |
| << regexError << "\n"; |
| pattern.reset(); |
| } |
| return pattern; |
| } |
| |
| bool CompileGoImpl::setup(const Action &jobAction) |
| { |
| // Set triple. |
| triple_ = driver_.triple(); |
| |
| // Set calling convention. |
| setCConv(); |
| |
| // 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); |
| go_loc_show_column = |
| driver_.reconcileOptionPair(gollvm::options::OPT_fshow_column, |
| gollvm::options::OPT_fno_show_column, |
| true); |
| |
| // AutoFDO. |
| opt::Arg *sprofarg = |
| args_.getLastArg(gollvm::options::OPT_fprofile_sample_use, |
| gollvm::options::OPT_fno_profile_sample_use, |
| gollvm::options::OPT_fprofile_sample_use_EQ); |
| if (sprofarg) { |
| opt::Arg *fnamearg = |
| args_.getLastArg(gollvm::options::OPT_fprofile_sample_use_EQ); |
| if (fnamearg == nullptr) { |
| // Using -fsample-profile-use / -fno-sample-profile-use without |
| // also using -fprofile-sample-use=XXXX doesn't make sense |
| errs() << progname_ << ": warning: " |
| << "-fprofile-sample-use / -fno-profile-sample-use " |
| << "flags ignored (since no -fprofile-sample-use=<file> " |
| << "specified).\n"; |
| } else if (!sprofarg->getOption().matches( |
| gollvm::options::OPT_fno_profile_sample_use)) { |
| StringRef fname = fnamearg->getValue(); |
| if (!llvm::sys::fs::exists(fname)) { |
| errs() << progname_ << ": unable to access file: " << fname << "\n"; |
| return false; |
| } |
| sampleProfileFile_ = fname.str(); |
| } |
| } |
| |
| // Capture optimization record. |
| opt::Arg *optrecordarg = |
| args_.getLastArg(gollvm::options::OPT_fsave_optimization_record, |
| gollvm::options::OPT_fno_save_optimization_record, |
| gollvm::options::OPT_foptimization_record_file_EQ); |
| if (optrecordarg && !optrecordarg->getOption().matches( |
| gollvm::options::OPT_fno_save_optimization_record)) { |
| opt::Arg *fnamearg = |
| args_.getLastArg(gollvm::options::OPT_foptimization_record_file_EQ); |
| if (fnamearg != nullptr) { |
| StringRef fname = fnamearg->getValue(); |
| std::error_code EC; |
| optRecordFile_ = std::make_unique<llvm::ToolOutputFile>( |
| fname, EC, llvm::sys::fs::OF_None); |
| if (EC) { |
| errs() << "error: unable to open file '" |
| << fname << "' to emit optimization remarks\n"; |
| return false; |
| } |
| context_.setMainRemarkStreamer(std::make_unique<llvm::remarks::RemarkStreamer>( |
| std::make_unique<llvm::remarks::YAMLRemarkSerializer>( |
| optRecordFile_->os(), |
| llvm::remarks::SerializerMode::Separate), |
| fname)); |
| context_.setLLVMRemarkStreamer( |
| std::make_unique<LLVMRemarkStreamer>(*context_.getMainRemarkStreamer())); |
| if (! sampleProfileFile_.empty()) |
| context_.setDiagnosticsHotnessRequested(true); |
| optRecordFile_->keep(); |
| } |
| } |
| |
| // Vet/honor -Rpass= and friends. |
| if (opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_Rpass_EQ)) { |
| remarkCtl_.optimizationRemarkPattern_ = |
| generateOptimizationRemarkRegex(args_, arg); |
| } |
| if (opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_Rpass_missed_EQ)) { |
| remarkCtl_.optimizationRemarkMissedPattern_ = |
| generateOptimizationRemarkRegex(args_, arg); |
| } |
| if (opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_Rpass_analysis_EQ)) { |
| remarkCtl_.optimizationRemarkAnalysisPattern_ = |
| generateOptimizationRemarkRegex(args_, arg); |
| } |
| |
| TargetOptions Options; |
| |
| auto jat = jobAction.type(); |
| assert(jat == Action::A_CompileAndAssemble || |
| jat == Action::A_Compile); |
| Options.DisableIntegratedAS = !(jat == Action::A_CompileAndAssemble); |
| llvm::DebugCompressionType dct = llvm::DebugCompressionType::None; |
| if (!driver_.determineDebugCompressionType(&dct)) |
| return false; |
| Options.CompressDebugSections = dct; |
| |
| // 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 -march |
| opt::Arg *cpuarg = args_.getLastArg(gollvm::options::OPT_march_EQ); |
| if (!setupArchCpu(cpuarg, targetCpuAttr_, targetFeaturesAttr_, triple_, progname_)) |
| return false; |
| |
| // Create target machine |
| Optional<llvm::CodeModel::Model> CM = None; |
| target_.reset( |
| TheTarget->createTargetMachine(triple_.getTriple(), |
| targetCpuAttr_, targetFeaturesAttr_, |
| 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( |
| std::make_unique<BEDiagnosticHandler>(&this->hasError_, |
| this->remarkCtl_)); |
| |
| llvm::Optional<unsigned> enable_gc = |
| driver_.getLastArgAsInteger(gollvm::options::OPT_enable_gc_EQ, 0u); |
| enable_gc_ = enable_gc && *enable_gc; |
| |
| // 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()); |
| |
| // Data layout. |
| std::string dlstr = target_->createDataLayout().getStringRepresentation(); |
| if (enable_gc_) |
| dlstr += "-ni:1"; // non-integral pointer in address space 1 |
| module_->setDataLayout(dlstr); |
| |
| module_->setPICLevel(driver_.getPicLevel()); |
| if (driver_.picIsPIE()) |
| module_->setPIELevel(driver_.getPieLevel()); |
| |
| // Now construct Llvm_backend helper. |
| unsigned addrspace = enable_gc_ ? 1 : 0; |
| bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get(), addrspace, triple_, cconv_)); |
| |
| // 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)); |
| bridge_->setTargetCpuAttr(targetCpuAttr_); |
| bridge_->setTargetFeaturesAttr(targetFeaturesAttr_); |
| if (!sampleProfileFile_.empty()) |
| bridge_->setEnableAutoFDO(); |
| |
| // -f[no-]omit-frame-pointer |
| bool omitFp = |
| driver_.reconcileOptionPair(gollvm::options::OPT_fomit_frame_pointer, |
| gollvm::options::OPT_fno_omit_frame_pointer, |
| true); |
| bridge_->setNoFpElim(!omitFp); |
| |
| bool supportSplitStack = true; |
| #ifndef USING_SPLIT_STACK |
| supportSplitStack = false; |
| #endif |
| |
| bool useSplitStack = |
| driver_.reconcileOptionPair(gollvm::options::OPT_fsplit_stack, |
| gollvm::options::OPT_fno_split_stack, |
| supportSplitStack); |
| bridge_->setUseSplitStack(useSplitStack); |
| |
| // Honor -fdebug-prefix=... option. |
| for (const auto &arg : driver_.args().getAllArgValues(gollvm::options::OPT_fdebug_prefix_map_EQ)) |
| bridge_->addDebugPrefix(llvm::StringRef(arg).split('=')); |
| |
| // GC support. |
| if (enable_gc_) { |
| bridge_->setGCStrategy("go"); |
| linkGoGC(); |
| linkGoGCPrinter(); |
| } |
| |
| // 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; |
| memset(&args, 0, sizeof(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()); |
| opt::Arg *ecfg = |
| args_.getLastArg(gollvm::options::OPT_fgo_embedcfg_EQ); |
| args.embedcfg = (ecfg == nullptr ? NULL : ecfg->getValue()); |
| opt::Arg *icfg = |
| args_.getLastArg(gollvm::options::OPT_fgo_importcfg_EQ); |
| args.importcfg = (icfg == nullptr ? NULL : icfg->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.debug_optimization = |
| driver_.reconcileOptionPair(gollvm::options::OPT_fgo_debug_optimization, |
| gollvm::options::OPT_fno_go_debug_optimization, |
| false); |
| args.nil_check_size_threshold = |
| (args.compiling_runtime && args.pkgpath && strcmp(args.pkgpath, "runtime") == 0 ? |
| 4096 : -1); |
| args.linemap = linemap_.get(); |
| args.backend = bridge_.get(); |
| go_create_gogo (&args); |
| |
| if (args.compiling_runtime) |
| bridge_->setCompilingRuntime(); |
| |
| /* 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); |
| |
| // Set up the search path to use for locating Go packages. |
| setupGoSearchPath(); |
| |
| return true; |
| } |
| |
| // The Go search path (dirs in which to search for package objects, |
| // *.gox files, archives, etc) is populated based on command line |
| // options (-L, -B, -I) and on the installation directory/prefix. For |
| // an example command line of the form |
| // |
| // <compiler> mumble.go -c -I /I1 -I /I2 -L /L1 -B /B1 |
| // |
| // the Go package search path will start out with the include dirs |
| // (/I1 and /I2). We then walk through each of the remaining dirs (-L |
| // args, -B args, and system install dirs) and if the dir D in |
| // question contains a "go" subdir we add D/go/<version> and |
| // D/go/<version>/<triple>. Finally we add each of the -L/-I/install |
| // dirs directly to the search path. |
| // |
| // With this in mind, assuming that the install dir is /install, |
| // version is 7.0.0, and the target triple x86_64-pc-linux-gnu, for |
| // the args above we would get a search path of |
| // |
| // /I1 |
| // /I2 |
| // /L1/go/7.0.0 |
| // /L1/go/7.0.0/x86_64-pc-linux-gnu |
| // /B1/go/7.0.0 |
| // /B1/go/7.0.0/x86_64-pc-linux-gnu |
| // /install/go/7.0.0 |
| // /install/go/7.0.0/x86_64-pc-linux-gnu |
| // /L1 |
| // /B1 |
| // /install |
| |
| void CompileGoImpl::setupGoSearchPath() |
| { |
| // 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()); |
| } |
| |
| // Make up a list of dirs starting with -L args, then -B args, |
| // and finally the installation dir. |
| std::vector<std::string> dirs; |
| |
| // First -L args. |
| for (auto &dir : args_.getAllArgValues(gollvm::options::OPT_L)) { |
| if (!sys::fs::is_directory(dir)) |
| continue; |
| dirs.push_back(dir); |
| } |
| |
| // Add in -B args. |
| for (const std::string &pdir : driver_.prefixes()) { |
| if (!sys::fs::is_directory(pdir)) |
| continue; |
| dirs.push_back(pdir); |
| } |
| |
| // Finish up with the library dir from the install. |
| dirs.push_back(driver_.installedLibDir()); |
| |
| // First pass to add go/<version> and go/<version>/triple variants |
| for (auto &dir : dirs) { |
| std::stringstream b1; |
| b1 << dir << sys::path::get_separator().str() << "go" |
| << sys::path::get_separator().str() << GOLLVM_LIBVERSION; |
| if (sys::fs::is_directory(b1.str())) { |
| go_add_search_path(b1.str().c_str()); |
| std::stringstream b2; |
| b2 << b1.str() << sys::path::get_separator().str() << triple_.str(); |
| if (sys::fs::is_directory(b2.str())) |
| go_add_search_path(b2.str().c_str()); |
| } |
| } |
| |
| // Second pass with raw dir. |
| for (auto &dir : dirs) |
| go_add_search_path(dir.c_str()); |
| } |
| |
| // Set cconv according to the triple_ value. |
| void CompileGoImpl::setCConv() |
| { |
| assert(triple_.getArch() != Triple::UnknownArch); |
| switch (triple_.getArch()) { |
| case Triple::x86_64: |
| cconv_ = CallingConvId::X86_64_SysV; |
| break; |
| case Triple::aarch64: |
| cconv_ = CallingConvId::ARM_AAPCS; |
| break; |
| case Triple::riscv64: |
| cconv_ = CallingConvId::RISCV64_C; |
| break; |
| default: |
| errs() << "currently Gollvm is not supported on architecture " |
| << triple_.getArchName().str()<< "\n"; |
| break; |
| } |
| } |
| |
| 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; |
| } |
| |
| bool CompileGoImpl::enableVectorization(bool slp) |
| { |
| bool enable = (olvl_ > 1); |
| if (slp) |
| return driver_.reconcileOptionPair(gollvm::options::OPT_fslp_vectorize, |
| gollvm::options::OPT_fno_slp_vectorize, |
| enable); |
| else |
| return driver_.reconcileOptionPair(gollvm::options::OPT_fvectorize, |
| gollvm::options::OPT_fno_vectorize, |
| enable); |
| } |
| |
| static void addAddDiscriminatorsPass(const llvm::PassManagerBuilder &Builder, |
| llvm::legacy::PassManagerBase &PM) { |
| PM.add(createAddDiscriminatorsPass()); |
| } |
| |
| 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; |
| pmb.SLPVectorize = enableVectorization(true); |
| pmb.LoopVectorize = enableVectorization(false); |
| |
| bool needDwarfDiscr = false; |
| if (! sampleProfileFile_.empty()) { |
| pmb.PGOSampleUse = sampleProfileFile_; |
| needDwarfDiscr = true; |
| } |
| opt::Arg *dbgprofarg = |
| args_.getLastArg(gollvm::options::OPT_fdebug_info_for_profiling, |
| gollvm::options::OPT_fno_debug_info_for_profiling); |
| if (dbgprofarg) { |
| if (dbgprofarg->getOption().matches(gollvm::options::OPT_fdebug_info_for_profiling)) |
| needDwarfDiscr = true; |
| else |
| needDwarfDiscr = false; |
| } |
| if (needDwarfDiscr) |
| pmb.addExtension(llvm::PassManagerBuilder::EP_EarlyAsPossible, |
| addAddDiscriminatorsPass); |
| |
| |
| FPM.add(new TargetLibraryInfoWrapperPass(*tlii_)); |
| if (! args_.hasArg(gollvm::options::OPT_noverify)) |
| FPM.add(createVerifierPass()); |
| |
| pmb.populateFunctionPassManager(FPM); |
| pmb.populateModulePassManager(MPM); |
| } |
| |
| bool CompileGoImpl::invokeBackEnd(const Action &jobAction) |
| { |
| 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); |
| |
| // Disable inlining getg in some cases on x86_64. |
| if (triple_.getArch() == llvm::Triple::x86_64) { |
| modulePasses.add(createGoSafeGetgPass()); |
| } |
| |
| // Add statepoint insertion pass to the end of optimization pipeline, |
| // right before lowering to machine IR. |
| if (enable_gc_) { |
| modulePasses.add(createGoStatepointsLegacyPass()); |
| modulePasses.add(createRemoveAddrSpacePass(target_->createDataLayout())); |
| } |
| |
| legacy::PassManager codeGenPasses; |
| bool noverify = args_.hasArg(gollvm::options::OPT_noverify); |
| CodeGenFileType ft = (jobAction.type() == Action::A_CompileAndAssemble ? |
| CGFT_ObjectFile : CGFT_AssemblyFile); |
| |
| // 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)); |
| goto run; |
| } |
| |
| // Set up codegen passes |
| codeGenPasses.add( |
| createTargetTransformInfoWrapperPass(target_->getTargetIRAnalysis())); |
| |
| // Codegen setup |
| codeGenPasses.add(new TargetLibraryInfoWrapperPass(*tlii_)); |
| |
| // Add stackmap machine IR pass to the end of the machine passes, |
| // right before AsmPrinter. |
| // FIXME: the code below is essentially duplicate of |
| // LLVMTargetMachine::addPassesToEmitFile (and its callee). |
| // TODO: error check. |
| { |
| LLVMTargetMachine *lltm = (LLVMTargetMachine*)(target_.get()); // FIXME: TargetMachine doesn't support llvm::cast? |
| TargetPassConfig *passConfig = lltm->createPassConfig(codeGenPasses); |
| // Set PassConfig options provided by TargetMachine. |
| passConfig->setDisableVerify(noverify); |
| codeGenPasses.add(passConfig); |
| MachineModuleInfoWrapperPass *MMIWP = new MachineModuleInfoWrapperPass(lltm); |
| codeGenPasses.add(MMIWP); |
| passConfig->addISelPasses(); |
| passConfig->addMachinePasses(); |
| passConfig->setInitialized(); |
| |
| codeGenPasses.add(createGoNilChecksPass()); |
| codeGenPasses.add(createGoWrappersPass()); |
| |
| if (enable_gc_) |
| codeGenPasses.add(createGoAnnotationPass()); |
| |
| lltm->addAsmPrinter(codeGenPasses, *OS, nullptr, ft, MMIWP->getMMI().getContext()); |
| |
| codeGenPasses.add(createFreeMachineFunctionPass()); |
| } |
| |
| run: |
| // 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_emit_llvm)) |
| codeGenPasses.run(*module_.get()); |
| |
| if (hasError_) |
| return false; |
| |
| return true; |
| } |
| |
| //...................................................................... |
| |
| CompileGo::CompileGo(ToolChain &tc, const std::string &executablePath) |
| : InternalTool("gocompiler", tc, executablePath), |
| impl_(new CompileGoImpl(*this, tc, executablePath)) |
| { |
| } |
| |
| 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 |