Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CLI/src/Compile.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "lua.h"
3
#include "lualib.h"
4
5
#include "Luau/CodeGen.h"
6
#include "Luau/Compiler.h"
7
#include "Luau/BytecodeBuilder.h"
8
#include "Luau/Parser.h"
9
#include "Luau/TimeTrace.h"
10
11
#include "Luau/FileUtils.h"
12
#include "Luau/Flags.h"
13
14
#include <memory>
15
16
#ifdef _WIN32
17
#include <io.h>
18
#include <fcntl.h>
19
#endif
20
21
LUAU_FASTFLAG(DebugLuauTimeTracing)
22
23
enum class CompileFormat
24
{
25
Text,
26
Binary,
27
Remarks,
28
Codegen, // Prints annotated native code including IR and assembly
29
CodegenAsm, // Prints annotated native code assembly
30
CodegenIr, // Prints annotated native code IR
31
CodegenVerbose, // Prints annotated native code including IR, assembly and outlined code
32
CodegenNull,
33
Null
34
};
35
36
enum class RecordStats
37
{
38
None,
39
Total,
40
File,
41
Function
42
};
43
44
struct GlobalOptions
45
{
46
int optimizationLevel = 1;
47
int debugLevel = 1;
48
int typeInfoLevel = 0;
49
50
const char* vectorLib = nullptr;
51
const char* vectorCtor = nullptr;
52
const char* vectorType = nullptr;
53
} globalOptions;
54
55
static Luau::CompileOptions copts()
56
{
57
Luau::CompileOptions result = {};
58
result.optimizationLevel = globalOptions.optimizationLevel;
59
result.debugLevel = globalOptions.debugLevel;
60
result.typeInfoLevel = globalOptions.typeInfoLevel;
61
62
result.vectorLib = globalOptions.vectorLib;
63
result.vectorCtor = globalOptions.vectorCtor;
64
result.vectorType = globalOptions.vectorType;
65
66
return result;
67
}
68
69
static std::optional<CompileFormat> getCompileFormat(const char* name)
70
{
71
if (strcmp(name, "text") == 0)
72
return CompileFormat::Text;
73
else if (strcmp(name, "binary") == 0)
74
return CompileFormat::Binary;
75
else if (strcmp(name, "text") == 0)
76
return CompileFormat::Text;
77
else if (strcmp(name, "remarks") == 0)
78
return CompileFormat::Remarks;
79
else if (strcmp(name, "codegen") == 0)
80
return CompileFormat::Codegen;
81
else if (strcmp(name, "codegenasm") == 0)
82
return CompileFormat::CodegenAsm;
83
else if (strcmp(name, "codegenir") == 0)
84
return CompileFormat::CodegenIr;
85
else if (strcmp(name, "codegenverbose") == 0)
86
return CompileFormat::CodegenVerbose;
87
else if (strcmp(name, "codegennull") == 0)
88
return CompileFormat::CodegenNull;
89
else if (strcmp(name, "null") == 0)
90
return CompileFormat::Null;
91
else
92
return std::nullopt;
93
}
94
95
static void report(const char* name, const Luau::Location& location, const char* type, const char* message)
96
{
97
fprintf(stderr, "%s(%d,%d): %s: %s\n", name, location.begin.line + 1, location.begin.column + 1, type, message);
98
}
99
100
static void reportError(const char* name, const Luau::ParseError& error)
101
{
102
report(name, error.getLocation(), "SyntaxError", error.what());
103
}
104
105
static void reportError(const char* name, const Luau::CompileError& error)
106
{
107
report(name, error.getLocation(), "CompileError", error.what());
108
}
109
110
static std::string getCodegenAssembly(
111
const char* name,
112
const std::string& bytecode,
113
Luau::CodeGen::AssemblyOptions options,
114
Luau::CodeGen::LoweringStats* stats
115
)
116
{
117
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
118
lua_State* L = globalState.get();
119
120
if (luau_load(L, name, bytecode.data(), bytecode.size(), 0) == 0)
121
return Luau::CodeGen::getAssembly(L, -1, options, stats);
122
123
fprintf(stderr, "Error loading bytecode %s\n", name);
124
return "";
125
}
126
127
static void annotateInstruction(void* context, std::string& text, int fid, int instpos)
128
{
129
Luau::BytecodeBuilder& bcb = *(Luau::BytecodeBuilder*)context;
130
131
bcb.annotateInstruction(text, fid, instpos);
132
}
133
134
struct CompileStats
135
{
136
size_t lines;
137
size_t bytecode;
138
size_t bytecodeInstructionCount;
139
size_t codegen;
140
141
double readTime;
142
double miscTime;
143
double parseTime;
144
double compileTime;
145
double codegenTime;
146
147
Luau::CodeGen::LoweringStats lowerStats;
148
149
CompileStats& operator+=(const CompileStats& that)
150
{
151
this->lines += that.lines;
152
this->bytecode += that.bytecode;
153
this->bytecodeInstructionCount += that.bytecodeInstructionCount;
154
this->codegen += that.codegen;
155
this->readTime += that.readTime;
156
this->miscTime += that.miscTime;
157
this->parseTime += that.parseTime;
158
this->compileTime += that.compileTime;
159
this->codegenTime += that.codegenTime;
160
this->lowerStats += that.lowerStats;
161
162
return *this;
163
}
164
165
CompileStats operator+(const CompileStats& other) const
166
{
167
CompileStats result(*this);
168
result += other;
169
return result;
170
}
171
};
172
173
#define WRITE_NAME(INDENT, NAME) fprintf(fp, INDENT "\"" #NAME "\": ")
174
#define WRITE_PAIR(INDENT, NAME, FORMAT) fprintf(fp, INDENT "\"" #NAME "\": " FORMAT, stats.NAME)
175
#define WRITE_PAIR_STRING(INDENT, NAME, FORMAT) fprintf(fp, INDENT "\"" #NAME "\": " FORMAT, stats.NAME.c_str())
176
177
void serializeFunctionStats(FILE* fp, const Luau::CodeGen::FunctionStats& stats)
178
{
179
fprintf(fp, " {\n");
180
WRITE_PAIR_STRING(" ", name, "\"%s\",\n");
181
WRITE_PAIR(" ", line, "%d,\n");
182
WRITE_PAIR(" ", bcodeCount, "%u,\n");
183
WRITE_PAIR(" ", irCount, "%u,\n");
184
WRITE_PAIR(" ", asmCount, "%u,\n");
185
WRITE_PAIR(" ", asmSize, "%u,\n");
186
187
WRITE_NAME(" ", bytecodeSummary);
188
const size_t nestingLimit = stats.bytecodeSummary.size();
189
190
if (nestingLimit == 0)
191
fprintf(fp, "[]");
192
else
193
{
194
fprintf(fp, "[\n");
195
for (size_t i = 0; i < nestingLimit; ++i)
196
{
197
const std::vector<unsigned>& counts = stats.bytecodeSummary[i];
198
fprintf(fp, " [");
199
for (size_t j = 0; j < counts.size(); ++j)
200
{
201
fprintf(fp, "%u", counts[j]);
202
if (j < counts.size() - 1)
203
fprintf(fp, ", ");
204
}
205
fprintf(fp, "]");
206
if (i < stats.bytecodeSummary.size() - 1)
207
fprintf(fp, ",\n");
208
}
209
fprintf(fp, "\n ]");
210
}
211
212
fprintf(fp, "\n }");
213
}
214
215
void serializeBlockLinearizationStats(FILE* fp, const Luau::CodeGen::BlockLinearizationStats& stats)
216
{
217
fprintf(fp, "{\n");
218
219
WRITE_PAIR(" ", constPropInstructionCount, "%u,\n");
220
WRITE_PAIR(" ", timeSeconds, "%f\n");
221
222
fprintf(fp, " }");
223
}
224
225
void serializeLoweringStats(FILE* fp, const Luau::CodeGen::LoweringStats& stats)
226
{
227
fprintf(fp, "{\n");
228
229
WRITE_PAIR(" ", totalFunctions, "%u,\n");
230
WRITE_PAIR(" ", skippedFunctions, "%u,\n");
231
WRITE_PAIR(" ", spillsToSlot, "%d,\n");
232
WRITE_PAIR(" ", spillsToRestore, "%d,\n");
233
WRITE_PAIR(" ", maxSpillSlotsUsed, "%u,\n");
234
WRITE_PAIR(" ", blocksPreOpt, "%u,\n");
235
WRITE_PAIR(" ", blocksPostOpt, "%u,\n");
236
WRITE_PAIR(" ", maxBlockInstructions, "%u,\n");
237
WRITE_PAIR(" ", regAllocErrors, "%d,\n");
238
WRITE_PAIR(" ", loweringErrors, "%d,\n");
239
240
WRITE_NAME(" ", blockLinearizationStats);
241
serializeBlockLinearizationStats(fp, stats.blockLinearizationStats);
242
fprintf(fp, ",\n");
243
244
WRITE_NAME(" ", functions);
245
const size_t functionCount = stats.functions.size();
246
247
if (functionCount == 0)
248
fprintf(fp, "[]");
249
else
250
{
251
fprintf(fp, "[\n");
252
for (size_t i = 0; i < functionCount; ++i)
253
{
254
serializeFunctionStats(fp, stats.functions[i]);
255
if (i < functionCount - 1)
256
fprintf(fp, ",\n");
257
}
258
fprintf(fp, "\n ]");
259
}
260
261
fprintf(fp, "\n }");
262
}
263
264
void serializeCompileStats(FILE* fp, const CompileStats& stats)
265
{
266
fprintf(fp, "{\n");
267
268
WRITE_PAIR(" ", lines, "%zu,\n");
269
WRITE_PAIR(" ", bytecode, "%zu,\n");
270
WRITE_PAIR(" ", bytecodeInstructionCount, "%zu,\n");
271
WRITE_PAIR(" ", codegen, "%zu,\n");
272
WRITE_PAIR(" ", readTime, "%f,\n");
273
WRITE_PAIR(" ", miscTime, "%f,\n");
274
WRITE_PAIR(" ", parseTime, "%f,\n");
275
WRITE_PAIR(" ", compileTime, "%f,\n");
276
WRITE_PAIR(" ", codegenTime, "%f,\n");
277
278
WRITE_NAME(" ", lowerStats);
279
serializeLoweringStats(fp, stats.lowerStats);
280
281
fprintf(fp, "\n }");
282
}
283
284
#undef WRITE_NAME
285
#undef WRITE_PAIR
286
#undef WRITE_PAIR_STRING
287
288
static double recordDeltaTime(double& timer)
289
{
290
double now = Luau::TimeTrace::getClock();
291
double delta = now - timer;
292
timer = now;
293
return delta;
294
}
295
296
static bool compileFile(
297
const char* name,
298
CompileFormat format,
299
Luau::CodeGen::AssemblyOptions::Target assemblyTarget,
300
CompileStats& stats,
301
bool dumpConstants
302
)
303
{
304
double currts = Luau::TimeTrace::getClock();
305
306
std::optional<std::string> source = readFile(name);
307
if (!source)
308
{
309
fprintf(stderr, "Error opening %s\n", name);
310
return false;
311
}
312
313
stats.readTime += recordDeltaTime(currts);
314
315
// NOTE: Normally, you should use Luau::compile or luau_compile (see lua_require as an example)
316
// This function is much more complicated because it supports many output human-readable formats through internal interfaces
317
318
try
319
{
320
Luau::BytecodeBuilder bcb;
321
322
Luau::CodeGen::AssemblyOptions options;
323
options.target = assemblyTarget;
324
options.outputBinary = format == CompileFormat::CodegenNull;
325
326
if (!options.outputBinary)
327
{
328
options.includeAssembly = format != CompileFormat::CodegenIr;
329
options.includeIr = format != CompileFormat::CodegenAsm;
330
options.includeIrTypes = format != CompileFormat::CodegenAsm;
331
options.includeOutlinedCode = format == CompileFormat::CodegenVerbose;
332
}
333
334
options.annotator = annotateInstruction;
335
options.annotatorContext = &bcb;
336
337
if (format == CompileFormat::Text)
338
{
339
uint32_t flags = Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
340
Luau::BytecodeBuilder::Dump_Remarks | Luau::BytecodeBuilder::Dump_Types;
341
if (dumpConstants)
342
flags |= Luau::BytecodeBuilder::Dump_Constants;
343
bcb.setDumpFlags(flags);
344
bcb.setDumpSource(*source);
345
}
346
else if (format == CompileFormat::Remarks)
347
{
348
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
349
bcb.setDumpSource(*source);
350
}
351
else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenAsm || format == CompileFormat::CodegenIr ||
352
format == CompileFormat::CodegenVerbose)
353
{
354
bcb.setDumpFlags(
355
Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
356
Luau::BytecodeBuilder::Dump_Remarks
357
);
358
bcb.setDumpSource(*source);
359
}
360
361
stats.miscTime += recordDeltaTime(currts);
362
363
Luau::Allocator allocator;
364
Luau::AstNameTable names(allocator);
365
Luau::ParseResult result = Luau::Parser::parse(source->c_str(), source->size(), names, allocator);
366
367
if (!result.errors.empty())
368
throw Luau::ParseErrors(result.errors);
369
370
stats.lines += result.lines;
371
stats.parseTime += recordDeltaTime(currts);
372
373
Luau::compileOrThrow(bcb, result, names, copts());
374
stats.bytecode += bcb.getBytecode().size();
375
stats.bytecodeInstructionCount = bcb.getTotalInstructionCount();
376
stats.compileTime += recordDeltaTime(currts);
377
378
switch (format)
379
{
380
case CompileFormat::Text:
381
printf("%s", bcb.dumpEverything().c_str());
382
break;
383
case CompileFormat::Remarks:
384
printf("%s", bcb.dumpSourceRemarks().c_str());
385
break;
386
case CompileFormat::Binary:
387
fwrite(bcb.getBytecode().data(), 1, bcb.getBytecode().size(), stdout);
388
break;
389
case CompileFormat::Codegen:
390
case CompileFormat::CodegenAsm:
391
case CompileFormat::CodegenIr:
392
case CompileFormat::CodegenVerbose:
393
printf("%s", getCodegenAssembly(name, bcb.getBytecode(), options, &stats.lowerStats).c_str());
394
break;
395
case CompileFormat::CodegenNull:
396
stats.codegen += getCodegenAssembly(name, bcb.getBytecode(), options, &stats.lowerStats).size();
397
stats.codegenTime += recordDeltaTime(currts);
398
break;
399
case CompileFormat::Null:
400
break;
401
}
402
403
return true;
404
}
405
catch (Luau::ParseErrors& e)
406
{
407
for (auto& error : e.getErrors())
408
reportError(name, error);
409
return false;
410
}
411
catch (Luau::CompileError& e)
412
{
413
reportError(name, e);
414
return false;
415
}
416
}
417
418
static void displayHelp(const char* argv0)
419
{
420
printf("Usage: %s [--mode] [options] [file list]\n", argv0);
421
printf("\n");
422
printf("Available modes:\n");
423
printf(" binary, text, remarks, codegen, codegenir, codegenasm, codegenverbose, codegennull, null\n");
424
printf("\n");
425
printf("Available options:\n");
426
printf(" -h, --help: Display this usage message.\n");
427
printf(" -O<n>: compile with optimization level n (default 1, n should be between 0 and 2).\n");
428
printf(" -g<n>: compile with debug level n (default 1, n should be between 0 and 2).\n");
429
printf(" --target=<target>: compile code for specific architecture (a64, x64, a64_nf, x64_ms).\n");
430
printf(" --timetrace: record compiler time tracing information into trace.json\n");
431
printf(" --record-stats=<granularity>: granularity of compilation stats (total, file, function).\n");
432
printf(" --bytecode-summary: Compute bytecode operation distribution.\n");
433
printf(" --dump-constants: Dump constant table for each function (text mode only).\n");
434
printf(" --stats-file=<filename>: file in which compilation stats will be recored (default 'stats.json').\n");
435
printf(" --vector-lib=<name>: name of the library providing vector type operations.\n");
436
printf(" --vector-ctor=<name>: name of the function constructing a vector value.\n");
437
printf(" --vector-type=<name>: name of the vector type.\n");
438
printf(" --fflags=<flags>: comma-separated list of fast flags to enable/disable (--fflags=true,false,LuauFlag1=true,LuauFlag2=false).\n");
439
}
440
441
static int assertionHandler(const char* expr, const char* file, int line, const char* function)
442
{
443
printf("%s(%d): ASSERTION FAILED: %s\n", file, line, expr);
444
return 1;
445
}
446
447
std::string escapeFilename(const std::string& filename)
448
{
449
std::string escaped;
450
escaped.reserve(filename.size());
451
452
for (const char ch : filename)
453
{
454
switch (ch)
455
{
456
case '\\':
457
escaped.push_back('/');
458
break;
459
case '"':
460
escaped.push_back('\\');
461
escaped.push_back(ch);
462
break;
463
default:
464
escaped.push_back(ch);
465
}
466
}
467
468
return escaped;
469
}
470
471
int main(int argc, char** argv)
472
{
473
Luau::assertHandler() = assertionHandler;
474
475
setLuauFlagsDefault();
476
477
CompileFormat compileFormat = CompileFormat::Text;
478
Luau::CodeGen::AssemblyOptions::Target assemblyTarget = Luau::CodeGen::AssemblyOptions::Host;
479
RecordStats recordStats = RecordStats::None;
480
std::string statsFile("stats.json");
481
bool bytecodeSummary = false;
482
bool dumpConstants = false;
483
484
for (int i = 1; i < argc; i++)
485
{
486
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
487
{
488
displayHelp(argv[0]);
489
return 0;
490
}
491
else if (strncmp(argv[i], "-O", 2) == 0)
492
{
493
int level = atoi(argv[i] + 2);
494
if (level < 0 || level > 2)
495
{
496
fprintf(stderr, "Error: Optimization level must be between 0 and 2 inclusive.\n");
497
return 1;
498
}
499
globalOptions.optimizationLevel = level;
500
}
501
else if (strncmp(argv[i], "-g", 2) == 0)
502
{
503
int level = atoi(argv[i] + 2);
504
if (level < 0 || level > 2)
505
{
506
fprintf(stderr, "Error: Debug level must be between 0 and 2 inclusive.\n");
507
return 1;
508
}
509
globalOptions.debugLevel = level;
510
}
511
else if (strncmp(argv[i], "-t", 2) == 0)
512
{
513
int level = atoi(argv[i] + 2);
514
if (level < 0 || level > 1)
515
{
516
fprintf(stderr, "Error: Type info level must be between 0 and 1 inclusive.\n");
517
return 1;
518
}
519
globalOptions.typeInfoLevel = level;
520
}
521
else if (strncmp(argv[i], "--target=", 9) == 0)
522
{
523
const char* value = argv[i] + 9;
524
525
if (strcmp(value, "a64") == 0)
526
assemblyTarget = Luau::CodeGen::AssemblyOptions::A64;
527
else if (strcmp(value, "a64_nf") == 0)
528
assemblyTarget = Luau::CodeGen::AssemblyOptions::A64_NoFeatures;
529
else if (strcmp(value, "x64") == 0)
530
assemblyTarget = Luau::CodeGen::AssemblyOptions::X64_SystemV;
531
else if (strcmp(value, "x64_ms") == 0)
532
assemblyTarget = Luau::CodeGen::AssemblyOptions::X64_Windows;
533
else
534
{
535
fprintf(stderr, "Error: unknown target\n");
536
return 1;
537
}
538
}
539
else if (strcmp(argv[i], "--timetrace") == 0)
540
{
541
FFlag::DebugLuauTimeTracing.value = true;
542
}
543
else if (strncmp(argv[i], "--record-stats=", 15) == 0)
544
{
545
const char* value = argv[i] + 15;
546
547
if (strcmp(value, "total") == 0)
548
recordStats = RecordStats::Total;
549
else if (strcmp(value, "file") == 0)
550
recordStats = RecordStats::File;
551
else if (strcmp(value, "function") == 0)
552
recordStats = RecordStats::Function;
553
else
554
{
555
fprintf(stderr, "Error: unknown 'granularity' for '--record-stats'.\n");
556
return 1;
557
}
558
}
559
else if (strncmp(argv[i], "--bytecode-summary", 18) == 0)
560
{
561
bytecodeSummary = true;
562
}
563
else if (strcmp(argv[i], "--dump-constants") == 0)
564
{
565
dumpConstants = true;
566
}
567
else if (strncmp(argv[i], "--stats-file=", 13) == 0)
568
{
569
statsFile = argv[i] + 13;
570
571
if (statsFile.size() == 0)
572
{
573
fprintf(stderr, "Error: filename missing for '--stats-file'.\n\n");
574
return 1;
575
}
576
}
577
else if (strncmp(argv[i], "--fflags=", 9) == 0)
578
{
579
setLuauFlags(argv[i] + 9);
580
}
581
else if (strncmp(argv[i], "--vector-lib=", 13) == 0)
582
{
583
globalOptions.vectorLib = argv[i] + 13;
584
}
585
else if (strncmp(argv[i], "--vector-ctor=", 14) == 0)
586
{
587
globalOptions.vectorCtor = argv[i] + 14;
588
}
589
else if (strncmp(argv[i], "--vector-type=", 14) == 0)
590
{
591
globalOptions.vectorType = argv[i] + 14;
592
}
593
else if (argv[i][0] == '-' && argv[i][1] == '-' && getCompileFormat(argv[i] + 2))
594
{
595
compileFormat = *getCompileFormat(argv[i] + 2);
596
}
597
else if (argv[i][0] == '-')
598
{
599
fprintf(stderr, "Error: Unrecognized option '%s'.\n\n", argv[i]);
600
displayHelp(argv[0]);
601
return 1;
602
}
603
}
604
605
if (bytecodeSummary && (recordStats != RecordStats::Function))
606
{
607
fprintf(stderr, "'Error: Required '--record-stats=function' for '--bytecode-summary'.\n");
608
return 1;
609
}
610
611
#if !defined(LUAU_ENABLE_TIME_TRACE)
612
if (FFlag::DebugLuauTimeTracing)
613
{
614
fprintf(stderr, "To run with --timetrace, Luau has to be built with LUAU_ENABLE_TIME_TRACE enabled\n");
615
return 1;
616
}
617
#endif
618
619
const std::vector<std::string> files = getSourceFiles(argc, argv);
620
621
#ifdef _WIN32
622
if (compileFormat == CompileFormat::Binary)
623
_setmode(_fileno(stdout), _O_BINARY);
624
#endif
625
626
const size_t fileCount = files.size();
627
CompileStats stats = {};
628
629
std::vector<CompileStats> fileStats;
630
if (recordStats == RecordStats::File || recordStats == RecordStats::Function)
631
fileStats.reserve(fileCount);
632
633
int failed = 0;
634
unsigned functionStats = (recordStats == RecordStats::Function ? Luau::CodeGen::FunctionStats_Enable : 0) |
635
(bytecodeSummary ? Luau::CodeGen::FunctionStats_BytecodeSummary : 0);
636
for (const std::string& path : files)
637
{
638
CompileStats fileStat = {};
639
fileStat.lowerStats.functionStatsFlags = functionStats;
640
failed += !compileFile(path.c_str(), compileFormat, assemblyTarget, fileStat, dumpConstants);
641
stats += fileStat;
642
if (recordStats == RecordStats::File || recordStats == RecordStats::Function)
643
fileStats.push_back(fileStat);
644
}
645
646
if (compileFormat == CompileFormat::Null)
647
{
648
printf(
649
"Compiled %d KLOC into %d KB bytecode (read %.2fs, parse %.2fs, compile %.2fs)\n",
650
int(stats.lines / 1000),
651
int(stats.bytecode / 1024),
652
stats.readTime,
653
stats.parseTime,
654
stats.compileTime
655
);
656
}
657
else if (compileFormat == CompileFormat::CodegenNull)
658
{
659
printf(
660
"Compiled %d KLOC into %d KB bytecode => %d KB native code (%.2fx) (read %.2fs, parse %.2fs, compile %.2fs, codegen %.2fs)\n",
661
int(stats.lines / 1000),
662
int(stats.bytecode / 1024),
663
int(stats.codegen / 1024),
664
stats.bytecode == 0 ? 0.0 : double(stats.codegen) / double(stats.bytecode),
665
stats.readTime,
666
stats.parseTime,
667
stats.compileTime,
668
stats.codegenTime
669
);
670
671
printf(
672
"Lowering: regalloc failed: %d, lowering failed %d; spills to stack: %d, spills to restore: %d, max spill slot %u\n",
673
stats.lowerStats.regAllocErrors,
674
stats.lowerStats.loweringErrors,
675
stats.lowerStats.spillsToSlot,
676
stats.lowerStats.spillsToRestore,
677
stats.lowerStats.maxSpillSlotsUsed
678
);
679
}
680
681
if (recordStats != RecordStats::None)
682
{
683
FILE* fp = fopen(statsFile.c_str(), "w");
684
685
if (!fp)
686
{
687
fprintf(stderr, "Unable to open 'stats.json'\n");
688
return 1;
689
}
690
691
if (recordStats == RecordStats::Total)
692
{
693
serializeCompileStats(fp, stats);
694
}
695
else if (recordStats == RecordStats::File || recordStats == RecordStats::Function)
696
{
697
fprintf(fp, "{\n");
698
for (size_t i = 0; i < fileCount; ++i)
699
{
700
std::string escaped(escapeFilename(files[i]));
701
fprintf(fp, " \"%s\": ", escaped.c_str());
702
serializeCompileStats(fp, fileStats[i]);
703
fprintf(fp, i == (fileCount - 1) ? "\n" : ",\n");
704
}
705
fprintf(fp, "}");
706
}
707
708
fclose(fp);
709
}
710
711
return failed ? 1 : 0;
712
}
713
714