Repository navigation
Link LiteRT into the Linux and Windows v0.19 hosts - #112
Merged
Merged
Conversation
LiteRT-LM hands external weights to LiteRT by writing C++ members of its options struct. The desktop hosts linked upstream's prebuilt libLiteRt, which carries its own libc++, so Linux lost the weights and Windows crashed. Build v0.19+ desktop hosts with LiteRT compiled in, as on Apple and Android, and fail the build if the host still imports libLiteRt. Fixes #111
leehack
marked this pull request as ready for review
November 8, 2026 02:25
This was referenced Nov 8, 2026
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.
Outcome
Fixes #111. From v0.19, Linux (x64, arm64) and Windows x64 compile LiteRT from the pinned
LITERT_REFintolibLiteRtLm/LiteRtLm.dll, as Apple and Android already do. EmbeddingGemma-2 engine creation now succeeds on all three desktop targets and produces correct vectors. The same change also repairs desktop explicit-GPU generation, which aborts on the published v0.19.1 Linux and Windows bundles. The C ABI and bundle layout are unchanged. Nothing is tagged, released or dispatched; the follow-up stable rebuild would bev0.19.1-1.Why
LiteRT-LM hands external and in-memory weights to LiteRT by writing C++ members of the opaque options struct (
litert::Options::SetExternalWeightScopedFile/SetWeightInMemoryMap, header-only,reinterpret_cast<LiteRtOptionsT*>). On Linux and Windows the host was source-built against libstdc++ / the MSVC STL but, with--define=litert_runtime_link_mode=dynamic, linked upstream's prebuiltlibLiteRt, which carries its own static libc++. That write is not ABI-valid, so Linux dropped the weights (Failed to open tflite_weights) and Windows faulted (libLiteRt.dll+0x7a8d, libc++unordered_maplookup).v0.19 made it worse for GPU: TopK samplers are now source-built into the host, while the WebGPU accelerator was the prebuilt libc++ one. On an NVIDIA L4 the published Linux x64 bundle aborts at the first GPU decode with
free(): invalid sizeinml_drift::TensorDescriptor::~TensorDescriptor<-TopKWebGpuSampler::SetInferenceFuncAndInputTensors(core-dump backtrace in the evidence), and Windows D3D12 crashes the same way (0xC0000409).In static mode upstream's
default_static_gpu_acceleratorcompilesml_drift_webgpu_acceleratorinto the host, so core, accelerator, samplers and LiteRT-LM share one C++ runtime, the same configuration as macOS and Android.Changes
tools/build_upstream_runtime.py:uses_shared_desktop_litert()keepslitert_runtime_link_mode=dynamiconly for Linux/Windows v0.16–v0.18; v0.19+ desktop uses the samelitert_link_capi_so=truecommand as Apple/Android. Older tags build exactly as before.validate_desktop_litert_linkage(): a v0.19+ Linux/Windows host that still importslibLiteRt.so/libLiteRt.dll(ELFDT_NEEDED, PE imports + delay imports) fails the build. CI logs showValidated in-process LiteRT linkageon linux-x64, linux-arm64 and windows-x64.docs/platform_strategy.md(v0.19 desktop linkage and why),docs/upstream_patch_audit.md(Linux arm64 v0.18.1-2 ignores litert_lm_engine_settings_set_num_threads: CPU runs on one thread #89 context limited to v0.16–v0.18).Bundle layout and C ABI: unchanged. Bundles still ship upstream
libLiteRt.*andlibLiteRtWebGpuAccelerator.*(llamadart's hook requires and preloads them), but the v0.19 host neither links nor dlopens them (Statically linked GPU accelerator registered.in every run). Exported C API / bridge symbols passvalidate_exported_symbolsand the full-platform verify job.Trade-off vs building
libLiteRtfrom source with the host's C++ runtime: that keeps two binaries, still pairs a source core with the prebuilt libc++ WebGPU accelerator (the #47/#50 L4 crash class) and the source-built samplers, and would need the accelerator rebuilt too. Linking in-process removes every cross-runtime boundary with one define. Cost:libLiteRtLm.so49.9 → 59.9 MBlibLiteRtLm.so51.5 → 56.5 MBLiteRtLm.dll19.1 → 27.1 MBDropping the now-unused
libLiteRt+ accelerator (~12–15 MB per target) would need a llamadart change and is out of scope here. CI build time rises (linux-x64 50 → 60 min of a 90 min limit, windows-x64 57 → 84 min of 120).Validation
CI at head
d1a95f19: PR Release Qualification run 42430614932, 19/19 checks pass: all 8 builds, full-platform verify (runtime matrix, dependencies, SwiftPM), ASR / Whisper routing / finish-reason smokes and Qwen3-0.6B CPU inference + thread probe on Linux x64, Windows x64 and macOS arm64. Local:python3 -m unittest discover -s tools -p 'test_*.py'441 OK, 3 skipped.Real hosts (GCE, account-scoped project), exact CI artifacts from this head. llamadart
main60b616f1(pins v0.19.1) with the #1369 guard bypassed locally andLLAMADART_LITERT_LM_LIB_DIRpointing at the PR bundle. Models: EmbeddingGemma-224d962e9(e7a8a220…7f4c), Qwen3-0.6B8414150f(555579ff…b0b4). Control: macOS arm64 (M4 Max) on published v0.19.1, which reproduces the earlier control bit for bit.PR runtime hashes: linux-arm64
libLiteRtLm.sob582d717…, linux-x64libLiteRtLm.sob8594f05…, windows-x64LiteRtLm.dll518412b8….tflite_weights)litert-lm-multimodal-embeddinge2e 3/3 passtflite_weights)Flatbuffer model initialized(0xC0000409)free(): invalid size); CPU dispose failed 1/30xC0000409)Embedding values (unnormalized, 768-d, vs macOS arm64 control; batch equals single-input on every host and pass; pass 2 equals pass 1):
The earlier
./tflite_weightsworkaround produced garbage (text head279.6vs-30.0); now the text head is-29.99, 25.88, 62.18, -30.39everywhere. Linux x64 and Windows x64 (different OS, compiler and STL, same CPU model) are bit-identical on text, both images and silence, so the x86 vs arm64 gap is ISA kernel numerics, not the weight handoff. Only non-silent audio differs between OSes on the same ISA; the Dart tone PCM hashes identically on macOS and Linux (3f895001…), so that difference is in the native audio front end (likely libm). The pairwise similarity matrix of the six inputs matches the macOS one within 0.008.Text generation (greedy, 3 prompts × 2 passes): CPU output is identical to the published v0.19.1 output on the same host (Linux x64, Linux arm64, Windows x64). GPU output is identical across Linux Vulkan and Windows D3D12 and across repeats; there is no published GPU baseline because it crashes.
Not covered / follow-ups
*.xnnpack_cachefiles locked in-process afterdispose(Access denied on delete). The published v0.19.1 runtime does the same for Qwen chat, so it predates this PR; it makes the llamadart e2e suite's temp-dir teardown fail on Windows.v0.19.1-1stable rebuild needs separate maintainer approval; then llamadart repins and removes the #1369 guard for Linux and Windows.Evidence (logs, JSON vectors, scripts, GPU core-dump backtrace): local
llamadart-evidence/i111-pr112-d1a95f1/. All GCE VMs, the firewall rule and buckets were deleted.