mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-03 00:20:40 +03:00
381 lines
9 KiB
C++
381 lines
9 KiB
C++
#include "gen/llvm.h"
|
|
#include "llvm/Linker.h"
|
|
#include "llvm/System/Program.h"
|
|
#if _WIN32
|
|
#include "llvm/Support/SystemUtils.h"
|
|
#endif
|
|
|
|
#include "root.h"
|
|
#include "mars.h"
|
|
#include "module.h"
|
|
|
|
#define NO_COUT_LOGGER
|
|
#include "gen/logger.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef std::vector<llvm::Module*> Module_vector;
|
|
|
|
void linkModules(llvm::Module* dst, const Module_vector& MV)
|
|
{
|
|
if (MV.empty())
|
|
return;
|
|
|
|
llvm::Linker linker("ldc", dst);
|
|
|
|
std::string err;
|
|
for (Module_vector::const_iterator i=MV.begin(); i!=MV.end(); ++i)
|
|
{
|
|
if (!linker.LinkInModule(*i, &err))
|
|
{
|
|
error("%s", err.c_str());
|
|
fatal();
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static llvm::sys::Path gExePath;
|
|
|
|
int linkExecutable(const char* argv0)
|
|
{
|
|
Logger::println("*** Linking executable ***");
|
|
|
|
// error string
|
|
std::string errstr;
|
|
|
|
// find the llvm-ld program
|
|
llvm::sys::Path ldpath = llvm::sys::Program::FindProgramByName("llvm-ld");
|
|
if (ldpath.isEmpty())
|
|
{
|
|
ldpath.set("llvm-ld");
|
|
}
|
|
|
|
// build arguments
|
|
std::vector<const char*> args;
|
|
|
|
// first the program name ??
|
|
args.push_back("llvm-ld");
|
|
|
|
// output filename
|
|
std::string exestr;
|
|
if (global.params.exefile)
|
|
{ // explicit
|
|
exestr = global.params.exefile;
|
|
}
|
|
else
|
|
{ // inferred
|
|
// try root module name
|
|
if (Module::rootModule)
|
|
exestr = Module::rootModule->toChars();
|
|
else
|
|
exestr = "a.out";
|
|
}
|
|
if (global.params.os == OSWindows && !(exestr.substr(exestr.length()-4) == ".exe"))
|
|
exestr.append(".exe");
|
|
|
|
std::string outopt = "-o=" + exestr;
|
|
args.push_back(outopt.c_str());
|
|
|
|
// set the global gExePath
|
|
gExePath.set(exestr);
|
|
assert(gExePath.isValid());
|
|
|
|
// create path to exe
|
|
llvm::sys::Path exedir(gExePath);
|
|
exedir.set(gExePath.getDirname());
|
|
if (!exedir.exists())
|
|
{
|
|
exedir.createDirectoryOnDisk(true, &errstr);
|
|
if (!errstr.empty())
|
|
{
|
|
error("failed to create path to linking output: %s\n%s", exedir.c_str(), errstr.c_str());
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
// strip debug info
|
|
if (!global.params.symdebug)
|
|
args.push_back("-strip-debug");
|
|
|
|
// optimization level
|
|
if (!global.params.optimize)
|
|
args.push_back("-disable-opt");
|
|
else
|
|
{
|
|
const char* s = 0;
|
|
switch(global.params.optimizeLevel)
|
|
{
|
|
case 0:
|
|
args.push_back("-disable-opt");
|
|
args.push_back("-globaldce");
|
|
break;
|
|
case 1:
|
|
args.push_back("-disable-opt");
|
|
args.push_back("-globaldce");
|
|
args.push_back("-mem2reg");
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
// use default optimization
|
|
break;
|
|
default:
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
// inlining
|
|
if (!(global.params.useInline || global.params.llvmInline))
|
|
{
|
|
args.push_back("-disable-inlining");
|
|
}
|
|
|
|
// additional linker switches
|
|
for (int i = 0; i < global.params.linkswitches->dim; i++)
|
|
{
|
|
char *p = (char *)global.params.linkswitches->data[i];
|
|
args.push_back(p);
|
|
}
|
|
|
|
// native please
|
|
args.push_back("-native");
|
|
|
|
|
|
// user libs
|
|
for (int i = 0; i < global.params.libfiles->dim; i++)
|
|
{
|
|
char *p = (char *)global.params.libfiles->data[i];
|
|
args.push_back(p);
|
|
}
|
|
|
|
// default libs
|
|
switch(global.params.os) {
|
|
case OSLinux:
|
|
case OSMacOSX:
|
|
args.push_back("-ldl");
|
|
case OSFreeBSD:
|
|
args.push_back("-lpthread");
|
|
args.push_back("-lm");
|
|
break;
|
|
|
|
case OSWindows:
|
|
// FIXME: I'd assume kernel32 etc
|
|
break;
|
|
}
|
|
|
|
// object files
|
|
for (int i = 0; i < global.params.objfiles->dim; i++)
|
|
{
|
|
char *p = (char *)global.params.objfiles->data[i];
|
|
args.push_back(p);
|
|
}
|
|
|
|
// print link command?
|
|
if (!global.params.quiet || global.params.verbose)
|
|
{
|
|
// Print it
|
|
for (int i = 0; i < args.size(); i++)
|
|
printf("%s ", args[i]);
|
|
printf("\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
// terminate args list
|
|
args.push_back(NULL);
|
|
|
|
// try to call linker!!!
|
|
if (int status = llvm::sys::Program::ExecuteAndWait(ldpath, &args[0], NULL, NULL, 0,0, &errstr))
|
|
{
|
|
error("linking failed:\nstatus: %d", status);
|
|
if (!errstr.empty())
|
|
error("message: %s", errstr.c_str());
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
int linkObjToExecutable(const char* argv0)
|
|
{
|
|
Logger::println("*** Linking executable ***");
|
|
|
|
// error string
|
|
std::string errstr;
|
|
|
|
const char *cc;
|
|
#if !_WIN32
|
|
cc = getenv("CC");
|
|
if (!cc)
|
|
#endif
|
|
cc = "gcc";
|
|
|
|
// find gcc for linking
|
|
llvm::sys::Path gcc = llvm::sys::Program::FindProgramByName(cc);
|
|
if (gcc.isEmpty())
|
|
{
|
|
gcc.set(cc);
|
|
}
|
|
|
|
// build arguments
|
|
std::vector<const char*> args;
|
|
|
|
// first the program name ??
|
|
args.push_back(cc);
|
|
|
|
// object files
|
|
for (int i = 0; i < global.params.objfiles->dim; i++)
|
|
{
|
|
char *p = (char *)global.params.objfiles->data[i];
|
|
args.push_back(p);
|
|
}
|
|
|
|
// output filename
|
|
std::string exestr;
|
|
if (global.params.exefile)
|
|
{ // explicit
|
|
exestr = global.params.exefile;
|
|
}
|
|
else
|
|
{ // inferred
|
|
// try root module name
|
|
if (Module::rootModule)
|
|
exestr = Module::rootModule->toChars();
|
|
else if (global.params.objfiles->dim)
|
|
exestr = FileName::removeExt((char*)global.params.objfiles->data[0]);
|
|
else
|
|
exestr = "a.out";
|
|
}
|
|
if (global.params.os == OSWindows && !(exestr.rfind(".exe") == exestr.length()-4))
|
|
exestr.append(".exe");
|
|
|
|
args.push_back("-o");
|
|
args.push_back(exestr.c_str());
|
|
|
|
// set the global gExePath
|
|
gExePath.set(exestr);
|
|
assert(gExePath.isValid());
|
|
|
|
// create path to exe
|
|
llvm::sys::Path exedir(gExePath);
|
|
exedir.set(gExePath.getDirname());
|
|
if (!exedir.exists())
|
|
{
|
|
exedir.createDirectoryOnDisk(true, &errstr);
|
|
if (!errstr.empty())
|
|
{
|
|
error("failed to create path to linking output: %s\n%s", exedir.c_str(), errstr.c_str());
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
// additional linker switches
|
|
for (int i = 0; i < global.params.linkswitches->dim; i++)
|
|
{
|
|
char *p = (char *)global.params.linkswitches->data[i];
|
|
args.push_back(p);
|
|
}
|
|
|
|
// user libs
|
|
for (int i = 0; i < global.params.libfiles->dim; i++)
|
|
{
|
|
char *p = (char *)global.params.libfiles->data[i];
|
|
args.push_back(p);
|
|
}
|
|
|
|
// default libs
|
|
switch(global.params.os) {
|
|
case OSLinux:
|
|
case OSMacOSX:
|
|
args.push_back("-ldl");
|
|
// fallthrough
|
|
case OSFreeBSD:
|
|
args.push_back("-lpthread");
|
|
args.push_back("-lm");
|
|
break;
|
|
|
|
case OSSolaris:
|
|
args.push_back("-lm");
|
|
args.push_back("-lumem");
|
|
// solaris TODO
|
|
break;
|
|
|
|
case OSWindows:
|
|
// FIXME: I'd assume kernel32 etc
|
|
break;
|
|
}
|
|
|
|
// print link command?
|
|
if (!global.params.quiet || global.params.verbose)
|
|
{
|
|
// Print it
|
|
for (int i = 0; i < args.size(); i++)
|
|
printf("%s ", args[i]);
|
|
printf("\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
Logger::println("Linking with: ");
|
|
std::vector<const char*>::const_iterator I = args.begin(), E = args.end();
|
|
std::ostream& logstr = Logger::cout();
|
|
for (; I != E; ++I)
|
|
if (*I)
|
|
logstr << "'" << *I << "'" << " ";
|
|
logstr << "\n" << std::flush;
|
|
|
|
|
|
// terminate args list
|
|
args.push_back(NULL);
|
|
|
|
// try to call linker!!!
|
|
if (int status = llvm::sys::Program::ExecuteAndWait(gcc, &args[0], NULL, NULL, 0,0, &errstr))
|
|
{
|
|
error("linking failed:\nstatus: %d", status);
|
|
if (!errstr.empty())
|
|
error("message: %s", errstr.c_str());
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void deleteExecutable()
|
|
{
|
|
if (!gExePath.isEmpty())
|
|
{
|
|
assert(gExePath.isValid());
|
|
assert(!gExePath.isDirectory());
|
|
gExePath.eraseFromDisk(false);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
int runExectuable()
|
|
{
|
|
assert(!gExePath.isEmpty());
|
|
assert(gExePath.isValid());
|
|
|
|
// build arguments
|
|
std::vector<const char*> args;
|
|
for (size_t i = 0; i < global.params.runargs_length; i++)
|
|
{
|
|
char *a = global.params.runargs[i];
|
|
args.push_back(a);
|
|
}
|
|
// terminate args list
|
|
args.push_back(NULL);
|
|
|
|
// try to call linker!!!
|
|
std::string errstr;
|
|
int status = llvm::sys::Program::ExecuteAndWait(gExePath, &args[0], NULL, NULL, 0,0, &errstr);
|
|
if (!errstr.empty())
|
|
{
|
|
error("failed to execute program");
|
|
if (!errstr.empty())
|
|
error("error message: %s", errstr.c_str());
|
|
fatal();
|
|
}
|
|
return status;
|
|
}
|