blob: f6262c89d6364bed44c3b31aac0f27ecbd946604 [file] [log] [blame]
//===-- 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 "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/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/TargetRegistry.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.
// FIXME: no support yet for -march (bring over from CompileGo.cpp)
opt::Arg *cpuarg = args_.getLastArg(gollvm::options::OPT_march_EQ);
if (cpuarg != nullptr) {
errs() << progname_ << ": internal error: option '"
<< cpuarg->getAsString(args_)
<< "' not yet implemented in integrated assembler\n";
assert(false);
return false;
}
// 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);
// 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(new MCObjectFileInfo());
MCContext Ctx(MAI.get(), MRI.get(), MOFI.get(), &SrcMgr, &MCOptions);
bool PIC = (driver_.getPicLevel() != PICLevel::NotPIC);
MOFI->InitMCObjectFileInfo(triple_, PIC, Ctx);
Ctx.setGenDwarfForAssembly(true);
// 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");
// Build up the feature string from the target feature list.
std::string FS;
std::string CPU;
std::unique_ptr<MCStreamer> Str;
std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
std::unique_ptr<MCSubtargetInfo> STI(
TheTarget->createMCSubtargetInfo(Trip, CPU, FS));
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);
// 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