Repository navigation
Link and run 32-bit programs (32-bit support, phase 2) - #335
Merged
Merged
Conversation
Six tasks: lowering takes the target's data layout, a guard against ABI-by-environment triples whose widths disagree, Boehm for x86, a standalone x86 build of the async runtime, per-arch library resolution with a machine-type check, and 32-bit programs that link and run under gc, rc and none. x86 libraries live in an x86 subdirectory of each existing library path, so x64 layouts, packages and CI are unchanged. x86 builds are opt-in through prepare_3rdParty.bat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The type converter sized pointers at 8 bytes and aligned i64 at 4 for every target but wasm32, because it was built with LLVM's default layout. MLIRGenModule already puts the TargetMachine's layout on the module; lowering now reads it. On i686 that ends 64-bit pointer arithmetic (288 of 513 corpus modules had ptrtoint ptr to i64; now 0). On x64 no corpus file changes: compare-ir.sh reports 3 DIFFs (00funcs, 00object_func, 00union_bin_ops2), and each is run-to-run nondeterminism the mask does not cover - rerunning the new compiler reproduces the baseline form. The hardcoded wasm32 layout is gone. It carried f128:64 and lacked i128:128, matching neither of LLVM's wasm32 derivations. Adds test/compare-ir.sh, which diffs masked --emit=llvm output of the test corpus against a stored baseline, and a wasm32 i128:128 check to test/check-datalayout.sh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lowering now fails with a diagnostic when the module has no llvm.data_layout, instead of falling
back to LLVM's default, and parses the layout with DataLayout::parse so a malformed string is an
error rather than an abort. Two unit tests run the pass on hand-built modules to cover both, since
the CLI cannot produce such a module.
00bigint_struct_layout.ts covers the only values whose layout the old default could move on x64:
a bigint after a smaller field in a non-packed struct (optionals, classes, tuples, object
literals, arrays of them). It also covers the one case the old layout miscompiled on x64: in the
union {flag, a: s32, b: s32} | {flag, v: bigint} both members measured 12 bytes, so the storage
went to the s32 member and the bigint lost its top half when stored (read back as 1, not
9007199254740993). With the real layout {i1, i64} is 16 bytes and is the storage.
compare-ir.sh now uses test/mask-ir.sh (committed here, extended to mask FH<n> of any length) and
stops if the mask is missing, instead of reporting every file as different.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds build_gc_{release,debug}_vs_x86.bat and their _shared twins, each a
faithful copy of the x64 script with only -A x64 -> -A Win32 and the
x64 -> x86 path segments changed (build dir + CMAKE_INSTALL_PREFIX).
Runtime library, threads and cplusplus settings are unchanged.
prepare_3rdParty.bat gains an optional second argument (ARCH, defaults
to x64) and an opt-in x86 block that builds
3rdParty/gc/x86/{release,debug} and 3rdParty/gcdll/x86/{release,debug}
via `prepare_3rdParty.bat <release|debug> x86`, mirroring the existing
x64 gc/gcdll blocks. With no second argument, ARCH stays x64 and the
new block is skipped, so existing invocations are unaffected.
Verified: all four x86 builds (static release/debug, shared
release/debug) produce IMAGE_FILE_MACHINE_I386 binaries (gc.lib,
import gc.lib, gc.dll), no compiler warnings, and the x64 trees under
3rdParty/gc/x64 and 3rdParty/gcdll/x64 are byte-for-byte and
timestamp-identical before and after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The in-tree lib/TypeScriptAsyncRuntime target is an add_mlir_library inside
a project that requires the x64-only MLIR/Clang packages, so it cannot be
configured as Win32. The library itself has no LLVM/MLIR link dependency
(its one MLIR header is header-only), so tslang/runtime/CMakeLists.txt
builds the same sources standalone, without LLVM or MLIR link deps, for
any target arch. scripts/build_tslang_runtime_{release,debug}_x86.bat
drive it for x86, installing into __build/tslang-runtime/<cfg>/x86.
Verified at x64 first: compiled and ran 00async_await.ts,
00async_result_types.ts and 00owned_async.ts once against the in-tree
runtime and once against this standalone build (--tslang-lib-path pointed
at each); stdout and exit codes matched, and --verbose linker output
confirmed each link picked up its own TypeScriptAsyncRuntime.lib via a
distinct -libpath. The x86 outputs are IMAGE_FILE_MACHINE_I386 and link
LIBCMT (release) / LIBCMTD (debug), matching the static-CRT release/debug
split used everywhere else in the project.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
For an x86 target the compiler looks in the x86 subdirectory of --tslang-lib-path, so the path to pass is the parent, not the x86 folder. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
For Windows x86 targets, the gc, shared gc and runtime lib paths now resolve to their x86 subdirectory, however the path was obtained (flag, environment or the gcdll default). A missing x86 build is a hard error that names the build script, and a library built for another machine is refused before the linker runs; x64 targets get the mirror check on their flat path. Dump::coffMachine reads the machine of an object, a static library or an import library. The flat-path existence checks in the getters are skipped for x86, where the flat path holds x64 libraries by design. check-x86-libs.sh covers the error paths and a correctly staged layout. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
check-x86-run.sh builds test/x86/hello.ts and test/x86/gc_stress.ts for i686-pc-windows-msvc with --opt --no-default-lib under -mm=gc, -mm=rc and -mm=none, and checks each executable is I386 (0x14C), exits 0 and prints exactly the expected text. gc_stress keeps a 1000-node list alive across about a million garbage nodes plus strings and arrays; its sum is wrong if a collection freed any of it. The script unsets the *_LIB_PATH environment variables and stages the x86 gc.lib into <tmp>/gc/x86, so it depends only on explicit flags. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The x86 gc scripts now also copy their output into the x86 subdirectory of the matching x64 lib directory (gc: lib/x86/gc.lib; gcdll: lib/x86/gc.lib and gc.dll), so the lib path users already pass for x64 serves x86 too. prepare_3rdParty.bat checks the copied location, so an older x86 build is re-run and gets the copy. The "no x86 build" error names the directory to pass, and the gc.dll the link picked gets the same COFF machine check as the libraries. check-x86-run.sh uses that real layout instead of staging gc.lib. Also: check-x86-libs.sh takes the x64 runtime from the in-tree build (or a second argument) and checks a wrong-machine gc.dll; both check scripts are executable; mask-ir.sh masks td_/tsrel_/tsret_ IDs of any length and lists 00array8_tuple_spread as noisy; the debug x86 runtime uses the debug LLVM headers; lowering checks the data layout before the @dllname renames; comments and the design spec record the layout as built and two Phase 4 findings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is phase 2 of 32-bit support. 32-bit Windows programs (
-mtriple=i686-pc-windows-msvc) now link and run under-mm=gc,-mm=rcand-mm=none. The plan is indocs/superpowers/plans/2026-09-21-32-bit-phase-2-native-libs.md. The spec's Phase 2 section has an "As built" table and the open issues.LLVMTypeConverteris now built from the module'sllvm.data_layout, not from LLVM's default layout. This is what makes i686 struct and union sizes andgetIntPtrTypecorrect. The number of i686 modules withptrtoint ptr … to i64goes from 288 to 0. A missing layout is now a hard error, and so is a malformed one; unit tests cover both. The wasm32 layout that was hardcoded here is gone; it was wrong (it lackedi128:128).x86_64-pc-linux-gnux32, mips64gnuabin32and aarch64ilp32, the pointer width of the architecture and of the ABI differ. MLIRGen and lowering would then disagree, so the compiler now refuses these triples and names the triple in the error.prepare_3rdParty.bat <cfg> x86builds it. This is opt-in; with no second argument, nothing changes. It runs the newscripts\build_gc_<cfg>[_shared]_vs_x86.bat, which copy their output into3rdParty\gc[dll]\x64\<cfg>\lib\x86\. The--gc-lib-path/--gc-shared-lib-pathvalues already used for x64 therefore work for x86 without change. The x64 files stay byte-identical; only thex86\subdirectory is added.tslang/runtime/CMakeLists.txtis a standalone project that buildsTypeScriptAsyncRuntimefor any arch, with no LLVM or MLIR link dependency. It uses the same sources as the in-tree library. I checked it against the in-tree library at x64 first: three async tests gave identical output with each. Build it withscripts\build_tslang_runtime_<cfg>_x86.bat, then pass--tslang-lib-path=__build\tslang-runtime\<cfg>.<lib path>\x86\.Dump::coffMachinereads the COFF machine ofgc.lib,gc.dllandTypeScriptAsyncRuntime.lib. A library built for the wrong arch is an error before the link, and that applies in both directions: an x86 lib found by an x64 build is caught too.Behavior change on x64: fixes a miscompile
Lowering used to size types with LLVM's default layout, which aligns
i64at 4 bytes. That under-sized a union member such as{i1, i64}at 12 bytes; its real size is 16. So in{flag; a: s32; b: s32} | {flag; v: bigint}the bigint lost its high half:9007199254740993read back as1. This happened on JIT and exe, at every opt level and under every memory model. The new corpus test00bigint_struct_layoutcovers it.Apart from that fix, the x64 IR is unchanged. The other targets were checked too, by compiling the whole corpus with the compiler from before and after this change:
00bigint_struct_layout.The remaining differences are run-to-run noise. Some generated names contain hash IDs, and equal-size union members are ordered by hash.
tslang/test/mask-ir.shmasks most of this, and lists the files it can't mask.Not in this PR (open issues, recorded in the spec)
./lib.dll: Can't open: Unknown error (0xC1).mlirGenImportSharedLibloads the DLL into the 64-bit compiler to read__decls. Building the x86 DLL itself works. The fix is to read__declsfrom the file instead, planned for phase 4.TypeScriptRuntime(the JIT DLL), because--emit=jitrefuses x86 targets.00for_awaitunrealized_conversion_cast: upstreamConvertAsyncToLLVMPassuses LLVM's default layout.00bigint_struct_layoutdocuments it and does not assert that field.let p: Node | nullis not narrowed in a loop condition or body;for (…; p; …)produces an invalid op.const arr: T[] = []; arr.push(…)is rejected.Test plan
ctest -j 16 -C Releasepasses 2766/2766. That is the old 2765 plus00bigint_struct_layout.MLIRGenTestspasses 144/144, including the newLoweringDataLayouttests.tslang/test/check-x86-run.shpasses 6/6:helloandgc_stress× gc/rc/none, each a PE with machine0x14C, exiting 0 with exact output. Under gc,gc_stressran 102 collections and freed 31.7 MB in a 628 KiB heap, and the list it keeps alive still sums correctly. The script runs release builds only; debug x86 builds were checked by hand.tslang/test/check-x86-libs.shpasses 11/11: a missing x86 directory, a wrong-arch lib or DLL in either direction, and correct x86 exe and DLL layouts.tslang/test/check-datalayout.shpasses 6/6, including the wasm32i128:128layout and the refusal ofx86_64-pc-linux-gnux32.tslang/test/compare-ir.shagainst the pre-change compiler; results are above.Only the ctest suite and
MLIRGenTestsrun in CI. The four scripts (check-datalayout.sh,compare-ir.sh,check-x86-libs.shandcheck-x86-run.sh) are run by hand. The two x86 scripts need the x86 libraries built first, and CI does not build them.🤖 Generated with Claude Code