Skip to content

Drop ModuleInfo from LDC release builds - #519

Open
TurkeyMan wants to merge 2 commits into
masterfrom
agent/no-moduleinfo
Open

Drop ModuleInfo from LDC release builds#519
TurkeyMan wants to merge 2 commits into
masterfrom
agent/no-moduleinfo

Conversation

@TurkeyMan

@TurkeyMan TurkeyMan commented Aug 14, 2026

Copy link
Copy Markdown
Member

Depends on open-watt/urt#225 -- the submodule bump points at it. The Makefile guard here scans urt for module ctors, so this will not build against urt master until that lands.

target before after saved
Linux x86-64 release 206,277 179,413 26,864
ESP32-C6 release 142,732 119,004 23,728 (16.6%)

All of it .data. ModuleInfo exists to run module ctors/dtors and to enumerate unittests; urt no longer declares a module ctor, so only the test runner still needs it, which is why CONFIG=unittest keeps it. (urt#225 carries a second, independent saving of 2,752 bytes RAM / 16,872 bytes binary that does not depend on this flag at all.)

The guard is the part worth reviewing

--fno-moduleinfo is dangerous in a way the compiler will not tell you about: LDC silently skips module ctors under it. No error, no warning -- the initialisation simply never runs. I confirmed that directly rather than assuming it.

So the build scans for a module ctor and refuses to proceed if it finds one:

Makefile:103: *** --fno-moduleinfo would silently skip module ctors declared in: src/manager/features.d.  Stop.

And because that scan is only as good as $(shell) -- which is unusable under some Windows makes, pre-existing and unrelated to this change -- a scan that cannot find a file known to exist leaves the flag off rather than trusting a guard that never ran. Windows loses the saving; it does not get a silent hazard. ESP firmware builds go through WSL, where the guard works.

If you would rather this were opt-in, it is a one-line change.

A related hazard that turned out not to bite: a module-scope AA literal cannot be statically initialised in general, so if the compiler had emitted a hidden module ctor for one, this flag would have silently left it empty -- and the guard greps for a written static this(, so it would never have seen it. Checked directly: LDC statically initialises them, including struct-keyed ones. No exposure.

Also here

  • ramreport.py classifies sections by substring. ESP-IDF emits .dram0.bss / .dram0.data, which the exact-match list missed entirely, so the tool previously reported nothing on ESP targets. rodata has to be excluded explicitly or "data" swallows it.
  • dyn_cast on the BaseObject hierarchy, walking the CollectionTypeInfo chain the objects already carry. This has no call sites yet -- it is groundwork so downcasts stay checked once TypeInfo goes away. --fno-rtti (~18KB more) is blocked by runtime new in urt's AA implementation, not by anything here; see urt#225 for the measurements.

Verification

Linux LDC unittest 134/134, Windows DMD 135/135, ESP32-C6 firmware links and builds. The release binary carries zero __ModuleInfo symbols, boots, and runs the recorder -- which exercises find_type_details, whose assert(false) survives -release -- with no halt.

@TurkeyMan
TurkeyMan force-pushed the agent/no-moduleinfo branch 2 times, most recently from 3547add to 5c863aa Compare August 14, 2026 17:02
Saves 26,864 bytes of static RAM on x86-64 and 23,728 bytes on ESP32-C6
(142,732 -> 119,004, 16.6%), all of it .data. urt no longer declares a module
ctor, so ModuleInfo's only remaining job is enumerating unittests, which is why
CONFIG=unittest keeps it.

The flag is dangerous in a way the compiler will not tell you about: LDC
silently skips module ctors under it - no error, no warning, the initialisation
simply never runs. So the build scans for one and refuses to proceed if it finds
one, naming the file. And because the scan is only as good as $(shell), which is
unusable under some Windows makes, a scan that cannot find a file known to exist
leaves the flag off rather than trusting a guard that never ran.

ramreport.py classifies sections by substring: ESP-IDF emits .dram0.bss and
.dram0.data, which the exact-match list missed entirely, and rodata has to be
excluded explicitly or "data" swallows it.

Needs the matching urt change; the submodule bump points at it.
Walks the CollectionTypeInfo chain the objects already carry, so downcasts stay
checked once TypeInfo goes away. No call sites yet: converting them is only
worth doing alongside --fno-rtti, which is still blocked by runtime `new` in
urt's AA implementation.
@TurkeyMan
TurkeyMan force-pushed the agent/no-moduleinfo branch from 5c863aa to e1ff26d Compare August 16, 2026 01:45
@TurkeyMan

Copy link
Copy Markdown
Member Author

Rebased onto master and re-pinned at the rebased urt#225 (19c0b08), which is now current with urt master.

Note for whoever builds this: --fno-moduleinfo requires a newer LDC than 1.34 (it fails there with "Unknown command line argument"). Verified with LDC 1.41: debug and COMPILER=ldc CONFIG=release both build clean. Worth stating a minimum LDC version somewhere if release builds are expected to work on older toolchains.

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