diff --git a/doc/source/reference/language/options.rst b/doc/source/reference/language/options.rst index f3476685a..009f6b3a3 100644 --- a/doc/source/reference/language/options.rst +++ b/doc/source/reference/language/options.rst @@ -371,6 +371,10 @@ Debugging and Profiling - bool - false - Prints detailed compile time breakdown at the end of compilation. + * - ``log_module_compile_time`` + - bool + - false + - Prints per-module compile-time breakdown (parse / infer with pass count / optimize / macro (in infer) / macro mods) plus function count for each required module. Also enables per-context simulate timing logs and the top-level aggregate summary. CLI: ``-log-compile-time``. -------------------- RTTI diff --git a/doc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst b/doc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst index e39beb178..40d8a296e 100644 --- a/doc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst +++ b/doc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst @@ -16,6 +16,7 @@ Keep context alive after main function. Whether to use very safe context (delete of data is delayed, to avoid table[foo]=table[bar] lifetime bugs). Threshold for reporting candidates for function calls. If less than this number, we always report them. Maximum number of inference passes. +Maximum call expression nesting depth during inference. Stack size. Whether to intern strings. Whether to use persistent heap (or linear heap). @@ -65,6 +66,7 @@ Fails compilation if AOT is not available. Fails compilation if AOT export is not available. Log compile time. Log total compile time. +Log detailed per-module compile-time breakdown. Each required module emits its own block with parse / infer (with pass count) / optimize / macro (in infer) / macro mods times plus function count. Also enables a separate ``simulate (...) took X, modName (fileName)`` line for every ``Program::simulate`` call and the top-level aggregate summary at the end of compilation. Wired to the daslang CLI flag ``-log-compile-time``. Disables fast call optimization. Reuse stack memory after variables go out of scope. Force in-scope for POD-like types. diff --git a/include/daScript/ast/ast.h b/include/daScript/ast/ast.h index 163987f95..0b00e222e 100644 --- a/include/daScript/ast/ast.h +++ b/include/daScript/ast/ast.h @@ -1568,6 +1568,7 @@ namespace das bool fail_on_lack_of_aot_export = false; // remove_unused_symbols = false is missing in the module, which is passed to AOT /*option*/ bool log_compile_time = false; // if true, then compile time will be printed at the end of the compilation /*option*/ bool log_total_compile_time = false; // if true, then detailed compile time will be printed at the end of the compilation + /*option*/ bool log_module_compile_time = false; // if true, every required module logs its own parse / infer (with pass count) / optimize / macro (in infer) / macro mods breakdown + function count; also enables per-context simulate timing and the top-level aggregate summary (CLI: -log-compile-time) /*option*/ bool no_fast_call = false; // disable fastcall /*option*/ bool scoped_stack_allocator = true; // reuse stack memory after variables out of scope /*option*/ bool force_inscope_pod = false; // force in-scope for POD-like types @@ -1757,6 +1758,7 @@ namespace das int totalFunctions = 0; int totalVariables = 0; int newLambdaIndex = 1; + int inferPassesUsed = 0; // sum of inferTypesDirty inner-loop pass counts across all inferTypes calls (incl. restartInfer legs) for this module; reset by parseDaScript once per module-compile; used by per-module compile-time log vector errors; vector aotErrors; uint32_t globalInitStackSize = 0; diff --git a/mouse-data/docs/add-cli-flag-to-daslang-executable.md b/mouse-data/docs/add-cli-flag-to-daslang-executable.md new file mode 100644 index 000000000..fe28c47f6 --- /dev/null +++ b/mouse-data/docs/add-cli-flag-to-daslang-executable.md @@ -0,0 +1,51 @@ +--- +slug: add-cli-flag-to-daslang-executable +title: How do I add a new command-line flag to the daslang executable that maps to a CodeOfPolicies field? +created: 2026-05-16 +last_verified: 2026-05-16 +links: [] +--- + +Touch four spots in `utils/daScript/main.cpp`: + +**1. File-scope static** (near the other flag statics around lines 34-57): + +```cpp +static bool logModuleCompileTime = false; +``` + +**2. Argv parser branch** in the main argv loop (`for ( int i=1; i < argc; ++i ) { if ( argv[i][0]=='-' ) { string cmd(argv[i]+1); ...`). Add an `else if` near related flags: + +```cpp +} else if ( cmd=="log-compile-time" ) { + logModuleCompileTime = true; +``` + +Single-dash style (`-log-compile-time`) for short or hyphenated flags. Double-dash (`--track-smart-ptr`) used for some longer ones. Existing convention is mostly single-dash — match the neighbors. + +**3. Wire to policies in BOTH population sites:** + +There are TWO CodeOfPolicies setup paths in `main.cpp`: +- `getPolicies()` near top of file — used by the AOT path (`das_aot_main`) +- `compile_and_run()` later — used by the normal run / JIT path + +Both need the new line: + +```cpp +policies.log_module_compile_time = logModuleCompileTime; +``` + +If you only update one, the flag silently does nothing in the other mode. (Easy to verify with `git grep -n "policies\.no_lint" utils/daScript/main.cpp` — every existing flag is set at both sites.) + +**4. Help text** in `print_help()`: + +```cpp +<< " -log-compile-time log detailed per-module compile-time breakdown (parse/infer/opt/macro/simulate)\n" +``` + +**Optional: also wire daslang-live** if the flag matters for the live-reload host. Its argv parser is `utils/daslang-live/main.cpp:648-680` and its `CodeOfPolicies` is constructed inside `compile_script()` ~line 171. Skip if the flag is daslang-only. + +**Rebuild target:** `cmake --build build --config Release --target daslang -j 64` (≈30s incremental). The static lives in `daslang.cpp`'s translation unit — no header changes, no library re-link of dependents. If you also touched `module_builtin_rtti.cpp` for the underlying policy registration, the full target list grows (rebuild test_aot if you care about AOT path). + +## Questions +- How do I add a new command-line flag to the daslang executable that maps to a CodeOfPolicies field? diff --git a/mouse-data/docs/add-field-to-codeofpolicies-full-checklist.md b/mouse-data/docs/add-field-to-codeofpolicies-full-checklist.md new file mode 100644 index 000000000..30a9a32f7 --- /dev/null +++ b/mouse-data/docs/add-field-to-codeofpolicies-full-checklist.md @@ -0,0 +1,46 @@ +--- +slug: add-field-to-codeofpolicies-full-checklist +title: What's the full checklist for adding a new field to CodeOfPolicies so it works end-to-end (compiler reads it, options.das_root maps it, RTTI sees it, das2rst documents it)? +created: 2026-05-16 +last_verified: 2026-05-16 +links: [] +--- + +Adding a `CodeOfPolicies` field looks like one edit but is actually FOUR coordinated changes. Miss any of them and you get silent failures (no compile error, just wrong rendered docs / option not visible to RTTI). + +**1. Declare the field** in `include/daScript/ast/ast.h` inside `struct CodeOfPolicies` (~line 1489). Annotate `/*option*/` if you want it overridable via per-file `options foo = true`: + +```cpp +/*option*/ bool log_module_compile_time = false; // short trailing comment is fine +``` + +**2. Register it for RTTI** in `src/builtin/module_builtin_rtti.cpp` (search for the existing `addField("...")` calls in the CodeOfPolicies registration block, ~line 940): + +```cpp +addField("log_module_compile_time"); +``` + +Without this, `for_each_field` (used by `daslib/rst.das`) doesn't see the field and `das2rst` silently skips it. + +**3. Add a description line** in `doc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst`. This file is **strictly positional**: it maps line-by-line to RTTI-registered fields in *offset-sorted order* (which equals C++ declaration order for non-virtual structs). Place the new line at the position matching where you declared the field in step 1. + +**Silent shift trap:** if the handmade file ends up short by ONE line vs registered fields, EVERY field after the gap gets its predecessor's description — but no error, no warning. The only way to spot it is reading the rendered `doc/source/stdlib/generated/rtti.rst` and confirming field name + description agree. + +The reader code is `daslib/rst.das:817-880` (`document_topic`). It computes `headerLen = got - expected` and uses the first `headerLen` lines as preamble, then maps the remaining lines 1:1 to fields. So adding a line in the WRONG slot doesn't crash — it just silently mis-binds. + +**4. Add a row** to the language-reference options table at `doc/source/reference/language/options.rst` (search for `log_total_compile_time` — it's a worked example near the bottom). Only needed if the field is `/*option*/` (user-overridable per file). + +**Then regen + verify:** + +```bash +cmake --build build --config Release --target daslang -j 64 +bin/daslang doc/reflections/das2rst.das +grep -n "your_new_field" doc/source/stdlib/generated/rtti.rst # must show with correct description +``` + +If the description shown next to your field is for the WRONG field, your handmade file is short by one — find the missing description and add it. (I hit this in PR #2677: `max_call_depth` had been missing a description for who-knows-how-long, silently shifting ~30 later fields' descriptions up by one in the generated rtti.rst.) + +Bonus: if you also want a CLI flag for your option in `daslang`, the wiring is described separately under `add-cli-flag-to-daslang-executable`. + +## Questions +- What's the full checklist for adding a new field to CodeOfPolicies so it works end-to-end (compiler reads it, options.das_root maps it, RTTI sees it, das2rst documents it)? diff --git a/mouse-data/docs/das2rst-handmade-file-positional-mapping-and-silent-shift-trap.md b/mouse-data/docs/das2rst-handmade-file-positional-mapping-and-silent-shift-trap.md new file mode 100644 index 000000000..180d2078a --- /dev/null +++ b/mouse-data/docs/das2rst-handmade-file-positional-mapping-and-silent-shift-trap.md @@ -0,0 +1,37 @@ +--- +slug: das2rst-handmade-file-positional-mapping-and-silent-shift-trap +title: How does daslib/rst.das map handmade .rst description lines to struct fields, and what is the silent-misalignment trap when one line is missing? +created: 2026-05-16 +last_verified: 2026-05-16 +links: [] +--- + +`doc/source/stdlib/handmade/structure_annotation--.rst` files (and similar `class_annotation-*.rst`) are read **positionally** by `daslib/rst.das:817-880` (`document_topic`): + +1. Reader splits the file on `\n`, strips trailing blank lines → `got` lines. +2. Compares to `expected = length(tab) - 1` (one row per field, after header row). +3. Computes `headerLen = got - expected`. +4. First `headerLen` lines printed verbatim as preamble (typically the struct description). +5. **Last `expected` lines** mapped 1:1 to fields in **offset-sorted order** (= C++ declaration order for non-virtual structs). + +So for a struct with N fields, you want N+1 non-blank lines (1 struct description + N field descriptions). The mapping is by POSITION, not by field name — there's no field-name annotation in the .rst, just one description per line. + +**The silent shift trap:** if the file ends up with N lines instead of N+1 (one missing description anywhere in the middle), every field from the gap onwards inherits its successor's description. No compile error, no warning — `das2rst` happily produces garbage docs. The only way to detect: + +```bash +bin/daslang doc/reflections/das2rst.das +grep -A 1 "field_name_you_know" doc/source/stdlib/generated/rtti.rst # description text on next line must match +``` + +If the description belongs to a different field, your handmade file is short by one line somewhere between the file start and that field. Binary-search backwards to find where the shift begins — the first field with a wrong description marks the slot where one line is missing. + +**Why this is fragile:** the file uses no field-name markers, no JSON, no positional separators. A future contributor adds a new C++ field, forgets the handmade entry → the next field's description (and everything after) drifts. Has happened multiple times to `CodeOfPolicies`. + +**Fix recipe when you spot it:** read the offset of the misalignment in the rendered file, count back to find which C++ field has no description, add a line at the matching position in handmade. Repeat until rendered matches expected. + +If `bin/daslang doc/reflections/das2rst.das` panics with "has less documentation than values" — that's the FRIENDLY case (file has FEWER lines than fields). The silent case is when somebody added the right number of lines but in the wrong positions, or when an EARLIER field's description is missing. + +PR #2677 fixed a pre-existing silent shift in `structure_annotation-rtti-CodeOfPolicies.rst` (missing description for `max_call_depth`). About 30 fields downstream had their previous-field's description showing. + +## Questions +- How does daslib/rst.das map handmade .rst description lines to struct fields, and what is the silent-misalignment trap when one line is missing? diff --git a/mouse-data/docs/when-do-i-need-unsafe-reinterpret-lt-jsonvalue-gt-around-a-body-key-null-lookup-and-when-can-i-just-use-the-const-view-directly.md b/mouse-data/docs/when-do-i-need-unsafe-reinterpret-lt-jsonvalue-gt-around-a-body-key-null-lookup-and-when-can-i-just-use-the-const-view-directly.md new file mode 100644 index 000000000..fcf00294b --- /dev/null +++ b/mouse-data/docs/when-do-i-need-unsafe-reinterpret-lt-jsonvalue-gt-around-a-body-key-null-lookup-and-when-can-i-just-use-the-const-view-directly.md @@ -0,0 +1,35 @@ +--- +slug: when-do-i-need-unsafe-reinterpret-lt-jsonvalue-gt-around-a-body-key-null-lookup-and-when-can-i-just-use-the-const-view-directly +title: 'When do I need `unsafe(reinterpret(...))` around a `body?["key"] ?? null` lookup, and when can I just use the const view directly?' +created: 2026-05-16 +last_verified: 2026-05-16 +links: [] +--- + +**Only when you store the pointer somewhere that wants `JsonValue?` (non-const-data).** For read-only operations (`is _string` / `as _string` / `write_json(v)` / `v != null` / `v.value is _object`) the const view from `?[]` works directly — no `unsafe` cast needed. + +The `?[]` operator from `daslib/json_boost` has two overloads (json_boost.das:29-37): + +```das +def operator ?[] (a : JsonValue const? ==const; key : string) : JsonValue? const { ... } +def operator ?[] (var a : JsonValue? ==const; key : string) : JsonValue? { ... } +``` + +Indexing a `const`-bound view returns `JsonValue? const` (const-pointer wrapper, data is mutable). For reading the value out (the common case in any parser), this is fine. The `unsafe(reinterpret(...))` strips the outer const so you can: + +- **Assign to a non-const struct field** — the load-bearing case. Example: `result.params = params_v` in `daslib/jsonrpc.das:188` requires `var params_v = unsafe(reinterpret(body?["params"] ?? null))`. Drop the reinterpret and you get `error[30915]: can only copy compatible type; JsonValue?& = JsonValue? const&`. +- **Pass to a function that takes `var` JsonValue?** — same root cause. + +What does NOT need the cast (idiomatic): + +```das +let id_v = body?["id"] ?? null // const view +if (id_v != null && id_v.value is _string) // read OK +let s = id_v.value as _string // read OK +let j = write_json(id_v) // takes JsonValue? const, OK +``` + +Historical context: `live_api_stdio.das` (PR #2674, before the `daslib/jsonrpc` extraction in PR #2679) wrapped every `?[]` lookup in `unsafe(reinterpret(...))` defensively. When the same code moved into `daslib/jsonrpc.das`, four of the six call sites turned out to be read-only and the cast was dropped. Watch for this pattern next time you migrate JSON-traversal code into a library — the `unsafe` is usually noise. + +## Questions +- When do I need `unsafe(reinterpret<JsonValue?>(...))` around a `body?[\"key\"] ?? null` lookup, and when can I just use the const view directly? diff --git a/src/ast/ast_infer_type.cpp b/src/ast/ast_infer_type.cpp index b74905e37..f65c56bf3 100644 --- a/src/ast/ast_infer_type.cpp +++ b/src/ast/ast_infer_type.cpp @@ -5744,6 +5744,8 @@ namespace das { // run macros til any of them does work, then reinfer and restart (i.e. infer after each macro) void Program::inferTypes(TextWriter &logs, ModuleGroup &libGroup) { newLambdaIndex = 1; + // inferPassesUsed is NOT reset here — parseDaScript resets it once per module + // before the restartInfer: loop, so multiple inferTypes legs accumulate properly. inferTypesDirty(logs, false); bool anyMacrosDidWork = false; bool anyMacrosFailedToInfer = false; @@ -5832,6 +5834,7 @@ namespace das { for (pass = 0; pass < maxInferPasses; ++pass) { if (macroException) break; + inferPassesUsed++; // count each body invocation; avoids undercount when loop breaks early (pass is 0-based) failToCompile = false; errors.clear(); InferTypes context(this, &logs); diff --git a/src/ast/ast_parse.cpp b/src/ast/ast_parse.cpp index 9e54da8ec..e32ea2bb1 100644 --- a/src/ast/ast_parse.cpp +++ b/src/ast/ast_parse.cpp @@ -674,6 +674,11 @@ namespace das { program->library.renameModule(program->thisModule.get(), moduleName); ReuseCacheGuard rcg; auto time0 = ref_time_ticks(); + // Per-module timing snapshots, used by log_module_compile_time. We still + // accumulate into *totParse/*totInfer/*totOpt/*totM for the top-level summary. + uint64_t myParseT = 0, myInferT = 0, myOptT = 0, myMacroModT = 0; + auto macroTicks0 = daScriptEnvironment::getBound()->macroTimeTicks; + program->inferPassesUsed = 0; // reset once per module; inferTypesDirty accumulates across all inferTypes legs (incl. restartInfer) if ( trySerializeProgramModule(program, access, fileName, libGroup, logs) ) { return program; @@ -767,7 +772,8 @@ namespace das { return program; } parserState = DasParserState(); - *totParse += get_time_usec(time0); + myParseT = get_time_usec(time0); + *totParse += myParseT; if ( err || program->failed() ) { daScriptEnvironment::getBound()->g_Program.reset(); daScriptEnvironment::getBound()->g_compilerLog = nullptr; @@ -786,10 +792,19 @@ namespace das { program->thisModule->isSolidContext = true; } callCompilationCallback(moduleName, fileName, "infer"); - auto timeI = ref_time_ticks(); - restartInfer: program->inferTypes(logs, libGroup); - if ( policies.macro_context_collect ) libGroup.collectMacroContexts(); - *totInfer += get_time_usec(timeI); + // restartInfer: timer must be inside the label so each leg is timed independently. + // The pre-edit form (timeI set ONCE before the label) over-counted *totInfer on + // patchAnnotations() restarts: get_time_usec(timeI) included all previous legs, + // and we added it again each iteration. + restartInfer: + { + auto timeI = ref_time_ticks(); + program->inferTypes(logs, libGroup); + if ( policies.macro_context_collect ) libGroup.collectMacroContexts(); + uint64_t inferLegT = get_time_usec(timeI); + myInferT += inferLegT; + *totInfer += inferLegT; + } if ( !program->failed() ) { program->buildAccessFlags(logs); // this is used by the lint pass if ( program->patchAnnotations() ) { @@ -816,7 +831,8 @@ namespace das { } } if ( policies.macro_context_collect ) libGroup.collectMacroContexts(); - *totOpt += get_time_usec(timeO); + myOptT = get_time_usec(timeO); + *totOpt += myOptT; if (!program->failed()) program->verifyAndFoldContracts(); if (!program->failed()) { @@ -867,12 +883,29 @@ namespace das { program->allocateStack(logs,true,false); if (!program->failed()) program->makeMacroModule(logs); - *totM += get_time_usec(timeM); + myMacroModT = get_time_usec(timeM); + *totM += myMacroModT; } } daScriptEnvironment::getBound()->g_Program.reset(); if ( policies.macro_context_collect ) libGroup.collectMacroContexts(); - if ( program->options.getBoolOption("log_compile_time",policies.log_compile_time) ) { + bool logModule = program->options.getBoolOption("log_module_compile_time",policies.log_module_compile_time); + // For the entry script (isDep == false) the heaviest post-parseDaScript work + // (markExecutableSymbolUse / removeUnusedSymbols / deriveAliases / allocateStack / + // validateAst) runs in compileDaScript AFTER this point, so the per-module + // breakdown here would be misleadingly low. The top-level summary emitted later + // in compileDaScript covers the entry's full cost authoritatively. + if ( logModule && isDep ) { + auto dt = get_time_usec(time0) / 1000000.; + auto macroDelta = ref_time_delta_to_usec(daScriptEnvironment::getBound()->macroTimeTicks - macroTicks0); + logs << "compiler took " << dt << ", " << program->thisModule->name << " (" << fileName << ") -- " << program->totalFunctions << " functions\n" + << "\tparse " << (myParseT / 1000000.) << "\n" + << "\tinfer " << (myInferT / 1000000.) << " (" << program->inferPassesUsed << " passes)\n" + << "\toptimize " << (myOptT / 1000000.) << "\n" + << "\tmacro (in infer) " << (macroDelta / 1000000.) << "\n" + << "\tmacro mods " << (myMacroModT / 1000000.) << "\n" + ; + } else if ( program->options.getBoolOption("log_compile_time",policies.log_compile_time) ) { auto dt = get_time_usec(time0) / 1000000.; logs << "compiler took " << dt << ", " << fileName << "\n"; } @@ -1331,14 +1364,15 @@ namespace das { res->thisNamespace = "_anon_" + to_string(normalizedPathHash(fileName, getDasRoot())); } res->validateAst(); - if ( res->options.getBoolOption("log_total_compile_time",policies.log_total_compile_time) ) { + if ( res->options.getBoolOption("log_total_compile_time",policies.log_total_compile_time) + || res->options.getBoolOption("log_module_compile_time",policies.log_module_compile_time) ) { auto totT = get_time_usec(time0); - logs << "compiler took " << (totT / 1000000.) << ", " << fileName << "\n" + logs << "total compile took " << (totT / 1000000.) << ", " << fileName << " -- " << res->totalFunctions << " functions\n" << "\trequire " << (preqT / 1000000.) << "\n" << "\tparse " << (*totParse / 1000000.) << "\n" << "\tinfer " << (*totInfer / 1000000.) << "\n" << "\toptimize " << (*totOpt / 1000000.) << "\n" - << "\tmacro " << (ref_time_delta_to_usec(daScriptEnvironment::getBound()->macroTimeTicks) / 1000000.) << "\n" + << "\tmacro (in infer) " << (ref_time_delta_to_usec(daScriptEnvironment::getBound()->macroTimeTicks) / 1000000.) << "\n" << "\tmacro mods " << (*totM / 1000000.) << "\n" ; } diff --git a/src/ast/ast_simulate.cpp b/src/ast/ast_simulate.cpp index 8a7ea8ec7..fe942c00d 100644 --- a/src/ast/ast_simulate.cpp +++ b/src/ast/ast_simulate.cpp @@ -3985,9 +3985,12 @@ namespace das if ( !options.getBoolOption("rtti",policies.rtti) ) { context.thisProgram = nullptr; } - if ( options.getBoolOption("log_total_compile_time",policies.log_total_compile_time) ) { + if ( options.getBoolOption("log_total_compile_time",policies.log_total_compile_time) + || options.getBoolOption("log_module_compile_time",policies.log_module_compile_time) ) { auto dt = get_time_usec(time0) / 1000000.; - logs << "simulate (including init script) took " << dt << "\n"; + logs << "simulate (including init script) took " << dt << ", "; + if ( !thisModule->name.empty() ) logs << thisModule->name << " (" << thisModule->fileName << ")\n"; + else logs << thisModule->fileName << "\n"; } dapiSimulateContext(context); return errors.size() == 0; diff --git a/src/builtin/module_builtin_rtti.cpp b/src/builtin/module_builtin_rtti.cpp index 40e330655..0811c339e 100644 --- a/src/builtin/module_builtin_rtti.cpp +++ b/src/builtin/module_builtin_rtti.cpp @@ -939,6 +939,7 @@ namespace das { addField("fail_on_lack_of_aot_export"); addField("log_compile_time"); addField("log_total_compile_time"); + addField("log_module_compile_time"); addField("no_fast_call"); addField("scoped_stack_allocator"); addField("force_inscope_pod"); diff --git a/utils/daScript/main.cpp b/utils/daScript/main.cpp index cc4f387df..c5de8ad20 100644 --- a/utils/daScript/main.cpp +++ b/utils/daScript/main.cpp @@ -55,6 +55,7 @@ static bool version2syntax = true; static bool gen2MakeSyntax = false; static bool trackAllocations = false; static bool heapReportAtExit = false; +static bool logModuleCompileTime = false; static CodeOfPolicies getPolicies() { CodeOfPolicies policies; @@ -71,6 +72,7 @@ static CodeOfPolicies getPolicies() { policies.scoped_stack_allocator = scopedStackAllocator; policies.track_allocations = trackAllocations; policies.no_lint = noLint; + policies.log_module_compile_time = logModuleCompileTime; return policies; } @@ -141,7 +143,7 @@ int das_aot_main ( int argc, char * argv[] ) { _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif if ( argc<=3 ) { - tout << "daslang -aot [-v2Syntax] [-v1Syntax] [-v2makeSyntax] [-project ] [-dasroot ] [-q] [-j] [-aot-macros] [-cross-platform] [-no-lint]\n"; + tout << "daslang -aot [-v2Syntax] [-v1Syntax] [-v2makeSyntax] [-project ] [-dasroot ] [-q] [-j] [-aot-macros] [-cross-platform] [-no-lint] [-log-compile-time]\n"; return -1; } bool dryRun = false; @@ -199,6 +201,8 @@ int das_aot_main ( int argc, char * argv[] ) { noDynamicModules = true; } else if ( strcmp(argv[ai],"-no-lint")==0 ) { noLint = true; + } else if ( strcmp(argv[ai],"-log-compile-time")==0 ) { + logModuleCompileTime = true; } else if ( strcmp(argv[ai],"--")==0 ) { scriptArgs = true; } else if ( !scriptArgs ) { @@ -276,6 +280,7 @@ int compile_and_run ( const string & fn, const string & mainFnName, bool outputP policies.scoped_stack_allocator = scopedStackAllocator; policies.track_allocations = trackAllocations; policies.no_lint = noLint; + policies.log_module_compile_time = logModuleCompileTime; policies.persistent_heap = true; if ( auto program = compileDaScript(fn,access,tout,dummyGroup,policies) ) { if ( program->failed() ) { @@ -442,6 +447,7 @@ void print_help() { << " --das-profiler-leaks track live heap allocations and dump leaks on context destroy\n" << " -no-dynamic-modules skip loading dynamic modules from dasroot and project root\n" << " -no-lint skip the lint pass (Program::lint)\n" + << " -log-compile-time log detailed per-module compile-time breakdown (parse / infer with pass count / optimize / macro (in infer) / macro mods / simulate) + function count\n" << " -- separator for script arguments\n" << "daslang -aot {-q} {-p}\n" << " -project path to project file\n" @@ -449,6 +455,7 @@ void print_help() { << " -q suppress all output\n" << " -dry-run no changes will be written\n" << " -dasroot set path to daslang root folder (with daslib)\n" + << " -log-compile-time log per-module compile-time breakdown during AOT generation\n" ; } @@ -583,6 +590,8 @@ int MAIN_FUNC_NAME ( int argc, char * argv[] ) { compileOnly = true; } else if ( cmd=="no-lint" ) { noLint = true; + } else if ( cmd=="log-compile-time" ) { + logModuleCompileTime = true; } else if ( cmd=="project-root" || cmd=="project_root" ) { project_root = argv[i + 1]; i++;