Skip to content

Enable NEON intrinsics on arm64ec - #57

Merged
DoumanAsh merged 1 commit into
DoumanAsh:masterfrom
athei:arm64ec-neon
Sep 28, 2026
Merged

DoumanAsh merged 1 commit into
DoumanAsh:masterfrom
athei:arm64ec-neon

Conversation

@athei

@athei athei commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

The crate does not build for arm64ec-pc-windows-msvc with the xxh3 feature:

error: cannot find macro `vld1q_u8` in this scope
   --> src/xxh3.rs:287:51
error[E0425]: cannot find function `vreinterpretq_u64_u8` in this scope
error[E0425]: cannot find type `uint64x2_t` in this scope
...
error: could not compile `xxhash-rust` (lib) due to 36 previous errors

On arm64ec rustc sets target_arch = "arm64ec" and target_feature = "neon". The NEON accumulate and scramble functions are selected by target_feature = "neon", but the vld1q_u8! macro, the two use core::arch::aarch64::* imports and the vmlal_high_u32 line are gated on target_arch = "aarch64", so none of them exist on arm64ec. core::arch::aarch64 is available there: stdarch declares the module under cfg(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 test and cargo test --release with all features pass on aarch64-apple-darwin, which runs the same NEON path. cargo build --target arm64ec-pc-windows-msvc fails before the change and succeeds after it, for --features xxh3 and 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.

@DoumanAsh

Copy link
Copy Markdown
Owner

Oh, thanks, I wasn't aware that there is separate arch for windows targets
I will merge it later and make new release

@DoumanAsh
DoumanAsh merged commit 3482237 into DoumanAsh:master Sep 28, 2026
7 checks passed
@DoumanAsh

Copy link
Copy Markdown
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.
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.

2 participants