Skip to content

Missing Scala Native on Windows AARCH64 #11

Description

@MateuszKubuszok

Currently the integration tests on Windows AARCH64 are disabled for Scala Native unit tests, and FFI IT tests.

We were unable to make them work, and probably it might be a an issue with what Scala Native allows us to do.

The following is the result of Claude's investigation, memory reading, and past attempts. It might be wrong, incorrect, etc, but that would require a separate dedicated investigation and input from Scala Native mainteiners - cc @WojciechMazur


Scala Native: Windows ARM64 (aarch64) Support Gap

Summary

Scala Native cannot produce native ARM64 binaries on Windows ARM64. The build-time assembly code for delimited continuations (setjmp.S, delimcc.c) is missing a Windows ARM64 code path. Everything else in the toolchain (target triple detection, runtime platform detection, LLVM IR codegen, GC) already handles aarch64 correctly.

As a workaround, Chocolatey's LLVM package installs x86_64 LLVM on Windows ARM, so Scala Native silently falls back to producing x86_64 binaries. These run under Windows' Prism x64 emulation layer, masking the underlying gap.

Affected Files

1. nativelib/src/main/resources/scala-native/delimcc/setjmp.S

This assembly file implements scalanative_setjmp / scalanative_longjmp for delimited continuations. It has architecture-specific code paths:

Architecture OS Status
__x86_64__ Unix (__linux__, __APPLE__, __FreeBSD__) Implemented
__i386__ Unix Implemented
__aarch64__ Unix Implemented
__x86_64__ Windows (_WIN64) Implemented
__aarch64__ Windows (_WIN64) Missing

The existing __aarch64__ block uses Unix calling conventions (preserves x19-x30, d8-d15 with FPCR/FPSR). The Windows ARM64 ABI differs in register preservation requirements. A dedicated __aarch64__ && _WIN64 section is needed, analogous to how __x86_64__ has separate Unix and _WIN64 sections with different register save/restore logic (Unix saves callee-saved GPRs; Windows x64 additionally saves xmm6-xmm15).

What's needed: A new assembly block handling __aarch64__ && _WIN64 that follows the Windows ARM64 ABI:

  • Callee-saved GPRs: x19-x28, x29 (frame pointer), x30 (link register)
  • Callee-saved FP/SIMD: d8-d15
  • Stack pointer alignment: 16 bytes
  • No FPCR/FPSR save (unlike Unix ARM64)

2. nativelib/src/main/resources/scala-native/delimcc.c

The __aarch64__ section defines jump buffer constants:

#define ASM_JMPBUF_SIZE 192
#define JMPBUF_STACK_POINTER_OFFSET (104 / 8)

These values match the Unix ARM64 jmp_buf layout. The Windows ARM64 CONTEXT structure has a different layout and size. A _WIN64 && __aarch64__ section with correct offsets is needed (similar to the existing _WIN64 && __x86_64__ section that has its own distinct constants).

What Already Works

These components are architecture-agnostic or already handle aarch64 on Windows:

Target triple detection (Discover.scala)

Parses clang --version output to extract the target triple. Accepts any architecture clang reports, including aarch64. No Windows-specific architecture restrictions.

Target triple parsing (TargetTriple.scala)

Supports 60+ architectures including aarch64. Platform checks (targetsWindows, targetsMsys, etc.) distinguish only by OS, not architecture.

Runtime platform detection (platform.c)

Already handles Windows ARM64:

#if defined(_M_AMD64)
    arch = "amd64";
#elif defined(_X86_)
    arch = "x86";
#elif defined(_M_ARM64)
    arch = "aarch64";

LLVM IR code generation, GC, exception handling

Architecture-agnostic — these operate on LLVM IR and delegate architecture-specific code generation to LLVM/clang.

LLVM Toolchain Complication

The Chocolatey LLVM package (choco install llvm) on Windows ARM installs x86_64 LLVM running under x64 emulation, not native ARM64 LLVM:

This means Scala Native auto-detects x86_64-pc-windows-msvc as the target on Windows ARM, silently producing x86_64 binaries. The binaries work (under Prism emulation) but are not native ARM64.

To test native ARM64 builds, one must install the official LLVM-<version>-woa64.exe ARM64 installer directly (available since LLVM 12, see Arm Learning Paths).

Scala Native CI Coverage

Scala Native's own CI (.github/workflows/run-tests-windows.yml) only tests on windows-2022 (x86_64). No ARM64 Windows CI exists. No open issues requesting Windows ARM64 support as of April 2026.

Reproduction

On a windows-11-arm GitHub Actions runner (or any Windows ARM64 machine):

# Install ARM64-native LLVM (not via choco, which gives x64)
# Download LLVM-19.1.0-woa64.exe from https://github.com/llvm/llvm-project/releases

# Verify target is aarch64
clang --version
# Expected: Target: aarch64-pc-windows-msvc

# Attempt to compile any Scala Native project
sbt nativeLink
# Expected failure: compilation error in setjmp.S due to missing
# __aarch64__ + _WIN64 assembly path, or incorrect jmpbuf layout
# from using Unix-ABI constants on Windows

Without installing ARM64-native LLVM, choco install llvm gives x86_64 clang and Scala Native silently falls back to x86_64 code generation, masking the issue.

Proposed Fix

  1. Add __aarch64__ && _WIN64 assembly section to setjmp.S following the Windows ARM64 ABI calling convention
  2. Add _WIN64 && __aarch64__ jmpbuf constants to delimcc.c matching the Windows ARM64 CONTEXT layout
  3. Add a windows-11-arm CI matrix entry to run-tests-windows.yml with ARM64-native LLVM
  4. (Optional) Update LLVM.scala msysExtras to not hardcode x86_64 defines if targeting aarch64 under MSYS

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions