mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-07 03:16:05 +03:00
1284 lines
30 KiB
C
1284 lines
30 KiB
C
// Compiler implementation of the D programming language
|
|
// Copyright (c) 1999-2008 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// License for redistribution is by either the Artistic License
|
|
// in artistic.txt, or the GNU General Public License in gnu.txt.
|
|
// See the included readme.txt for details.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <string>
|
|
#include <cstdarg>
|
|
|
|
#if __DMC__
|
|
#include <dos.h>
|
|
#endif
|
|
|
|
#if POSIX
|
|
#include <errno.h>
|
|
#elif _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "mem.h"
|
|
#include "root.h"
|
|
|
|
#include "mars.h"
|
|
#include "module.h"
|
|
#include "mtype.h"
|
|
#include "id.h"
|
|
#include "cond.h"
|
|
#include "expression.h"
|
|
#include "lexer.h"
|
|
|
|
#include "gen/logger.h"
|
|
#include "gen/linker.h"
|
|
|
|
void getenv_setargv(const char *envvar, int *pargc, char** *pargv);
|
|
|
|
Global global;
|
|
|
|
Global::Global()
|
|
{
|
|
mars_ext = "d";
|
|
sym_ext = "d";
|
|
hdr_ext = "di";
|
|
doc_ext = "html";
|
|
ddoc_ext = "ddoc";
|
|
|
|
// LLVMDC
|
|
obj_ext = "bc";
|
|
ll_ext = "ll";
|
|
bc_ext = "bc";
|
|
#if _WIN32
|
|
nativeobj_ext = "obj";
|
|
#elif POSIX
|
|
nativeobj_ext = "o";
|
|
#else
|
|
#error "fix this"
|
|
#endif
|
|
|
|
copyright = "Copyright (c) 1999-2008 by Digital Mars and Tomas Lindquist Olsen";
|
|
written = "written by Walter Bright and Tomas Lindquist Olsen";
|
|
version = "v1.034";
|
|
llvmdc_version = "0.1";
|
|
global.structalign = 8;
|
|
|
|
memset(¶ms, 0, sizeof(Param));
|
|
}
|
|
|
|
char *Loc::toChars() const
|
|
{
|
|
OutBuffer buf;
|
|
char *p;
|
|
|
|
if (filename)
|
|
{
|
|
buf.printf("%s", filename);
|
|
}
|
|
|
|
if (linnum)
|
|
buf.printf("(%d)", linnum);
|
|
buf.writeByte(0);
|
|
return (char *)buf.extractData();
|
|
}
|
|
|
|
Loc::Loc(Module *mod, unsigned linnum)
|
|
{
|
|
this->linnum = linnum;
|
|
this->filename = mod ? mod->srcfile->toChars() : NULL;
|
|
}
|
|
|
|
/**************************************
|
|
* Print error message and exit.
|
|
*/
|
|
|
|
void error(Loc loc, const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
verror(loc, format, ap);
|
|
va_end( ap );
|
|
}
|
|
|
|
void verror(Loc loc, const char *format, va_list ap)
|
|
{
|
|
if (!global.gag)
|
|
{
|
|
char *p = loc.toChars();
|
|
|
|
if (*p)
|
|
fprintf(stdmsg, "%s: ", p);
|
|
mem.free(p);
|
|
|
|
fprintf(stdmsg, "Error: ");
|
|
vfprintf(stdmsg, format, ap);
|
|
fprintf(stdmsg, "\n");
|
|
fflush(stdmsg);
|
|
}
|
|
global.errors++;
|
|
}
|
|
|
|
/***************************************
|
|
* Call this after printing out fatal error messages to clean up and exit
|
|
* the compiler.
|
|
*/
|
|
|
|
void fatal()
|
|
{
|
|
#if 0
|
|
halt();
|
|
#endif
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/**************************************
|
|
* Try to stop forgetting to remove the breakpoints from
|
|
* release builds.
|
|
*/
|
|
void halt()
|
|
{
|
|
#ifdef DEBUG
|
|
*(char*)0=0;
|
|
#endif
|
|
}
|
|
|
|
extern void backend_init();
|
|
extern void backend_term();
|
|
|
|
void usage()
|
|
{
|
|
printf("LLVM D Compiler %s (based on DMD %s and LLVM 2.4svn)\n%s\n%s\n",
|
|
global.llvmdc_version, global.version, global.copyright, global.written);
|
|
printf("\
|
|
D Language Documentation: http://www.digitalmars.com/d/1.0/index.html\n\
|
|
LLVMDC Homepage: http://www.dsource.org/projects/llvmdc\n\
|
|
Usage:\n\
|
|
llvmdc files.d ... { -switch }\n\
|
|
\n\
|
|
files.d D source files\n%s\
|
|
-of<filename> name output file to <filename>\n\
|
|
-o- do not write object file\n\
|
|
-od<objdir> write object files to directory <objdir>\n\
|
|
-op do not strip paths from source file\n\
|
|
-oq write object files with fully qualified names\n\
|
|
\n\
|
|
-c do not link\n\
|
|
-L<linkerflag> pass <linkerflag> to llvm-ld\n\
|
|
\n\
|
|
-g add symbolic debug info\n\
|
|
-gc add symbolic debug info, pretend to be C\n\
|
|
\n\
|
|
-w enable warnings\n\
|
|
\n\
|
|
-H generate 'header' file\n\
|
|
-Hd<hdrdir> write 'header' file to <hdrdir> directory\n\
|
|
-Hf<filename> write 'header' file to <filename>\n\
|
|
\n\
|
|
-D generate documentation\n\
|
|
-Dd<docdir> write documentation file to <docdir> directory\n\
|
|
-Df<filename> write documentation file to <filename>\n\
|
|
\n\
|
|
Codegen control:\n\
|
|
-m<arch> emit code specific to <arch> being one of:\n\
|
|
x86 x86-64 ppc32 ppc64\n\
|
|
-t<os> emit code specific to <os> being one of:\n\
|
|
Linux, Windows, MacOSX, FreeBSD\n\
|
|
\n\
|
|
-O optimize, same as -O2\n\
|
|
-O<n> optimize at level <n> (0-5)\n\
|
|
-inline do function inlining\n\
|
|
\n\
|
|
-debug enables asserts, invariants, contracts, boundscheck\n\
|
|
and sets debug=1\n\
|
|
-release disables asserts, invariants, contracts boundscheck\n\
|
|
\n\
|
|
-enable-<feature> and\n\
|
|
-disable-<feature> where <feature> is one of\n\
|
|
asserts assert statements (default: on)\n\
|
|
invariants class and struct invariants (default: on)\n\
|
|
contracts function contracts (default: on)\n\
|
|
boundscheck array bounds checking (default: on)\n\
|
|
-debug=level compile in debug stmts <= level (default: 0)\n\
|
|
-debug=ident compile in debug stmts identified by ident\n\
|
|
-version=level compile in version code >= level\n\
|
|
-version=ident compile in version code identified by ident\n\
|
|
\n\
|
|
-noasm do not allow use of inline asm\n\
|
|
-noruntime do not allow code that generates implicit runtime calls\n\
|
|
-noverify do not run the validation pass before writing bitcode\n\
|
|
-unittest compile in unit tests\n\
|
|
-d allow deprecated features\n\
|
|
\n\
|
|
-annotate annotate the bitcode with human readable source code\n\
|
|
-dis disassemble module after compiling\n\
|
|
-ignore ignore unsupported pragmas\n\
|
|
\n\
|
|
Path options:\n\
|
|
-I<path> where to look for imports\n\
|
|
-J<path> where to look for string imports\n\
|
|
-defaultlib=name set default library for non-debug build\n\
|
|
-debuglib=name set default library for debug build\n\
|
|
\n\
|
|
Misc options:\n\
|
|
-v verbose\n\
|
|
-vv very verbose (does not include -v)\n\
|
|
-quiet suppress unnecessary messages\n\
|
|
-run srcfile args... run resulting program, passing args\n\
|
|
--help print help\n\
|
|
",
|
|
#if WIN32
|
|
" @cmdfile read arguments from cmdfile\n"
|
|
#else
|
|
""
|
|
#endif
|
|
);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i;
|
|
Array files;
|
|
char *p;
|
|
Module *m;
|
|
int status = EXIT_SUCCESS;
|
|
int argcstart = argc;
|
|
bool very_verbose = false;
|
|
|
|
// Check for malformed input
|
|
if (argc < 1 || !argv)
|
|
{
|
|
Largs:
|
|
error("missing or null command line arguments");
|
|
fatal();
|
|
}
|
|
for (i = 0; i < argc; i++)
|
|
{
|
|
if (!argv[i])
|
|
goto Largs;
|
|
}
|
|
|
|
#if __DMC__ // DMC unique support for response files
|
|
if (response_expand(&argc,&argv)) // expand response files
|
|
error("can't open response file");
|
|
#endif
|
|
|
|
files.reserve(argc - 1);
|
|
|
|
// Set default values
|
|
#if _WIN32
|
|
char buf[MAX_PATH];
|
|
GetModuleFileName(NULL, buf, MAX_PATH);
|
|
global.params.argv0 = buf;
|
|
#else
|
|
global.params.argv0 = argv[0];
|
|
#endif
|
|
global.params.link = 1;
|
|
global.params.useAssert = 1;
|
|
global.params.useInvariants = 1;
|
|
global.params.useIn = 1;
|
|
global.params.useOut = 1;
|
|
global.params.useArrayBounds = 1;
|
|
global.params.useSwitchError = 1;
|
|
global.params.useInline = 0; // this one messes things up to a point where codegen breaks
|
|
global.params.llvmInline = 0; // use this one instead to know if inline passes should be run
|
|
global.params.obj = 1;
|
|
global.params.Dversion = 2;
|
|
global.params.quiet = 1;
|
|
|
|
global.params.linkswitches = new Array();
|
|
global.params.libfiles = new Array();
|
|
global.params.objfiles = new Array();
|
|
global.params.ddocfiles = new Array();
|
|
|
|
global.params.is64bit = sizeof(void*) == 8 ? 1 : 0;
|
|
|
|
uint16_t endiantest = 0xFF00;
|
|
uint8_t endianres = ((uint8_t*)&endiantest)[0];
|
|
if (endianres == 0x00)
|
|
global.params.isLE = true;
|
|
else if (endianres == 0xFF)
|
|
global.params.isLE = false;
|
|
else {
|
|
error("Endian test is broken");
|
|
fatal();
|
|
}
|
|
|
|
global.params.llvmArch = 0;
|
|
global.params.forceBE = 0;
|
|
global.params.noruntime = 0;
|
|
global.params.novalidate = 0;
|
|
global.params.optimizeLevel = -1;
|
|
global.params.runtimeImppath = 0;
|
|
global.params.useInlineAsm = 1;
|
|
|
|
// Predefine version identifiers
|
|
#if IN_LLVM
|
|
VersionCondition::addPredefinedGlobalIdent("LLVM");
|
|
VersionCondition::addPredefinedGlobalIdent("LLVMDC");
|
|
#endif
|
|
|
|
// setup default target os to be build os
|
|
#if _WIN32
|
|
global.params.os = OSWindows;
|
|
#elif linux
|
|
global.params.os = OSLinux;
|
|
#elif __APPLE__
|
|
global.params.os = OSMacOSX;
|
|
#elif __FreeBSD__
|
|
global.params.os = OSFreeBSD;
|
|
#else
|
|
#error Unsupported OS
|
|
#endif /* linux */
|
|
|
|
assert(global.params.os != OSinvalid);
|
|
|
|
//VersionCondition::addPredefinedGlobalIdent("D_Bits");
|
|
VersionCondition::addPredefinedGlobalIdent("all");
|
|
|
|
#if _WIN32
|
|
inifile(global.params.argv0, "llvmdc.ini");
|
|
#elif POSIX
|
|
inifile(global.params.argv0, "llvmdc.conf");
|
|
#else
|
|
#error
|
|
#endif
|
|
getenv_setargv("DFLAGS", &argc, &argv);
|
|
|
|
#if 0
|
|
for (i = 0; i < argc; i++)
|
|
{
|
|
printf("argv[%d] = '%s'\n", i, argv[i]);
|
|
}
|
|
#endif
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
p = argv[i];
|
|
if (*p == '-')
|
|
{
|
|
if (strcmp(p + 1, "d") == 0)
|
|
global.params.useDeprecated = 1;
|
|
else if (strcmp(p + 1, "c") == 0)
|
|
global.params.link = 0;
|
|
else if (strcmp(p + 1, "fPIC") == 0)
|
|
global.params.pic = 1;
|
|
else if (strcmp(p + 1, "g") == 0)
|
|
global.params.symdebug = 1;
|
|
else if (strcmp(p + 1, "gc") == 0)
|
|
global.params.symdebug = 2;
|
|
else if (strcmp(p + 1, "v") == 0)
|
|
global.params.verbose = 1;
|
|
else if (strcmp(p + 1, "vv") == 0) {
|
|
Logger::enable();
|
|
very_verbose = true;
|
|
}
|
|
else if (strcmp(p + 1, "v1") == 0)
|
|
global.params.Dversion = 1;
|
|
else if (strcmp(p + 1, "w") == 0)
|
|
global.params.warnings = 1;
|
|
else if (p[1] == 'O')
|
|
{
|
|
global.params.optimize = 1;
|
|
global.params.optimizeLevel = 2;
|
|
if (p[2] != 0) {
|
|
int optlevel = atoi(p+2);
|
|
if (optlevel < 0 || optlevel > 5) {
|
|
error("Optimization level must be between 0 and 5. Using default (%d)",
|
|
global.params.optimizeLevel);
|
|
}
|
|
else {
|
|
global.params.optimizeLevel = optlevel;
|
|
}
|
|
}
|
|
}
|
|
else if (strcmp(p + 1, "forcebe") == 0)
|
|
global.params.forceBE = 1;
|
|
else if (strcmp(p + 1, "noruntime") == 0)
|
|
global.params.noruntime = 1;
|
|
else if (strcmp(p + 1, "noverify") == 0)
|
|
global.params.novalidate = 1;
|
|
else if (strcmp(p + 1, "dis") == 0)
|
|
global.params.disassemble = 1;
|
|
else if (strcmp(p + 1, "annotate") == 0)
|
|
global.params.llvmAnnotate = 1;
|
|
else if (strncmp(p + 1, "enable-", 7) == 0 ||
|
|
strncmp(p + 1, "disable-", 8) == 0)
|
|
{
|
|
bool enable = (p[1] == 'e');
|
|
char* feature = p + 1 + (enable ? 7 : 8);
|
|
if (strcmp(feature, "asserts") == 0)
|
|
global.params.useAssert = enable;
|
|
else if (strcmp(feature, "boundscheck") == 0)
|
|
global.params.useArrayBounds = enable;
|
|
else if (strcmp(feature, "contracts") == 0)
|
|
{
|
|
global.params.useIn = enable;
|
|
global.params.useOut = enable;
|
|
}
|
|
else if (strcmp(feature, "invariants") == 0)
|
|
global.params.useInvariants = enable;
|
|
else
|
|
error("unrecognized feature '%s'", feature);
|
|
}
|
|
else if (strcmp(p + 1, "noasm") == 0)
|
|
global.params.useInlineAsm = 0;
|
|
else if (p[1] == 'o')
|
|
{
|
|
switch (p[2])
|
|
{
|
|
case '-':
|
|
global.params.obj = 0;
|
|
break;
|
|
|
|
case 'd':
|
|
if (!p[3])
|
|
goto Lnoarg;
|
|
global.params.objdir = p + 3;
|
|
break;
|
|
|
|
case 'f':
|
|
if (!p[3])
|
|
goto Lnoarg;
|
|
global.params.objname = p + 3;
|
|
break;
|
|
|
|
case 'p':
|
|
if (p[3])
|
|
goto Lerror;
|
|
global.params.preservePaths = 1;
|
|
break;
|
|
|
|
case 'q':
|
|
if (p[3])
|
|
goto Lerror;
|
|
global.params.fqnNames = 1;
|
|
break;
|
|
|
|
case 0:
|
|
error("-o no longer supported, use -of or -od");
|
|
break;
|
|
|
|
default:
|
|
goto Lerror;
|
|
}
|
|
}
|
|
else if (p[1] == 'D')
|
|
{ global.params.doDocComments = 1;
|
|
switch (p[2])
|
|
{
|
|
case 'd':
|
|
if (!p[3])
|
|
goto Lnoarg;
|
|
global.params.docdir = p + 3;
|
|
break;
|
|
case 'f':
|
|
if (!p[3])
|
|
goto Lnoarg;
|
|
global.params.docname = p + 3;
|
|
break;
|
|
|
|
case 0:
|
|
break;
|
|
|
|
default:
|
|
goto Lerror;
|
|
}
|
|
}
|
|
#ifdef _DH
|
|
else if (p[1] == 'H')
|
|
{ global.params.doHdrGeneration = 1;
|
|
switch (p[2])
|
|
{
|
|
case 'd':
|
|
if (!p[3])
|
|
goto Lnoarg;
|
|
global.params.hdrdir = p + 3;
|
|
break;
|
|
|
|
case 'f':
|
|
if (!p[3])
|
|
goto Lnoarg;
|
|
global.params.hdrname = p + 3;
|
|
break;
|
|
|
|
case 0:
|
|
break;
|
|
|
|
default:
|
|
goto Lerror;
|
|
}
|
|
}
|
|
#endif
|
|
else if (strcmp(p + 1, "ignore") == 0)
|
|
global.params.ignoreUnsupportedPragmas = 1;
|
|
else if (strcmp(p + 1, "inline") == 0) {
|
|
// TODO
|
|
// the ast rewrites dmd does for inlining messes up the ast.
|
|
// someday maybe we can support it, for now llvm does an excellent job at inlining
|
|
global.params.useInline = 0; //1
|
|
global.params.llvmInline = 1;
|
|
}
|
|
else if (strcmp(p + 1, "quiet") == 0)
|
|
global.params.quiet = 1;
|
|
else if (strcmp(p + 1, "release") == 0)
|
|
{
|
|
global.params.useInvariants = 0;
|
|
global.params.useIn = 0;
|
|
global.params.useOut = 0;
|
|
global.params.useAssert = 0;
|
|
global.params.useArrayBounds = 0;
|
|
}
|
|
else if (strcmp(p + 1, "unittest") == 0)
|
|
global.params.useUnitTests = 1;
|
|
else if (p[1] == 'I')
|
|
{
|
|
if (!global.params.imppath)
|
|
global.params.imppath = new Array();
|
|
global.params.imppath->push(p + 2);
|
|
}
|
|
else if (p[1] == 'J')
|
|
{
|
|
if (!global.params.fileImppath)
|
|
global.params.fileImppath = new Array();
|
|
global.params.fileImppath->push(p + 2);
|
|
}
|
|
else if (memcmp(p + 1, "debug", 5) == 0 && p[6] != 'l')
|
|
{
|
|
// Parse:
|
|
// -debug
|
|
// -debug=number
|
|
// -debug=identifier
|
|
if (p[6] == '=')
|
|
{
|
|
if (isdigit(p[7]))
|
|
{ long level;
|
|
|
|
errno = 0;
|
|
level = strtol(p + 7, &p, 10);
|
|
if (*p || errno || level > INT_MAX)
|
|
goto Lerror;
|
|
DebugCondition::setGlobalLevel((int)level);
|
|
}
|
|
else if (Lexer::isValidIdentifier(p + 7))
|
|
DebugCondition::addGlobalIdent(p + 7);
|
|
else
|
|
goto Lerror;
|
|
}
|
|
else if (p[6])
|
|
goto Lerror;
|
|
else
|
|
{
|
|
global.params.useInvariants = 1;
|
|
global.params.useIn = 1;
|
|
global.params.useOut = 1;
|
|
global.params.useAssert = 1;
|
|
global.params.useArrayBounds = 1;
|
|
global.params.debuglevel = 1;
|
|
}
|
|
}
|
|
else if (memcmp(p + 1, "version", 5) == 0)
|
|
{
|
|
// Parse:
|
|
// -version=number
|
|
// -version=identifier
|
|
if (p[8] == '=')
|
|
{
|
|
if (isdigit(p[9]))
|
|
{ long level;
|
|
|
|
errno = 0;
|
|
level = strtol(p + 9, &p, 10);
|
|
if (*p || errno || level > INT_MAX)
|
|
goto Lerror;
|
|
VersionCondition::setGlobalLevel((int)level);
|
|
}
|
|
else if (Lexer::isValidIdentifier(p + 9))
|
|
VersionCondition::addGlobalIdent(p + 9);
|
|
else
|
|
goto Lerror;
|
|
}
|
|
else
|
|
goto Lerror;
|
|
}
|
|
else if (strcmp(p + 1, "-b") == 0)
|
|
global.params.debugb = 1;
|
|
else if (strcmp(p + 1, "-c") == 0)
|
|
global.params.debugc = 1;
|
|
else if (strcmp(p + 1, "-f") == 0)
|
|
global.params.debugf = 1;
|
|
else if (strcmp(p + 1, "-help") == 0)
|
|
{ usage();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
else if (strcmp(p + 1, "-r") == 0)
|
|
global.params.debugr = 1;
|
|
else if (strcmp(p + 1, "-x") == 0)
|
|
global.params.debugx = 1;
|
|
else if (strcmp(p + 1, "-y") == 0)
|
|
global.params.debugy = 1;
|
|
else if (p[1] == 'L')
|
|
{
|
|
global.params.linkswitches->push(p + 2);
|
|
}
|
|
else if (memcmp(p + 1, "defaultlib=", 11) == 0)
|
|
{
|
|
if(!global.params.defaultlibnames)
|
|
global.params.defaultlibnames = new Array();
|
|
global.params.defaultlibnames->push(p + 1 + 11);
|
|
}
|
|
else if (memcmp(p + 1, "debuglib=", 9) == 0)
|
|
{
|
|
if(!global.params.debuglibnames)
|
|
global.params.debuglibnames = new Array();
|
|
global.params.debuglibnames->push(p + 1 + 9);
|
|
}
|
|
else if (strcmp(p + 1, "run") == 0)
|
|
{ global.params.run = 1;
|
|
global.params.runargs_length = ((i >= argcstart) ? argc : argcstart) - i - 1;
|
|
if (global.params.runargs_length)
|
|
{
|
|
files.push(argv[i + 1]);
|
|
global.params.runargs = &argv[i + 2];
|
|
i += global.params.runargs_length;
|
|
global.params.runargs_length--;
|
|
}
|
|
else
|
|
{ global.params.run = 0;
|
|
goto Lnoarg;
|
|
}
|
|
}
|
|
else if (p[1] == 'm')
|
|
{
|
|
global.params.llvmArch = p+2;
|
|
}
|
|
else if (p[1] == 't')
|
|
{
|
|
if(strcmp(p + 2, "Linux") == 0)
|
|
global.params.os = OSLinux;
|
|
else if(strcmp(p + 2, "Windows") == 0)
|
|
global.params.os = OSWindows;
|
|
else if(strcmp(p + 2, "MacOSX") == 0)
|
|
global.params.os = OSMacOSX;
|
|
else if(strcmp(p + 2, "FreeBSD") == 0)
|
|
global.params.os = OSFreeBSD;
|
|
else
|
|
error("unrecognized target os '%s'", p + 2);
|
|
}
|
|
else
|
|
{
|
|
Lerror:
|
|
error("unrecognized switch '%s'", argv[i]);
|
|
continue;
|
|
|
|
Lnoarg:
|
|
error("argument expected for switch '%s'", argv[i]);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
files.push(p);
|
|
}
|
|
if (global.errors)
|
|
{
|
|
fatal();
|
|
}
|
|
if (files.dim == 0)
|
|
{ usage();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
Array* libs;
|
|
if (global.params.symdebug)
|
|
libs = global.params.debuglibnames;
|
|
else
|
|
libs = global.params.defaultlibnames;
|
|
|
|
if (libs)
|
|
{
|
|
for (int i = 0; i < libs->dim; i++)
|
|
{
|
|
char *arg = (char *)mem.malloc(64);
|
|
strcpy(arg, "-l");
|
|
strncat(arg, (char *)libs->data[i], 64);
|
|
global.params.linkswitches->push(arg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char *arg;
|
|
arg = (char *)mem.malloc(64);
|
|
strcpy(arg, "-lllvmdc-runtime");
|
|
global.params.linkswitches->push(arg);
|
|
arg = (char *)mem.malloc(64);
|
|
strcpy(arg, "-ltango-cc-tango");
|
|
global.params.linkswitches->push(arg);
|
|
arg = (char *)mem.malloc(64);
|
|
strcpy(arg, "-ltango-gc-basic");
|
|
global.params.linkswitches->push(arg);
|
|
}
|
|
|
|
if (global.params.run)
|
|
global.params.quiet = 1;
|
|
|
|
if (global.params.useUnitTests)
|
|
global.params.useAssert = 1;
|
|
|
|
if (!global.params.obj)
|
|
global.params.link = 0;
|
|
|
|
if (global.params.link)
|
|
{
|
|
global.params.exefile = global.params.objname;
|
|
global.params.objname = NULL;
|
|
}
|
|
else if (global.params.run)
|
|
{
|
|
error("flags conflict with -run");
|
|
fatal();
|
|
}
|
|
else
|
|
{
|
|
if (global.params.objname && files.dim > 1)
|
|
{
|
|
error("multiple source files, but only one .obj name");
|
|
fatal();
|
|
}
|
|
}
|
|
|
|
bool allowForceEndianness = false;
|
|
|
|
if (global.params.llvmArch == 0) {
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
global.params.llvmArch = "x86-64";
|
|
#elif defined(__i386__) || defined(_M_IX86)
|
|
global.params.llvmArch = "x86";
|
|
#elif defined(__ppc__) || defined(_M_PPC)
|
|
if (global.params.is64bit)
|
|
global.params.llvmArch = "ppc64";
|
|
else
|
|
global.params.llvmArch = "ppc32";
|
|
#else
|
|
#error
|
|
#endif
|
|
}
|
|
|
|
if (strcmp(global.params.llvmArch,"x86")==0) {
|
|
VersionCondition::addPredefinedGlobalIdent("X86");
|
|
global.params.isLE = true;
|
|
global.params.is64bit = false;
|
|
global.params.cpu = ARCHx86;
|
|
global.params.tt_arch = "i686";
|
|
global.params.data_layout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-f80:32:32-v64:64:64-v128:128:128-a0:0:64";
|
|
if (global.params.useInlineAsm) {
|
|
VersionCondition::addPredefinedGlobalIdent("LLVM_InlineAsm_X86");
|
|
}
|
|
}
|
|
else if (strcmp(global.params.llvmArch,"x86-64")==0) {
|
|
VersionCondition::addPredefinedGlobalIdent("X86_64");
|
|
global.params.isLE = true;
|
|
global.params.is64bit = true;
|
|
global.params.cpu = ARCHx86_64;
|
|
global.params.tt_arch = "x86_64";
|
|
global.params.data_layout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64";
|
|
}
|
|
else if (strcmp(global.params.llvmArch,"ppc32")==0) {
|
|
VersionCondition::addPredefinedGlobalIdent("PPC");
|
|
global.params.isLE = false;
|
|
global.params.is64bit = false;
|
|
global.params.cpu = ARCHppc;
|
|
global.params.tt_arch = "powerpc";
|
|
global.params.data_layout = "E-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64";
|
|
}
|
|
else if (strcmp(global.params.llvmArch,"ppc64")==0) {
|
|
VersionCondition::addPredefinedGlobalIdent("PPC64");
|
|
global.params.isLE = false;
|
|
global.params.is64bit = true;
|
|
global.params.cpu = ARCHppc_64;
|
|
global.params.tt_arch = "powerpc64";
|
|
global.params.data_layout = "E-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64";
|
|
}
|
|
else {
|
|
assert(0 && "Invalid arch");
|
|
}
|
|
|
|
assert(global.params.cpu != ARCHinvalid);
|
|
|
|
if (allowForceEndianness && global.params.forceBE) {
|
|
VersionCondition::addPredefinedGlobalIdent("BigEndian");
|
|
global.params.isLE = false;
|
|
}
|
|
else if (global.params.isLE) {
|
|
VersionCondition::addPredefinedGlobalIdent("LittleEndian");
|
|
}
|
|
else {
|
|
VersionCondition::addPredefinedGlobalIdent("BigEndian");
|
|
}
|
|
|
|
if (global.params.is64bit) {
|
|
VersionCondition::addPredefinedGlobalIdent("LLVM64");
|
|
}
|
|
|
|
|
|
// setup version idents and tt_os for chosen target os
|
|
switch(global.params.os)
|
|
{
|
|
case OSWindows:
|
|
VersionCondition::addPredefinedGlobalIdent("Windows");
|
|
VersionCondition::addPredefinedGlobalIdent("Win32");
|
|
VersionCondition::addPredefinedGlobalIdent("mingw32");
|
|
global.params.tt_os = "-pc-mingw32";
|
|
break;
|
|
|
|
case OSLinux:
|
|
VersionCondition::addPredefinedGlobalIdent("linux");
|
|
VersionCondition::addPredefinedGlobalIdent("Posix");
|
|
global.params.tt_os = "-pc-linux-gnu";
|
|
break;
|
|
|
|
case OSMacOSX:
|
|
VersionCondition::addPredefinedGlobalIdent("darwin");
|
|
VersionCondition::addPredefinedGlobalIdent("Posix");
|
|
global.params.tt_os = "-pc-darwin-gnu";
|
|
break;
|
|
|
|
case OSFreeBSD:
|
|
VersionCondition::addPredefinedGlobalIdent("freebsd");
|
|
VersionCondition::addPredefinedGlobalIdent("Posix");
|
|
break;
|
|
|
|
default:
|
|
assert(false && "Target OS not supported");
|
|
}
|
|
|
|
|
|
// Initialization
|
|
Type::init();
|
|
Id::initialize();
|
|
Module::init();
|
|
initPrecedence();
|
|
|
|
backend_init();
|
|
|
|
//printf("%d source files\n",files.dim);
|
|
|
|
// Build import search path
|
|
if (global.params.imppath)
|
|
{
|
|
for (i = 0; i < global.params.imppath->dim; i++)
|
|
{
|
|
char *path = (char *)global.params.imppath->data[i];
|
|
Array *a = FileName::splitPath(path);
|
|
|
|
if (a)
|
|
{
|
|
if (!global.path)
|
|
global.path = new Array();
|
|
global.path->append(a);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Build string import search path
|
|
if (global.params.fileImppath)
|
|
{
|
|
for (i = 0; i < global.params.fileImppath->dim; i++)
|
|
{
|
|
char *path = (char *)global.params.fileImppath->data[i];
|
|
Array *a = FileName::splitPath(path);
|
|
|
|
if (a)
|
|
{
|
|
if (!global.filePath)
|
|
global.filePath = new Array();
|
|
global.filePath->append(a);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create Modules
|
|
Array modules;
|
|
modules.reserve(files.dim);
|
|
for (i = 0; i < files.dim; i++)
|
|
{ Identifier *id;
|
|
char *ext;
|
|
char *name;
|
|
|
|
p = (char *) files.data[i];
|
|
|
|
p = FileName::name(p); // strip path
|
|
ext = FileName::ext(p);
|
|
if (ext)
|
|
{
|
|
#if IN_LLVM
|
|
if (strcmp(ext, global.nativeobj_ext) == 0 ||
|
|
strcmp(ext, global.obj_ext) == 0)
|
|
#elif TARGET_LINUX
|
|
if (strcmp(ext, global.obj_ext) == 0)
|
|
#else
|
|
if (stricmp(ext, global.obj_ext) == 0)
|
|
#endif
|
|
{
|
|
global.params.objfiles->push(files.data[i]);
|
|
continue;
|
|
}
|
|
|
|
#if TARGET_LINUX || __MINGW32__
|
|
if (strcmp(ext, "a") == 0)
|
|
#else
|
|
if (stricmp(ext, "lib") == 0)
|
|
#endif
|
|
{
|
|
global.params.libfiles->push(files.data[i]);
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(ext, global.ddoc_ext) == 0)
|
|
{
|
|
global.params.ddocfiles->push(files.data[i]);
|
|
continue;
|
|
}
|
|
|
|
#if !TARGET_LINUX
|
|
if (stricmp(ext, "res") == 0)
|
|
{
|
|
global.params.resfile = (char *)files.data[i];
|
|
continue;
|
|
}
|
|
|
|
if (stricmp(ext, "def") == 0)
|
|
{
|
|
global.params.deffile = (char *)files.data[i];
|
|
continue;
|
|
}
|
|
|
|
if (stricmp(ext, "exe") == 0)
|
|
{
|
|
global.params.exefile = (char *)files.data[i];
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
if (stricmp(ext, global.mars_ext) == 0 ||
|
|
stricmp(ext, global.hdr_ext) == 0 ||
|
|
stricmp(ext, "htm") == 0 ||
|
|
stricmp(ext, "html") == 0 ||
|
|
stricmp(ext, "xhtml") == 0)
|
|
{
|
|
ext--; // skip onto '.'
|
|
assert(*ext == '.');
|
|
name = (char *)mem.malloc((ext - p) + 1);
|
|
memcpy(name, p, ext - p);
|
|
name[ext - p] = 0; // strip extension
|
|
|
|
if (name[0] == 0 ||
|
|
strcmp(name, "..") == 0 ||
|
|
strcmp(name, ".") == 0)
|
|
{
|
|
Linvalid:
|
|
error("invalid file name '%s'", (char *)files.data[i]);
|
|
fatal();
|
|
}
|
|
}
|
|
else
|
|
{ error("unrecognized file extension %s\n", ext);
|
|
fatal();
|
|
}
|
|
}
|
|
else
|
|
{ name = p;
|
|
if (!*name)
|
|
goto Linvalid;
|
|
}
|
|
|
|
id = new Identifier(name, 0);
|
|
m = new Module((char *) files.data[i], id, global.params.doDocComments, global.params.doHdrGeneration);
|
|
modules.push(m);
|
|
}
|
|
|
|
// Read files, parse them
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
if (global.params.verbose)
|
|
printf("parse %s\n", m->toChars());
|
|
if (!Module::rootModule)
|
|
Module::rootModule = m;
|
|
m->importedFrom = m;
|
|
m->read(0);
|
|
m->parse();
|
|
m->buildTargetFiles();
|
|
m->deleteObjFile();
|
|
if (m->isDocFile)
|
|
{
|
|
m->gendocfile();
|
|
|
|
// Remove m from list of modules
|
|
modules.remove(i);
|
|
i--;
|
|
}
|
|
}
|
|
if (global.errors)
|
|
fatal();
|
|
#ifdef _DH
|
|
if (global.params.doHdrGeneration)
|
|
{
|
|
/* Generate 'header' import files.
|
|
* Since 'header' import files must be independent of command
|
|
* line switches and what else is imported, they are generated
|
|
* before any semantic analysis.
|
|
*/
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
if (global.params.verbose)
|
|
printf("import %s\n", m->toChars());
|
|
m->genhdrfile();
|
|
}
|
|
}
|
|
if (global.errors)
|
|
fatal();
|
|
#endif
|
|
|
|
// Do semantic analysis
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
if (global.params.verbose)
|
|
printf("semantic %s\n", m->toChars());
|
|
m->semantic();
|
|
}
|
|
if (global.errors)
|
|
fatal();
|
|
|
|
// Do pass 2 semantic analysis
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
if (global.params.verbose)
|
|
printf("semantic2 %s\n", m->toChars());
|
|
m->semantic2();
|
|
}
|
|
if (global.errors)
|
|
fatal();
|
|
|
|
// Do pass 3 semantic analysis
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
if (global.params.verbose)
|
|
printf("semantic3 %s\n", m->toChars());
|
|
m->semantic3();
|
|
}
|
|
if (global.errors)
|
|
fatal();
|
|
|
|
#if !IN_LLVM
|
|
// Scan for functions to inline
|
|
if (global.params.useInline)
|
|
{
|
|
/* The problem with useArrayBounds and useAssert is that the
|
|
* module being linked to may not have generated them, so if
|
|
* we inline functions from those modules, the symbols for them will
|
|
* not be found at link time.
|
|
*/
|
|
if (!global.params.useArrayBounds && !global.params.useAssert)
|
|
{
|
|
#endif
|
|
// Do pass 3 semantic analysis on all imported modules,
|
|
// since otherwise functions in them cannot be inlined
|
|
for (i = 0; i < Module::amodules.dim; i++)
|
|
{
|
|
m = (Module *)Module::amodules.data[i];
|
|
if (global.params.verbose)
|
|
printf("semantic3 %s\n", m->toChars());
|
|
m->semantic3();
|
|
}
|
|
if (global.errors)
|
|
fatal();
|
|
#if !IN_LLVM
|
|
}
|
|
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
if (global.params.verbose)
|
|
printf("inline scan %s\n", m->toChars());
|
|
m->inlineScan();
|
|
}
|
|
}
|
|
if (global.errors)
|
|
fatal();
|
|
#endif
|
|
|
|
// Generate output files
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
if (global.params.verbose)
|
|
printf("code %s\n", m->toChars());
|
|
if (global.params.obj)
|
|
{
|
|
m->genobjfile(0);
|
|
global.params.objfiles->push(m->objfile->name->str);
|
|
}
|
|
if (global.errors)
|
|
m->deleteObjFile();
|
|
else
|
|
{
|
|
if (global.params.doDocComments)
|
|
m->gendocfile();
|
|
}
|
|
}
|
|
|
|
backend_term();
|
|
if (global.errors)
|
|
fatal();
|
|
|
|
if (!global.params.objfiles->dim)
|
|
{
|
|
if (global.params.link)
|
|
error("no object files to link");
|
|
}
|
|
else
|
|
{
|
|
if (global.params.link)
|
|
//status = runLINK();
|
|
linkExecutable(global.params.argv0);
|
|
|
|
if (global.params.run)
|
|
{
|
|
if (!status)
|
|
{
|
|
status = runExectuable();
|
|
|
|
/* Delete .obj files and .exe file
|
|
*/
|
|
for (i = 0; i < modules.dim; i++)
|
|
{
|
|
m = (Module *)modules.data[i];
|
|
m->deleteObjFile();
|
|
}
|
|
deleteExecutable();
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
/***********************************
|
|
* Parse and append contents of environment variable envvar
|
|
* to argc and argv[].
|
|
* The string is separated into arguments, processing \ and ".
|
|
*/
|
|
|
|
void getenv_setargv(const char *envvar, int *pargc, char** *pargv)
|
|
{
|
|
char *env;
|
|
char *p;
|
|
Array *argv;
|
|
int argc;
|
|
|
|
int wildcard; // do wildcard expansion
|
|
int instring;
|
|
int slash;
|
|
char c;
|
|
int j;
|
|
|
|
env = getenv(envvar);
|
|
if (!env)
|
|
return;
|
|
|
|
env = mem.strdup(env); // create our own writable copy
|
|
|
|
argc = *pargc;
|
|
argv = new Array();
|
|
argv->setDim(argc);
|
|
|
|
for (int i = 0; i < argc; i++)
|
|
argv->data[i] = (void *)(*pargv)[i];
|
|
|
|
j = 1; // leave argv[0] alone
|
|
while (1)
|
|
{
|
|
wildcard = 1;
|
|
switch (*env)
|
|
{
|
|
case ' ':
|
|
case '\t':
|
|
env++;
|
|
break;
|
|
|
|
case 0:
|
|
goto Ldone;
|
|
|
|
case '"':
|
|
wildcard = 0;
|
|
default:
|
|
argv->push(env); // append
|
|
//argv->insert(j, env); // insert at position j
|
|
j++;
|
|
argc++;
|
|
p = env;
|
|
slash = 0;
|
|
instring = 0;
|
|
c = 0;
|
|
|
|
while (1)
|
|
{
|
|
c = *env++;
|
|
switch (c)
|
|
{
|
|
case '"':
|
|
p -= (slash >> 1);
|
|
if (slash & 1)
|
|
{ p--;
|
|
goto Laddc;
|
|
}
|
|
instring ^= 1;
|
|
slash = 0;
|
|
continue;
|
|
|
|
case ' ':
|
|
case '\t':
|
|
if (instring)
|
|
goto Laddc;
|
|
*p = 0;
|
|
//if (wildcard)
|
|
//wildcardexpand(); // not implemented
|
|
break;
|
|
|
|
case '\\':
|
|
slash++;
|
|
*p++ = c;
|
|
continue;
|
|
|
|
case 0:
|
|
*p = 0;
|
|
//if (wildcard)
|
|
//wildcardexpand(); // not implemented
|
|
goto Ldone;
|
|
|
|
default:
|
|
Laddc:
|
|
slash = 0;
|
|
*p++ = c;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ldone:
|
|
*pargc = argc;
|
|
*pargv = (char **)argv->data;
|
|
}
|