SAOC 2026: ImportC Weekly Update #3
Niyati Jain
niyati.jainn15 at gmail.com
Wed Oct 7 16:16:18 UTC 2026
**SAOC 2026: ImportC Weekly Update #3**
This week I spent most of it in Windows ABI details, stack
behavior, and how dmd's ImportC actually generates code versus
real cl.exe. It was a genuinely good week for actually
understanding D's runtime internals rather than just working
around them.
With this, I closed out the Windows half of Milestone 1: got
sljit's zero-wrapper example building and running correctly via
ImportC on Windows, found and fixed the actual root cause, and
spent most of the week isolating a second, much harder bug down
to a precise, actionable finding in **sljit's own unmodified test
suite**.
**The real root cause of the Windows link failures:**
`sljitLir.c` includes `<windows.h>` but never includes
`importc_msvc_builtins.h`, and druntime's own auto-included
`importc.h` doesn't pull that in either, so the
MSVC-intrinsic-to-D-template redirect in `__builtins_msvc.d`
never fires. `__cpuidex`, `_umul128`, the `__readgs*` family,
etc. all fell through as plain unresolved externs at link time,
18 symbols total. Fixed by force-including the header with cl's
real `/FI` flag (not gcc's `-include`, since dmd shells out to
the actual cl.exe preprocessor on Windows), with zero changes to
sljit's vendored source. That resolved 16 of 18.
The remaining 2 turned out to be a separate issue:
`RtlSetVolatileMemory`, `__arch_inverted`, and `__arch_avx10ver`
aren't real linkable symbols at all, even in real MSVC builds,
they're compiler-recognized pseudo-intrinsics that cl.exe expands
inline, and `__builtins_msvc.d` doesn't cover them yet. Wrote
real implementations for those three myself. `MapViewOfFileNuma2`
just needed `mincore.lib` linked, found by grepping every system
`.lib` on the runner for the actual export rather than guessing.
With both fixes in, the zero-wrapper example builds and runs
clean: it JIT-compiles a function at runtime and calls it
successfully, confirmed in real CI, no modifications to sljit's
source.
**Challenges, and working through them:**
The harder part was Milestone 1's other ask: confirm sljit's real
test suite runs unmodified from D. Once the link errors above
were fixed, the compiled test suite started exiting with code
`127` part-way through its 138 tests, non-deterministically, a
different test failing on every run.
I bisected by inserting checkpoint prints between test calls, and
the crash point genuinely moved between runs, once around test
11-29 broadly, once with test 11 completing entirely internally,
once making it all the way to test 17. That pointed at real
**memory corruption** rather than one deterministic bug, so I
pulled in `cdb` and ran the actual test binary under it. That
gave a real exception record for the first time:
`STATUS_STACK_OVERFLOW (0xc00000fd)`.
I ruled out the obvious explanations one at a time against real
CI runs: a missing `_chkstk` stack-probe call (tested directly
with an 8KB buffer and then a deliberate write to byte 65535 of
an untouched 64KB buffer, both ran clean under ImportC, so
ImportC's codegen for large single frames checked out fine in
isolation), optimized vs. unoptimized codegen (identical crash
either way), and a bigger linked stack (made it crash earlier
instead of fixing it, which was itself informative).
I asked my mentor, Nicholas Wilson, about it, and his suggestion
to try AddressSanitizer via LDC was the right instinct for
intermittent corruption. Porting it over surfaced a separate
finding along the way: LDC's own `importc.h` has no equivalent of
dmd's MSVC-intrinsic redirect mechanism at all, confirmed by
grepping its entire druntime source for `__cpuidex` and finding
nothing. He advised that porting dmd's `#pragma
function_decl(ignore, ...)` mechanism to LDC wasn't worth the
detour, so I dropped that path.
The real breakthrough came from going back to basics: I rebuilt
the identical, unmodified sljit source with real `cl.exe`
directly, completely bypassing D and ImportC. At first it crashed
the exact same way, which told me the problem wasn't specific to
ImportC, but it also wasn't sljit's own logic, since applying
sljit's real `CMakeLists.txt` flags fixed it. Their build sets
`target_compile_definitions(sljit_test PRIVATE
SLJIT_HAVE_CONFIG_PRE)` and `/STACK:0x400000` on MSVC, two things
our from-source build was never setting since we compile the
three files directly rather than through their CMake config.
Applying both exactly: **the native cl.exe build now runs all 138
tests clean end to end**, with a real, meaningful exit, and only
one genuine logic-level failure (`test34`, unrelated to the
crash). The exact same source and exact same flags under dmd's
ImportC, though, still segfault. Same everything except the
compiler, which isolates this to a real codegen bug in dmd's
ImportC specifically.
Thank you to Nicholas for the steady direction through this one,
especially the nudge toward ASan and the questions that kept me
from chasing unrelated paths.
If anyone's run into ImportC codegen differing from real cl.exe
on a large translation unit like this before, or has a lead on
where to start looking in dmd's backend, I'd be glad to hear it,
here or once I open the dmd issue.
Confirmed — the most recent run (37641216210) has the
zero-wrapper example passing cleanly. Here's the Status section
to paste:
**Status:**
- sljit standalone package with the fix applied:
https://github.com/niy-ati/sljit
- Windows CI, zero-wrapper example passing:
https://github.com/niy-ati/importC/actions/runs/37641216210
**Milestone 1 status:** everything is done except this one piece.
The zero-wrapper example, the CI matrix, and the earlier dmd
fixes are all in. Sljit's own test suite running unmodified is
the one requirement still open, and it's now blocked on a real
dmd bug rather than anything on our side, so next step is filing
that properly rather than more workarounds.
**Next week:** write up the ImportC codegen bug with a minimal
repro and file it upstream against dmd, upstream the three
Windows intrinsic gaps
(`__arch_inverted`/`__arch_avx10ver`/`RtlSetVolatileMemory`) as a
real PR against `__builtins_msvc.d`, and get the sljit dub
package published now that the Windows zero-wrapper example
itself is fully green.
More information about the Digitalmars-d
mailing list