| //===-- IntegAssembler.cpp ------------------------------------------------===// |
| // |
| // Copyright 2020 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 "IntegAssembler" methods. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "IntegAssembler.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 "Driver.h" |
| #include "ToolChain.h" |
| |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/Config/llvm-config.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/MC/MCAsmBackend.h" |
| #include "llvm/MC/MCAsmInfo.h" |
| #include "llvm/MC/MCCodeEmitter.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCInstrInfo.h" |
| #include "llvm/MC/MCObjectFileInfo.h" |
| #include "llvm/MC/MCObjectWriter.h" |
| #include "llvm/MC/MCParser/MCAsmParser.h" |
| #include "llvm/MC/MCParser/MCTargetAsmParser.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/MC/MCSectionMachO.h" |
| #include "llvm/MC/MCStreamer.h" |
| #include "llvm/MC/MCSubtargetInfo.h" |
| #include "llvm/MC/MCTargetOptions.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/Support/CommandLine.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/Host.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/Process.h" |
| #include "llvm/Support/Program.h" |
| #include "llvm/Support/Regex.h" |
| #include "llvm/Support/Signals.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Target/TargetMachine.h" |
| |
| #include <sstream> |
| |
| using namespace llvm; |
| |
| namespace gollvm { |
| namespace driver { |
| |
| class IntegAssemblerImpl { |
| public: |
| IntegAssemblerImpl(IntegAssembler &ia, ToolChain &tc, const std::string &executablePath); |
| |
| // Perform compilation. |
| bool performAction(Compilation &compilation, |
| const Action &jobAction, |
| const ArtifactList &inputArtifacts, |
| const Artifact &output); |
| |
| private: |
| IntegAssembler &ia_; |
| Triple triple_; |
| const ToolChain &toolchain_; |
| Driver &driver_; |
| LLVMContext context_; |
| const char *progname_; |
| std::string executablePath_; |
| opt::InputArgList &args_; |
| std::string inputFileName_; |
| std::string objOutFileName_; |
| std::unique_ptr<raw_fd_ostream> objout_; |
| |
| bool resolveInputOutput(const Action &jobAction, |
| const ArtifactList &inputArtifacts, |
| const Artifact &output); |
| bool invokeAssembler(); |
| }; |
| |
| IntegAssemblerImpl::IntegAssemblerImpl(IntegAssembler &ia, ToolChain &tc, const std::string &executablePath) |
| : ia_(ia), |
| triple_(tc.driver().triple()), |
| toolchain_(tc), |
| driver_(tc.driver()), |
| progname_(tc.driver().progname()), |
| executablePath_(executablePath), |
| args_(tc.driver().args()) |
| { |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| InitializeAllAsmPrinters(); |
| InitializeAllAsmParsers(); |
| } |
| |
| bool IntegAssemblerImpl::resolveInputOutput(const Action &jobAction, |
| const ArtifactList &inputArtifacts, |
| const Artifact &output) |
| { |
| // Collect input file. Should be only one. |
| if (inputArtifacts.size() != 1) { |
| errs() << progname_ << ": expected exactly one input file, " |
| << inputArtifacts.size() << " provided.\n"; |
| return false; |
| } |
| inputFileName_ = inputArtifacts[0]->file(); |
| objOutFileName_ = output.file(); |
| |
| // Remove output on signal. |
| if (objOutFileName_ != "-") |
| sys::RemoveFileOnSignal(objOutFileName_); |
| |
| // Open output file. |
| std::error_code EC; |
| sys::fs::OpenFlags OpenFlags = sys::fs::OF_None; |
| objout_ = std::make_unique<raw_fd_ostream>( |
| objOutFileName_, EC, OpenFlags); |
| if (EC) { |
| errs() << progname_ << ": error opening " << objOutFileName_ << ": " |
| << EC.message() << '\n'; |
| return false; |
| } |
| return true; |
| } |
| |
| bool IntegAssemblerImpl::invokeAssembler() |
| { |
| // Get the target specific parser. |
| std::string Error; |
| const Target *TheTarget = TargetRegistry::lookupTarget("", triple_, Error); |
| if (!TheTarget) { |
| errs() << progname_ << ": unknown/unsupported target " |
| << triple_.str() << "\n"; |
| return false; |
| } |
| |
| ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = |
| MemoryBuffer::getFileOrSTDIN(inputFileName_); |
| if (std::error_code EC = Buffer.getError()) { |
| Error = EC.message(); |
| errs() << progname_ << ": opening/reading " << inputFileName_ << ": " |
| << EC.message() << "\n"; |
| return false; |
| } |
| |
| auto Trip = triple_.str(); |
| SourceMgr SrcMgr; |
| |
| // Tell SrcMgr about this buffer, which is what the parser will pick up. |
| SrcMgr.AddNewSourceBuffer(std::move(*Buffer), SMLoc()); |
| |
| // Record the location of the include directories so that the lexer can find |
| // it later. |
| SrcMgr.setIncludeDirs(driver_.args().getAllArgValues(gollvm::options::OPT_I)); |
| |
| std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(Trip)); |
| assert(MRI && "Unable to create target register info!"); |
| |
| MCTargetOptions MCOptions; |
| std::unique_ptr<MCAsmInfo> MAI( |
| TheTarget->createMCAsmInfo(*MRI, Trip, MCOptions)); |
| assert(MAI && "Unable to create target asm info!"); |
| |
| // Note: -Xassembler and -Wa, options should already have been |
| // examined at this point. |
| |
| // Support for compressed debug. |
| llvm::DebugCompressionType CompressDebugSections = |
| llvm::DebugCompressionType::None; |
| if (!driver_.determineDebugCompressionType(&CompressDebugSections)) |
| return false; |
| |
| // Ensure MCAsmInfo initialization occurs before any use, otherwise sections |
| // may be created with a combination of default and explicit settings. |
| MAI->setCompressDebugSections(CompressDebugSections); |
| |
| // Build up the feature string from the target feature list. |
| std::string FS; |
| std::string CPU; |
| opt::Arg *cpuarg = args_.getLastArg(gollvm::options::OPT_march_EQ); |
| setupArchCpu(cpuarg, CPU, FS, triple_, progname_); |
| std::unique_ptr<MCStreamer> Str; |
| std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); |
| std::unique_ptr<MCSubtargetInfo> STI( |
| TheTarget->createMCSubtargetInfo(Trip, CPU, FS)); |
| |
| MCContext Ctx(triple_, MAI.get(), MRI.get(), STI.get(), &SrcMgr, &MCOptions); |
| |
| bool PIC = (driver_.getPicLevel() != PICLevel::NotPIC); |
| Ctx.setGenDwarfForAssembly(true); |
| |
| // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and |
| // MCObjectFileInfo needs a MCContext reference in order to initialize itself. |
| std::unique_ptr<MCObjectFileInfo> MOFI( |
| TheTarget->createMCObjectFileInfo(Ctx, PIC)); |
| Ctx.setObjectFileInfo(MOFI.get()); |
| |
| // Use current dir (llvm-goc does not yet support -fdebug-compilation-dir) |
| SmallString<128> CWD; |
| if (!sys::fs::current_path(CWD)) |
| Ctx.setCompilationDir(CWD); |
| |
| // Honor -fdebug-prefix=... option. |
| for (const auto &arg : driver_.args().getAllArgValues(gollvm::options::OPT_fdebug_prefix_map_EQ)) { |
| std::pair<StringRef, StringRef> p = StringRef(arg).split('='); |
| Ctx.addDebugPrefixMapEntry(std::string(p.first), std::string(p.second)); |
| } |
| |
| |
| StringRef BaseName = llvm::sys::path::filename(inputFileName_); |
| Ctx.setMainFileName(BaseName); |
| // FIXME: incorporate version here? |
| Ctx.setDwarfDebugProducer("llvm-goc"); |
| |
| raw_pwrite_stream *Out = objout_.get(); |
| std::unique_ptr<buffer_ostream> BOS; |
| |
| if (!objout_->supportsSeeking()) { |
| BOS = std::make_unique<buffer_ostream>(*objout_); |
| Out = BOS.get(); |
| } |
| |
| std::unique_ptr<MCCodeEmitter> CE( |
| TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx)); |
| std::unique_ptr<MCAsmBackend> MAB( |
| TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); |
| std::unique_ptr<MCObjectWriter> OW = MAB->createObjectWriter(*Out); |
| |
| Triple T(driver_.triple()); |
| unsigned RelaxAll = 0; |
| unsigned IncrementalLinkerCompatible = 0; |
| Str.reset(TheTarget->createMCObjectStreamer( |
| T, Ctx, std::move(MAB), std::move(OW), std::move(CE), *STI, |
| RelaxAll, IncrementalLinkerCompatible, |
| /*DWARFMustBeAtTheEnd*/ true)); |
| |
| bool NoExecStack = true; |
| Str.get()->initSections(NoExecStack, *STI); |
| |
| // Assembly to object compilation should leverage assembly info. |
| Str->setUseAssemblerInfoForParsing(true); |
| |
| std::unique_ptr<MCAsmParser> Parser( |
| createMCAsmParser(SrcMgr, Ctx, *Str.get(), *MAI)); |
| |
| std::unique_ptr<MCTargetAsmParser> TAP( |
| TheTarget->createMCAsmParser(*STI, *Parser, *MCII, MCOptions)); |
| if (!TAP) { |
| errs() << progname_ << ": error: unknown triple" |
| << triple_.str() << "\n"; |
| return false; |
| } |
| |
| // FIXME: add support for -Wa,-defsym here? |
| |
| Parser->setTargetParser(*TAP.get()); |
| bool NoInitialTextSection = false; |
| auto Failed = Parser->Run(NoInitialTextSection); |
| |
| // Close Streamer first. |
| // It might have a reference to the output stream. |
| Str.reset(); |
| // Close the output stream early. |
| BOS.reset(); |
| objout_.reset(); |
| |
| // Delete output file if there were errors. |
| if (Failed) { |
| if (objOutFileName_ != "-") |
| sys::fs::remove(objOutFileName_); |
| } |
| |
| return !Failed; |
| } |
| |
| bool IntegAssemblerImpl::performAction(Compilation &compilation, |
| const Action &jobAction, |
| const ArtifactList &inputArtifacts, |
| const Artifact &output) |
| { |
| if (ia_.emitMinusVOrHashHashHash(triple_, output, jobAction)) |
| return true; |
| |
| // Resolve input/output files. |
| if (!resolveInputOutput(jobAction, inputArtifacts, output)) |
| return false; |
| |
| // Invoke back end |
| if (!invokeAssembler()) |
| return false; |
| |
| return true; |
| } |
| |
| //........................................................................ |
| |
| IntegAssembler::IntegAssembler(ToolChain &tc, const std::string &executablePath) |
| : InternalTool("integassembler", tc, executablePath), |
| impl_(new IntegAssemblerImpl(*this, tc, executablePath)) |
| { |
| } |
| |
| IntegAssembler::~IntegAssembler() |
| { |
| } |
| |
| bool IntegAssembler::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 |