Skip to content

Define the parser's diagnostic messages once, not in every translation unit - #664

Merged
ASDAlexander77 merged 1 commit into
mainfrom
diagnostics-extern
Oct 23, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
diagnostics-extern

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Summary

tslang/ts-new-parser/diagnostics.h defined its 1967 DiagnosticMessageStore objects static at namespace scope. Each object holds two std::wstrings. As a result, every translation unit that includes the parser headers got its own copy of the table, plus dynamic initializers for it. Those headers are reached through scanner.h, which covers all of MLIRGen and everything that includes MLIRTypeHelper.h. The cost was about 0.6 MB of tslang.exe per TU, and every new file split paid it again. The own-passes split, for example, grew tslang.exe by 12 MB for this reason alone.

  • The header now declares each message extern through a variadic TS_DIAGNOSTIC(name, ...) macro. A new file, diagnostics.cpp, defines TS_DIAGNOSTICS_DEFINE and includes the header, which emits the single definition. The table is still the single source of truth.
  • diagnostics.cpp is added to ts-new-parser-lib, ts-new-scanner and ts-new-parser.
  • The header also gains an include guard.

extern was chosen over inline because the initializers are dynamic. With inline, every TU would still compile all 1967 initializers, and the result would depend on the linker folding them, which GCC does not do: it keeps a guarded init in every TU.

Nothing depends on per-TU identity. Messages are compared by .code, never by address. They are used only inside function bodies in parser.cpp and scanner.cpp. No namespace-scope initializer reads one, so cross-TU init order does not matter.

Results

  • Release tslang.exe (Windows, MSVC 2026): 104,951,808 → 86,646,272 bytes (−18.3 MB, −17.4%)

Test plan

  • Windows Release ALL build is clean
  • ctest -j 12 -C Release: 100% passed, 5369/5369 (1 disabled, 1 skipped, as before)
  • Linux (WSL, gcc 15.2): ts-new-scanner, ts-new-parser and tslang build and link
  • Parser diagnostics print correctly on Windows and Linux (let x = ; → Expression expected.)

🤖 Generated with Claude Code

…n unit

diagnostics.h defined its 1967 DiagnosticMessageStore objects `static` at
namespace scope. Each holds two std::wstring, so every TU that includes the
parser headers (through scanner.h: all of MLIRGen and the passes reached via
MLIRTypeHelper.h) got its own copy of the table plus its dynamic
initializers, about 0.6 MB of tslang.exe per TU and ~3900 string
allocations per TU at startup.

The header now declares them `extern` through a TS_DIAGNOSTIC(name, ...)
macro, and the new diagnostics.cpp defines TS_DIAGNOSTICS_DEFINE and
includes it to emit the single definition. The table stays one source of
truth. diagnostics.cpp is added to ts-new-parser-lib, ts-new-scanner and
ts-new-parser. The header also gains an include guard.

Nothing depends on per-TU identity: messages are compared by `.code`, and
none is read by a namespace-scope initializer in another TU, so cross-TU
initialization order does not matter.

Release tslang.exe (Windows, MSVC): 104,951,808 -> 86,646,272 bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit be09118 into main Oct 23, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the diagnostics-extern branch October 23, 2026 11:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant