Skip to content

Implement unknown function support for ARM64EC#1929

Open
BoiledElectricity wants to merge 8 commits into
KhronosGroup:mainfrom
BoiledElectricity:arm64ec-unknown-functions-1884
Open

Implement unknown function support for ARM64EC#1929
BoiledElectricity wants to merge 8 commits into
KhronosGroup:mainfrom
BoiledElectricity:arm64ec-unknown-functions-1884

Conversation

@BoiledElectricity

@BoiledElectricity BoiledElectricity commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #1884.

The loader's unknown-function trampolines (the phys-dev and device extension chains) didn't have an ARM64EC path, so unknown entrypoints would fall over on Windows on Arm. This adds one.

What's here:

  • asm: Each trampoline now gets an ARM64EC custom entry thunk ahead of a signature-less body. x64 callers enter through __os_arm64x_x64_jump, while ARM64EC callers come in through __os_arm64x_check_icall. The implementation uses the ksarm64.h helpers under _ARM64EC_.
  • build: SYSTEM_PROCESSOR now comes from the generator platform, update_deps.py learns the arm64ec architecture, and _M_ARM64EC maps to the 64-bit assembly offsets. Also resolves armasm64 by full path since CMake's MARMASM detection only recognizes plain ARM64.
  • tests: On ARM64EC, GetProcAddress returns an x64 fast-forward-sequence stub, while the loader's internal &fn points at the native ARM64EC implementation. Both are valid entrypoints, but they aren't required to be bit-identical, so the proc-addr tests compare behavior rather than raw pointer identity on ARM64EC and remain strict everywhere else.
  • ci: Adds a native Windows 11 Arm job covering ARM64 and ARM64EC in both Debug and Release configurations.

This ended up touching a little more than just the trampoline assembly. Getting ARM64EC support fully wired through required build-system updates, test fixes for the expected pointer-identity split, and CI coverage to keep it from regressing.

Wires up the build for Windows on Arm arm64ec/arm64x targets (KhronosGroup#1884):
- CMakeLists: derive SYSTEM_PROCESSOR from CMAKE_GENERATOR_PLATFORM (the -A target)
  when set, so cross-targeting picks the right assembler; fall back to host otherwise
- update_deps.py: accept the arm64ec architecture
- asm_offset.c: treat _M_ARM64EC as 64-bit ARM

The adjustor thunks in unknown_ext_chain_marmasm.asm are still TODO.
Completes unknown-function support for arm64ec/arm64x (KhronosGroup#1884).

The trampolines forward unknown device and physical-device extension calls
without knowing their signature. On arm64ec they can be entered from emulated
x64 code, so each one becomes an adjustor thunk: a custom entry thunk for the
x64-caller path plus the signature-less body for the arm64ec-caller path. The
arm64ec variants are guarded by _ARM64EC_ and use the SDK ksarm64.h macros
(A64NAME, NESTED_ENTRY, ARM64EC_CUSTOM_ENTRY_THUNK); the arm64 and arm32 paths
are unchanged.

The entry thunk loads the real target and tail-jumps __os_arm64x_x64_jump so the
target's own entry thunk performs the calling-convention work. It is emitted
before the body so __AddEntryThunkPointer wires the two together, and the body is
non-COMDAT so that wiring is generated. The body routes the target through
__os_arm64x_check_icall (leaving the caller-provided exit thunk in x10 untouched;
the dispatch-table offset uses an immediate rather than x10) and tail-branches to
it, saving and restoring fp/lr with writeback around the checker call so the
caller's return address survives.

Validated on Windows-on-Arm: the full loader test suite passes when built with
-A arm64ec.
@ci-tester-lunarg

Copy link
Copy Markdown

Author BoiledElectricity not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg

Copy link
Copy Markdown

Author BoiledElectricity not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg

Copy link
Copy Markdown

CI Vulkan-Loader build queued with queue ID 766241.

@ci-tester-lunarg

Copy link
Copy Markdown

CI Vulkan-Loader build # 3544 running.

@ci-tester-lunarg

Copy link
Copy Markdown

CI Vulkan-Loader build # 3544 passed.

@charles-lunarg

charles-lunarg commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

So I haven't done a full review, but I did want to share my efforts to properly test the change in github actions.

The current code doesn't build an native arm64x binary, that requires 2 steps. I have added the necessary CMake code and added a github actions ci job that exercises it, plus build the repo as x64 and runs the tests using the arm64x binary to validate everything works. https://github.com/charles-lunarg/Vulkan-Loader/tree/add_arm64x_ci_test

The test failures are in tests that query for various functions with different mechanisms and check that the function pointer is the same. These failures could imply something with the PR is wrong, or that those tests are faulty in an arm64x mode as functions queried from GetProcAddress and vkGetInstanceProcAddr may just be different. Still, it is very promising that all of the unknown function tests execute without issue.

Github actions run link: https://github.com/charles-lunarg/Vulkan-Loader/actions/runs/27234600513/job/80423918750
(I removed all other jobs in the workflow in case I need to iterate this one job).

On arm64x the loader has two views of each entry point: GetProcAddress hands an
x64 caller (an arm64ec build, or an x64 process emulated on an Arm64 host) an
export's x64 fast-forward-sequence thunk, while the loader resolves its own name
to the native arm64ec function. Same command, different address, and the Vulkan
spec doesn't require them to match.

The proc-addr tests asserted raw pointer identity between the two, which fails on
arm64x. Compare behavior instead (both non-null, both resolve a known command to
the same pointer, unknown commands return null) when running on an Arm64 host,
and keep strict identity everywhere else.
CMake's MARMASM compiler detection (CMakeDetermineASM_MARMASMCompiler.cmake)
only matches "ARM64", so for an arm64ec target it selects the 32-bit armasm,
which cannot assemble arm64ec code. It also leaves CMAKE_ASM_MARMASM_COMPILER as
a bare "armasm64", which the assembler check runs directly and so fails when the
dev tools are not on PATH (e.g. a stock CI configure). Either way the check fails
and unknown function support gets silently disabled.

Find armasm64 next to the C compiler and point the language at its full path so a
plain `-A arm64`/`-A arm64ec` configure works without a manual
-DCMAKE_ASM_MARMASM_COMPILER override or a developer command prompt. Drop this
once CMake's detection matches ARM64EC (KhronosGroup#1884).
Add a windows_arm job on the native windows-11-arm runners that builds and runs
the test suite for the arm64 and arm64ec targets. These exercise the MARMASM
assembler paths in unknown_ext_chain_marmasm.asm (including the arm64ec adjustor
thunks) that the existing x64 Windows jobs cannot reach (KhronosGroup#1884).
@BoiledElectricity BoiledElectricity force-pushed the arm64ec-unknown-functions-1884 branch from d7bdc5c to 775cbca Compare June 9, 2026 22:01
@ci-tester-lunarg

Copy link
Copy Markdown

Author BoiledElectricity not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg

Copy link
Copy Markdown

Author BoiledElectricity not on autobuild list. Waiting for curator authorization before starting CI build.

@BoiledElectricity

BoiledElectricity commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@charles-lunarg Thanks for digging into this and testing a real arm64x binary. Turns out there was a gap on my side.

Yeah my CI was testing only standalone arm64 and arm64ec builds. I never actually ran the x64 process + ARM64X loader case you were testing.

The failure was the proc-addr tests still doing a strict pointer identity check there. I switched that to a runtime check using IsWow64Process2 so it handles arm64x properly.

I also pulled in your arm64x build support and added a windows_arm64x CI job.

Everything's green now. x64 passes 642/642 against the ARM64X binary both locally and in CI.

@charles-lunarg

Copy link
Copy Markdown
Collaborator

Rather than cherry-pick my branch's commits onto yours, you just created new commits with the same content.
Could you show actual attribution by using my commits instead? Yeah, the CMake code comes from MSVC, but the build.yml was all mine.

@BoiledElectricity

BoiledElectricity commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

Rather than cherry-pick my branch's commits onto yours, you just created new commits with the same content. Could you show actual attribution by using my commits instead? Yeah, the CMake code comes from MSVC, but the build.yml was all mine.

@charles-lunarg I’m so sorry that was 100% my fault, I wasn’t even thinking about preserving the commit history. I had made another copy of the repo with your changes pulled, and then just copied over not even thinking. and then just started moving forward. I didn’t mean anything by it. I’m not home right now, but when I am, I will fix. Again, I’m sorry.

Since building arm64x is a two step process, we use CMake to cache the
binary artifacts from the first build step so it can apply them during the
second build step.

(cherry picked from commit 3bde50c)
@BoiledElectricity BoiledElectricity force-pushed the arm64ec-unknown-functions-1884 branch from 775cbca to 7a352c8 Compare June 10, 2026 15:39
charles-lunarg and others added 2 commits June 10, 2026 11:47
(cherry picked from commit 6e9edf7)
The cherry-picked job came in at the 2-space indent it had as the only job on its
source branch. Bump it to 4 spaces so it sits alongside the existing jobs.
@BoiledElectricity BoiledElectricity force-pushed the arm64ec-unknown-functions-1884 branch from 7a352c8 to 480416b Compare June 10, 2026 15:48
@ci-tester-lunarg

Copy link
Copy Markdown

Author BoiledElectricity not on autobuild list. Waiting for curator authorization before starting CI build.

1 similar comment
@ci-tester-lunarg

Copy link
Copy Markdown

Author BoiledElectricity not on autobuild list. Waiting for curator authorization before starting CI build.

@ci-tester-lunarg

Copy link
Copy Markdown

CI Vulkan-Loader build queued with queue ID 767931.

@ci-tester-lunarg

Copy link
Copy Markdown

CI Vulkan-Loader build # 3547 running.

@ci-tester-lunarg

Copy link
Copy Markdown

CI Vulkan-Loader build # 3547 passed.

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.

Implement unknown function support for ARM64EC/ARM64X architecture for Windows on Arm

3 participants