Repository navigation
Enable NEON intrinsics on arm64ec - #57
Merged
Merged
Conversation
Owner
|
Oh, thanks, I wasn't aware that there is separate arch for windows targets |
DoumanAsh
approved these changes
Sep 28, 2026
Owner
|
I released 0.8.19 with your PR |
athei
added a commit
to athei/mtld3d
that referenced
this pull request
Sep 28, 2026
…and EC=1) (#880) Nothing in the build or test system covered an arm64 Wine. Under a CrossOver-sourced arm64 Wine, our i686 and x86_64 PE builds run under the x86 translators (xtajit and FEX) with the aarch64 `mtld3d.so`, which is what arm64 users run in production, yet no make target installed into such a Wine, tested there, or measured there. There was also no way to build the PE side natively for that Wine. This adds two opt-in switches. Without either, every target behaves as before: `make -n` output is byte-identical to main for all 24 targets checked, including `ISOLATED=1` test and bench and `bench-ab BASE=main`. `ARM64=1` covers the x86 builds on an arm64 Wine. `ARM64=1 make install` installs the i686 and x86_64 builds into `$WINE_ARM64` and replaces the `aarch64-windows` `d3d9.dll` and `mtld3d.dll`, ours or Wine's own, with x64 fake-module markers. wineboot on an arm64 Wine fills `system32` from `aarch64-windows`, so an ARM64X image there wins for x64 processes; deleting it instead leaves `system32` without a `d3d9.dll` and x64 imports fail. i386 processes need nothing, since `syswow64` comes from `i386-windows`. `make test` gains `test-e2e-{i686,x86_64}-arm64` and `make conformance` gains `conformance-{i686,x86_64}-arm64`, which run the SDK's `d3d9_test.exe` for each arch under that Wine and judge it against that arch's existing entries. `EC=1` adds an ARM64X build of `d3d9.dll` and `mtld3d.dll`: each crate is built for `aarch64-pc-windows-msvc` and `arm64ec-pc-windows-msvc` as a static library, and one `lld-link /machine:arm64x` joins the halves. The link uses llvm-mingw's CRT and Wine's ARM64X `libwinecrt0.a` and `libntdll.a`, because xwin cannot fetch the MSVC ARM64EC CRT. It needs LLD 23, since LLD 22 gives the EC view the ARM64 TLS directory and the shim crashes at load. llvm-mingw's `__tlregdtor` registers nothing, so snmalloc is built with `SNMALLOC_USE_THREAD_CLEANUP` and a TLS callback returns each exiting thread's allocator; the new `thread_exit.rs` e2e test checks that allocators are reused (4 distinct over 32 threads, against 32 when they leak). Three CRT stand-ins in `arm64_crt.rs` cover `type_info`'s vtable (every profile is `panic = "abort"`) and `__cpuid`/`__cpuidex` (only zstd calls them). xxhash-rust is patched to the fork commit of DoumanAsh/xxhash-rust#57 so its NEON path builds on arm64ec; the patch goes when a release carries it. `EC=1 make install` puts the pair into `$WINE_ARM64/aarch64-windows`, `EC=1 make test` adds `test-e2e-arm64x`, and `EC=1 make conformance` adds `conformance-arm64x`. Every arm64 leg clones `$WINE_ARM64` with APFS clonefile, installs into its own copy through the same install targets, and boots a fresh prefix, so the legs never touch the real tree or each other. They run with `--jobs 1` and skip `devices_and_windows_come_and_go_on_several_threads_at_once` and `concurrent_retargets_deliver_every_window_message_to_its_own_device`, which hang on stock CrossOver winemac's lock order between `win_data_mutex` and win32u's `surfaces_lock`, through a new `--skip` option of the e2e runner applied after `--filter`. `ARM64=1` and `EC=1` also apply to `make bench` and `bench-ab`, and `make bench-variants` compares one commit across layouts: the x86_64 or i686 build on `WINE_SDK` against the same build on `WINE_ARM64`, and with `EC=1` the x86_64 build against ARM64X on `WINE_ARM64`. `bench-ab` records each leg's runtime and variant in `layouts.txt`, and `bench-compare` accepts legs that differ in layout but share an image. The new path variables `WINE_ARM64`, `WINE_SDK_ARM64X`, `LLVM_MINGW` and `ARM64X_LLD` come from the environment only, with no default in the Makefile; a target that needs one stops and names it. The ARM64X link archives come from the "ARM64X link libraries" step of the Wine build scripts. ARM64X stays opt-in because it is slower for chatty workloads. Every call from x64 code into ARM64EC costs about 22 ns in FEX, the same for our DLL, Wine's own ARM64X d3d9 and kernel32, and a profile of the loop puts 94% of the time in FEX's code cache and `xtajit64.dll`. Small D3D9 calls therefore get slower while calls with real bodies get faster. A pure ARM64EC DLL without the ARM64 half was not taken because Wine's loader layout expects ARM64X builtins in `aarch64-windows`. Rules check: new build-time make variables `EC`, `ARM64` and the four env-only paths above; new C flags for the two arm64 targets only (`-fno-exceptions -fno-rtti -fno-threadsafe-statics -DSNMALLOC_USE_THREAD_CLEANUP -DARM64_SYSREG`, plus `-DZSTD_NO_INTRINSICS` for arm64ec, which defines `_M_X64` and would otherwise select zstd's SSE2 path), with a build check that fails if snmalloc gains another thread-safe static guard; two immutable statics (`TYPE_INFO_VFTABLE`, `THREAD_EXIT_CALLBACK`) whose doc blocks say why they are statics; the `[patch.crates-io]` source swap for xxhash-rust; `windows/shim/mtld3d.def`, which duplicates the export list the x86 cdylib derives, because the ARM64X link is a manual lld-link; and the new e2e binary `thread_exit.rs` with its COVERAGE row. No configuration keys, wire fields, derives or lint allows are added. Verification against main 6127876. The e2e and conformance runs used 3da483b, whose tree matches this branch except for the two later bench commits; `make check` and the three `make check` variants ran on the final tree. - `make check`, `EC=1 make check` and `ARM64=1 make check` passed (formatting, Clippy for all PE targets including both arm64 ones, audit, documentation). - `make test ISOLATED=1` passed: 882 passed / 0 failed / 11 ignored for each PE architecture, including `thread_exit`. - `EC=1 ARM64=1 make test ISOLATED=1` under a CrossOver 27 arm64 Wine: `test-e2e-i686-arm64`, `test-e2e-x86_64-arm64` and `test-e2e-arm64x` each 880 passed / 0 failed / 11 ignored / 2 skipped. The layer log's image ID matched the intended build's PDB GUID in each leg, and `thread_exit` passed in the EC view (4 distinct allocators over 32 threads). - `PROD=1 EC=1 make` links both ARM64X DLLs with no assert imports and imports through `KERNEL32.dll` like the x86 builds. - `EC=1 ARM64=1 make conformance ISOLATED=1`: all three arm64 legs abort in the `device` subtest in CrossOver 27's win32u (`make_ratio`, sysparams.c:324), which rejects some of the scaled sizes our adapter mode list serves. That is #879, fixed separately. The sites that ran before the abort show no new failures, and the stateblock and d3d9ex subtests match the baseline. - `ISOLATED=1 EC=1 make bench-variants`, frame p50 on one machine: the ARM64X build against the x86_64 build on the same arm64 Wine is 13.2% slower for the WoW 1.12 busy frame, 5.4% faster for WoW 3.3.5a, 38.2% faster for WoW-style query polling and 2.6 times the cost per `SetRenderState`. The i686 build on the arm64 Wine against the x86_64 Wine is faster on every workload (WoW 1.12 frame 0.810 to 0.747 ms, 3.3.5a 0.642 to 0.547 ms); that comparison changes the Wine build as well as the host arch. The arm64 legs need a local arm64 Wine and toolchain, so CI runs only the default legs.
athei
added a commit
to athei/mtld3d
that referenced
this pull request
Sep 29, 2026
## Symptoms The windows workspace carried a `[patch.crates-io]` entry pointing xxhash-rust at the fork athei/xxhash-rust (rev 4cb4b3a), added in #880, because no crates.io release had the arm64ec NEON gates from DoumanAsh/xxhash-rust#57. xxhash-rust 0.8.19 is that release, and its `src/` is byte-identical to the fork rev. ## Changes The `[patch.crates-io]` entry is removed from `windows/Cargo.toml`, and the workspace requirement is raised to `0.8.19`, the first release that builds on arm64ec. `windows/Cargo.lock` and `unix/Cargo.lock` both move to xxhash-rust 0.8.19 from crates.io. The Unix workspace depends on `windows/core` by path, so it inherits the raised requirement; with the old Unix lock, `--locked` exits 101. ## Alternatives Keep the patch: there is no reason to, since 0.8.19 carries the same source. ## Verification - `make check`: green. - `EC=1 make check`: green, compiles xxhash-rust 0.8.19 for `arm64ec-pc-windows-msvc`. - `EC=1 make windows-arm64x`: green. - `make test ISOLATED=1`: nextest 1751/1751 and 709 passed with 2 skipped; e2e 888 passed, 0 failed, 11 ignored on both legs. - `EC=1 make test` was not run.
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.
The crate does not build for
arm64ec-pc-windows-msvcwith thexxh3feature:On arm64ec rustc sets
target_arch = "arm64ec"andtarget_feature = "neon". The NEON accumulate and scramble functions are selected bytarget_feature = "neon", but thevld1q_u8!macro, the twouse core::arch::aarch64::*imports and thevmlal_high_u32line are gated ontarget_arch = "aarch64", so none of them exist on arm64ec.core::arch::aarch64is available there: stdarch declares the module undercfg(any(target_arch = "aarch64", target_arch = "arm64ec"))(core_arch/src/mod.rs). arm64ec was added to that cfg by rust-lang/stdarch#1550.This changes those four gates to
any(target_arch = "aarch64", target_arch = "arm64ec"). Nothing changes for other targets.Verified with stable 1.98.1:
cargo testandcargo test --releasewith all features pass on aarch64-apple-darwin, which runs the same NEON path.cargo build --target arm64ec-pc-windows-msvcfails before the change and succeeds after it, for--features xxh3and for all features. aarch64-pc-windows-msvc, x86_64-pc-windows-msvc, i686-pc-windows-msvc and x86_64-apple-darwin still build. In the arm64ec disassembly LLVM keeps the NEON code on v0-v15, which is the register range ARM64EC allows.