mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-28 22:21:31 +03:00
563 lines
17 KiB
C++
563 lines
17 KiB
C++
//===-- toobj.cpp ---------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "driver/toobj.h"
|
||
|
||
#include "driver/cl_options.h"
|
||
#include "driver/cache.h"
|
||
#include "driver/targetmachine.h"
|
||
#include "driver/tool.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/optimizer.h"
|
||
#include "llvm/IR/AssemblyAnnotationWriter.h"
|
||
#include "llvm/IR/Verifier.h"
|
||
#if LDC_LLVM_VER >= 309
|
||
#include "llvm/Analysis/ModuleSummaryAnalysis.h"
|
||
#endif
|
||
#if LDC_LLVM_VER >= 400
|
||
#include "llvm/Analysis/ProfileSummaryInfo.h"
|
||
#include "llvm/Bitcode/BitcodeWriter.h"
|
||
#else
|
||
#include "llvm/Bitcode/ReaderWriter.h"
|
||
#endif
|
||
#if LDC_LLVM_VER >= 307
|
||
#include "llvm/IR/LegacyPassManager.h"
|
||
#else
|
||
#include "llvm/PassManager.h"
|
||
#endif
|
||
#include "llvm/Support/CommandLine.h"
|
||
#include "llvm/Support/FileSystem.h"
|
||
#include "llvm/Support/FormattedStream.h"
|
||
#include "llvm/Support/Program.h"
|
||
#if LDC_LLVM_VER >= 307
|
||
#include "llvm/Support/Path.h"
|
||
#endif
|
||
#include "llvm/Target/TargetMachine.h"
|
||
#if LDC_LLVM_VER >= 307
|
||
#include "llvm/Analysis/TargetTransformInfo.h"
|
||
#endif
|
||
#if LDC_LLVM_VER >= 306
|
||
#include "llvm/Target/TargetSubtargetInfo.h"
|
||
#endif
|
||
#include "llvm/IR/Module.h"
|
||
#include <cstddef>
|
||
#include <fstream>
|
||
|
||
#if LDC_LLVM_VER >= 306
|
||
using LLErrorInfo = std::error_code;
|
||
#define ERRORINFO_STRING(errinfo) errinfo.message().c_str()
|
||
#else
|
||
using LLErrorInfo = std::string;
|
||
#define ERRORINFO_STRING(errinfo) errinfo.c_str()
|
||
#endif
|
||
|
||
static llvm::cl::opt<bool>
|
||
NoIntegratedAssembler("no-integrated-as", llvm::cl::ZeroOrMore,
|
||
llvm::cl::Hidden,
|
||
llvm::cl::desc("Disable integrated assembler"));
|
||
|
||
// based on llc code, University of Illinois Open Source License
|
||
static void codegenModule(llvm::TargetMachine &Target, llvm::Module &m,
|
||
llvm::raw_fd_ostream &out,
|
||
llvm::TargetMachine::CodeGenFileType fileType) {
|
||
using namespace llvm;
|
||
|
||
// Create a PassManager to hold and optimize the collection of passes we are
|
||
// about to build.
|
||
#if LDC_LLVM_VER >= 307
|
||
legacy::
|
||
#endif
|
||
PassManager Passes;
|
||
|
||
#if LDC_LLVM_VER >= 307
|
||
// The DataLayout is already set at the module (in module.cpp,
|
||
// method Module::genLLVMModule())
|
||
// FIXME: Introduce new command line switch default-data-layout to
|
||
// override the module data layout
|
||
#elif LDC_LLVM_VER == 306
|
||
Passes.add(new DataLayoutPass());
|
||
#else
|
||
if (const DataLayout *DL = Target.getDataLayout())
|
||
Passes.add(new DataLayoutPass(*DL));
|
||
else
|
||
Passes.add(new DataLayoutPass(&m));
|
||
#endif
|
||
|
||
#if LDC_LLVM_VER >= 307
|
||
// Add internal analysis passes from the target machine.
|
||
Passes.add(
|
||
createTargetTransformInfoWrapperPass(Target.getTargetIRAnalysis()));
|
||
#else
|
||
Target.addAnalysisPasses(Passes);
|
||
#endif
|
||
|
||
#if LDC_LLVM_VER < 307
|
||
llvm::formatted_raw_ostream fout(out);
|
||
#endif
|
||
if (Target.addPassesToEmitFile(Passes,
|
||
#if LDC_LLVM_VER >= 307
|
||
out,
|
||
#else
|
||
fout,
|
||
#endif
|
||
fileType, codeGenOptLevel())) {
|
||
llvm_unreachable("no support for asm output");
|
||
}
|
||
|
||
Passes.run(m);
|
||
}
|
||
|
||
static void assemble(const std::string &asmpath, const std::string &objpath) {
|
||
std::vector<std::string> args;
|
||
args.push_back("-O3");
|
||
args.push_back("-c");
|
||
args.push_back("-xassembler");
|
||
args.push_back(asmpath);
|
||
args.push_back("-o");
|
||
args.push_back(objpath);
|
||
|
||
// Only specify -m32/-m64 for architectures where the two variants actually
|
||
// exist (as e.g. the GCC ARM toolchain doesn't recognize the switches).
|
||
// MIPS does not have -m32/-m64 but requires -mabi=.
|
||
if (global.params.targetTriple->get64BitArchVariant().getArch() !=
|
||
llvm::Triple::UnknownArch &&
|
||
global.params.targetTriple->get32BitArchVariant().getArch() !=
|
||
llvm::Triple::UnknownArch) {
|
||
if (global.params.targetTriple->get64BitArchVariant().getArch() ==
|
||
llvm::Triple::mips64 ||
|
||
global.params.targetTriple->get64BitArchVariant().getArch() ==
|
||
llvm::Triple::mips64el) {
|
||
switch (getMipsABI()) {
|
||
case MipsABI::EABI:
|
||
args.push_back("-mabi=eabi");
|
||
args.push_back("-march=mips32r2");
|
||
break;
|
||
case MipsABI::O32:
|
||
args.push_back("-mabi=32");
|
||
args.push_back("-march=mips32r2");
|
||
break;
|
||
case MipsABI::N32:
|
||
args.push_back("-mabi=n32");
|
||
args.push_back("-march=mips64r2");
|
||
break;
|
||
case MipsABI::N64:
|
||
args.push_back("-mabi=64");
|
||
args.push_back("-march=mips64r2");
|
||
break;
|
||
case MipsABI::Unknown:
|
||
break;
|
||
}
|
||
} else {
|
||
if (global.params.is64bit) {
|
||
args.push_back("-m64");
|
||
} else {
|
||
args.push_back("-m32");
|
||
}
|
||
}
|
||
}
|
||
|
||
// Run the compiler to assembly the program.
|
||
int R = executeToolAndWait(getGcc(), args, global.params.verbose);
|
||
if (R) {
|
||
error(Loc(), "Error while invoking external assembler.");
|
||
fatal();
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
namespace {
|
||
using namespace llvm;
|
||
static void printDebugLoc(const DebugLoc &debugLoc, formatted_raw_ostream &os) {
|
||
os << debugLoc.getLine() << ":" << debugLoc.getCol();
|
||
#if LDC_LLVM_VER >= 307
|
||
if (DILocation *IDL = debugLoc.getInlinedAt()) {
|
||
os << "@";
|
||
printDebugLoc(IDL, os);
|
||
}
|
||
#else
|
||
if (MDNode *N = debugLoc.getInlinedAt(getGlobalContext())) {
|
||
DebugLoc IDL = DebugLoc::getFromDILocation(N);
|
||
if (!IDL.isUnknown()) {
|
||
os << "@";
|
||
printDebugLoc(IDL, os);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
class AssemblyAnnotator : public AssemblyAnnotationWriter {
|
||
// Find the MDNode which corresponds to the DISubprogram data that described F.
|
||
#if LDC_LLVM_VER >= 307
|
||
static DISubprogram *FindSubprogram(const Function *F,
|
||
DebugInfoFinder &Finder)
|
||
#else
|
||
static MDNode *FindSubprogram(const Function *F, DebugInfoFinder &Finder)
|
||
#endif
|
||
{
|
||
#if LDC_LLVM_VER >= 307
|
||
for (DISubprogram *Subprogram : Finder.subprograms())
|
||
if (Subprogram->describes(F))
|
||
return Subprogram;
|
||
return nullptr;
|
||
#else
|
||
for (DISubprogram Subprogram : Finder.subprograms()) {
|
||
if (Subprogram.describes(F)) {
|
||
return Subprogram;
|
||
}
|
||
}
|
||
return nullptr;
|
||
#endif
|
||
}
|
||
|
||
static llvm::StringRef GetDisplayName(const Function *F) {
|
||
llvm::DebugInfoFinder Finder;
|
||
Finder.processModule(*F->getParent());
|
||
#if LDC_LLVM_VER >= 307
|
||
if (DISubprogram *N = FindSubprogram(F, Finder))
|
||
#else
|
||
if (MDNode *N = FindSubprogram(F, Finder))
|
||
#endif
|
||
{
|
||
#if LDC_LLVM_VER >= 500
|
||
return N->getName();
|
||
#elif LDC_LLVM_VER >= 307
|
||
return N->getDisplayName();
|
||
#else
|
||
llvm::DISubprogram sub(N);
|
||
return sub.getDisplayName();
|
||
#endif
|
||
}
|
||
return "";
|
||
}
|
||
|
||
public:
|
||
void emitFunctionAnnot(const Function *F,
|
||
formatted_raw_ostream &os) LLVM_OVERRIDE {
|
||
os << "; [#uses = " << F->getNumUses() << ']';
|
||
|
||
// show demangled name
|
||
llvm::StringRef funcName = GetDisplayName(F);
|
||
if (!funcName.empty()) {
|
||
os << " [display name = " << funcName << ']';
|
||
}
|
||
os << '\n';
|
||
}
|
||
|
||
void printInfoComment(const Value &val,
|
||
formatted_raw_ostream &os) LLVM_OVERRIDE {
|
||
bool padding = false;
|
||
if (!val.getType()->isVoidTy()) {
|
||
os.PadToColumn(50);
|
||
padding = true;
|
||
os << "; [#uses = " << val.getNumUses();
|
||
if (isa<GetElementPtrInst>(&val) || isa<PHINode>(&val)) {
|
||
// Only print type for instructions where it is not obvious
|
||
// from being repeated in its parameters. Might need to be
|
||
// extended, but GEPs/PHIs are the most common ones.
|
||
os << ", type = " << *val.getType();
|
||
} else if (isa<AllocaInst>(&val)) {
|
||
os << ", size/byte = "
|
||
<< gDataLayout->getTypeAllocSize(val.getType()->getContainedType(0));
|
||
}
|
||
os << ']';
|
||
}
|
||
|
||
const Instruction *instr = dyn_cast<Instruction>(&val);
|
||
if (!instr) {
|
||
return;
|
||
}
|
||
|
||
#if LDC_LLVM_VER >= 307
|
||
if (const DebugLoc &debugLoc = instr->getDebugLoc())
|
||
#else
|
||
const DebugLoc &debugLoc = instr->getDebugLoc();
|
||
if (!debugLoc.isUnknown())
|
||
#endif
|
||
{
|
||
if (!padding) {
|
||
os.PadToColumn(50);
|
||
padding = true;
|
||
os << ';';
|
||
}
|
||
os << " [debug line = ";
|
||
printDebugLoc(debugLoc, os);
|
||
os << ']';
|
||
}
|
||
if (const DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(instr)) {
|
||
#if LDC_LLVM_VER >= 307
|
||
DILocalVariable *Var(DDI->getVariable());
|
||
#else
|
||
DIVariable Var(DDI->getVariable());
|
||
#endif
|
||
if (!padding) {
|
||
os.PadToColumn(50);
|
||
os << ";";
|
||
}
|
||
#if LDC_LLVM_VER >= 307
|
||
os << " [debug variable = " << Var->getName() << ']';
|
||
#else
|
||
os << " [debug variable = " << Var.getName() << ']';
|
||
#endif
|
||
} else if (const DbgValueInst *DVI = dyn_cast<DbgValueInst>(instr)) {
|
||
#if LDC_LLVM_VER >= 307
|
||
DILocalVariable *Var(DVI->getVariable());
|
||
#else
|
||
DIVariable Var(DVI->getVariable());
|
||
#endif
|
||
if (!padding) {
|
||
os.PadToColumn(50);
|
||
os << ";";
|
||
}
|
||
#if LDC_LLVM_VER >= 307
|
||
os << " [debug variable = " << Var->getName() << ']';
|
||
#else
|
||
os << " [debug variable = " << Var.getName() << ']';
|
||
#endif
|
||
} else if (const CallInst *callinstr = dyn_cast<CallInst>(instr)) {
|
||
const Function *F = callinstr->getCalledFunction();
|
||
if (!F) {
|
||
return;
|
||
}
|
||
|
||
StringRef funcName = GetDisplayName(F);
|
||
if (!funcName.empty()) {
|
||
if (!padding) {
|
||
os.PadToColumn(50);
|
||
os << ";";
|
||
}
|
||
os << " [display name = " << funcName << ']';
|
||
}
|
||
} else if (const InvokeInst *invokeinstr = dyn_cast<InvokeInst>(instr)) {
|
||
const Function *F = invokeinstr->getCalledFunction();
|
||
if (!F) {
|
||
return;
|
||
}
|
||
|
||
StringRef funcName = GetDisplayName(F);
|
||
if (!funcName.empty()) {
|
||
if (!padding) {
|
||
os.PadToColumn(50);
|
||
os << ";";
|
||
}
|
||
os << " [display name = " << funcName << ']';
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
void writeObjectFile(llvm::Module *m, const char *filename) {
|
||
IF_LOG Logger::println("Writing object file to: %s", filename);
|
||
LLErrorInfo errinfo;
|
||
{
|
||
llvm::raw_fd_ostream out(filename, errinfo, llvm::sys::fs::F_None);
|
||
#if LDC_LLVM_VER >= 306
|
||
if (!errinfo)
|
||
#else
|
||
if (errinfo.empty())
|
||
#endif
|
||
{
|
||
codegenModule(*gTargetMachine, *m, out,
|
||
llvm::TargetMachine::CGFT_ObjectFile);
|
||
} else {
|
||
error(Loc(), "cannot write object file '%s': %s", filename,
|
||
ERRORINFO_STRING(errinfo));
|
||
fatal();
|
||
}
|
||
}
|
||
}
|
||
|
||
bool shouldAssembleExternally() {
|
||
// There is no integrated assembler on AIX because XCOFF is not supported.
|
||
// Starting with LLVM 3.5 the integrated assembler can be used with MinGW.
|
||
return global.params.output_o &&
|
||
(NoIntegratedAssembler ||
|
||
global.params.targetTriple->getOS() == llvm::Triple::AIX);
|
||
}
|
||
|
||
bool shouldOutputObjectFile() {
|
||
return global.params.output_o && !shouldAssembleExternally();
|
||
}
|
||
|
||
bool shouldDoLTO(llvm::Module *m) {
|
||
#if LDC_LLVM_VER < 309
|
||
return false;
|
||
#else
|
||
#if LDC_LLVM_VER == 309
|
||
// LLVM 3.9 bug: can't do ThinLTO with modules that have module-scope inline
|
||
// assembly blocks (duplicate definitions upon importing from such a module).
|
||
// https://llvm.org/bugs/show_bug.cgi?id=30610
|
||
if (opts::isUsingThinLTO() && !m->getModuleInlineAsm().empty())
|
||
return false;
|
||
#endif
|
||
return opts::isUsingLTO();
|
||
#endif
|
||
}
|
||
} // end of anonymous namespace
|
||
|
||
void writeModule(llvm::Module *m, const char *filename) {
|
||
const bool doLTO = shouldDoLTO(m);
|
||
const bool outputObj = shouldOutputObjectFile();
|
||
const bool assembleExternally = shouldAssembleExternally();
|
||
|
||
// Use cached object code if possible.
|
||
// TODO: combine LDC's cache and LTO (the advantage is skipping the IR
|
||
// optimization).
|
||
const bool useIR2ObjCache =
|
||
!opts::cacheDir.empty() && outputObj && !doLTO;
|
||
llvm::SmallString<32> moduleHash;
|
||
if (useIR2ObjCache) {
|
||
llvm::SmallString<128> cacheDir(opts::cacheDir.c_str());
|
||
llvm::sys::fs::make_absolute(cacheDir);
|
||
opts::cacheDir = cacheDir.c_str();
|
||
|
||
IF_LOG Logger::println("Use IR-to-Object cache in %s",
|
||
opts::cacheDir.c_str());
|
||
LOG_SCOPE
|
||
|
||
cache::calculateModuleHash(m, moduleHash);
|
||
std::string cacheFile = cache::cacheLookup(moduleHash);
|
||
if (!cacheFile.empty()) {
|
||
cache::recoverObjectFile(moduleHash, filename);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// run optimizer
|
||
ldc_optimize_module(m);
|
||
|
||
// make sure the output directory exists
|
||
const auto directory = llvm::sys::path::parent_path(filename);
|
||
if (!directory.empty()) {
|
||
if (auto ec = llvm::sys::fs::create_directories(directory)) {
|
||
error(Loc(), "failed to create output directory: %s\n%s",
|
||
directory.data(), ec.message().c_str());
|
||
fatal();
|
||
}
|
||
}
|
||
|
||
const auto outputFlags = {global.params.output_o, global.params.output_bc,
|
||
global.params.output_ll, global.params.output_s};
|
||
const auto numOutputFiles =
|
||
std::count_if(outputFlags.begin(), outputFlags.end(),
|
||
[](OUTPUTFLAG flag) { return flag != 0; });
|
||
|
||
const auto replaceExtensionWith = [=](const char *ext) -> std::string {
|
||
if (numOutputFiles == 1)
|
||
return filename;
|
||
llvm::SmallString<128> buffer(filename);
|
||
llvm::sys::path::replace_extension(buffer, ext);
|
||
return buffer.str();
|
||
};
|
||
|
||
// write LLVM bitcode
|
||
if (global.params.output_bc || (doLTO && outputObj)) {
|
||
std::string bcpath =
|
||
(doLTO && outputObj) ? filename : replaceExtensionWith(global.bc_ext);
|
||
Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str());
|
||
LLErrorInfo errinfo;
|
||
llvm::raw_fd_ostream bos(bcpath.c_str(), errinfo, llvm::sys::fs::F_None);
|
||
if (bos.has_error()) {
|
||
error(Loc(), "cannot write LLVM bitcode file '%s': %s", bcpath.c_str(),
|
||
ERRORINFO_STRING(errinfo));
|
||
fatal();
|
||
}
|
||
if (opts::isUsingThinLTO()) {
|
||
#if LDC_LLVM_VER >= 309
|
||
Logger::println("Creating module summary for ThinLTO");
|
||
#if LDC_LLVM_VER == 309
|
||
// When the function freq info callback is set to nullptr, LLVM will
|
||
// calculate it automatically for us.
|
||
llvm::ModuleSummaryIndexBuilder indexBuilder(
|
||
m, /* function freq callback */ nullptr);
|
||
auto &moduleSummaryIndex = indexBuilder.getIndex();
|
||
#else
|
||
llvm::ProfileSummaryInfo PSI(*m);
|
||
// Set PSIptr to nullptr when there is no PGO information available, such
|
||
// that LLVM will not try to find PGO information for each function inside
|
||
// `buildModuleSummaryIndex`. (micro-optimization)
|
||
auto PSIptr = m->getProfileSummary() ? &PSI : nullptr;
|
||
|
||
// When the function freq info callback is set to nullptr, LLVM will
|
||
// calculate it automatically for us.
|
||
auto moduleSummaryIndex = buildModuleSummaryIndex(
|
||
*m, /* function freq callback */ nullptr, PSIptr);
|
||
#endif
|
||
|
||
llvm::WriteBitcodeToFile(m, bos, true, &moduleSummaryIndex,
|
||
/* generate ThinLTO hash */ true);
|
||
#endif
|
||
} else {
|
||
llvm::WriteBitcodeToFile(m, bos);
|
||
}
|
||
}
|
||
|
||
// write LLVM IR
|
||
if (global.params.output_ll) {
|
||
const auto llpath = replaceExtensionWith(global.ll_ext);
|
||
Logger::println("Writing LLVM IR to: %s\n", llpath.c_str());
|
||
LLErrorInfo errinfo;
|
||
llvm::raw_fd_ostream aos(llpath.c_str(), errinfo, llvm::sys::fs::F_None);
|
||
if (aos.has_error()) {
|
||
error(Loc(), "cannot write LLVM IR file '%s': %s", llpath.c_str(),
|
||
ERRORINFO_STRING(errinfo));
|
||
fatal();
|
||
}
|
||
AssemblyAnnotator annotator;
|
||
m->print(aos, &annotator);
|
||
}
|
||
|
||
// write native assembly
|
||
if (global.params.output_s || assembleExternally) {
|
||
std::string spath;
|
||
if (!global.params.output_s) {
|
||
llvm::SmallString<16> buffer;
|
||
llvm::sys::fs::createUniqueFile("ldc-%%%%%%%.s", buffer);
|
||
spath = buffer.str();
|
||
} else {
|
||
spath = replaceExtensionWith(global.s_ext);
|
||
}
|
||
|
||
Logger::println("Writing asm to: %s\n", spath.c_str());
|
||
LLErrorInfo errinfo;
|
||
{
|
||
llvm::raw_fd_ostream out(spath.c_str(), errinfo, llvm::sys::fs::F_None);
|
||
#if LDC_LLVM_VER >= 306
|
||
if (!errinfo)
|
||
#else
|
||
if (errinfo.empty())
|
||
#endif
|
||
{
|
||
codegenModule(*gTargetMachine, *m, out,
|
||
llvm::TargetMachine::CGFT_AssemblyFile);
|
||
} else {
|
||
error(Loc(), "cannot write asm: %s", ERRORINFO_STRING(errinfo));
|
||
fatal();
|
||
}
|
||
}
|
||
|
||
if (assembleExternally) {
|
||
assemble(spath, filename);
|
||
}
|
||
|
||
if (!global.params.output_s) {
|
||
llvm::sys::fs::remove(spath);
|
||
}
|
||
}
|
||
|
||
if (outputObj && !doLTO) {
|
||
writeObjectFile(m, filename);
|
||
if (useIR2ObjCache) {
|
||
cache::cacheObjectFile(filename, moduleHash);
|
||
}
|
||
}
|
||
}
|
||
|
||
#undef ERRORINFO_STRING
|