1230 lines
52 KiB
C++
1230 lines
52 KiB
C++
/*
|
|
Copyright (c) 2010-2023, Intel Corporation
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are
|
|
met:
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of Intel Corporation nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/** @file main.cpp
|
|
@brief main() entrypoint implementation for ispc
|
|
*/
|
|
|
|
#include "ispc.h"
|
|
#include "module.h"
|
|
#include "target_registry.h"
|
|
#include "type.h"
|
|
#include "util.h"
|
|
|
|
#include <cstdarg>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef ISPC_HOST_IS_WINDOWS
|
|
#include <time.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#endif // ISPC_HOST_IS_WINDOWS
|
|
|
|
#include <llvm/Support/CommandLine.h>
|
|
#include <llvm/Support/Debug.h>
|
|
#include <llvm/Support/FileSystem.h>
|
|
#include <llvm/Support/Signals.h>
|
|
#if ISPC_LLVM_VERSION >= ISPC_LLVM_14_0
|
|
#include <llvm/MC/TargetRegistry.h>
|
|
#else
|
|
#include <llvm/Support/TargetRegistry.h>
|
|
#endif
|
|
#include <llvm/Support/TargetSelect.h>
|
|
#include <llvm/Support/ToolOutputFile.h>
|
|
|
|
using namespace ispc;
|
|
|
|
#ifdef ISPC_HOST_IS_WINDOWS
|
|
#define strcasecmp stricmp
|
|
#ifndef BUILD_DATE
|
|
#define BUILD_DATE __DATE__
|
|
#endif
|
|
#if _MSC_VER >= 1900
|
|
#define ISPC_VS_VERSION "Visual Studio 2015 and later"
|
|
#else
|
|
#define ISPC_VS_VERSION "Visual Studio 2013 and earlier"
|
|
#endif
|
|
#endif // ISPC_HOST_IS_WINDOWS
|
|
|
|
static void lPrintVersion() {
|
|
printf("%s\n", ISPC_VERSION_STRING);
|
|
#ifdef ISPC_HOST_IS_WINDOWS
|
|
printf("Supported Visual Studio versions: %s.\n", ISPC_VS_VERSION);
|
|
#endif
|
|
}
|
|
|
|
[[noreturn]] static void usage(int ret) {
|
|
lPrintVersion();
|
|
printf("\nusage: ispc\n");
|
|
printf(" [--addressing={32,64}]\t\tSelect 32- or 64-bit addressing. (Note that 32-bit\n");
|
|
printf(" \t\taddressing calculations are done by default, even\n");
|
|
printf(" \t\ton 64-bit target architectures.)\n");
|
|
printf(" [--arch={%s}]\t\tSelect target architecture\n", g->target_registry->getSupportedArchs().c_str());
|
|
#ifndef ISPC_HOST_IS_WINDOWS
|
|
printf(" [--colored-output]\t\tAlways use terminal colors in error/warning messages\n");
|
|
#endif
|
|
printf(" [--cpu=<type>]\t\t\tAn alias for [--device=<type>] switch\n");
|
|
printf(" [-D<foo>]\t\t\t\t#define given value when running preprocessor\n");
|
|
printf(" [--dev-stub <filename>]\t\tEmit device-side offload stub functions to file\n");
|
|
printf(" ");
|
|
char cpuHelp[2048];
|
|
snprintf(cpuHelp, sizeof(cpuHelp), "[--device=<type>]\t\t\tSelect target device\n<type>={%s}\n",
|
|
Target::SupportedCPUs().c_str());
|
|
PrintWithWordBreaks(cpuHelp, 16, TerminalWidth(), stdout);
|
|
printf(" [--dllexport]\t\t\tMake non-static functions DLL exported. Windows target only\n");
|
|
printf(" [--dwarf-version={2,3,4}]\t\tGenerate source-level debug information with given DWARF version "
|
|
"(triggers -g). Ignored for Windows target\n");
|
|
printf(" [-E]\t\t\t\tRun only the preprocessor\n");
|
|
printf(" [--emit-asm]\t\t\tGenerate assembly language file as output\n");
|
|
printf(" [--emit-llvm]\t\t\tEmit LLVM bitcode file as output\n");
|
|
printf(" [--emit-llvm-text]\t\t\tEmit LLVM bitcode file as output in textual form\n");
|
|
printf(" [--emit-obj]\t\t\tGenerate object file file as output (default)\n");
|
|
#ifdef ISPC_XE_ENABLED
|
|
printf(" [--emit-spirv]\t\t\tGenerate SPIR-V file as output\n");
|
|
// AOT compilation is temporary disabled on Windows
|
|
#ifndef ISPC_HOST_IS_WINDOWS
|
|
printf(" [--emit-zebin]\t\t\tGenerate L0 binary as output\n");
|
|
#endif
|
|
#endif
|
|
printf(" [--enable-llvm-intrinsics]\t\tEnable experimental feature to call LLVM intrinsics from ISPC "
|
|
"source code\n");
|
|
printf(" [--error-limit=<value>]\t\tLimit maximum number of errors emitting by ISPC to <value>\n");
|
|
printf(" [--force-alignment=<value>]\t\tForce alignment in memory allocations routine to be <value>\n");
|
|
printf(" [-g]\t\t\t\tGenerate source-level debug information\n");
|
|
printf(" [--help]\t\t\t\tPrint help\n");
|
|
printf(" [--help-dev]\t\t\tPrint help for developer options\n");
|
|
printf(" [--host-stub <filename>]\t\tEmit host-side offload stub functions to file\n");
|
|
printf(" [-h <name>/--header-outfile=<name>]\tOutput filename for header\n");
|
|
printf(" [-I <path>]\t\t\t\tAdd <path> to #include file search path\n");
|
|
printf(" [--ignore-preprocessor-errors]\tSuppress errors from the preprocessor\n");
|
|
printf(" [--instrument]\t\t\tEmit instrumentation to gather performance data\n");
|
|
printf(" [--math-lib=<option>]\t\tSelect math library\n");
|
|
printf(" default\t\t\t\tUse ispc's built-in math functions\n");
|
|
printf(" fast\t\t\t\tUse high-performance but lower-accuracy math functions\n");
|
|
printf(" svml\t\t\t\tUse the Intel(r) SVML math libraries\n");
|
|
printf(" system\t\t\t\tUse the system's math library (*may be quite slow*)\n");
|
|
printf(" [-MMM <filename>]\t\t\tWrite #include dependencies to given file.\n");
|
|
printf(" [-M]\t\t\t\tOutput a rule suitable for `make' describing the dependencies of the main source file to "
|
|
"stdout.\n");
|
|
printf(" [-MF <filename>]\t\t\tWhen used with `-M', specifies a file to write the dependencies to.\n");
|
|
printf(" [-MT <filename>]\t\t\tWhen used with `-M', changes the target of the rule emitted by dependency "
|
|
"generation.\n");
|
|
printf(" [--no-omit-frame-pointer]\t\tDisable frame pointer omission. It may be useful for profiling\n");
|
|
printf(" [--nostdlib]\t\t\tDon't make the ispc standard library available\n");
|
|
printf(" [--no-pragma-once]\t\t\tDon't use #pragma once in created headers\n");
|
|
printf(" [--nocpp]\t\t\t\tDon't run the C preprocessor\n");
|
|
printf(" [-o <name>/--outfile=<name>]\tOutput filename (may be \"-\" for standard output)\n");
|
|
printf(" [-O0/-O(1/2/3)]\t\t\tSet optimization level. Default behavior is to optimize for speed.\n");
|
|
printf(" -O0\t\t\t\tOptimizations disabled.\n");
|
|
printf(" -O1\t\t\t\tOptimization for size.\n");
|
|
printf(" -O2/O3\t\t\t\tOptimization for speed.\n");
|
|
printf(" [--opt=<option>]\t\t\tSet optimization option\n");
|
|
printf(" disable-assertions\t\tRemove assertion statements from final code.\n");
|
|
printf(" disable-fma\t\t\tDisable 'fused multiply-add' instructions (on targets that support them)\n");
|
|
printf(" disable-loop-unroll\t\tDisable loop unrolling.\n");
|
|
printf(" disable-zmm\t\tDisable using zmm registers for avx512 targets in favour of ymm. This also affects "
|
|
"ABI.\n");
|
|
#ifdef ISPC_XE_ENABLED
|
|
printf(" emit-xe-hardware-mask\t\tEnable emitting of Xe implicit hardware mask.\n");
|
|
printf(" enable-xe-foreach-varying\t\tEnable experimental foreach support inside varying control flow.\n");
|
|
#endif
|
|
printf(" fast-masked-vload\t\tFaster masked vector loads on SSE (may go past end of array)\n");
|
|
printf(" fast-math\t\t\tPerform non-IEEE-compliant optimizations of numeric expressions\n");
|
|
printf(" force-aligned-memory\t\tAlways issue \"aligned\" vector load and store instructions\n");
|
|
printf(" reset-ftz-daz\t\t\tReset FTZ/DAZ flags on ISPC extern function entrance / restore on return\n");
|
|
printf(" [--pic]\t\t\t\tGenerate position-independent code. Ignored for Windows target\n");
|
|
printf(" [--quiet]\t\t\t\tSuppress all output\n");
|
|
printf(" [--support-matrix]\t\t\tPrint full matrix of supported targets, architectures and OSes\n");
|
|
printf(" ");
|
|
char targetHelp[2048];
|
|
snprintf(targetHelp, sizeof(targetHelp),
|
|
"[--target=<t>]\t\t\tSelect target ISA and width.\n"
|
|
"<t>={%s}",
|
|
g->target_registry->getSupportedTargets().c_str());
|
|
PrintWithWordBreaks(targetHelp, 24, TerminalWidth(), stdout);
|
|
printf(" ");
|
|
snprintf(targetHelp, sizeof(targetHelp), "[--target-os=<os>]\t\t\tSelect target OS. <os>={%s}",
|
|
g->target_registry->getSupportedOSes().c_str());
|
|
PrintWithWordBreaks(targetHelp, 24, TerminalWidth(), stdout);
|
|
printf(" [--time-trace]\t\t\tTurn on time profiler. Generates JSON file based on output filename.\n");
|
|
printf(" [--time-trace-granularity=<value>]\tMinimum time granularity (in microseconds) traced by time "
|
|
"profiler.\n");
|
|
printf(" [--vectorcall/--no-vectorcall]\tEnable/disable vectorcall calling convention on Windows (x64 only). "
|
|
"Disabled by default\n");
|
|
printf(" [--version]\t\t\t\tPrint ispc version\n");
|
|
#ifdef ISPC_XE_ENABLED
|
|
printf(" [--vc-options=<\"-option1 -option2...\">]\t\t\t\tPass additional options to Vector Compiler backend\n");
|
|
#endif
|
|
printf(" [--werror]\t\t\t\tTreat warnings as errors\n");
|
|
printf(" [--woff]\t\t\t\tDisable warnings\n");
|
|
printf(" [--wno-perf]\t\t\tDon't issue warnings related to performance-related issues\n");
|
|
printf(" [--x86-asm-syntax=<option>]\t\tSelect style of code if generating assembly\n");
|
|
printf(" intel\t\t\t\tEmit Intel-style assembly\n");
|
|
printf(" att\t\t\t\tEmit AT&T-style assembly\n");
|
|
#ifdef ISPC_XE_ENABLED
|
|
printf(" [--xe-stack-mem-size=<value>\t\tSet size of stateless stack memory in VC backend.\n");
|
|
#endif
|
|
printf(" [@<filename>]\t\t\tRead additional arguments from the given file\n");
|
|
printf(" <file to compile or \"-\" for stdin>\n");
|
|
exit(ret);
|
|
}
|
|
|
|
[[noreturn]] static void linkUsage(int ret) {
|
|
lPrintVersion();
|
|
printf("\nusage: ispc link\n");
|
|
printf("\nLink several IR or SPIR-V files to selected output format: LLVM BC (default), LLVM text or SPIR-V\n");
|
|
printf(" [--emit-llvm]\t\t\tEmit LLVM bitcode file as output\n");
|
|
printf(" [--emit-llvm-text]\t\t\tEmit LLVM bitcode file as output in textual form\n");
|
|
#ifdef ISPC_XE_ENABLED
|
|
printf(" [--emit-spirv]\t\t\tEmit SPIR-V file as output\n");
|
|
#endif
|
|
printf(" [-o <name>/--outfile=<name>]\tOutput filename (may be \"-\" for standard output)\n");
|
|
printf(" <files to link or \"-\" for stdin>\n");
|
|
printf("\nExamples:\n");
|
|
printf(" Link two SPIR-V files to LLVM BC output:\n");
|
|
printf(" ispc link test_a.spv test_b.spv --emit-llvm -o test.bc\n");
|
|
printf(" Link LLVM bitcode files to SPIR-V output:\n");
|
|
printf(" ispc link test_a.bc test_b.bc --emit-spirv -o test.spv\n");
|
|
exit(ret);
|
|
}
|
|
|
|
[[noreturn]] static void devUsage(int ret) {
|
|
lPrintVersion();
|
|
printf("\nusage (developer options): ispc\n");
|
|
printf(" [--ast-dump=user|all]\t\tDump AST for user code or all the code including stdlib. If no argument is "
|
|
"given, dump AST for user code only\n");
|
|
printf(" [--debug]\t\t\t\tPrint information useful for debugging ispc\n");
|
|
printf(" [--debug-llvm]\t\t\tEnable LLVM debugging information (dumps to stderr)\n");
|
|
printf(" [--debug-phase=<value>]\t\tSet optimization phases to dump. "
|
|
"--debug-phase=first,210:220,300,305,310:last\n");
|
|
printf(" [--[no-]discard-value-names]\tDo not discard/Discard value names when generating LLVM IR.\n");
|
|
printf(" [--dump-file[=<path>]]\t\tDump module IR to file(s) in "
|
|
"current directory, or to <path> if specified\n");
|
|
printf(" [--fuzz-seed=<value>]\t\tSeed value for RNG for fuzz testing\n");
|
|
printf(" [--fuzz-test]\t\t\tRandomly perturb program input to test error conditions\n");
|
|
printf(" [--off-phase=<value>]\t\tSwitch off optimization phases. --off-phase=first,210:220,300,305,310:last\n");
|
|
printf(" [--opt=<option>]\t\t\tSet optimization option\n");
|
|
printf(" disable-all-on-optimizations\t\tDisable optimizations that take advantage of \"all on\" mask\n");
|
|
printf(" disable-blended-masked-stores\t\tScalarize masked stores on SSE (vs. using vblendps)\n");
|
|
printf(" disable-blending-removal\t\tDisable eliminating blend at same scope\n");
|
|
printf(" disable-coalescing\t\t\tDisable gather coalescing\n");
|
|
printf(" disable-coherent-control-flow\t\tDisable coherent control flow optimizations\n");
|
|
printf(" disable-gather-scatter-flattening\tDisable flattening when all lanes are on\n");
|
|
printf(" disable-gather-scatter-optimizations\tDisable improvements to gather/scatter\n");
|
|
printf(" disable-handle-pseudo-memory-ops\tLeave __pseudo_* calls for gather/scatter/etc. in final IR\n");
|
|
printf(" disable-uniform-control-flow\t\tDisable uniform control flow optimizations\n");
|
|
printf(" disable-uniform-memory-optimizations\tDisable uniform-based coherent memory access\n");
|
|
#ifdef ISPC_XE_ENABLED
|
|
printf(" disable-xe-gather-coalescing\t\tDisable Xe gather coalescing\n");
|
|
printf(" threshold-for-xe-gather-coalescing=<0>\tMinimal number of eliminated memory instructions for "
|
|
"Xe gather coalescing.\n");
|
|
printf(" build-llvm-loads-on-xe-gather-coalescing\t\tExperimental: build standard llvm loads on "
|
|
"Xe gather coalescing.\n");
|
|
printf(" enable-xe-unsafe-masked-load\t\tEnable Xe unsafe masked load\n");
|
|
#endif
|
|
printf(" [--print-target]\t\t\tPrint target's information\n");
|
|
printf(" [--yydebug]\t\t\t\tPrint debugging information during parsing\n");
|
|
exit(ret);
|
|
}
|
|
|
|
/** Define an abstract base-class that implements the parsing of an character source and
|
|
* the breaking of it into the individual arguments
|
|
*/
|
|
class ArgFactory {
|
|
private:
|
|
char *AllocateString(std::string string) {
|
|
int len = string.length();
|
|
char *ptr = new char[len + 1];
|
|
strncpy(ptr, string.c_str(), len);
|
|
ptr[len] = '\0';
|
|
return ptr;
|
|
}
|
|
|
|
/** Method provided by the derived classes to retrieve the next character from the stream.
|
|
*/
|
|
virtual char GetNextChar() = 0;
|
|
|
|
public:
|
|
ArgFactory() {}
|
|
|
|
char *GetNextArg() {
|
|
bool insideDQ = false;
|
|
bool insideSQ = false;
|
|
std::string arg;
|
|
char c = GetNextChar();
|
|
|
|
// First consume any white-space before the argument
|
|
while (isspace(c))
|
|
c = GetNextChar();
|
|
|
|
if (c == '\0')
|
|
// Reached the end so no more arguments
|
|
return NULL;
|
|
|
|
// c now has the first character of the next argument, so collect the rest
|
|
while (c != '\0' && !(isspace(c) && !insideDQ && !insideSQ)) {
|
|
if (c == '\"' && !insideSQ) {
|
|
c = GetNextChar();
|
|
insideDQ = !insideDQ;
|
|
continue;
|
|
}
|
|
if (c == '\'' && !insideDQ) {
|
|
c = GetNextChar();
|
|
insideSQ = !insideSQ;
|
|
continue;
|
|
}
|
|
arg += c;
|
|
c = GetNextChar();
|
|
}
|
|
|
|
return AllocateString(arg);
|
|
}
|
|
};
|
|
|
|
/** Define a class to break the contents of an open file into the individual arguments */
|
|
class FileArgFactory : public ArgFactory {
|
|
private:
|
|
FILE *InputFile;
|
|
|
|
virtual char GetNextChar() {
|
|
int c = fgetc(InputFile);
|
|
if (c == EOF) {
|
|
return '\0';
|
|
} else {
|
|
return c;
|
|
}
|
|
}
|
|
|
|
public:
|
|
FileArgFactory(FILE *file) : InputFile(file) {}
|
|
};
|
|
|
|
/** Define a class to break a NUL-terminated string into the individual arguments */
|
|
class StringArgFactory : public ArgFactory {
|
|
private:
|
|
const char *InputString;
|
|
|
|
virtual char GetNextChar() {
|
|
char c = *InputString;
|
|
|
|
if (c != '\0')
|
|
++InputString;
|
|
|
|
return c;
|
|
}
|
|
|
|
public:
|
|
StringArgFactory(const char *string) : InputString(string) {}
|
|
};
|
|
|
|
// Forward reference
|
|
static void lAddSingleArg(char *arg, std::vector<char *> &argv);
|
|
|
|
/** Add all args from a given factory to the argv passed as parameters, which could
|
|
* include recursing into another ArgFactory.
|
|
*/
|
|
static void lAddArgsFromFactory(ArgFactory &Args, std::vector<char *> &argv) {
|
|
while (true) {
|
|
char *NextArg = Args.GetNextArg();
|
|
if (NextArg == NULL)
|
|
break;
|
|
lAddSingleArg(NextArg, argv);
|
|
}
|
|
}
|
|
|
|
/** Parse an open file for arguments and add them to the argv passed as parameters */
|
|
static void lAddArgsFromFile(FILE *file, std::vector<char *> &argv) {
|
|
FileArgFactory args(file);
|
|
lAddArgsFromFactory(args, argv);
|
|
}
|
|
|
|
/** Parse a string for arguments and add them to the argv passed as parameters */
|
|
static void lAddArgsFromString(const char *string, std::vector<char *> &argv) {
|
|
StringArgFactory args(string);
|
|
lAddArgsFromFactory(args, argv);
|
|
}
|
|
|
|
/** Add a single argument to the argv passed as parameters. If the argument is of the
|
|
* form @<filename> and <filename> exists and is readable, the arguments in the file will be
|
|
* inserted into argv in place of the original argument.
|
|
*/
|
|
static void lAddSingleArg(char *arg, std::vector<char *> &argv) {
|
|
if (arg[0] == '@') {
|
|
char *filename = &arg[1];
|
|
FILE *file = fopen(filename, "r");
|
|
if (file != NULL) {
|
|
lAddArgsFromFile(file, argv);
|
|
fclose(file);
|
|
arg = NULL;
|
|
}
|
|
}
|
|
if (arg != NULL) {
|
|
argv.push_back(arg);
|
|
}
|
|
}
|
|
|
|
/** We take arguments from both the command line as well as from the
|
|
* ISPC_ARGS environment variable - and each of these can include a file containing
|
|
* additional arguments using @<filename>. This function returns a new set of
|
|
* arguments representing the ones from all these sources merged together.
|
|
*/
|
|
static void lGetAllArgs(int Argc, char *Argv[], std::vector<char *> &argv) {
|
|
// Copy over the command line arguments (passed in)
|
|
for (int i = 0; i < Argc; ++i)
|
|
lAddSingleArg(Argv[i], argv);
|
|
|
|
// See if we have any set via the environment variable
|
|
const char *env = getenv("ISPC_ARGS");
|
|
if (env)
|
|
lAddArgsFromString(env, argv);
|
|
}
|
|
|
|
static void lSignal(void *) { FATAL("Unhandled signal sent to process; terminating."); }
|
|
|
|
// ArgErrors accumulates error and warning messages during arguments parsing
|
|
// prints them after the parsing is done. We need to delay printing to take
|
|
// into account such options as --quite, --nowrap --werror, which affects how
|
|
// errors and warnings are treated and printed.
|
|
class ArgErrors {
|
|
enum class MsgType { warning, error };
|
|
std::vector<std::pair<MsgType, std::string>> m_messages;
|
|
void AddMessage(MsgType msg_type, const char *format, va_list args) {
|
|
char *messageBuf;
|
|
if (vasprintf(&messageBuf, format, args) == -1) {
|
|
fprintf(stderr, "vasprintf() unable to allocate memory!\n");
|
|
exit(-1);
|
|
}
|
|
|
|
m_messages.push_back(std::make_pair(msg_type, messageBuf));
|
|
|
|
free(messageBuf);
|
|
}
|
|
|
|
public:
|
|
ArgErrors(){};
|
|
void AddError(const char *format, ...) PRINTF_FUNC {
|
|
va_list args;
|
|
va_start(args, format);
|
|
AddMessage(MsgType::error, format, args);
|
|
va_end(args);
|
|
}
|
|
void AddWarning(const char *format, ...) PRINTF_FUNC {
|
|
va_list args;
|
|
va_start(args, format);
|
|
AddMessage(MsgType::warning, format, args);
|
|
va_end(args);
|
|
}
|
|
void Emit() {
|
|
bool errors = false;
|
|
for (auto &message : m_messages) {
|
|
if (message.first == MsgType::error || g->warningsAsErrors) {
|
|
errors = true;
|
|
Error(SourcePos(), "%s", message.second.c_str());
|
|
} else {
|
|
Warning(SourcePos(), "%s", message.second.c_str());
|
|
}
|
|
}
|
|
if (errors) {
|
|
exit(-1);
|
|
}
|
|
}
|
|
};
|
|
|
|
static int ParsingPhaseName(char *stage, ArgErrors &errorHandler) {
|
|
if (strncmp(stage, "first", 5) == 0) {
|
|
return 0;
|
|
} else if (strncmp(stage, "last", 4) == 0) {
|
|
return LAST_OPT_NUMBER;
|
|
} else {
|
|
int t = atoi(stage);
|
|
if (t < 0 || t > LAST_OPT_NUMBER) {
|
|
errorHandler.AddError("Phases must be from 0 to %d. %s is incorrect.", LAST_OPT_NUMBER, stage);
|
|
return 0;
|
|
} else {
|
|
return t;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For boolean command line options there are actually three states. Beyond "on" and "off", there's "unspecified",
|
|
// which might trigger a different default behavior.
|
|
enum class BooleanOptValue { none, enabled, disabled };
|
|
|
|
static void setCallingConv(BooleanOptValue vectorCall, Arch arch) {
|
|
// Restrict vectorcall to just x86_64 - vectorcall for x86 not supported yet.
|
|
if (g->target_os == TargetOS::windows && vectorCall == BooleanOptValue::enabled &&
|
|
// Arch is not properly set yet, we assume none is x86_64.
|
|
(arch == Arch::x86_64 || arch == Arch::none)) {
|
|
g->calling_conv = CallingConv::x86_vectorcall;
|
|
} else {
|
|
g->calling_conv = CallingConv::defaultcall;
|
|
}
|
|
}
|
|
|
|
static void writeCompileTimeFile(const char *outFileName) {
|
|
llvm::SmallString<128> jsonFileName(outFileName);
|
|
jsonFileName.append(".json");
|
|
llvm::sys::fs::OpenFlags flags = llvm::sys::fs::OF_Text;
|
|
std::error_code error;
|
|
std::unique_ptr<llvm::ToolOutputFile> of(new llvm::ToolOutputFile(jsonFileName.c_str(), error, flags));
|
|
|
|
if (error) {
|
|
Error(SourcePos(), "Cannot open json file \"%s\".\n", jsonFileName.c_str());
|
|
}
|
|
|
|
llvm::raw_fd_ostream &fos(of->os());
|
|
llvm::timeTraceProfilerWrite(fos);
|
|
of->keep();
|
|
return;
|
|
}
|
|
|
|
static std::string ParsePath(char *path, ArgErrors &errorHandler) {
|
|
constexpr int parsing_limit = 1024;
|
|
auto len = strnlen(path, parsing_limit);
|
|
return std::string{path, len};
|
|
}
|
|
|
|
static std::set<int> ParsingPhases(char *stages, ArgErrors &errorHandler) {
|
|
constexpr int parsing_limit = 100;
|
|
std::set<int> phases;
|
|
auto len = strnlen(stages, parsing_limit);
|
|
if (len == 0) {
|
|
errorHandler.AddError("Empty phase list.");
|
|
return phases;
|
|
}
|
|
if (len == parsing_limit && stages[parsing_limit] != '\0') {
|
|
errorHandler.AddError("Phase list is too long.");
|
|
return phases;
|
|
}
|
|
int begin = ParsingPhaseName(stages, errorHandler);
|
|
int end = begin;
|
|
|
|
for (unsigned i = 0; i < strlen(stages); i++) {
|
|
if ((stages[i] == ',') || (i == strlen(stages) - 1)) {
|
|
for (int j = begin; j < end + 1; j++) {
|
|
phases.insert(j);
|
|
}
|
|
begin = ParsingPhaseName(stages + i + 1, errorHandler);
|
|
end = begin;
|
|
} else if (stages[i] == ':') {
|
|
end = ParsingPhaseName(stages + i + 1, errorHandler);
|
|
}
|
|
}
|
|
return phases;
|
|
}
|
|
|
|
static void lParseInclude(const char *path) {
|
|
#ifdef ISPC_HOST_IS_WINDOWS
|
|
char delim = ';';
|
|
#else
|
|
char delim = ':';
|
|
#endif
|
|
size_t pos = 0, pos_end;
|
|
std::string str_path(path);
|
|
do {
|
|
pos_end = str_path.find(delim, pos);
|
|
size_t len = (pos_end == std::string::npos) ?
|
|
// Not found, copy till end of the string.
|
|
std::string::npos
|
|
:
|
|
// Copy [pos, pos_end).
|
|
(pos_end - pos);
|
|
std::string s = str_path.substr(pos, len);
|
|
g->includePath.push_back(s);
|
|
pos = pos_end + 1;
|
|
} while (pos_end != std::string::npos);
|
|
}
|
|
|
|
extern int yydebug;
|
|
|
|
int main(int Argc, char *Argv[]) {
|
|
std::vector<char *> argv;
|
|
lGetAllArgs(Argc, Argv, argv);
|
|
int argc = argv.size();
|
|
|
|
llvm::sys::AddSignalHandler(lSignal, NULL);
|
|
|
|
// initialize available LLVM targets
|
|
#ifdef ISPC_X86_ENABLED
|
|
LLVMInitializeX86TargetInfo();
|
|
LLVMInitializeX86Target();
|
|
LLVMInitializeX86AsmPrinter();
|
|
LLVMInitializeX86AsmParser();
|
|
LLVMInitializeX86Disassembler();
|
|
LLVMInitializeX86TargetMC();
|
|
#endif
|
|
|
|
#ifdef ISPC_ARM_ENABLED
|
|
LLVMInitializeARMTargetInfo();
|
|
LLVMInitializeARMTarget();
|
|
LLVMInitializeARMAsmPrinter();
|
|
LLVMInitializeARMAsmParser();
|
|
LLVMInitializeARMDisassembler();
|
|
LLVMInitializeARMTargetMC();
|
|
|
|
LLVMInitializeAArch64TargetInfo();
|
|
LLVMInitializeAArch64Target();
|
|
LLVMInitializeAArch64AsmPrinter();
|
|
LLVMInitializeAArch64AsmParser();
|
|
LLVMInitializeAArch64Disassembler();
|
|
LLVMInitializeAArch64TargetMC();
|
|
#endif
|
|
|
|
#ifdef ISPC_WASM_ENABLED
|
|
LLVMInitializeWebAssemblyAsmParser();
|
|
LLVMInitializeWebAssemblyAsmPrinter();
|
|
LLVMInitializeWebAssemblyDisassembler();
|
|
LLVMInitializeWebAssemblyTarget();
|
|
LLVMInitializeWebAssemblyTargetInfo();
|
|
LLVMInitializeWebAssemblyTargetMC();
|
|
#endif
|
|
char *file = NULL;
|
|
const char *headerFileName = NULL;
|
|
const char *outFileName = NULL;
|
|
const char *depsFileName = NULL;
|
|
const char *depsTargetName = NULL;
|
|
const char *hostStubFileName = NULL;
|
|
const char *devStubFileName = NULL;
|
|
|
|
std::vector<std::string> linkFileNames;
|
|
// Initiailize globals early so that we can set various option values
|
|
// as we're parsing below
|
|
g = new Globals;
|
|
|
|
Module::OutputType ot = Module::Object;
|
|
Module::OutputFlags flags = Module::NoFlags;
|
|
Arch arch = Arch::none;
|
|
std::vector<ISPCTarget> targets;
|
|
const char *cpu = NULL, *intelAsmSyntax = NULL;
|
|
BooleanOptValue vectorCall = BooleanOptValue::none;
|
|
BooleanOptValue discardValueNames = BooleanOptValue::none;
|
|
|
|
ArgErrors errorHandler;
|
|
|
|
// If the first argument is "link"
|
|
// ISPC will be used in a linkage mode
|
|
if (argc > 1 && !strncmp(argv[1], "link", 4)) {
|
|
// Use bitcode format by default
|
|
ot = Module::Bitcode;
|
|
|
|
if (argc < 2) {
|
|
// Not sufficient number of arguments
|
|
linkUsage(-1);
|
|
}
|
|
for (int i = 2; i < argc; ++i) {
|
|
if (!strcmp(argv[i], "--help")) {
|
|
linkUsage(0);
|
|
} else if (!strcmp(argv[i], "-o")) {
|
|
if (++i != argc) {
|
|
outFileName = argv[i];
|
|
} else {
|
|
errorHandler.AddError("No output file specified after -o option.");
|
|
}
|
|
} else if (!strncmp(argv[i], "--outfile=", 10)) {
|
|
outFileName = argv[i] + strlen("--outfile=");
|
|
#ifdef ISPC_XE_ENABLED
|
|
} else if (!strcmp(argv[i], "--emit-spirv")) {
|
|
ot = Module::SPIRV;
|
|
#endif
|
|
} else if (!strcmp(argv[i], "--emit-llvm")) {
|
|
ot = Module::Bitcode;
|
|
} else if (!strcmp(argv[i], "--emit-llvm-text")) {
|
|
ot = Module::BitcodeText;
|
|
} else if (argv[i][0] == '-') {
|
|
errorHandler.AddError("Unknown option \"%s\".", argv[i]);
|
|
} else {
|
|
file = argv[i];
|
|
linkFileNames.push_back(file);
|
|
}
|
|
}
|
|
// Emit accumulted errors and warnings, if any.
|
|
errorHandler.Emit();
|
|
|
|
if (linkFileNames.size() == 0) {
|
|
Error(SourcePos(), "No input files were specified.");
|
|
exit(1);
|
|
}
|
|
|
|
if (outFileName == NULL) {
|
|
Warning(SourcePos(), "No output file name specified. "
|
|
"The inputs will be linked and warnings/errors will "
|
|
"be issued, but no output will be generated.");
|
|
}
|
|
|
|
return Module::LinkAndOutput(linkFileNames, ot, outFileName);
|
|
}
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (!strcmp(argv[i], "--help")) {
|
|
usage(0);
|
|
} else if (!strcmp(argv[i], "--help-dev")) {
|
|
devUsage(0);
|
|
} else if (!strncmp(argv[i], "link", 4)) {
|
|
errorHandler.AddError(
|
|
"Option \"link\" can't be used in compilation mode. Use \"ispc link --help\" for details");
|
|
} else if (!strcmp(argv[i], "--support-matrix")) {
|
|
g->target_registry->printSupportMatrix();
|
|
exit(0);
|
|
} else if (!strncmp(argv[i], "-D", 2)) {
|
|
g->cppArgs.push_back(argv[i]);
|
|
} else if (!strncmp(argv[i], "--addressing=", 13)) {
|
|
if (atoi(argv[i] + 13) == 64)
|
|
// FIXME: this doesn't make sense on 32 bit platform.
|
|
g->opt.force32BitAddressing = false;
|
|
else if (atoi(argv[i] + 13) == 32)
|
|
g->opt.force32BitAddressing = true;
|
|
else {
|
|
errorHandler.AddError("Addressing width \"%s\" invalid -- only 32 and "
|
|
"64 are allowed.",
|
|
argv[i] + 13);
|
|
}
|
|
} else if (!strncmp(argv[i], "--arch=", 7)) {
|
|
Arch prev_arch = arch;
|
|
|
|
arch = ParseArch(argv[i] + 7);
|
|
if (arch == Arch::error) {
|
|
errorHandler.AddError("Unsupported value for --arch, supported values are: %s",
|
|
g->target_registry->getSupportedArchs().c_str());
|
|
}
|
|
|
|
if (prev_arch != Arch::none && prev_arch != arch) {
|
|
std::string prev_arch_str = ArchToString(prev_arch);
|
|
std::string arch_str = ArchToString(arch);
|
|
errorHandler.AddWarning("Overwriting --arch=%s with --arch=%s", prev_arch_str.c_str(),
|
|
arch_str.c_str());
|
|
}
|
|
} else if (!strcmp(argv[i], "--ast-dump")) {
|
|
g->astDump = Globals::ASTDumpKind::User;
|
|
} else if (!strncmp(argv[i], "--ast-dump=", 11)) {
|
|
const char *ast = argv[i] + 11;
|
|
if (!strcmp(ast, "user"))
|
|
g->astDump = Globals::ASTDumpKind::User;
|
|
else if (!strcmp(ast, "all"))
|
|
g->astDump = Globals::ASTDumpKind::All;
|
|
else {
|
|
errorHandler.AddError("Unknown --ast-dump= value \"%s\".", ast);
|
|
}
|
|
} else if (!strncmp(argv[i], "--x86-asm-syntax=", 17)) {
|
|
intelAsmSyntax = argv[i] + 17;
|
|
if (!((std::string(intelAsmSyntax) == "intel") || (std::string(intelAsmSyntax) == "att"))) {
|
|
intelAsmSyntax = NULL;
|
|
errorHandler.AddError("Invalid value for --x86-asm-syntax: \"%s\" -- "
|
|
"only intel and att are allowed.",
|
|
argv[i] + 17);
|
|
}
|
|
} else if (!strncmp(argv[i], "--device=", 9)) {
|
|
cpu = argv[i] + 9;
|
|
} else if (!strncmp(argv[i], "--cpu=", 6)) {
|
|
cpu = argv[i] + 6;
|
|
} else if (!strcmp(argv[i], "--fast-math")) {
|
|
errorHandler.AddError("--fast-math option has been renamed to --opt=fast-math!");
|
|
} else if (!strcmp(argv[i], "--fast-masked-vload")) {
|
|
errorHandler.AddError("--fast-masked-vload option has been renamed to "
|
|
"--opt=fast-masked-vload!");
|
|
} else if (!strcmp(argv[i], "--debug"))
|
|
g->debugPrint = true;
|
|
else if (!strcmp(argv[i], "--debug-llvm"))
|
|
llvm::DebugFlag = true;
|
|
else if (!strcmp(argv[i], "--discard-value-names"))
|
|
discardValueNames = BooleanOptValue::enabled;
|
|
else if (!strcmp(argv[i], "--no-discard-value-names"))
|
|
discardValueNames = BooleanOptValue::disabled;
|
|
else if (!strcmp(argv[i], "--dllexport"))
|
|
g->dllExport = true;
|
|
else if (!strncmp(argv[i], "--dwarf-version=", 16)) {
|
|
int val = atoi(argv[i] + 16);
|
|
if (2 <= val && val <= 4) {
|
|
g->generateDebuggingSymbols = true;
|
|
g->generateDWARFVersion = val;
|
|
} else {
|
|
errorHandler.AddError("Invalid value for DWARF version: \"%s\" -- "
|
|
"only 2, 3 and 4 are allowed.",
|
|
argv[i] + 16);
|
|
}
|
|
} else if (!strcmp(argv[i], "--print-target"))
|
|
g->printTarget = true;
|
|
else if (!strcmp(argv[i], "--no-omit-frame-pointer"))
|
|
g->NoOmitFramePointer = true;
|
|
else if (!strcmp(argv[i], "--instrument"))
|
|
g->emitInstrumentation = true;
|
|
else if (!strcmp(argv[i], "--no-pragma-once"))
|
|
g->noPragmaOnce = true;
|
|
else if (!strcmp(argv[i], "-g")) {
|
|
g->generateDebuggingSymbols = true;
|
|
} else if (!strcmp(argv[i], "-E")) {
|
|
g->onlyCPP = true;
|
|
ot = Module::CPPStub;
|
|
} else if (!strcmp(argv[i], "--emit-asm"))
|
|
ot = Module::Asm;
|
|
else if (!strcmp(argv[i], "--emit-llvm"))
|
|
ot = Module::Bitcode;
|
|
else if (!strcmp(argv[i], "--emit-llvm-text"))
|
|
ot = Module::BitcodeText;
|
|
else if (!strcmp(argv[i], "--emit-obj"))
|
|
ot = Module::Object;
|
|
#ifdef ISPC_XE_ENABLED
|
|
else if (!strcmp(argv[i], "--emit-spirv"))
|
|
ot = Module::SPIRV;
|
|
else if (!strcmp(argv[i], "--emit-zebin"))
|
|
ot = Module::ZEBIN;
|
|
#endif
|
|
else if (!strcmp(argv[i], "--enable-llvm-intrinsics")) {
|
|
g->enableLLVMIntrinsics = true;
|
|
} else if (!strcmp(argv[i], "-I")) {
|
|
if (++i != argc) {
|
|
lParseInclude(argv[i]);
|
|
} else {
|
|
errorHandler.AddError("No path specified after -I option.");
|
|
}
|
|
} else if (!strncmp(argv[i], "-I", 2)) {
|
|
lParseInclude(argv[i] + 2);
|
|
} else if (!strcmp(argv[i], "--ignore-preprocessor-errors")) {
|
|
g->ignoreCPPErrors = true;
|
|
} else if (!strcmp(argv[i], "--fuzz-test"))
|
|
g->enableFuzzTest = true;
|
|
else if (!strncmp(argv[i], "--fuzz-seed=", 12))
|
|
g->fuzzTestSeed = atoi(argv[i] + 12);
|
|
else if (!strcmp(argv[i], "--target")) {
|
|
// FIXME: should remove this way of specifying the target...
|
|
if (++i != argc) {
|
|
auto result = ParseISPCTargets(argv[i]);
|
|
targets = result.first;
|
|
if (!result.second.empty()) {
|
|
errorHandler.AddError("Incorrect targets: %s. Choices are: %s.", result.second.c_str(),
|
|
g->target_registry->getSupportedTargets().c_str());
|
|
}
|
|
} else {
|
|
errorHandler.AddError("No target specified after --target option.");
|
|
}
|
|
} else if (!strncmp(argv[i], "--target=", 9)) {
|
|
auto result = ParseISPCTargets(argv[i] + 9);
|
|
targets = result.first;
|
|
if (!result.second.empty()) {
|
|
errorHandler.AddError("Incorrect targets: %s. Choices are: %s.", result.second.c_str(),
|
|
g->target_registry->getSupportedTargets().c_str());
|
|
}
|
|
} else if (!strncmp(argv[i], "--target-os=", 12)) {
|
|
g->target_os = ParseOS(argv[i] + 12);
|
|
if (g->target_os == TargetOS::error) {
|
|
errorHandler.AddError("Unsupported value for --target-os, supported values are: %s",
|
|
g->target_registry->getSupportedOSes().c_str());
|
|
}
|
|
} else if (!strcmp(argv[i], "--no-vectorcall")) {
|
|
vectorCall = BooleanOptValue::disabled;
|
|
} else if (!strcmp(argv[i], "--vectorcall")) {
|
|
vectorCall = BooleanOptValue::enabled;
|
|
} else if (!strncmp(argv[i], "--math-lib=", 11)) {
|
|
const char *lib = argv[i] + 11;
|
|
if (!strcmp(lib, "default"))
|
|
g->mathLib = Globals::MathLib::Math_ISPC;
|
|
else if (!strcmp(lib, "fast"))
|
|
g->mathLib = Globals::MathLib::Math_ISPCFast;
|
|
else if (!strcmp(lib, "svml"))
|
|
g->mathLib = Globals::MathLib::Math_SVML;
|
|
else if (!strcmp(lib, "system"))
|
|
g->mathLib = Globals::MathLib::Math_System;
|
|
else {
|
|
errorHandler.AddError("Unknown --math-lib= option \"%s\".", lib);
|
|
}
|
|
} else if (!strncmp(argv[i], "--opt=", 6)) {
|
|
const char *opt = argv[i] + 6;
|
|
if (!strcmp(opt, "fast-math"))
|
|
g->opt.fastMath = true;
|
|
else if (!strcmp(opt, "fast-masked-vload"))
|
|
g->opt.fastMaskedVload = true;
|
|
else if (!strcmp(opt, "disable-assertions"))
|
|
g->opt.disableAsserts = true;
|
|
else if (!strcmp(opt, "disable-loop-unroll"))
|
|
g->opt.unrollLoops = false;
|
|
else if (!strcmp(opt, "disable-fma"))
|
|
g->opt.disableFMA = true;
|
|
else if (!strcmp(opt, "disable-zmm"))
|
|
g->opt.disableZMM = true;
|
|
else if (!strcmp(opt, "force-aligned-memory"))
|
|
g->opt.forceAlignedMemory = true;
|
|
else if (!strcmp(opt, "reset-ftz-daz"))
|
|
g->opt.resetFTZ_DAZ = true;
|
|
|
|
// These are only used for performance tests of specific
|
|
// optimizations
|
|
else if (!strcmp(opt, "disable-all-on-optimizations"))
|
|
g->opt.disableMaskAllOnOptimizations = true;
|
|
else if (!strcmp(opt, "disable-coalescing"))
|
|
g->opt.disableCoalescing = true;
|
|
else if (!strcmp(opt, "disable-handle-pseudo-memory-ops"))
|
|
g->opt.disableHandlePseudoMemoryOps = true;
|
|
else if (!strcmp(opt, "disable-blended-masked-stores"))
|
|
g->opt.disableBlendedMaskedStores = true;
|
|
else if (!strcmp(opt, "disable-coherent-control-flow"))
|
|
g->opt.disableCoherentControlFlow = true;
|
|
else if (!strcmp(opt, "disable-uniform-control-flow"))
|
|
g->opt.disableUniformControlFlow = true;
|
|
else if (!strcmp(opt, "disable-gather-scatter-optimizations"))
|
|
g->opt.disableGatherScatterOptimizations = true;
|
|
else if (!strcmp(opt, "disable-blending-removal"))
|
|
g->opt.disableMaskedStoreToStore = true;
|
|
else if (!strcmp(opt, "disable-gather-scatter-flattening"))
|
|
g->opt.disableGatherScatterFlattening = true;
|
|
else if (!strcmp(opt, "disable-uniform-memory-optimizations"))
|
|
g->opt.disableUniformMemoryOptimizations = true;
|
|
#ifdef ISPC_XE_ENABLED
|
|
else if (!strcmp(opt, "disable-xe-gather-coalescing"))
|
|
g->opt.disableXeGatherCoalescing = true;
|
|
else if (!strncmp(opt, "threshold-for-xe-gather-coalescing=", 37))
|
|
g->opt.thresholdForXeGatherCoalescing = atoi(opt + 37);
|
|
else if (!strcmp(opt, "build-llvm-loads-on-xe-gather-coalescing"))
|
|
g->opt.buildLLVMLoadsOnXeGatherCoalescing = true;
|
|
else if (!strcmp(opt, "emit-xe-hardware-mask"))
|
|
g->opt.emitXeHardwareMask = true;
|
|
else if (!strcmp(opt, "enable-xe-foreach-varying"))
|
|
g->opt.enableForeachInsideVarying = true;
|
|
else if (!strcmp(opt, "enable-xe-unsafe-masked-load"))
|
|
g->opt.enableXeUnsafeMaskedLoad = true;
|
|
#endif
|
|
else {
|
|
errorHandler.AddError("Unknown --opt= option \"%s\".", opt);
|
|
}
|
|
} else if (!strncmp(argv[i], "--force-alignment=", 18)) {
|
|
g->forceAlignment = atoi(argv[i] + 18);
|
|
} else if (!strcmp(argv[i], "--time-trace")) {
|
|
g->enableTimeTrace = true;
|
|
} else if (!strncmp(argv[i], "--time-trace-granularity=", 25)) {
|
|
g->timeTraceGranularity = atoi(argv[i] + 25);
|
|
} else if (!strcmp(argv[i], "--woff") || !strcmp(argv[i], "-woff")) {
|
|
g->disableWarnings = true;
|
|
g->emitPerfWarnings = false;
|
|
} else if (!strcmp(argv[i], "--werror"))
|
|
g->warningsAsErrors = true;
|
|
else if (!strncmp(argv[i], "--error-limit=", 14)) {
|
|
int errLimit = atoi(argv[i] + 14);
|
|
if (errLimit >= 0)
|
|
g->errorLimit = errLimit;
|
|
else
|
|
errorHandler.AddError("Invalid value for --error-limit: \"%d\" -- "
|
|
"value cannot be a negative number.",
|
|
errLimit);
|
|
} else if (!strcmp(argv[i], "--nowrap"))
|
|
g->disableLineWrap = true;
|
|
else if (!strcmp(argv[i], "--wno-perf") || !strcmp(argv[i], "-wno-perf"))
|
|
g->emitPerfWarnings = false;
|
|
else if (!strcmp(argv[i], "-o")) {
|
|
if (++i != argc) {
|
|
outFileName = argv[i];
|
|
} else {
|
|
errorHandler.AddError("No output file specified after -o option.");
|
|
}
|
|
} else if (!strncmp(argv[i], "--outfile=", 10))
|
|
outFileName = argv[i] + strlen("--outfile=");
|
|
else if (!strcmp(argv[i], "-h")) {
|
|
if (++i != argc) {
|
|
headerFileName = argv[i];
|
|
} else {
|
|
errorHandler.AddError("No header file name specified after -h option.");
|
|
}
|
|
} else if (!strncmp(argv[i], "--header-outfile=", 17)) {
|
|
headerFileName = argv[i] + strlen("--header-outfile=");
|
|
} else if (!strcmp(argv[i], "-O0")) {
|
|
g->opt.level = 0;
|
|
g->codegenOptLevel = Globals::CodegenOptLevel::None;
|
|
} else if (!strcmp(argv[i], "-O") || !strcmp(argv[i], "-O1") || !strcmp(argv[i], "-O2") ||
|
|
!strcmp(argv[i], "-O3")) {
|
|
g->opt.level = 1;
|
|
g->codegenOptLevel = Globals::CodegenOptLevel::Aggressive;
|
|
if (!strcmp(argv[i], "-O1"))
|
|
g->opt.disableCoherentControlFlow = true;
|
|
} else if (!strcmp(argv[i], "-")) {
|
|
file = argv[i];
|
|
} else if (!strcmp(argv[i], "--nostdlib"))
|
|
g->includeStdlib = false;
|
|
else if (!strcmp(argv[i], "--nocpp"))
|
|
g->runCPP = false;
|
|
else if (!strcmp(argv[i], "--pic"))
|
|
flags |= Module::GeneratePIC;
|
|
#ifndef ISPC_IS_HOST_WINDOWS
|
|
else if (!strcmp(argv[i], "--colored-output"))
|
|
g->forceColoredOutput = true;
|
|
#endif // !ISPC_IS_HOST_WINDOWS
|
|
else if (!strcmp(argv[i], "--quiet"))
|
|
g->quiet = true;
|
|
else if (!strcmp(argv[i], "--yydebug")) {
|
|
yydebug = 1;
|
|
} else if (!strcmp(argv[i], "-MMM")) {
|
|
if (++i != argc) {
|
|
depsFileName = argv[i];
|
|
flags |= Module::GenerateFlatDeps;
|
|
} else {
|
|
errorHandler.AddError("No output file name specified after -MMM option.");
|
|
}
|
|
} else if (!strcmp(argv[i], "-M")) {
|
|
flags |= Module::GenerateMakeRuleForDeps | Module::OutputDepsToStdout;
|
|
} else if (!strcmp(argv[i], "-MF")) {
|
|
depsFileName = nullptr;
|
|
if (++i != argc) {
|
|
depsFileName = argv[i];
|
|
} else {
|
|
errorHandler.AddError("No output file name specified after -MF option.");
|
|
}
|
|
} else if (!strcmp(argv[i], "-MT")) {
|
|
depsTargetName = nullptr;
|
|
if (++i != argc) {
|
|
depsTargetName = argv[i];
|
|
} else {
|
|
errorHandler.AddError("No target name specified after -MT option.");
|
|
}
|
|
} else if (!strcmp(argv[i], "--dev-stub")) {
|
|
if (++i != argc) {
|
|
devStubFileName = argv[i];
|
|
} else {
|
|
errorHandler.AddError("No output file name specified after --dev-stub option.");
|
|
}
|
|
} else if (!strcmp(argv[i], "--host-stub")) {
|
|
if (++i != argc) {
|
|
hostStubFileName = argv[i];
|
|
} else {
|
|
errorHandler.AddError("No output file name specified after --host-stub option.");
|
|
}
|
|
} else if (strncmp(argv[i], "--debug-phase=", 14) == 0) {
|
|
errorHandler.AddWarning("Adding debug phases may change the way PassManager"
|
|
"handles the phases and it may possibly make some bugs go"
|
|
"away or introduce the new ones.");
|
|
g->debug_stages = ParsingPhases(argv[i] + strlen("--debug-phase="), errorHandler);
|
|
} else if (strncmp(argv[i], "--dump-file=", 12) == 0) {
|
|
g->dumpFile = true;
|
|
g->dumpFilePath = ParsePath(argv[i] + strlen("--dump-file="), errorHandler);
|
|
} else if (strncmp(argv[i], "--dump-file", 11) == 0) {
|
|
g->dumpFile = true;
|
|
}
|
|
|
|
else if (strncmp(argv[i], "--off-phase=", 12) == 0) {
|
|
g->off_stages = ParsingPhases(argv[i] + strlen("--off-phase="), errorHandler);
|
|
#ifdef ISPC_XE_ENABLED
|
|
} else if (!strncmp(argv[i], "--vc-options=", 13)) {
|
|
g->vcOpts = argv[i] + strlen("--vc-options=");
|
|
} else if (!strncmp(argv[i], "--xe-stack-mem-size=", 20)) {
|
|
unsigned int memSize = atoi(argv[i] + 20);
|
|
g->stackMemSize = memSize;
|
|
#endif
|
|
} else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
|
|
lPrintVersion();
|
|
return 0;
|
|
} else if (argv[i][0] == '-') {
|
|
errorHandler.AddError("Unknown option \"%s\".", argv[i]);
|
|
} else {
|
|
if (file != NULL) {
|
|
errorHandler.AddError("Multiple input files specified on command "
|
|
"line: \"%s\" and \"%s\".",
|
|
file, argv[i]);
|
|
} else {
|
|
file = argv[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit accumulted errors and warnings, if any.
|
|
// All the rest of errors and warnigns will be processed in regullar way.
|
|
errorHandler.Emit();
|
|
|
|
if (file == NULL) {
|
|
Error(SourcePos(), "No input file were specified. To read text from stdin use \"-\" as file name.");
|
|
exit(1);
|
|
}
|
|
|
|
// If [no]discard-value-names is explicitly specified, then use this value.
|
|
// Otherwise enable it only if the output is some form of bitcode.
|
|
// Note that discarding value names significantly saves compile time and memory consumption.
|
|
if (discardValueNames == BooleanOptValue::enabled) {
|
|
g->ctx->setDiscardValueNames(true);
|
|
} else if (discardValueNames == BooleanOptValue::disabled) {
|
|
g->ctx->setDiscardValueNames(false);
|
|
} else {
|
|
if (ot == Module::Bitcode || ot == Module::BitcodeText) {
|
|
g->ctx->setDiscardValueNames(false);
|
|
} else {
|
|
g->ctx->setDiscardValueNames(true);
|
|
}
|
|
}
|
|
|
|
// Default settings for PS4
|
|
if (g->target_os == TargetOS::ps4 || g->target_os == TargetOS::ps5) {
|
|
flags |= Module::GeneratePIC;
|
|
if (!cpu) {
|
|
if (g->target_os == TargetOS::ps4) {
|
|
// Default for PS4 is btver2, but do not enforce it.
|
|
cpu = "btver2";
|
|
} else {
|
|
// Default for PS5 is znver2, but do not enforce it.
|
|
cpu = "znver2";
|
|
}
|
|
}
|
|
if (arch != Arch::x86_64) {
|
|
Warning(SourcePos(), "--arch switch is ignored for PS4/PS5 target OS. x86-64 arch is used.");
|
|
arch = Arch::x86_64;
|
|
}
|
|
}
|
|
|
|
// Default setting for "custom_linux"
|
|
if (g->target_os == TargetOS::custom_linux) {
|
|
flags |= Module::GeneratePIC;
|
|
if (!cpu) {
|
|
cpu = "cortex-a57";
|
|
}
|
|
if (targets.empty()) {
|
|
targets.push_back(ISPCTarget::neon_i32x4);
|
|
std::string target_string = ISPCTargetToString(targets[0]);
|
|
Warning(SourcePos(),
|
|
"No --target specified on command-line."
|
|
" Using \"%s\".",
|
|
target_string.c_str());
|
|
}
|
|
}
|
|
|
|
if (g->enableFuzzTest) {
|
|
if (g->fuzzTestSeed == -1) {
|
|
#ifdef ISPC_HOST_IS_WINDOWS
|
|
int seed = (unsigned)time(NULL);
|
|
#else
|
|
int seed = getpid();
|
|
#endif
|
|
g->fuzzTestSeed = seed;
|
|
Warning(SourcePos(), "Using seed %d for fuzz testing", g->fuzzTestSeed);
|
|
}
|
|
#ifdef ISPC_HOST_IS_WINDOWS
|
|
srand(g->fuzzTestSeed);
|
|
#else
|
|
srand48(g->fuzzTestSeed);
|
|
#endif
|
|
}
|
|
|
|
if (depsFileName != NULL)
|
|
flags &= ~Module::OutputDepsToStdout;
|
|
|
|
if (depsFileName != NULL && 0 == (flags & (Module::GenerateFlatDeps | Module::GenerateMakeRuleForDeps))) {
|
|
Warning(SourcePos(), "Dependency file name specified with -MF, but no "
|
|
"mode specified; did you forget to specify -M or -MMM? "
|
|
"No dependency output will be generated.");
|
|
depsFileName = NULL;
|
|
}
|
|
|
|
if ((Module::GenerateFlatDeps | Module::GenerateMakeRuleForDeps) ==
|
|
(flags & (Module::GenerateFlatDeps | Module::GenerateMakeRuleForDeps))) {
|
|
Warning(SourcePos(), "Both -M and -MMM specified on the command line. "
|
|
"-MMM takes precedence.");
|
|
flags &= Module::GenerateMakeRuleForDeps;
|
|
}
|
|
|
|
if (g->onlyCPP && outFileName == nullptr) {
|
|
outFileName = "-"; // Assume stdout by default (-E mode)
|
|
}
|
|
|
|
if (outFileName == NULL && headerFileName == NULL &&
|
|
(depsFileName == NULL && 0 == (flags & Module::OutputDepsToStdout)) && hostStubFileName == NULL &&
|
|
devStubFileName == NULL) {
|
|
Warning(SourcePos(), "No output file or header file name specified. "
|
|
"Program will be compiled and warnings/errors will "
|
|
"be issued, but no output will be generated.");
|
|
}
|
|
|
|
if (g->target_os == TargetOS::windows && (flags & Module::GeneratePIC) != 0) {
|
|
Warning(SourcePos(), "--pic switch for Windows target will be ignored.");
|
|
}
|
|
|
|
if (g->target_os != TargetOS::windows && g->dllExport) {
|
|
Warning(SourcePos(), "--dllexport switch will be ignored, as the target OS is not Windows.");
|
|
}
|
|
|
|
if (vectorCall != BooleanOptValue::none &&
|
|
(g->target_os != TargetOS::windows ||
|
|
// This is a hacky check. Arch is properly set later, so we rely that default means x86_64.
|
|
(arch != Arch::x86_64 && arch != Arch::none))) {
|
|
Warning(SourcePos(), "--vectorcall/--no-vectorcall are supported only for x86_64 Windows target, so these "
|
|
"options will be ignored.");
|
|
}
|
|
|
|
if (targets.size() > 1)
|
|
g->isMultiTargetCompilation = true;
|
|
|
|
if ((ot == Module::Asm) && (intelAsmSyntax != NULL)) {
|
|
std::vector<const char *> Args(3);
|
|
Args[0] = "ispc (LLVM option parsing)";
|
|
Args[2] = nullptr;
|
|
if (std::string(intelAsmSyntax) == "intel")
|
|
Args[1] = "--x86-asm-syntax=intel";
|
|
else
|
|
Args[1] = "--x86-asm-syntax=att";
|
|
llvm::cl::ParseCommandLineOptions(2, Args.data());
|
|
}
|
|
|
|
for (auto target : targets) {
|
|
if (target == ISPCTarget::wasm_i32x4) {
|
|
Assert(targets.size() == 1 && "wasm32 supports only one target: i32x4");
|
|
arch = Arch::wasm32;
|
|
g->target_os = TargetOS::web;
|
|
}
|
|
#ifdef ISPC_XE_ENABLED
|
|
if (ISPCTargetIsGen(target)) {
|
|
Assert(targets.size() == 1 && "multi-target is not supported for Xe targets yet.");
|
|
// Generate .spv for Xe target instead of object by default.
|
|
if (ot == Module::Object) {
|
|
Warning(SourcePos(), "Emitting spir-v file for Xe targets.");
|
|
ot = Module::SPIRV;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// This needs to happen after the TargetOS is decided.
|
|
setCallingConv(vectorCall, arch);
|
|
if (g->enableTimeTrace) {
|
|
llvm::timeTraceProfilerInitialize(g->timeTraceGranularity, "ispc");
|
|
}
|
|
int ret = 0;
|
|
{
|
|
llvm::TimeTraceScope TimeScope("ExecuteCompiler");
|
|
ret = Module::CompileAndOutput(file, arch, cpu, targets, flags, ot, outFileName, headerFileName, depsFileName,
|
|
depsTargetName, hostStubFileName, devStubFileName);
|
|
}
|
|
|
|
if (g->enableTimeTrace) {
|
|
// Write to file only if compilation is successfull.
|
|
if ((ret == 0) && (outFileName != NULL)) {
|
|
writeCompileTimeFile(outFileName);
|
|
}
|
|
llvm::timeTraceProfilerCleanup();
|
|
}
|
|
|
|
// Free all bookkeeped objects.
|
|
BookKeeper::in().freeAll();
|
|
|
|
return ret;
|
|
}
|