ldc/driver/main.cpp
Martin bfac582294 Enforce backslashes in most paths on Windows
DMD does too, and the front-end's FileName class expects backslashes in
some places.
Due to the import and lib directories in the ldc2.conf file being
specified with forward slashes, most paths produced by LDC used both
slashes, e.g., `C:/LDC/ldc/runtime/druntime/src\object.d`, which looks
unprofessional, is problematic for the front-end and leads to differences
wrt. DMD when outputting module dependencies etc.
2016-09-07 10:03:33 +02:00

1176 lines
38 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- main.cpp --------------------------------------------------------===//
//
// LDC the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
#include "module.h"
#include "errors.h"
#include "id.h"
#include "hdrgen.h"
#include "json.h"
#include "mars.h"
#include "mtype.h"
#include "identifier.h"
#include "rmem.h"
#include "root.h"
#include "scope.h"
#include "ddmd/target.h"
#include "driver/cl_options.h"
#include "driver/codegenerator.h"
#include "driver/configfile.h"
#include "driver/exe_path.h"
#include "driver/ldc-version.h"
#include "driver/linker.h"
#include "driver/targetmachine.h"
#include "gen/cl_helpers.h"
#include "gen/irstate.h"
#include "gen/linkage.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/metadata.h"
#include "gen/modules.h"
#include "gen/objcgen.h"
#include "gen/optimizer.h"
#include "gen/passes/Passes.h"
#include "gen/runtime.h"
#include "gen/abi.h"
#include "llvm/InitializePasses.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
#if LDC_LLVM_VER >= 308
#include "llvm/Support/StringSaver.h"
#endif
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#if LDC_LLVM_VER >= 306
#include "llvm/Target/TargetSubtargetInfo.h"
#endif
#include "llvm/LinkAllIR.h"
#include "llvm/IR/LLVMContext.h"
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#if _WIN32
#include <windows.h>
#endif
// Needs Type already declared.
#include "cond.h"
// From druntime/src/core/runtime.d.
extern "C" {
int rt_init();
}
// In ddmd/doc.d
void gendocfile(Module *m);
using namespace opts;
extern void getenv_setargv(const char *envvar, int *pargc, char ***pargv);
static cl::opt<bool>
noDefaultLib("nodefaultlib",
cl::desc("Don't add a default library for linking implicitly"),
cl::ZeroOrMore, cl::Hidden);
static StringsAdapter impPathsStore("I", global.params.imppath);
static cl::list<std::string, StringsAdapter>
importPaths("I", cl::desc("Where to look for imports"),
cl::value_desc("path"), cl::location(impPathsStore),
cl::Prefix);
static cl::opt<std::string>
defaultLib("defaultlib",
cl::desc("Default libraries to link with (overrides previous)"),
cl::value_desc("lib1,lib2,..."), cl::ZeroOrMore);
static cl::opt<std::string> debugLib(
"debuglib",
cl::desc("Debug versions of default libraries (overrides previous)"),
cl::value_desc("lib1,lib2,..."), cl::ZeroOrMore);
static cl::opt<bool> linkDebugLib(
"link-debuglib",
cl::desc("Link with libraries specified in -debuglib, not -defaultlib"),
cl::ZeroOrMore);
#if LDC_LLVM_VER >= 309
static inline llvm::Optional<llvm::Reloc::Model> getRelocModel() {
if (mRelocModel.getNumOccurrences()) {
llvm::Reloc::Model R = mRelocModel;
return R;
}
return llvm::None;
}
#else
static inline llvm::Reloc::Model getRelocModel() { return mRelocModel; }
#endif
void printVersion() {
printf("LDC - the LLVM D compiler (%s):\n", global.ldc_version);
printf(" based on DMD %s and LLVM %s\n", global.version,
global.llvm_version);
printf(" built with %s\n", ldc::built_with_Dcompiler_version);
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
printf(" compiled with address sanitizer enabled\n");
#endif
#endif
printf(" Default target: %s\n", llvm::sys::getDefaultTargetTriple().c_str());
std::string CPU = llvm::sys::getHostCPUName();
if (CPU == "generic") {
CPU = "(unknown)";
}
printf(" Host CPU: %s\n", CPU.c_str());
printf(" http://dlang.org - http://wiki.dlang.org/LDC\n");
printf("\n");
// Without explicitly flushing here, only the target list is visible when
// redirecting stdout to a file.
fflush(stdout);
llvm::TargetRegistry::printRegisteredTargetsForVersion();
exit(EXIT_SUCCESS);
}
namespace {
// Helper function to handle -d-debug=* and -d-version=*
void processVersions(std::vector<std::string> &list, const char *type,
void (*setLevel)(unsigned),
void (*addIdent)(const char *)) {
for (const auto &i : list) {
const char *value = i.c_str();
if (isdigit(value[0])) {
errno = 0;
char *end;
long level = strtol(value, &end, 10);
if (*end || errno || level > INT_MAX) {
error(Loc(), "Invalid %s level: %s", type, i.c_str());
} else {
setLevel(static_cast<unsigned>(level));
}
} else {
char *cstr = mem.xstrdup(value);
if (Identifier::isValidIdentifier(cstr)) {
addIdent(cstr);
continue;
} else {
error(Loc(), "Invalid %s identifier or level: '%s'", type, i.c_str());
}
}
}
}
// Helper function to handle -transition=*
void processTransitions(std::vector<std::string> &list) {
for (const auto &i : list) {
if (i == "?") {
printf("Language changes listed by -transition=id:\n");
printf(" = all list information on all language changes\n");
printf(" = checkimports give deprecation messages about 10378 "
"anomalies\n");
printf(
" = complex,14488 list all usages of complex or imaginary types\n");
printf(" = field,3449 list all non - mutable fields which occupy an "
"object instance\n");
printf(" = import,10378 revert to single phase name lookup\n");
printf(" = tls list all variables going into thread local "
"storage\n");
exit(EXIT_SUCCESS);
} else if (i == "all") {
global.params.vtls = true;
global.params.vfield = true;
global.params.vcomplex = true;
global.params.bug10378 = true; // not set in DMD
global.params.check10378 = true; // not set in DMD
} else if (i == "checkimports") {
global.params.check10378 = true;
} else if (i == "complex" || i == "14488") {
global.params.vcomplex = true;
} else if (i == "field" || i == "3449") {
global.params.vfield = true;
} else if (i == "import" || i == "10378") {
global.params.bug10378 = true;
} else if (i == "tls") {
global.params.vtls = true;
} else {
error(Loc(), "Invalid transition %s", i.c_str());
}
}
}
char *dupPathString(const std::string &src) {
char *r = mem.xstrdup(src.c_str());
#if _WIN32
std::replace(r, r + src.length(), '/', '\\');
#endif
return r;
}
// Helper function to handle -of, -od, etc.
void initFromPathString(const char *&dest, const cl::opt<std::string> &src) {
dest = nullptr;
if (src.getNumOccurrences() != 0) {
if (src.empty()) {
error(Loc(), "Expected argument to '-%s'", src.ArgStr);
}
dest = dupPathString(src);
}
}
void hide(llvm::StringMap<cl::Option *> &map, const char *name) {
// Check if option exists first for resilience against LLVM changes
// between versions.
if (map.count(name)) {
map[name]->setHiddenFlag(cl::Hidden);
}
}
#if LDC_LLVM_VER >= 307
void rename(llvm::StringMap<cl::Option *> &map, const char *from,
const char *to) {
auto i = map.find(from);
if (i != map.end()) {
cl::Option *opt = i->getValue();
map.erase(i);
opt->setArgStr(to);
map[to] = opt;
}
}
#endif
/// Removes command line options exposed from within LLVM that are unlikely
/// to be useful for end users from the -help output.
void hideLLVMOptions() {
#if LDC_LLVM_VER >= 307
llvm::StringMap<cl::Option *> &map = cl::getRegisteredOptions();
#else
llvm::StringMap<cl::Option *> map;
cl::getRegisteredOptions(map);
#endif
hide(map, "bounds-checking-single-trap");
hide(map, "disable-debug-info-verifier");
hide(map, "disable-objc-arc-checkforcfghazards");
hide(map, "disable-spill-fusing");
hide(map, "cppfname");
hide(map, "cppfor");
hide(map, "cppgen");
hide(map, "enable-correct-eh-support");
hide(map, "enable-load-pre");
hide(map, "enable-misched");
hide(map, "enable-objc-arc-annotations");
hide(map, "enable-objc-arc-opts");
hide(map, "enable-scoped-noalias");
hide(map, "enable-tbaa");
hide(map, "exhaustive-register-search");
hide(map, "fatal-assembler-warnings");
hide(map, "internalize-public-api-file");
hide(map, "internalize-public-api-list");
hide(map, "join-liveintervals");
hide(map, "limit-float-precision");
hide(map, "mc-x86-disable-arith-relaxation");
hide(map, "mips16-constant-islands");
hide(map, "mips16-hard-float");
hide(map, "mlsm");
hide(map, "mno-ldc1-sdc1");
hide(map, "nvptx-sched4reg");
hide(map, "no-discriminators");
hide(map, "objc-arc-annotation-target-identifier"), hide(map, "pre-RA-sched");
hide(map, "print-after-all");
hide(map, "print-before-all");
hide(map, "print-machineinstrs");
hide(map, "profile-estimator-loop-weight");
hide(map, "profile-estimator-loop-weight");
hide(map, "profile-file");
hide(map, "profile-info-file");
hide(map, "profile-verifier-noassert");
hide(map, "regalloc");
hide(map, "rewrite-map-file");
hide(map, "rng-seed");
hide(map, "sample-profile-max-propagate-iterations");
hide(map, "shrink-wrap");
hide(map, "spiller");
hide(map, "stackmap-version");
hide(map, "stats");
hide(map, "strip-debug");
hide(map, "struct-path-tbaa");
hide(map, "time-passes");
hide(map, "unit-at-a-time");
hide(map, "verify-debug-info");
hide(map, "verify-dom-info");
hide(map, "verify-loop-info");
hide(map, "verify-regalloc");
hide(map, "verify-region-info");
hide(map, "verify-scev");
hide(map, "x86-early-ifcvt");
hide(map, "x86-use-vzeroupper");
hide(map, "x86-recip-refinement-steps");
// We enable -fdata-sections/-ffunction-sections by default where it makes
// sense for reducing code size, so hide them to avoid confusion.
//
// We need our own switch as these two are defined by LLVM and linked to
// static TargetMachine members, but the default we want to use depends
// on the target triple (and thus we do not know it until after the command
// line has been parsed).
hide(map, "fdata-sections");
hide(map, "ffunction-sections");
#if LDC_LLVM_VER >= 307
// LLVM 3.7 introduces compiling as shared library. The result
// is a clash in the command line options.
rename(map, "color", "llvm-color");
hide(map, "llvm-color");
opts::CreateColorOption();
#endif
}
const char *tryGetExplicitConfFile(int argc, char **argv) {
// begin at the back => use latest -conf= specification
for (int i = argc - 1; i >= 1; --i) {
if (strncmp(argv[i], "-conf=", 6) == 0) {
return argv[i] + 6;
}
}
return nullptr;
}
llvm::Triple tryGetExplicitTriple(int argc, char **argv) {
// most combinations of flags are illegal, this mimicks command line
// behaviour for legal ones only
llvm::Triple triple(llvm::sys::getDefaultTargetTriple());
const char *mtriple = nullptr;
const char *march = nullptr;
for (int i = 1; i < argc; ++i) {
if (sizeof(void *) != 4 && strcmp(argv[i], "-m32") == 0) {
triple = triple.get32BitArchVariant();
if (triple.getArch() == llvm::Triple::ArchType::x86)
triple.setArchName("i686"); // instead of i386
return triple;
}
if (sizeof(void *) != 8 && strcmp(argv[i], "-m64") == 0)
return triple.get64BitArchVariant();
if (strncmp(argv[i], "-mtriple=", 9) == 0)
mtriple = argv[i] + 9;
else if (strncmp(argv[i], "-march=", 7) == 0)
march = argv[i] + 7;
}
if (mtriple)
triple = llvm::Triple(llvm::Triple::normalize(mtriple));
if (march) {
std::string errorMsg; // ignore error, will show up later anyway
lookupTarget(march, triple, errorMsg); // modifies triple
}
return triple;
}
/// Parses switches from the command line, any response files and the global
/// config file and sets up global.params accordingly.
///
/// Returns a list of source file names.
void parseCommandLine(int argc, char **argv, Strings &sourceFiles,
bool &helpOnly) {
global.params.argv0 = exe_path::getExePath().data();
// Set some default values.
global.params.useSwitchError = 1;
global.params.color = isConsoleColorSupported();
global.params.linkswitches = new Strings();
global.params.libfiles = new Strings();
global.params.objfiles = new Strings();
global.params.ddocfiles = new Strings();
global.params.bitcodeFiles = new Strings();
global.params.moduleDeps = nullptr;
global.params.moduleDepsFile = nullptr;
// Build combined list of command line arguments.
llvm::SmallVector<const char *, 32> final_args;
final_args.push_back(argv[0]);
ConfigFile cfg_file;
const char *explicitConfFile = tryGetExplicitConfFile(argc, argv);
std::string cfg_triple = tryGetExplicitTriple(argc, argv).getTriple();
// just ignore errors for now, they are still printed
cfg_file.read(explicitConfFile, cfg_triple.c_str());
final_args.insert(final_args.end(), cfg_file.switches_begin(),
cfg_file.switches_end());
final_args.insert(final_args.end(), &argv[1], &argv[argc]);
cl::SetVersionPrinter(&printVersion);
hideLLVMOptions();
// pre-expand response files (LLVM's ParseCommandLineOptions() always uses
// TokenizeGNUCommandLine which eats backslashes)
#if LDC_LLVM_VER >= 308
llvm::BumpPtrAllocator A;
llvm::StringSaver Saver(A);
cl::ExpandResponseFiles(Saver,
#ifdef _WIN32
cl::TokenizeWindowsCommandLine
#else
cl::TokenizeGNUCommandLine
#endif
,
final_args);
#endif
cl::ParseCommandLineOptions(final_args.size(),
const_cast<char **>(final_args.data()),
"LDC - the LLVM D compiler\n");
helpOnly = mCPU == "help" ||
(std::find(mAttrs.begin(), mAttrs.end(), "help") != mAttrs.end());
// Print some information if -v was passed
// - path to compiler binary
// - version number
// - used config file
if (global.params.verbose) {
fprintf(global.stdmsg, "binary %s\n", exe_path::getExePath().c_str());
fprintf(global.stdmsg, "version %s (DMD %s, LLVM %s)\n",
global.ldc_version, global.version, global.llvm_version);
const std::string &path = cfg_file.path();
if (!path.empty()) {
fprintf(global.stdmsg, "config %s\n", path.c_str());
}
}
// Negated options
global.params.link = !compileOnly;
global.params.obj = !dontWriteObj;
global.params.useInlineAsm = !noAsm;
// String options: std::string --> char*
initFromPathString(global.params.objname, objectFile);
initFromPathString(global.params.objdir, objectDir);
initFromPathString(global.params.docdir, ddocDir);
initFromPathString(global.params.docname, ddocFile);
global.params.doDocComments |= global.params.docdir || global.params.docname;
initFromPathString(global.params.jsonfilename, jsonFile);
if (global.params.jsonfilename) {
global.params.doJsonGeneration = true;
}
initFromPathString(global.params.hdrdir, hdrDir);
initFromPathString(global.params.hdrname, hdrFile);
global.params.doHdrGeneration |=
global.params.hdrdir || global.params.hdrname;
if (moduleDeps.getNumOccurrences() != 0) {
global.params.moduleDeps = new OutBuffer;
if (!moduleDeps.empty())
global.params.moduleDepsFile = dupPathString(moduleDeps);
}
#if _WIN32
const auto toWinPaths = [](Strings *paths) {
if (!paths)
return;
for (unsigned i = 0; i < paths->dim; ++i)
(*paths)[i] = dupPathString((*paths)[i]);
};
toWinPaths(global.params.imppath);
toWinPaths(global.params.fileImppath);
#endif
// PGO options
#if LDC_WITH_PGO
if (genfileInstrProf.getNumOccurrences() > 0) {
global.params.genInstrProf = true;
if (genfileInstrProf.empty()) {
#if LDC_LLVM_VER >= 309
// profile-rt provides a default filename by itself
global.params.datafileInstrProf = nullptr;
#else
global.params.datafileInstrProf = "default.profraw";
#endif
} else {
initFromPathString(global.params.datafileInstrProf, genfileInstrProf);
}
} else {
global.params.genInstrProf = false;
// If we don't have to generate instrumentation, we could be given a
// profdata file:
initFromPathString(global.params.datafileInstrProf, usefileInstrProf);
}
#endif
processVersions(debugArgs, "debug", DebugCondition::setGlobalLevel,
DebugCondition::addGlobalIdent);
processVersions(versions, "version", VersionCondition::setGlobalLevel,
VersionCondition::addGlobalIdent);
processTransitions(transitions);
global.params.output_o =
(opts::output_o == cl::BOU_UNSET &&
!(opts::output_bc || opts::output_ll || opts::output_s))
? OUTPUTFLAGdefault
: opts::output_o == cl::BOU_TRUE ? OUTPUTFLAGset : OUTPUTFLAGno;
global.params.output_bc = opts::output_bc ? OUTPUTFLAGset : OUTPUTFLAGno;
global.params.output_ll = opts::output_ll ? OUTPUTFLAGset : OUTPUTFLAGno;
global.params.output_s = opts::output_s ? OUTPUTFLAGset : OUTPUTFLAGno;
global.params.cov = (global.params.covPercent <= 100);
templateLinkage = opts::linkonceTemplates ? LLGlobalValue::LinkOnceODRLinkage
: LLGlobalValue::WeakODRLinkage;
if (global.params.run || !runargs.empty()) {
// FIXME: how to properly detect the presence of a PositionalEatsArgs
// option without parameters? We want to emit an error in that case...
// You'd think getNumOccurrences would do it, but it just returns the
// number of parameters)
// NOTE: Hacked around it by detecting -run in getenv_setargv(), where
// we're looking for it anyway, and pre-setting the flag...
global.params.run = true;
if (!runargs.empty()) {
char const *name = runargs[0].c_str();
char const *ext = FileName::ext(name);
if (ext && FileName::equals(ext, "d") == 0 &&
FileName::equals(ext, "di") == 0) {
error(Loc(), "-run must be followed by a source file, not '%s'", name);
}
sourceFiles.push(mem.xstrdup(name));
runargs.erase(runargs.begin());
} else {
global.params.run = false;
error(Loc(), "Expected at least one argument to '-run'\n");
}
}
sourceFiles.reserve(fileList.size());
for (const auto &file : fileList) {
if (!file.empty()) {
char *copy = dupPathString(file);
sourceFiles.push(copy);
}
}
if (noDefaultLib) {
deprecation(
Loc(),
"-nodefaultlib is deprecated, as "
"-defaultlib/-debuglib now override the existing list instead of "
"appending to it. Please use the latter instead.");
} else {
// Parse comma-separated default library list.
std::stringstream libNames(linkDebugLib ? debugLib : defaultLib);
while (libNames.good()) {
std::string lib;
std::getline(libNames, lib, ',');
if (lib.empty()) {
continue;
}
char *arg = static_cast<char *>(mem.xmalloc(lib.size() + 3));
strcpy(arg, "-l");
strcpy(arg + 2, lib.c_str());
global.params.linkswitches->push(arg);
}
}
if (global.params.useUnitTests) {
global.params.useAssert = 1;
}
// -release downgrades default bounds checking level to BOUNDSCHECKsafeonly
// (only for safe functions).
global.params.useArrayBounds =
opts::nonSafeBoundsChecks ? BOUNDSCHECKon : BOUNDSCHECKsafeonly;
if (opts::boundsCheck != BOUNDSCHECKdefault) {
global.params.useArrayBounds = opts::boundsCheck;
}
// LDC output determination
// if we don't link, autodetect target from extension
if (!global.params.link && !global.params.lib && global.params.objname) {
const char *ext = FileName::ext(global.params.objname);
bool autofound = false;
if (!ext) {
// keep things as they are
} else if (strcmp(ext, global.ll_ext) == 0) {
global.params.output_ll = OUTPUTFLAGset;
autofound = true;
} else if (strcmp(ext, global.bc_ext) == 0) {
global.params.output_bc = OUTPUTFLAGset;
autofound = true;
} else if (strcmp(ext, global.s_ext) == 0) {
global.params.output_s = OUTPUTFLAGset;
autofound = true;
} else if (strcmp(ext, global.obj_ext) == 0 || strcmp(ext, "obj") == 0) {
// global.obj_ext hasn't been corrected yet for MSVC targets as we first
// need the command line to figure out the target...
// so treat both 'o' and 'obj' extensions as object files
global.params.output_o = OUTPUTFLAGset;
autofound = true;
} else {
// append dot, so forceExt won't change existing name even if it contains
// dots
size_t len = strlen(global.params.objname);
char *s = static_cast<char *>(mem.xmalloc(len + 1 + 1));
memcpy(s, global.params.objname, len);
s[len] = '.';
s[len + 1] = 0;
global.params.objname = s;
}
if (autofound && global.params.output_o == OUTPUTFLAGdefault) {
global.params.output_o = OUTPUTFLAGno;
}
}
// only link if possible
if (!global.params.obj || !global.params.output_o || global.params.lib) {
global.params.link = 0;
}
if (global.params.lib && global.params.dll) {
error(Loc(), "-lib and -shared switches cannot be used together");
}
#if LDC_LLVM_VER >= 309
if (global.params.dll && !mRelocModel.getNumOccurrences()) {
#else
if (global.params.dll && mRelocModel == llvm::Reloc::Default) {
#endif
mRelocModel = llvm::Reloc::PIC_;
}
if (global.params.link) {
global.params.exefile = global.params.objname;
global.params.oneobj = true;
if (global.params.objname) {
/* Use this to name the one object file with the same
* name as the exe file.
*/
global.params.objname =
FileName::forceExt(global.params.objname, global.obj_ext);
/* If output directory is given, use that path rather than
* the exe file path.
*/
if (global.params.objdir) {
const char *name = FileName::name(global.params.objname);
global.params.objname = FileName::combine(global.params.objdir, name);
}
}
} else if (global.params.run) {
error(Loc(), "flags conflict with -run");
fatal();
} else if (global.params.lib) {
global.params.libname = global.params.objname;
global.params.objname = nullptr;
} else {
if (global.params.objname && sourceFiles.dim > 1) {
global.params.oneobj = true;
// error("multiple source files, but only one .obj name");
// fatal();
}
}
if (soname.getNumOccurrences() > 0 && !global.params.dll) {
error(Loc(), "-soname can be used only when building a shared library");
}
}
void initializePasses() {
using namespace llvm;
// Initialize passes
PassRegistry &Registry = *PassRegistry::getPassRegistry();
initializeCore(Registry);
initializeTransformUtils(Registry);
initializeScalarOpts(Registry);
initializeObjCARCOpts(Registry);
initializeVectorization(Registry);
initializeInstCombine(Registry);
initializeIPO(Registry);
initializeInstrumentation(Registry);
initializeAnalysis(Registry);
initializeCodeGen(Registry);
#if LDC_LLVM_VER >= 309
initializeGlobalISel(Registry);
#endif
initializeTarget(Registry);
// Initialize passes not included above
#if LDC_LLVM_VER < 306
initializeDebugIRPass(Registry);
#endif
#if LDC_LLVM_VER < 308
initializeIPA(Registry);
#endif
#if LDC_LLVM_VER >= 400
initializeRewriteSymbolsLegacyPassPass(Registry);
#elif LDC_LLVM_VER >= 306
initializeRewriteSymbolsPass(Registry);
#endif
#if LDC_LLVM_VER >= 307
initializeSjLjEHPreparePass(Registry);
#endif
}
/// Register the MIPS ABI.
static void registerMipsABI() {
switch (getMipsABI()) {
case MipsABI::EABI:
VersionCondition::addPredefinedGlobalIdent("MIPS_EABI");
break;
case MipsABI::O32:
VersionCondition::addPredefinedGlobalIdent("MIPS_O32");
break;
case MipsABI::N32:
VersionCondition::addPredefinedGlobalIdent("MIPS_N32");
break;
case MipsABI::N64:
VersionCondition::addPredefinedGlobalIdent("MIPS_N64");
break;
case MipsABI::Unknown:
break;
}
}
/// Register the float ABI.
/// Also defines D_HardFloat or D_SoftFloat depending if FPU should be used
void registerPredefinedFloatABI(const char *soft, const char *hard,
const char *softfp = nullptr) {
// Use target floating point unit instead of s/w float routines
#if LDC_LLVM_VER >= 307
// FIXME: This is a semantic change!
bool useFPU = gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard;
#else
bool useFPU = !gTargetMachine->Options.UseSoftFloat;
#endif
VersionCondition::addPredefinedGlobalIdent(useFPU ? "D_HardFloat"
: "D_SoftFloat");
if (gTargetMachine->Options.FloatABIType == llvm::FloatABI::Soft) {
VersionCondition::addPredefinedGlobalIdent(useFPU && softfp ? softfp
: soft);
} else if (gTargetMachine->Options.FloatABIType == llvm::FloatABI::Hard) {
assert(useFPU && "Should be using the FPU if using float-abi=hard");
VersionCondition::addPredefinedGlobalIdent(hard);
} else {
assert(0 && "FloatABIType neither Soft or Hard");
}
}
/// Registers the predefined versions specific to the current target triple
/// and other target specific options with VersionCondition.
void registerPredefinedTargetVersions() {
switch (global.params.targetTriple->getArch()) {
case llvm::Triple::x86:
VersionCondition::addPredefinedGlobalIdent("X86");
if (global.params.useInlineAsm) {
VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86");
}
VersionCondition::addPredefinedGlobalIdent("D_HardFloat");
break;
case llvm::Triple::x86_64:
VersionCondition::addPredefinedGlobalIdent("X86_64");
if (global.params.useInlineAsm) {
VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86_64");
}
VersionCondition::addPredefinedGlobalIdent("D_HardFloat");
break;
case llvm::Triple::ppc:
VersionCondition::addPredefinedGlobalIdent("PPC");
registerPredefinedFloatABI("PPC_SoftFloat", "PPC_HardFloat");
break;
case llvm::Triple::ppc64:
case llvm::Triple::ppc64le:
VersionCondition::addPredefinedGlobalIdent("PPC64");
registerPredefinedFloatABI("PPC_SoftFloat", "PPC_HardFloat");
if (global.params.targetTriple->getOS() == llvm::Triple::Linux) {
VersionCondition::addPredefinedGlobalIdent(
global.params.targetTriple->getArch() == llvm::Triple::ppc64
? "ELFv1"
: "ELFv2");
}
break;
case llvm::Triple::arm:
case llvm::Triple::armeb:
VersionCondition::addPredefinedGlobalIdent("ARM");
registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP");
break;
case llvm::Triple::thumb:
VersionCondition::addPredefinedGlobalIdent("ARM");
VersionCondition::addPredefinedGlobalIdent(
"Thumb"); // For backwards compatibility.
VersionCondition::addPredefinedGlobalIdent("ARM_Thumb");
registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP");
break;
#if LDC_LLVM_VER == 305
case llvm::Triple::arm64:
case llvm::Triple::arm64_be:
#endif
case llvm::Triple::aarch64:
case llvm::Triple::aarch64_be:
VersionCondition::addPredefinedGlobalIdent("AArch64");
registerPredefinedFloatABI("ARM_SoftFloat", "ARM_HardFloat", "ARM_SoftFP");
break;
case llvm::Triple::mips:
case llvm::Triple::mipsel:
VersionCondition::addPredefinedGlobalIdent("MIPS");
registerPredefinedFloatABI("MIPS_SoftFloat", "MIPS_HardFloat");
registerMipsABI();
break;
case llvm::Triple::mips64:
case llvm::Triple::mips64el:
VersionCondition::addPredefinedGlobalIdent("MIPS64");
registerPredefinedFloatABI("MIPS_SoftFloat", "MIPS_HardFloat");
registerMipsABI();
break;
case llvm::Triple::sparc:
// FIXME: Detect SPARC v8+ (SPARC_V8Plus).
VersionCondition::addPredefinedGlobalIdent("SPARC");
registerPredefinedFloatABI("SPARC_SoftFloat", "SPARC_HardFloat");
break;
case llvm::Triple::sparcv9:
VersionCondition::addPredefinedGlobalIdent("SPARC64");
registerPredefinedFloatABI("SPARC_SoftFloat", "SPARC_HardFloat");
break;
case llvm::Triple::nvptx:
VersionCondition::addPredefinedGlobalIdent("NVPTX");
VersionCondition::addPredefinedGlobalIdent("D_HardFloat");
break;
case llvm::Triple::nvptx64:
VersionCondition::addPredefinedGlobalIdent("NVPTX64");
VersionCondition::addPredefinedGlobalIdent("D_HardFloat");
break;
case llvm::Triple::systemz:
VersionCondition::addPredefinedGlobalIdent("SystemZ");
VersionCondition::addPredefinedGlobalIdent(
"S390X"); // For backwards compatibility.
VersionCondition::addPredefinedGlobalIdent("D_HardFloat");
break;
default:
error(Loc(), "invalid cpu architecture specified: %s",
global.params.targetTriple->getArchName().str().c_str());
fatal();
}
// endianness
if (gDataLayout->isLittleEndian()) {
VersionCondition::addPredefinedGlobalIdent("LittleEndian");
} else {
VersionCondition::addPredefinedGlobalIdent("BigEndian");
}
// a generic 64bit version
if (global.params.isLP64) {
VersionCondition::addPredefinedGlobalIdent("D_LP64");
}
if (gTargetMachine->getRelocationModel() == llvm::Reloc::PIC_) {
VersionCondition::addPredefinedGlobalIdent("D_PIC");
}
// parse the OS out of the target triple
// see http://gcc.gnu.org/install/specific.html for details
// also llvm's different SubTargets have useful information
switch (global.params.targetTriple->getOS()) {
case llvm::Triple::Win32:
VersionCondition::addPredefinedGlobalIdent("Windows");
VersionCondition::addPredefinedGlobalIdent(global.params.is64bit ? "Win64"
: "Win32");
if (global.params.targetTriple->isKnownWindowsMSVCEnvironment()) {
VersionCondition::addPredefinedGlobalIdent("CRuntime_Microsoft");
}
if (global.params.targetTriple->isWindowsGNUEnvironment()) {
VersionCondition::addPredefinedGlobalIdent(
"mingw32"); // For backwards compatibility.
VersionCondition::addPredefinedGlobalIdent("MinGW");
}
if (global.params.targetTriple->isWindowsCygwinEnvironment()) {
error(Loc(), "Cygwin is not yet supported");
fatal();
VersionCondition::addPredefinedGlobalIdent("Cygwin");
}
break;
case llvm::Triple::Linux:
VersionCondition::addPredefinedGlobalIdent("linux");
VersionCondition::addPredefinedGlobalIdent("Posix");
if (global.params.targetTriple->getEnvironment() == llvm::Triple::Android) {
VersionCondition::addPredefinedGlobalIdent("Android");
VersionCondition::addPredefinedGlobalIdent("CRuntime_Bionic");
} else {
VersionCondition::addPredefinedGlobalIdent("CRuntime_Glibc");
}
break;
case llvm::Triple::Haiku:
VersionCondition::addPredefinedGlobalIdent("Haiku");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::Darwin:
case llvm::Triple::MacOSX:
VersionCondition::addPredefinedGlobalIdent("OSX");
VersionCondition::addPredefinedGlobalIdent(
"darwin"); // For backwards compatibility.
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::FreeBSD:
VersionCondition::addPredefinedGlobalIdent("FreeBSD");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::Solaris:
VersionCondition::addPredefinedGlobalIdent("Solaris");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::DragonFly:
VersionCondition::addPredefinedGlobalIdent("DragonFlyBSD");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::NetBSD:
VersionCondition::addPredefinedGlobalIdent("NetBSD");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::OpenBSD:
VersionCondition::addPredefinedGlobalIdent("OpenBSD");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
case llvm::Triple::AIX:
VersionCondition::addPredefinedGlobalIdent("AIX");
VersionCondition::addPredefinedGlobalIdent("Posix");
break;
default:
switch (global.params.targetTriple->getEnvironment()) {
case llvm::Triple::Android:
VersionCondition::addPredefinedGlobalIdent("Android");
break;
default:
error(Loc(), "target '%s' is not yet supported",
global.params.targetTriple->str().c_str());
fatal();
}
}
}
/// Registers all predefined D version identifiers for the current
/// configuration with VersionCondition.
void registerPredefinedVersions() {
VersionCondition::addPredefinedGlobalIdent("LDC");
VersionCondition::addPredefinedGlobalIdent("all");
VersionCondition::addPredefinedGlobalIdent("D_Version2");
if (global.params.doDocComments) {
VersionCondition::addPredefinedGlobalIdent("D_Ddoc");
}
if (global.params.cov) {
VersionCondition::addPredefinedGlobalIdent("D_Coverage");
}
if (global.params.useUnitTests) {
VersionCondition::addPredefinedGlobalIdent("unittest");
}
if (global.params.useAssert) {
VersionCondition::addPredefinedGlobalIdent("assert");
}
if (global.params.useArrayBounds == BOUNDSCHECKoff) {
VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks");
}
registerPredefinedTargetVersions();
if (global.params.hasObjectiveC) {
VersionCondition::addPredefinedGlobalIdent("D_ObjectiveC");
}
// Pass sanitizer arguments to linker. Requires clang.
if (opts::sanitize == opts::AddressSanitizer) {
VersionCondition::addPredefinedGlobalIdent("LDC_AddressSanitizer");
}
if (opts::sanitize == opts::MemorySanitizer) {
VersionCondition::addPredefinedGlobalIdent("LDC_MemorySanitizer");
}
if (opts::sanitize == opts::ThreadSanitizer) {
VersionCondition::addPredefinedGlobalIdent("LDC_ThreadSanitizer");
}
// Expose LLVM version to runtime
#define STR(x) #x
#define XSTR(x) STR(x)
VersionCondition::addPredefinedGlobalIdent("LDC_LLVM_" XSTR(LDC_LLVM_VER));
#undef XSTR
#undef STR
}
/// Dump all predefined version identifiers.
void dumpPredefinedVersions() {
if (global.params.verbose && global.params.versionids) {
fprintf(global.stdmsg, "predefs ");
int col = 10;
for (auto id : *global.params.versionids) {
int len = strlen(id) + 1;
if (col + len > 80) {
col = 10;
fprintf(global.stdmsg, "\n ");
}
col += len;
fprintf(global.stdmsg, " %s", id);
}
fprintf(global.stdmsg, "\n");
}
}
} // anonymous namespace
int cppmain(int argc, char **argv) {
#if LDC_LLVM_VER >= 309
llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
#else
llvm::sys::PrintStackTraceOnErrorSignal();
#endif
exe_path::initialize(argv[0]);
global._init();
global.version = ldc::dmd_version;
global.ldc_version = ldc::ldc_version;
global.llvm_version = ldc::llvm_version;
// Initialize LLVM before parsing the command line so that --version shows
// registered targets.
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllAsmParsers();
initializePasses();
bool helpOnly;
Strings files;
parseCommandLine(argc, argv, files, helpOnly);
if (files.dim == 0 && !helpOnly) {
cl::PrintHelpMessage();
return EXIT_FAILURE;
}
if (global.errors) {
fatal();
}
// Set up the TargetMachine.
ExplicitBitness::Type bitness = ExplicitBitness::None;
if ((m32bits || m64bits) && (!mArch.empty() || !mTargetTriple.empty())) {
error(Loc(), "-m32 and -m64 switches cannot be used together with -march "
"and -mtriple switches");
}
if (m32bits) {
bitness = ExplicitBitness::M32;
}
if (m64bits) {
if (bitness != ExplicitBitness::None) {
error(Loc(), "cannot use both -m32 and -m64 options");
}
bitness = ExplicitBitness::M64;
}
if (global.errors) {
fatal();
}
gTargetMachine = createTargetMachine(
mTargetTriple, mArch, mCPU, mAttrs, bitness, mFloatABI, getRelocModel(),
mCodeModel, codeGenOptLevel(), disableFpElim, disableLinkerStripDead);
#if LDC_LLVM_VER >= 308
static llvm::DataLayout DL = gTargetMachine->createDataLayout();
gDataLayout = &DL;
#elif LDC_LLVM_VER >= 307
gDataLayout = gTargetMachine->getDataLayout();
#elif LDC_LLVM_VER >= 306
gDataLayout = gTargetMachine->getSubtargetImpl()->getDataLayout();
#else
gDataLayout = gTargetMachine->getDataLayout();
#endif
{
llvm::Triple *triple = new llvm::Triple(gTargetMachine->getTargetTriple());
global.params.targetTriple = triple;
global.params.isWindows = triple->isOSWindows();
global.params.isLP64 = gDataLayout->getPointerSizeInBits() == 64;
global.params.is64bit = triple->isArch64Bit();
global.params.hasObjectiveC = objc_isSupported(*triple);
// mscoff enables slightly different handling of interface functions
// in the front end
global.params.mscoff = triple->isKnownWindowsMSVCEnvironment();
if (global.params.mscoff)
global.obj_ext = "obj";
}
// allocate the target abi
gABI = TargetABI::getTarget();
// Set predefined version identifiers.
registerPredefinedVersions();
dumpPredefinedVersions();
if (global.params.targetTriple->isOSWindows()) {
global.dll_ext = "dll";
global.lib_ext =
(global.params.targetTriple->isWindowsMSVCEnvironment() ? "lib" : "a");
} else {
global.dll_ext = "so";
global.lib_ext = "a";
}
Strings libmodules;
return mars_mainBody(files, libmodules);
}
void codegenModules(Modules &modules) {
// Generate one or more object/IR/bitcode files.
if (global.params.obj && !modules.empty()) {
ldc::CodeGenerator cg(getGlobalContext(), global.params.oneobj);
// When inlining is enabled, we are calling semantic3 on function
// declarations, which may _add_ members to the first module in the modules
// array. These added functions must be codegenned, because these functions
// may be "alwaysinline" and linker problems arise otherwise with templates
// that have __FILE__ as parameters (which must be `pragma(inline, true);`)
// Therefore, codegen is done in reverse order with members[0] last, to make
// sure these functions (added to members[0] by members[x>0]) are
// codegenned.
for (d_size_t i = modules.dim; i-- > 0;) {
Module *const m = modules[i];
if (global.params.verbose)
fprintf(global.stdmsg, "code %s\n", m->toChars());
cg.emit(m);
if (global.errors)
fatal();
}
}
freeRuntime();
llvm::llvm_shutdown();
}