//===- MCLDTargetMachine.cpp ----------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/Target/TargetMachine.h>
#include <mcld/Module.h>
#include <mcld/LinkerConfig.h>
#include <mcld/CodeGen/MCLinker.h>
#include <mcld/Support/raw_mem_ostream.h>
#include <mcld/Support/TargetRegistry.h>
#include <mcld/Support/ToolOutputFile.h>
#include <mcld/Support/MemoryArea.h>
#include <mcld/Target/TargetLDBackend.h>
#include <llvm/ADT/OwningPtr.h>
#include <llvm/Analysis/Passes.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Assembly/PrintModulePass.h>
#include <llvm/CodeGen/AsmPrinter.h>
#include <llvm/CodeGen/MachineFunctionAnalysis.h>
#include <llvm/CodeGen/MachineModuleInfo.h>
#include <llvm/CodeGen/GCStrategy.h>
#include <llvm/CodeGen/Passes.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/MC/MCAsmInfo.h>
#include <llvm/MC/MCStreamer.h>
#include <llvm/MC/MCInstrInfo.h>
#include <llvm/MC/MCSubtargetInfo.h>
#include <llvm/MC/MCObjectStreamer.h>
#include <llvm/MC/MCAssembler.h>
#include <llvm/MC/MCObjectWriter.h>
#include <llvm/MC/MCContext.h>
#include <llvm/PassManager.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/Debug.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/FormattedStream.h>
#include <llvm/Target/TargetInstrInfo.h>
#include <llvm/Target/TargetLowering.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/Target/TargetSubtargetInfo.h>
#include <llvm/Target/TargetLoweringObjectFile.h>
#include <llvm/Target/TargetRegisterInfo.h>
#include <llvm/Transforms/Scalar.h>
#include <string>
using namespace mcld;
using namespace llvm;
//===----------------------------------------------------------------------===//
/// Arguments
//===----------------------------------------------------------------------===//
// Enable or disable FastISel. Both options are needed, because
// FastISel is enabled by default with -fast, and we wish to be
// able to enable or disable fast-isel independently from -O0.
static cl::opt<cl::boolOrDefault>
ArgEnableFastISelOption("lfast-isel", cl::Hidden,
cl::desc("Enable the \"fast\" instruction selector"));
static cl::opt<bool>
ArgShowMCEncoding("lshow-mc-encoding",
cl::Hidden,
cl::desc("Show encoding in .s output"));
static cl::opt<bool>
ArgShowMCInst("lshow-mc-inst",
cl::Hidden,
cl::desc("Show instruction structure in .s output"));
static cl::opt<cl::boolOrDefault>
ArgAsmVerbose("fverbose-asm",
cl::desc("Put extra commentary information in the \
generated assembly code to make it more readable."),
cl::init(cl::BOU_UNSET));
static bool getVerboseAsm() {
switch (ArgAsmVerbose) {
default:
case cl::BOU_UNSET: return TargetMachine::getAsmVerbosityDefault();
case cl::BOU_TRUE: return true;
case cl::BOU_FALSE: return false;
}
}
//===---------------------------------------------------------------------===//
/// MCLDTargetMachine
//===----------------------------------------------------------------------===//
mcld::MCLDTargetMachine::MCLDTargetMachine(llvm::TargetMachine &pTM,
const mcld::Target& pTarget,
const std::string& pTriple)
: m_TM(pTM), m_pTarget(&pTarget), m_Triple(pTriple) {
}
mcld::MCLDTargetMachine::~MCLDTargetMachine() {
m_pTarget = 0;
}
const mcld::Target& mcld::MCLDTargetMachine::getTarget() const
{
return *m_pTarget;
}
/// Turn exception handling constructs into something the code generators can
/// handle.
static void addPassesToHandleExceptions(llvm::TargetMachine *TM,
PassManagerBase &PM) {
switch (TM->getMCAsmInfo()->getExceptionHandlingType()) {
case llvm::ExceptionHandling::SjLj:
// SjLj piggy-backs on dwarf for this bit. The cleanups done apply to both
// Dwarf EH prepare needs to be run after SjLj prepare. Otherwise,
// catch info can get misplaced when a selector ends up more than one block
// removed from the parent invoke(s). This could happen when a landing
// pad is shared by multiple invokes and is also a target of a normal
// edge from elsewhere.
PM.add(createSjLjEHPreparePass(TM->getTargetLowering()));
// FALLTHROUGH
case llvm::ExceptionHandling::DwarfCFI:
case llvm::ExceptionHandling::ARM:
case llvm::ExceptionHandling::Win64:
PM.add(createDwarfEHPass(TM->getTargetLowering()));
break;
case llvm::ExceptionHandling::None:
PM.add(createLowerInvokePass(TM->getTargetLowering()));
// The lower invoke pass may create unreachable code. Remove it.
PM.add(createUnreachableBlockEliminationPass());
break;
}
}
static llvm::MCContext *addPassesToGenerateCode(llvm::LLVMTargetMachine *TM,
PassManagerBase &PM,
bool DisableVerify)
{
// Targets may override createPassConfig to provide a target-specific sublass.
TargetPassConfig *PassConfig = TM->createPassConfig(PM);
// Set PassConfig options provided by TargetMachine.
PassConfig->setDisableVerify(DisableVerify);
PM.add(PassConfig);
PassConfig->addIRPasses();
addPassesToHandleExceptions(TM, PM);
PassConfig->addISelPrepare();
// Install a MachineModuleInfo class, which is an immutable pass that holds
// all the per-module stuff we're generating, including MCContext.
MachineModuleInfo *MMI =
new MachineModuleInfo(*TM->getMCAsmInfo(), *TM->getRegisterInfo(),
&TM->getTargetLowering()->getObjFileLowering());
PM.add(MMI);
MCContext *Context = &MMI->getContext(); // Return the MCContext by-ref.
// Set up a MachineFunction for the rest of CodeGen to work on.
PM.add(new MachineFunctionAnalysis(*TM));
// Enable FastISel with -fast, but allow that to be overridden.
if (ArgEnableFastISelOption == cl::BOU_TRUE ||
(TM->getOptLevel() == CodeGenOpt::None &&
ArgEnableFastISelOption != cl::BOU_FALSE))
TM->setFastISel(true);
// Ask the target for an isel.
if (PassConfig->addInstSelector())
return NULL;
PassConfig->addMachinePasses();
PassConfig->setInitialized();
return Context;
}
bool mcld::MCLDTargetMachine::addPassesToEmitFile(PassManagerBase &pPM,
mcld::ToolOutputFile& pOutput,
mcld::CodeGenFileType pFileType,
CodeGenOpt::Level pOptLvl,
mcld::Module& pModule,
LinkerConfig& pConfig,
bool pDisableVerify)
{
llvm::MCContext* Context =
addPassesToGenerateCode(static_cast<llvm::LLVMTargetMachine*>(&m_TM),
pPM, pDisableVerify);
if (!Context)
return true;
switch(pFileType) {
default:
case mcld::CGFT_NULLFile:
assert(0 && "fatal: file type is not set!");
break;
case CGFT_ASMFile: {
assert(Context != 0 && "Failed to get MCContext");
if (getTM().hasMCSaveTempLabels())
Context->setAllowTemporaryLabels(false);
if (addCompilerPasses(pPM,
pOutput.formatted_os(),
Context))
return true;
break;
}
case CGFT_OBJFile: {
assert(Context != 0 && "Failed to get MCContext");
if (getTM().hasMCSaveTempLabels())
Context->setAllowTemporaryLabels(false);
if (addAssemblerPasses(pPM,
pOutput.mem_os(),
Context))
return true;
break;
}
case CGFT_EXEFile: {
pConfig.setCodeGenType(LinkerConfig::Exec);
if (addLinkerPasses(pPM,
pConfig,
pModule,
pOutput.memory(),
Context))
return true;
break;
}
case CGFT_BINARY: {
pConfig.setCodeGenType(LinkerConfig::Binary);
if (addLinkerPasses(pPM,
pConfig,
pModule,
pOutput.memory(),
Context))
return true;
break;
}
case CGFT_DSOFile: {
pConfig.setCodeGenType(LinkerConfig::DynObj);
if (addLinkerPasses(pPM,
pConfig,
pModule,
pOutput.memory(),
Context))
return true;
break;
}
case CGFT_PARTIAL: {
pConfig.setCodeGenType(LinkerConfig::Object);
if (addLinkerPasses(pPM,
pConfig,
pModule,
pOutput.memory(),
Context))
return true;
break;
}
} // switch
return false;
}
bool mcld::MCLDTargetMachine::addCompilerPasses(PassManagerBase &pPM,
llvm::formatted_raw_ostream &pOutput,
llvm::MCContext *&Context)
{
const MCAsmInfo &MAI = *getTM().getMCAsmInfo();
const MCInstrInfo &MII = *getTM().getInstrInfo();
const MCRegisterInfo &MRI = *getTM().getRegisterInfo();
const MCSubtargetInfo &STI = getTM().getSubtarget<MCSubtargetInfo>();
MCInstPrinter *InstPrinter =
getTarget().get()->createMCInstPrinter(MAI.getAssemblerDialect(), MAI,
MII,
Context->getRegisterInfo(), STI);
MCCodeEmitter* MCE = 0;
MCAsmBackend *MAB = 0;
if (ArgShowMCEncoding) {
MCE = getTarget().get()->createMCCodeEmitter(MII, MRI, STI, *Context);
MAB = getTarget().get()->createMCAsmBackend(m_Triple,
getTM().getTargetCPU());
}
// now, we have MCCodeEmitter and MCAsmBackend, we can create AsmStreamer.
OwningPtr<MCStreamer> AsmStreamer(
getTarget().get()->createAsmStreamer(*Context, pOutput,
getVerboseAsm(),
getTM().hasMCUseLoc(),
getTM().hasMCUseCFI(),
getTM().hasMCUseDwarfDirectory(),
InstPrinter,
MCE, MAB,
ArgShowMCInst));
llvm::MachineFunctionPass* funcPass =
getTarget().get()->createAsmPrinter(getTM(), *AsmStreamer.get());
if (funcPass == 0)
return true;
// If successful, createAsmPrinter took ownership of AsmStreamer
AsmStreamer.take();
pPM.add(funcPass);
return false;
}
bool mcld::MCLDTargetMachine::addAssemblerPasses(PassManagerBase &pPM,
llvm::raw_ostream &pOutput,
llvm::MCContext *&Context)
{
// MCCodeEmitter
const MCInstrInfo &MII = *getTM().getInstrInfo();
const MCRegisterInfo &MRI = *getTM().getRegisterInfo();
const MCSubtargetInfo &STI = getTM().getSubtarget<MCSubtargetInfo>();
MCCodeEmitter* MCE =
getTarget().get()->createMCCodeEmitter(MII, MRI, STI, *Context);
// MCAsmBackend
MCAsmBackend* MAB =
getTarget().get()->createMCAsmBackend(m_Triple,getTM().getTargetCPU());
if (MCE == 0 || MAB == 0)
return true;
// now, we have MCCodeEmitter and MCAsmBackend, we can create AsmStreamer.
OwningPtr<MCStreamer> AsmStreamer(getTarget().get()->createMCObjectStreamer(
m_Triple,
*Context,
*MAB,
pOutput,
MCE,
getTM().hasMCRelaxAll(),
getTM().hasMCNoExecStack()));
AsmStreamer.get()->InitSections();
MachineFunctionPass *funcPass = getTarget().get()->createAsmPrinter(getTM(),
*AsmStreamer.get());
if (funcPass == 0)
return true;
// If successful, createAsmPrinter took ownership of AsmStreamer
AsmStreamer.take();
pPM.add(funcPass);
return false;
}
bool mcld::MCLDTargetMachine::addLinkerPasses(PassManagerBase &pPM,
LinkerConfig& pConfig,
mcld::Module& pModule,
mcld::MemoryArea& pOutput,
llvm::MCContext *&Context)
{
if (NULL == pOutput.handler())
return true;
// set up output's SOName
if (pConfig.options().soname().empty()) {
// if the output is a shared object, and the option -soname was not
// enable, set soname as the output file name. soname must be UTF-8 string.
pModule.setName(pOutput.handler()->path().filename().native());
}
else {
pModule.setName(pConfig.options().soname());
}
MachineFunctionPass* funcPass = getTarget().createMCLinker(m_Triple,
pConfig,
pModule,
pOutput);
if (NULL == funcPass)
return true;
pPM.add(funcPass);
return false;
}