mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-09 21:09:10 +00:00
8f94786ceb
Some checks failed
benchmark / callgrind (map[branch:main name:luau-lang/benchmark-data], ubuntu-22.04) (push) Has been cancelled
build / macos (push) Has been cancelled
build / macos-arm (push) Has been cancelled
build / ubuntu (push) Has been cancelled
build / windows (Win32) (push) Has been cancelled
build / windows (x64) (push) Has been cancelled
build / coverage (push) Has been cancelled
build / web (push) Has been cancelled
release / macos (push) Has been cancelled
release / ubuntu (push) Has been cancelled
release / windows (push) Has been cancelled
release / web (push) Has been cancelled
This PR refactors the CLI folder to use the same project split between include and src directories that we have for all the other artifacts in luau. It also includes the require-by-string implementation we already have as a feature of `Luau.CLI.lib`. Both of these changes are targeted at making it easier for embedding projects to setup an effective equivalent to the standalone `luau` executable with whatever runtime libraries they need attached and without having to unnecessarily duplicate code from luau itself.
697 lines
22 KiB
C++
697 lines
22 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#include "lua.h"
|
|
#include "lualib.h"
|
|
|
|
#include "Luau/CodeGen.h"
|
|
#include "Luau/Compiler.h"
|
|
#include "Luau/BytecodeBuilder.h"
|
|
#include "Luau/Parser.h"
|
|
#include "Luau/TimeTrace.h"
|
|
|
|
#include "Luau/FileUtils.h"
|
|
#include "Luau/Flags.h"
|
|
|
|
#include <memory>
|
|
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
LUAU_FASTFLAG(DebugLuauTimeTracing)
|
|
|
|
enum class CompileFormat
|
|
{
|
|
Text,
|
|
Binary,
|
|
Remarks,
|
|
Codegen, // Prints annotated native code including IR and assembly
|
|
CodegenAsm, // Prints annotated native code assembly
|
|
CodegenIr, // Prints annotated native code IR
|
|
CodegenVerbose, // Prints annotated native code including IR, assembly and outlined code
|
|
CodegenNull,
|
|
Null
|
|
};
|
|
|
|
enum class RecordStats
|
|
{
|
|
None,
|
|
Total,
|
|
File,
|
|
Function
|
|
};
|
|
|
|
struct GlobalOptions
|
|
{
|
|
int optimizationLevel = 1;
|
|
int debugLevel = 1;
|
|
int typeInfoLevel = 0;
|
|
|
|
const char* vectorLib = nullptr;
|
|
const char* vectorCtor = nullptr;
|
|
const char* vectorType = nullptr;
|
|
} globalOptions;
|
|
|
|
static Luau::CompileOptions copts()
|
|
{
|
|
Luau::CompileOptions result = {};
|
|
result.optimizationLevel = globalOptions.optimizationLevel;
|
|
result.debugLevel = globalOptions.debugLevel;
|
|
result.typeInfoLevel = globalOptions.typeInfoLevel;
|
|
|
|
result.vectorLib = globalOptions.vectorLib;
|
|
result.vectorCtor = globalOptions.vectorCtor;
|
|
result.vectorType = globalOptions.vectorType;
|
|
|
|
return result;
|
|
}
|
|
|
|
static std::optional<CompileFormat> getCompileFormat(const char* name)
|
|
{
|
|
if (strcmp(name, "text") == 0)
|
|
return CompileFormat::Text;
|
|
else if (strcmp(name, "binary") == 0)
|
|
return CompileFormat::Binary;
|
|
else if (strcmp(name, "text") == 0)
|
|
return CompileFormat::Text;
|
|
else if (strcmp(name, "remarks") == 0)
|
|
return CompileFormat::Remarks;
|
|
else if (strcmp(name, "codegen") == 0)
|
|
return CompileFormat::Codegen;
|
|
else if (strcmp(name, "codegenasm") == 0)
|
|
return CompileFormat::CodegenAsm;
|
|
else if (strcmp(name, "codegenir") == 0)
|
|
return CompileFormat::CodegenIr;
|
|
else if (strcmp(name, "codegenverbose") == 0)
|
|
return CompileFormat::CodegenVerbose;
|
|
else if (strcmp(name, "codegennull") == 0)
|
|
return CompileFormat::CodegenNull;
|
|
else if (strcmp(name, "null") == 0)
|
|
return CompileFormat::Null;
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
|
|
static void report(const char* name, const Luau::Location& location, const char* type, const char* message)
|
|
{
|
|
fprintf(stderr, "%s(%d,%d): %s: %s\n", name, location.begin.line + 1, location.begin.column + 1, type, message);
|
|
}
|
|
|
|
static void reportError(const char* name, const Luau::ParseError& error)
|
|
{
|
|
report(name, error.getLocation(), "SyntaxError", error.what());
|
|
}
|
|
|
|
static void reportError(const char* name, const Luau::CompileError& error)
|
|
{
|
|
report(name, error.getLocation(), "CompileError", error.what());
|
|
}
|
|
|
|
static std::string getCodegenAssembly(
|
|
const char* name,
|
|
const std::string& bytecode,
|
|
Luau::CodeGen::AssemblyOptions options,
|
|
Luau::CodeGen::LoweringStats* stats
|
|
)
|
|
{
|
|
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
|
|
lua_State* L = globalState.get();
|
|
|
|
if (luau_load(L, name, bytecode.data(), bytecode.size(), 0) == 0)
|
|
return Luau::CodeGen::getAssembly(L, -1, options, stats);
|
|
|
|
fprintf(stderr, "Error loading bytecode %s\n", name);
|
|
return "";
|
|
}
|
|
|
|
static void annotateInstruction(void* context, std::string& text, int fid, int instpos)
|
|
{
|
|
Luau::BytecodeBuilder& bcb = *(Luau::BytecodeBuilder*)context;
|
|
|
|
bcb.annotateInstruction(text, fid, instpos);
|
|
}
|
|
|
|
struct CompileStats
|
|
{
|
|
size_t lines;
|
|
size_t bytecode;
|
|
size_t bytecodeInstructionCount;
|
|
size_t codegen;
|
|
|
|
double readTime;
|
|
double miscTime;
|
|
double parseTime;
|
|
double compileTime;
|
|
double codegenTime;
|
|
|
|
Luau::CodeGen::LoweringStats lowerStats;
|
|
|
|
CompileStats& operator+=(const CompileStats& that)
|
|
{
|
|
this->lines += that.lines;
|
|
this->bytecode += that.bytecode;
|
|
this->bytecodeInstructionCount += that.bytecodeInstructionCount;
|
|
this->codegen += that.codegen;
|
|
this->readTime += that.readTime;
|
|
this->miscTime += that.miscTime;
|
|
this->parseTime += that.parseTime;
|
|
this->compileTime += that.compileTime;
|
|
this->codegenTime += that.codegenTime;
|
|
this->lowerStats += that.lowerStats;
|
|
|
|
return *this;
|
|
}
|
|
|
|
CompileStats operator+(const CompileStats& other) const
|
|
{
|
|
CompileStats result(*this);
|
|
result += other;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
#define WRITE_NAME(INDENT, NAME) fprintf(fp, INDENT "\"" #NAME "\": ")
|
|
#define WRITE_PAIR(INDENT, NAME, FORMAT) fprintf(fp, INDENT "\"" #NAME "\": " FORMAT, stats.NAME)
|
|
#define WRITE_PAIR_STRING(INDENT, NAME, FORMAT) fprintf(fp, INDENT "\"" #NAME "\": " FORMAT, stats.NAME.c_str())
|
|
|
|
void serializeFunctionStats(FILE* fp, const Luau::CodeGen::FunctionStats& stats)
|
|
{
|
|
fprintf(fp, " {\n");
|
|
WRITE_PAIR_STRING(" ", name, "\"%s\",\n");
|
|
WRITE_PAIR(" ", line, "%d,\n");
|
|
WRITE_PAIR(" ", bcodeCount, "%u,\n");
|
|
WRITE_PAIR(" ", irCount, "%u,\n");
|
|
WRITE_PAIR(" ", asmCount, "%u,\n");
|
|
WRITE_PAIR(" ", asmSize, "%u,\n");
|
|
|
|
WRITE_NAME(" ", bytecodeSummary);
|
|
const size_t nestingLimit = stats.bytecodeSummary.size();
|
|
|
|
if (nestingLimit == 0)
|
|
fprintf(fp, "[]");
|
|
else
|
|
{
|
|
fprintf(fp, "[\n");
|
|
for (size_t i = 0; i < nestingLimit; ++i)
|
|
{
|
|
const std::vector<unsigned>& counts = stats.bytecodeSummary[i];
|
|
fprintf(fp, " [");
|
|
for (size_t j = 0; j < counts.size(); ++j)
|
|
{
|
|
fprintf(fp, "%u", counts[j]);
|
|
if (j < counts.size() - 1)
|
|
fprintf(fp, ", ");
|
|
}
|
|
fprintf(fp, "]");
|
|
if (i < stats.bytecodeSummary.size() - 1)
|
|
fprintf(fp, ",\n");
|
|
}
|
|
fprintf(fp, "\n ]");
|
|
}
|
|
|
|
fprintf(fp, "\n }");
|
|
}
|
|
|
|
void serializeBlockLinearizationStats(FILE* fp, const Luau::CodeGen::BlockLinearizationStats& stats)
|
|
{
|
|
fprintf(fp, "{\n");
|
|
|
|
WRITE_PAIR(" ", constPropInstructionCount, "%u,\n");
|
|
WRITE_PAIR(" ", timeSeconds, "%f\n");
|
|
|
|
fprintf(fp, " }");
|
|
}
|
|
|
|
void serializeLoweringStats(FILE* fp, const Luau::CodeGen::LoweringStats& stats)
|
|
{
|
|
fprintf(fp, "{\n");
|
|
|
|
WRITE_PAIR(" ", totalFunctions, "%u,\n");
|
|
WRITE_PAIR(" ", skippedFunctions, "%u,\n");
|
|
WRITE_PAIR(" ", spillsToSlot, "%d,\n");
|
|
WRITE_PAIR(" ", spillsToRestore, "%d,\n");
|
|
WRITE_PAIR(" ", maxSpillSlotsUsed, "%u,\n");
|
|
WRITE_PAIR(" ", blocksPreOpt, "%u,\n");
|
|
WRITE_PAIR(" ", blocksPostOpt, "%u,\n");
|
|
WRITE_PAIR(" ", maxBlockInstructions, "%u,\n");
|
|
WRITE_PAIR(" ", regAllocErrors, "%d,\n");
|
|
WRITE_PAIR(" ", loweringErrors, "%d,\n");
|
|
|
|
WRITE_NAME(" ", blockLinearizationStats);
|
|
serializeBlockLinearizationStats(fp, stats.blockLinearizationStats);
|
|
fprintf(fp, ",\n");
|
|
|
|
WRITE_NAME(" ", functions);
|
|
const size_t functionCount = stats.functions.size();
|
|
|
|
if (functionCount == 0)
|
|
fprintf(fp, "[]");
|
|
else
|
|
{
|
|
fprintf(fp, "[\n");
|
|
for (size_t i = 0; i < functionCount; ++i)
|
|
{
|
|
serializeFunctionStats(fp, stats.functions[i]);
|
|
if (i < functionCount - 1)
|
|
fprintf(fp, ",\n");
|
|
}
|
|
fprintf(fp, "\n ]");
|
|
}
|
|
|
|
fprintf(fp, "\n }");
|
|
}
|
|
|
|
void serializeCompileStats(FILE* fp, const CompileStats& stats)
|
|
{
|
|
fprintf(fp, "{\n");
|
|
|
|
WRITE_PAIR(" ", lines, "%zu,\n");
|
|
WRITE_PAIR(" ", bytecode, "%zu,\n");
|
|
WRITE_PAIR(" ", bytecodeInstructionCount, "%zu,\n");
|
|
WRITE_PAIR(" ", codegen, "%zu,\n");
|
|
WRITE_PAIR(" ", readTime, "%f,\n");
|
|
WRITE_PAIR(" ", miscTime, "%f,\n");
|
|
WRITE_PAIR(" ", parseTime, "%f,\n");
|
|
WRITE_PAIR(" ", compileTime, "%f,\n");
|
|
WRITE_PAIR(" ", codegenTime, "%f,\n");
|
|
|
|
WRITE_NAME(" ", lowerStats);
|
|
serializeLoweringStats(fp, stats.lowerStats);
|
|
|
|
fprintf(fp, "\n }");
|
|
}
|
|
|
|
#undef WRITE_NAME
|
|
#undef WRITE_PAIR
|
|
#undef WRITE_PAIR_STRING
|
|
|
|
static double recordDeltaTime(double& timer)
|
|
{
|
|
double now = Luau::TimeTrace::getClock();
|
|
double delta = now - timer;
|
|
timer = now;
|
|
return delta;
|
|
}
|
|
|
|
static bool compileFile(const char* name, CompileFormat format, Luau::CodeGen::AssemblyOptions::Target assemblyTarget, CompileStats& stats)
|
|
{
|
|
double currts = Luau::TimeTrace::getClock();
|
|
|
|
std::optional<std::string> source = readFile(name);
|
|
if (!source)
|
|
{
|
|
fprintf(stderr, "Error opening %s\n", name);
|
|
return false;
|
|
}
|
|
|
|
stats.readTime += recordDeltaTime(currts);
|
|
|
|
// NOTE: Normally, you should use Luau::compile or luau_compile (see lua_require as an example)
|
|
// This function is much more complicated because it supports many output human-readable formats through internal interfaces
|
|
|
|
try
|
|
{
|
|
Luau::BytecodeBuilder bcb;
|
|
|
|
Luau::CodeGen::AssemblyOptions options;
|
|
options.target = assemblyTarget;
|
|
options.outputBinary = format == CompileFormat::CodegenNull;
|
|
|
|
if (!options.outputBinary)
|
|
{
|
|
options.includeAssembly = format != CompileFormat::CodegenIr;
|
|
options.includeIr = format != CompileFormat::CodegenAsm;
|
|
options.includeIrTypes = format != CompileFormat::CodegenAsm;
|
|
options.includeOutlinedCode = format == CompileFormat::CodegenVerbose;
|
|
}
|
|
|
|
options.annotator = annotateInstruction;
|
|
options.annotatorContext = &bcb;
|
|
|
|
if (format == CompileFormat::Text)
|
|
{
|
|
bcb.setDumpFlags(
|
|
Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
|
|
Luau::BytecodeBuilder::Dump_Remarks | Luau::BytecodeBuilder::Dump_Types
|
|
);
|
|
bcb.setDumpSource(*source);
|
|
}
|
|
else if (format == CompileFormat::Remarks)
|
|
{
|
|
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
|
|
bcb.setDumpSource(*source);
|
|
}
|
|
else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenAsm || format == CompileFormat::CodegenIr || format == CompileFormat::CodegenVerbose)
|
|
{
|
|
bcb.setDumpFlags(
|
|
Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
|
|
Luau::BytecodeBuilder::Dump_Remarks
|
|
);
|
|
bcb.setDumpSource(*source);
|
|
}
|
|
|
|
stats.miscTime += recordDeltaTime(currts);
|
|
|
|
Luau::Allocator allocator;
|
|
Luau::AstNameTable names(allocator);
|
|
Luau::ParseResult result = Luau::Parser::parse(source->c_str(), source->size(), names, allocator);
|
|
|
|
if (!result.errors.empty())
|
|
throw Luau::ParseErrors(result.errors);
|
|
|
|
stats.lines += result.lines;
|
|
stats.parseTime += recordDeltaTime(currts);
|
|
|
|
Luau::compileOrThrow(bcb, result, names, copts());
|
|
stats.bytecode += bcb.getBytecode().size();
|
|
stats.bytecodeInstructionCount = bcb.getTotalInstructionCount();
|
|
stats.compileTime += recordDeltaTime(currts);
|
|
|
|
switch (format)
|
|
{
|
|
case CompileFormat::Text:
|
|
printf("%s", bcb.dumpEverything().c_str());
|
|
break;
|
|
case CompileFormat::Remarks:
|
|
printf("%s", bcb.dumpSourceRemarks().c_str());
|
|
break;
|
|
case CompileFormat::Binary:
|
|
fwrite(bcb.getBytecode().data(), 1, bcb.getBytecode().size(), stdout);
|
|
break;
|
|
case CompileFormat::Codegen:
|
|
case CompileFormat::CodegenAsm:
|
|
case CompileFormat::CodegenIr:
|
|
case CompileFormat::CodegenVerbose:
|
|
printf("%s", getCodegenAssembly(name, bcb.getBytecode(), options, &stats.lowerStats).c_str());
|
|
break;
|
|
case CompileFormat::CodegenNull:
|
|
stats.codegen += getCodegenAssembly(name, bcb.getBytecode(), options, &stats.lowerStats).size();
|
|
stats.codegenTime += recordDeltaTime(currts);
|
|
break;
|
|
case CompileFormat::Null:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (Luau::ParseErrors& e)
|
|
{
|
|
for (auto& error : e.getErrors())
|
|
reportError(name, error);
|
|
return false;
|
|
}
|
|
catch (Luau::CompileError& e)
|
|
{
|
|
reportError(name, e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void displayHelp(const char* argv0)
|
|
{
|
|
printf("Usage: %s [--mode] [options] [file list]\n", argv0);
|
|
printf("\n");
|
|
printf("Available modes:\n");
|
|
printf(" binary, text, remarks, codegen\n");
|
|
printf("\n");
|
|
printf("Available options:\n");
|
|
printf(" -h, --help: Display this usage message.\n");
|
|
printf(" -O<n>: compile with optimization level n (default 1, n should be between 0 and 2).\n");
|
|
printf(" -g<n>: compile with debug level n (default 1, n should be between 0 and 2).\n");
|
|
printf(" --target=<target>: compile code for specific architecture (a64, x64, a64_nf, x64_ms).\n");
|
|
printf(" --timetrace: record compiler time tracing information into trace.json\n");
|
|
printf(" --record-stats=<granularity>: granularity of compilation stats (total, file, function).\n");
|
|
printf(" --bytecode-summary: Compute bytecode operation distribution.\n");
|
|
printf(" --stats-file=<filename>: file in which compilation stats will be recored (default 'stats.json').\n");
|
|
printf(" --vector-lib=<name>: name of the library providing vector type operations.\n");
|
|
printf(" --vector-ctor=<name>: name of the function constructing a vector value.\n");
|
|
printf(" --vector-type=<name>: name of the vector type.\n");
|
|
}
|
|
|
|
static int assertionHandler(const char* expr, const char* file, int line, const char* function)
|
|
{
|
|
printf("%s(%d): ASSERTION FAILED: %s\n", file, line, expr);
|
|
return 1;
|
|
}
|
|
|
|
std::string escapeFilename(const std::string& filename)
|
|
{
|
|
std::string escaped;
|
|
escaped.reserve(filename.size());
|
|
|
|
for (const char ch : filename)
|
|
{
|
|
switch (ch)
|
|
{
|
|
case '\\':
|
|
escaped.push_back('/');
|
|
break;
|
|
case '"':
|
|
escaped.push_back('\\');
|
|
escaped.push_back(ch);
|
|
break;
|
|
default:
|
|
escaped.push_back(ch);
|
|
}
|
|
}
|
|
|
|
return escaped;
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
Luau::assertHandler() = assertionHandler;
|
|
|
|
setLuauFlagsDefault();
|
|
|
|
CompileFormat compileFormat = CompileFormat::Text;
|
|
Luau::CodeGen::AssemblyOptions::Target assemblyTarget = Luau::CodeGen::AssemblyOptions::Host;
|
|
RecordStats recordStats = RecordStats::None;
|
|
std::string statsFile("stats.json");
|
|
bool bytecodeSummary = false;
|
|
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
|
|
{
|
|
displayHelp(argv[0]);
|
|
return 0;
|
|
}
|
|
else if (strncmp(argv[i], "-O", 2) == 0)
|
|
{
|
|
int level = atoi(argv[i] + 2);
|
|
if (level < 0 || level > 2)
|
|
{
|
|
fprintf(stderr, "Error: Optimization level must be between 0 and 2 inclusive.\n");
|
|
return 1;
|
|
}
|
|
globalOptions.optimizationLevel = level;
|
|
}
|
|
else if (strncmp(argv[i], "-g", 2) == 0)
|
|
{
|
|
int level = atoi(argv[i] + 2);
|
|
if (level < 0 || level > 2)
|
|
{
|
|
fprintf(stderr, "Error: Debug level must be between 0 and 2 inclusive.\n");
|
|
return 1;
|
|
}
|
|
globalOptions.debugLevel = level;
|
|
}
|
|
else if (strncmp(argv[i], "-t", 2) == 0)
|
|
{
|
|
int level = atoi(argv[i] + 2);
|
|
if (level < 0 || level > 1)
|
|
{
|
|
fprintf(stderr, "Error: Type info level must be between 0 and 1 inclusive.\n");
|
|
return 1;
|
|
}
|
|
globalOptions.typeInfoLevel = level;
|
|
}
|
|
else if (strncmp(argv[i], "--target=", 9) == 0)
|
|
{
|
|
const char* value = argv[i] + 9;
|
|
|
|
if (strcmp(value, "a64") == 0)
|
|
assemblyTarget = Luau::CodeGen::AssemblyOptions::A64;
|
|
else if (strcmp(value, "a64_nf") == 0)
|
|
assemblyTarget = Luau::CodeGen::AssemblyOptions::A64_NoFeatures;
|
|
else if (strcmp(value, "x64") == 0)
|
|
assemblyTarget = Luau::CodeGen::AssemblyOptions::X64_SystemV;
|
|
else if (strcmp(value, "x64_ms") == 0)
|
|
assemblyTarget = Luau::CodeGen::AssemblyOptions::X64_Windows;
|
|
else
|
|
{
|
|
fprintf(stderr, "Error: unknown target\n");
|
|
return 1;
|
|
}
|
|
}
|
|
else if (strcmp(argv[i], "--timetrace") == 0)
|
|
{
|
|
FFlag::DebugLuauTimeTracing.value = true;
|
|
}
|
|
else if (strncmp(argv[i], "--record-stats=", 15) == 0)
|
|
{
|
|
const char* value = argv[i] + 15;
|
|
|
|
if (strcmp(value, "total") == 0)
|
|
recordStats = RecordStats::Total;
|
|
else if (strcmp(value, "file") == 0)
|
|
recordStats = RecordStats::File;
|
|
else if (strcmp(value, "function") == 0)
|
|
recordStats = RecordStats::Function;
|
|
else
|
|
{
|
|
fprintf(stderr, "Error: unknown 'granularity' for '--record-stats'.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
else if (strncmp(argv[i], "--bytecode-summary", 18) == 0)
|
|
{
|
|
bytecodeSummary = true;
|
|
}
|
|
else if (strncmp(argv[i], "--stats-file=", 13) == 0)
|
|
{
|
|
statsFile = argv[i] + 13;
|
|
|
|
if (statsFile.size() == 0)
|
|
{
|
|
fprintf(stderr, "Error: filename missing for '--stats-file'.\n\n");
|
|
return 1;
|
|
}
|
|
}
|
|
else if (strncmp(argv[i], "--fflags=", 9) == 0)
|
|
{
|
|
setLuauFlags(argv[i] + 9);
|
|
}
|
|
else if (strncmp(argv[i], "--vector-lib=", 13) == 0)
|
|
{
|
|
globalOptions.vectorLib = argv[i] + 13;
|
|
}
|
|
else if (strncmp(argv[i], "--vector-ctor=", 14) == 0)
|
|
{
|
|
globalOptions.vectorCtor = argv[i] + 14;
|
|
}
|
|
else if (strncmp(argv[i], "--vector-type=", 14) == 0)
|
|
{
|
|
globalOptions.vectorType = argv[i] + 14;
|
|
}
|
|
else if (argv[i][0] == '-' && argv[i][1] == '-' && getCompileFormat(argv[i] + 2))
|
|
{
|
|
compileFormat = *getCompileFormat(argv[i] + 2);
|
|
}
|
|
else if (argv[i][0] == '-')
|
|
{
|
|
fprintf(stderr, "Error: Unrecognized option '%s'.\n\n", argv[i]);
|
|
displayHelp(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (bytecodeSummary && (recordStats != RecordStats::Function))
|
|
{
|
|
fprintf(stderr, "'Error: Required '--record-stats=function' for '--bytecode-summary'.\n");
|
|
return 1;
|
|
}
|
|
|
|
#if !defined(LUAU_ENABLE_TIME_TRACE)
|
|
if (FFlag::DebugLuauTimeTracing)
|
|
{
|
|
fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
const std::vector<std::string> files = getSourceFiles(argc, argv);
|
|
|
|
#ifdef _WIN32
|
|
if (compileFormat == CompileFormat::Binary)
|
|
_setmode(_fileno(stdout), _O_BINARY);
|
|
#endif
|
|
|
|
const size_t fileCount = files.size();
|
|
CompileStats stats = {};
|
|
|
|
std::vector<CompileStats> fileStats;
|
|
if (recordStats == RecordStats::File || recordStats == RecordStats::Function)
|
|
fileStats.reserve(fileCount);
|
|
|
|
int failed = 0;
|
|
unsigned functionStats = (recordStats == RecordStats::Function ? Luau::CodeGen::FunctionStats_Enable : 0) |
|
|
(bytecodeSummary ? Luau::CodeGen::FunctionStats_BytecodeSummary : 0);
|
|
for (const std::string& path : files)
|
|
{
|
|
CompileStats fileStat = {};
|
|
fileStat.lowerStats.functionStatsFlags = functionStats;
|
|
failed += !compileFile(path.c_str(), compileFormat, assemblyTarget, fileStat);
|
|
stats += fileStat;
|
|
if (recordStats == RecordStats::File || recordStats == RecordStats::Function)
|
|
fileStats.push_back(fileStat);
|
|
}
|
|
|
|
if (compileFormat == CompileFormat::Null)
|
|
{
|
|
printf(
|
|
"Compiled %d KLOC into %d KB bytecode (read %.2fs, parse %.2fs, compile %.2fs)\n",
|
|
int(stats.lines / 1000),
|
|
int(stats.bytecode / 1024),
|
|
stats.readTime,
|
|
stats.parseTime,
|
|
stats.compileTime
|
|
);
|
|
}
|
|
else if (compileFormat == CompileFormat::CodegenNull)
|
|
{
|
|
printf(
|
|
"Compiled %d KLOC into %d KB bytecode => %d KB native code (%.2fx) (read %.2fs, parse %.2fs, compile %.2fs, codegen %.2fs)\n",
|
|
int(stats.lines / 1000),
|
|
int(stats.bytecode / 1024),
|
|
int(stats.codegen / 1024),
|
|
stats.bytecode == 0 ? 0.0 : double(stats.codegen) / double(stats.bytecode),
|
|
stats.readTime,
|
|
stats.parseTime,
|
|
stats.compileTime,
|
|
stats.codegenTime
|
|
);
|
|
|
|
printf(
|
|
"Lowering: regalloc failed: %d, lowering failed %d; spills to stack: %d, spills to restore: %d, max spill slot %u\n",
|
|
stats.lowerStats.regAllocErrors,
|
|
stats.lowerStats.loweringErrors,
|
|
stats.lowerStats.spillsToSlot,
|
|
stats.lowerStats.spillsToRestore,
|
|
stats.lowerStats.maxSpillSlotsUsed
|
|
);
|
|
}
|
|
|
|
if (recordStats != RecordStats::None)
|
|
{
|
|
FILE* fp = fopen(statsFile.c_str(), "w");
|
|
|
|
if (!fp)
|
|
{
|
|
fprintf(stderr, "Unable to open 'stats.json'\n");
|
|
return 1;
|
|
}
|
|
|
|
if (recordStats == RecordStats::Total)
|
|
{
|
|
serializeCompileStats(fp, stats);
|
|
}
|
|
else if (recordStats == RecordStats::File || recordStats == RecordStats::Function)
|
|
{
|
|
fprintf(fp, "{\n");
|
|
for (size_t i = 0; i < fileCount; ++i)
|
|
{
|
|
std::string escaped(escapeFilename(files[i]));
|
|
fprintf(fp, " \"%s\": ", escaped.c_str());
|
|
serializeCompileStats(fp, fileStats[i]);
|
|
fprintf(fp, i == (fileCount - 1) ? "\n" : ",\n");
|
|
}
|
|
fprintf(fp, "}");
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
return failed ? 1 : 0;
|
|
}
|