The Differences Between ARM64EC, ARM64, and ARM64X#

ARM64EC and pure native ARM64 both run ARM64 code, but they differ in how they interoperate with emulated x86_64 binaries. The following sections describe what ARM64EC is, how to compile it, and how it compares to pure native ARM64 in system DLL compatibility, performance, legacy code support, binary size, structure alignment, and exception handling.

What Is ARM64EC?#

ARM64EC stands for ARM64 Emulation Compatible. It is a form of ARM64 code that can interoperate with emulated x86_64 code, whereas pure native ARM64 code cannot load or interoperate with emulated binaries. ARM64EC enables incremental porting of large codebases. Developers can port individual source files one at a time, rather than adopting the all-or-nothing approach required for pure ARM64. For example, when a program contains A.cpp and B.cpp, compiling A.cpp as ARM64EC generates ARM64 code inside its functions. When execution transfers between A.cpp and x86_64-compiled B.cpp, marshalling code maintains compatibility. When everything has been ported to ARM64EC and no unforeseen plug-ins need to be supported, developers can and should switch to native ARM64 because there is no remaining ARM64EC compatibility requirement.

ARM64EC and emulated code are not supported for kernel-mode code. For more information, see Microsoft documentation and Porting applications to Arm64 using Arm64EC ABI.

What Is ARM64X?#

ARM64X is a Portable Executable (PE) file format that can serve both ARM64 and ARM64EC/x86_64 processes from one filename. Windows shows each process only its matching view. Most applications still build as ARM64 or ARM64EC; ARM64X is intended for shared DLLs. An ARM64X file can be built as an all-in-one binary that holds both ARM64 and ARM64EC code, or as a pure forwarder: a small ARM64X DLL with no real code that redirects to separate ARM64 and ARM64EC/x86_64 implementations. In the recent NVIDIA WoA driver, nvopencla64x.dll is an ARM64X pure forwarder that loads the full Arm64EC nvopencl64.dll or native ARM64 nvopencla64.dll according to process type. See Arm64X PE files and Build Arm64X binaries for an overview and build steps.

How to Compile ARM64EC#

Use the compiler switch -arm64EC to compile C/C++ code as ARM64EC instead of pure native ARM64.

System DLL Compatibility#

Most user-mode system call functions exported from DLLs such as KERNEL32, USER32, and UCRTBASE are compiled as ARM64EC. This ensures compatibility with both native ARM64 applications and emulated x86_64 applications. ARM64X allows compatible native ARM64 and ARM64EC/x86_64 processes to load those DLLs from the same System32 path.

Performance Considerations#

ARM64EC may be slightly slower than pure native ARM64 because of interoperability overhead. In many cases, the performance difference is negligible.

Legacy Code Support#

x86/x86_64 assembly code can be retained in a separate .asm file and linked into an ARM64EC binary. Assembly cannot be inline with C/C++ code for ARM64EC; it must be ported to C/C++. Legacy x86/x86_64 DLLs are compatible with ARM64EC applications, allowing porting of EXEs while still supporting emulated plug-ins.

Binary Size and Structure Alignment#

ARM64EC binaries are typically slightly larger than pure ARM64 because of added interop code. ARM64EC maintains x86_64-style structure alignment, so data structures retain their size when ported from x86_64. In contrast, structure sizes may change when compiled as pure ARM64.

Exception Handling#

Exception handlers in ARM64EC receive a context structure of type AMD64_CONTEXT, rather than ARM64_CONTEXT as with pure native ARM64 code. The OS treats ARM64EC binaries as emulated x86_64, since they often contain x86_64 code blocks.