mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-04 17:11:44 +03:00

Adds a new command line option -mabi= and uses the value to compute the ABI to use. Adds the ABI to linker and assembler invocation, too. Allows consistent invocation of the whole tool chain.
573 lines
17 KiB
C++
573 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/targetmachine.h"
|
||
#include "driver/tool.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/optimizer.h"
|
||
#include "gen/programs.h"
|
||
#if LDC_LLVM_VER >= 305
|
||
#include "llvm/IR/AssemblyAnnotationWriter.h"
|
||
#include "llvm/IR/Verifier.h"
|
||
#else
|
||
#include "llvm/Assembly/AssemblyAnnotationWriter.h"
|
||
#include "llvm/Analysis/Verifier.h"
|
||
#endif
|
||
#include "llvm/Bitcode/ReaderWriter.h"
|
||
#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 < 304
|
||
#include "llvm/Support/PathV1.h"
|
||
#endif
|
||
#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
|
||
#if LDC_LLVM_VER >= 303
|
||
#include "llvm/IR/Module.h"
|
||
#else
|
||
#include "llvm/Module.h"
|
||
#endif
|
||
#include <cstddef>
|
||
#include <fstream>
|
||
|
||
#if LDC_LLVM_VER < 304
|
||
namespace llvm {
|
||
namespace sys {
|
||
namespace fs {
|
||
enum OpenFlags {
|
||
F_Excl = llvm::raw_fd_ostream::F_Excl,
|
||
F_Append = llvm::raw_fd_ostream::F_Append,
|
||
F_Binary = llvm::raw_fd_ostream::F_Binary
|
||
};
|
||
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// 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());
|
||
#elif LDC_LLVM_VER == 305
|
||
if (const DataLayout *DL = Target.getDataLayout())
|
||
Passes.add(new DataLayoutPass(*DL));
|
||
else
|
||
Passes.add(new DataLayoutPass(&m));
|
||
#elif LDC_LLVM_VER >= 302
|
||
if (const DataLayout *DL = Target.getDataLayout())
|
||
Passes.add(new DataLayout(*DL));
|
||
else
|
||
Passes.add(new DataLayout(&m));
|
||
#else
|
||
if (const TargetData *TD = Target.getTargetData())
|
||
Passes.add(new TargetData(*TD));
|
||
else
|
||
Passes.add(new TargetData(&m));
|
||
#endif
|
||
|
||
#if LDC_LLVM_VER >= 307
|
||
// Add internal analysis passes from the target machine.
|
||
Passes.add(createTargetTransformInfoWrapperPass(Target.getTargetIRAnalysis()));
|
||
#elif LDC_LLVM_VER >= 303
|
||
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("-x assembler");
|
||
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");
|
||
break;
|
||
case MipsABI::O32:
|
||
args.push_back("-mabi=32");
|
||
break;
|
||
case MipsABI::N32:
|
||
args.push_back("-mabi=n32");
|
||
break;
|
||
case MipsABI::N64:
|
||
args.push_back("-mabi=64");
|
||
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.
|
||
std::string gcc(getGcc());
|
||
int R = executeToolAndWait(gcc, 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;
|
||
#elif LDC_LLVM_VER >= 305
|
||
for (DISubprogram Subprogram : Finder.subprograms())
|
||
if (Subprogram.describes(F)) return Subprogram;
|
||
return nullptr;
|
||
#else
|
||
for (DebugInfoFinder::iterator I = Finder.subprogram_begin(),
|
||
E = Finder.subprogram_end();
|
||
I != E; ++I) {
|
||
DISubprogram Subprogram(*I);
|
||
if (Subprogram.describes(F)) return Subprogram;
|
||
}
|
||
return 0;
|
||
#endif
|
||
}
|
||
|
||
static llvm::StringRef GetDisplayName(const Function *F)
|
||
{
|
||
llvm::DebugInfoFinder Finder;
|
||
#if LDC_LLVM_VER >= 303
|
||
Finder.processModule(*F->getParent());
|
||
#else
|
||
Finder.processModule(const_cast<llvm::Module&>(*F->getParent()));
|
||
#endif
|
||
#if LDC_LLVM_VER >= 307
|
||
if (DISubprogram* N = FindSubprogram(F, Finder))
|
||
#else
|
||
if (MDNode* N = FindSubprogram(F, Finder))
|
||
#endif
|
||
{
|
||
#if 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() << " type = " << *val.getType() << ']';
|
||
}
|
||
|
||
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 << ']';
|
||
}
|
||
}
|
||
}
|
||
};
|
||
} // end of anonymous namespace
|
||
|
||
void writeModule(llvm::Module* m, std::string filename)
|
||
{
|
||
// run optimizer
|
||
ldc_optimize_module(m);
|
||
|
||
#if LDC_LLVM_VER >= 305
|
||
// 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.
|
||
bool const assembleExternally = global.params.output_o &&
|
||
global.params.targetTriple.getOS() == llvm::Triple::AIX;
|
||
#else
|
||
// (We require LLVM 3.5 with AIX.)
|
||
// We don't use the integrated assembler with MinGW as it does not support
|
||
// emitting DW2 exception handling tables.
|
||
bool const assembleExternally = global.params.output_o &&
|
||
global.params.targetTriple.getOS() == llvm::Triple::MinGW32;
|
||
#endif
|
||
|
||
// eventually do our own path stuff, dmd's is a bit strange.
|
||
typedef llvm::SmallString<128> LLPath;
|
||
|
||
// write LLVM bitcode
|
||
if (global.params.output_bc) {
|
||
LLPath bcpath = LLPath(filename);
|
||
llvm::sys::path::replace_extension(bcpath, global.bc_ext);
|
||
Logger::println("Writing LLVM bitcode to: %s\n", bcpath.c_str());
|
||
#if LDC_LLVM_VER >= 306
|
||
std::error_code errinfo;
|
||
#else
|
||
std::string errinfo;
|
||
#endif
|
||
llvm::raw_fd_ostream bos(bcpath.c_str(), errinfo,
|
||
#if LDC_LLVM_VER >= 305
|
||
llvm::sys::fs::F_None
|
||
#else
|
||
llvm::sys::fs::F_Binary
|
||
#endif
|
||
);
|
||
if (bos.has_error())
|
||
{
|
||
error(Loc(), "cannot write LLVM bitcode file '%s': %s", bcpath.c_str(),
|
||
#if LDC_LLVM_VER >= 306
|
||
errinfo
|
||
#else
|
||
errinfo.c_str()
|
||
#endif
|
||
);
|
||
fatal();
|
||
}
|
||
llvm::WriteBitcodeToFile(m, bos);
|
||
}
|
||
|
||
// write LLVM IR
|
||
if (global.params.output_ll) {
|
||
LLPath llpath = LLPath(filename);
|
||
llvm::sys::path::replace_extension(llpath, global.ll_ext);
|
||
Logger::println("Writing LLVM asm to: %s\n", llpath.c_str());
|
||
#if LDC_LLVM_VER >= 306
|
||
std::error_code errinfo;
|
||
#else
|
||
std::string errinfo;
|
||
#endif
|
||
llvm::raw_fd_ostream aos(llpath.c_str(), errinfo,
|
||
#if LDC_LLVM_VER >= 305
|
||
llvm::sys::fs::F_None
|
||
#else
|
||
llvm::sys::fs::F_Binary
|
||
#endif
|
||
);
|
||
if (aos.has_error())
|
||
{
|
||
error(Loc(), "cannot write LLVM asm file '%s': %s", llpath.c_str(),
|
||
#if LDC_LLVM_VER >= 306
|
||
errinfo
|
||
#else
|
||
errinfo.c_str()
|
||
#endif
|
||
);
|
||
fatal();
|
||
}
|
||
AssemblyAnnotator annotator;
|
||
m->print(aos, &annotator);
|
||
}
|
||
|
||
// write native assembly
|
||
if (global.params.output_s || assembleExternally) {
|
||
#if LDC_LLVM_VER >= 304
|
||
LLPath spath = LLPath(filename);
|
||
llvm::sys::path::replace_extension(spath, global.s_ext);
|
||
if (!global.params.output_s)
|
||
llvm::sys::fs::createUniqueFile("ldc-%%%%%%%.s", spath);
|
||
#else
|
||
// Pre-3.4 versions don't have a createUniqueFile overload that does
|
||
// not open the file.
|
||
llvm::sys::Path spath(filename);
|
||
spath.eraseSuffix();
|
||
spath.appendSuffix(std::string(global.s_ext));
|
||
if (!global.params.output_s)
|
||
spath.createTemporaryFileOnDisk();
|
||
#endif
|
||
|
||
Logger::println("Writing native asm to: %s\n", spath.c_str());
|
||
#if LDC_LLVM_VER >= 306
|
||
std::error_code errinfo;
|
||
#else
|
||
std::string errinfo;
|
||
#endif
|
||
{
|
||
llvm::raw_fd_ostream out(spath.c_str(), errinfo,
|
||
#if LDC_LLVM_VER >= 305
|
||
llvm::sys::fs::F_None
|
||
#else
|
||
llvm::sys::fs::F_Binary
|
||
#endif
|
||
);
|
||
#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 native asm: %s",
|
||
#if LDC_LLVM_VER >= 306
|
||
errinfo
|
||
#else
|
||
errinfo.c_str()
|
||
#endif
|
||
);
|
||
fatal();
|
||
}
|
||
}
|
||
|
||
if (assembleExternally)
|
||
{
|
||
LLPath objpath(filename);
|
||
assemble(spath.str(), objpath.str());
|
||
}
|
||
|
||
if (!global.params.output_s)
|
||
{
|
||
#if LDC_LLVM_VER < 305
|
||
bool existed;
|
||
llvm::sys::fs::remove(spath.str(), existed);
|
||
#else
|
||
llvm::sys::fs::remove(spath.str());
|
||
#endif
|
||
}
|
||
}
|
||
|
||
if (global.params.output_o && !assembleExternally) {
|
||
LLPath objpath = LLPath(filename);
|
||
Logger::println("Writing object file to: %s\n", objpath.c_str());
|
||
#if LDC_LLVM_VER >= 306
|
||
std::error_code errinfo;
|
||
#else
|
||
std::string errinfo;
|
||
#endif
|
||
{
|
||
llvm::raw_fd_ostream out(objpath.c_str(), errinfo,
|
||
#if LDC_LLVM_VER >= 305
|
||
llvm::sys::fs::F_None
|
||
#else
|
||
llvm::sys::fs::F_Binary
|
||
#endif
|
||
);
|
||
#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",
|
||
#if LDC_LLVM_VER >= 306
|
||
errinfo
|
||
#else
|
||
errinfo.c_str()
|
||
#endif
|
||
);
|
||
fatal();
|
||
}
|
||
}
|
||
}
|
||
}
|