Skip to content

Commit

Permalink
Auto merge of rust-lang#123144 - dpaoliello:arm64eclib, r=GuillaumeGo…
Browse files Browse the repository at this point in the history
…mez,ChrisDenton,wesleywiser

Add support for Arm64EC to the Standard Library

Adds the final pieces so that the standard library can be built for arm64ec-pc-windows-msvc (initially added in rust-lang#119199)

* Bumps `windows-sys` to 0.56.0, which adds support for Arm64EC.
* Correctly set the `isEC` parameter for LLVM's `writeArchive` function.
* Add `#![feature(asm_experimental_arch)]` to library crates where Arm64EC inline assembly is used, as it is currently unstable.
  • Loading branch information
bors committed Apr 18, 2024
2 parents c25473f + 32f5ca4 commit c5de414
Show file tree
Hide file tree
Showing 12 changed files with 73 additions and 47 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6332,9 +6332,9 @@ dependencies = [

[[package]]
name = "windows-bindgen"
version = "0.55.0"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "073ff8a486ebad239d557809d2cd5fe5e04ee1de29e09c6cd83fb0bae19b8a4c"
checksum = "a28e3ea6330cf17fdcdce8bf08d0549ce93769dca9bedc6c39c36c8c0e17db46"
dependencies = [
"proc-macro2",
"rayon",
Expand All @@ -6355,9 +6355,9 @@ dependencies = [

[[package]]
name = "windows-metadata"
version = "0.55.0"
version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b602635050172a1fc57a35040d4d225baefc6098fefd97094919921d95961a7d"
checksum = "3993f7827fff10c454e3a24847075598c7c08108304b8b07943c2c73d78f3b34"

[[package]]
name = "windows-sys"
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ impl<'a> LlvmArchiveBuilder<'a> {
members.as_ptr() as *const &_,
true,
kind,
self.sess.target.arch == "arm64ec",
);
let ret = if r.into_result().is_err() {
let err = llvm::LLVMRustGetLastError();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2303,6 +2303,7 @@ extern "C" {
Members: *const &RustArchiveMember<'_>,
WriteSymbtab: bool,
Kind: ArchiveKind,
isEC: bool,
) -> LLVMRustResult;
pub fn LLVMRustArchiveMemberNew<'a>(
Filename: *const c_char,
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) {
extern "C" LLVMRustResult
LLVMRustWriteArchive(char *Dst, size_t NumMembers,
const LLVMRustArchiveMemberRef *NewMembers,
bool WriteSymbtab, LLVMRustArchiveKind RustKind) {
bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) {

std::vector<NewArchiveMember> Members;
auto Kind = fromRust(RustKind);
Expand Down Expand Up @@ -207,7 +207,7 @@ LLVMRustWriteArchive(char *Dst, size_t NumMembers,
auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false);
#else
auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab : SymtabWritingMode::NoSymtab;
auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false);
auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC);
#endif
if (!Result)
return LLVMRustResult::Success;
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ pub fn spin_loop() {
crate::arch::riscv64::pause();
}

#[cfg(target_arch = "aarch64")]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))]
{
// SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets.
unsafe { crate::arch::aarch64::__isb(crate::arch::aarch64::SY) };
Expand Down
3 changes: 2 additions & 1 deletion library/panic_abort/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#![doc(issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]
#![panic_runtime]
#![allow(unused_features)]
#![feature(asm_experimental_arch)]
#![feature(core_intrinsics)]
#![feature(panic_runtime)]
#![feature(std_internals)]
Expand Down Expand Up @@ -78,7 +79,7 @@ pub unsafe fn __rust_start_panic(_payload: &mut dyn PanicPayload) -> u32 {
core::arch::asm!("int $$0x29", in("ecx") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
} else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] {
core::arch::asm!(".inst 0xDEFB", in("r0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
} else if #[cfg(target_arch = "aarch64")] {
} else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] {
core::arch::asm!("brk 0xF003", in("x0") FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
} else {
core::intrinsics::abort();
Expand Down
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
#![feature(allow_internal_unstable)]
#![feature(asm_experimental_arch)]
#![feature(c_unwind)]
#![feature(cfg_sanitizer_cfi)]
#![feature(cfg_target_thread_local)]
Expand Down
1 change: 1 addition & 0 deletions library/std/src/sys/pal/common/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub const MIN_ALIGN: usize = 8;
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "loongarch64",
target_arch = "mips64",
target_arch = "mips64r6",
Expand Down
40 changes: 20 additions & 20 deletions library/std/src/sys/pal/windows/c/windows_sys.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Bindings generated by `windows-bindgen` 0.55.0
// Bindings generated by `windows-bindgen` 0.56.0

#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
#[link(name = "advapi32")]
Expand Down Expand Up @@ -345,7 +345,7 @@ extern "system" {
}
#[link(name = "kernel32")]
extern "system" {
pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME) -> ();
pub fn GetSystemTimePreciseAsFileTime(lpsystemtimeasfiletime: *mut FILETIME);
}
#[link(name = "kernel32")]
extern "system" {
Expand Down Expand Up @@ -1018,7 +1018,7 @@ impl Clone for CONTEXT_0_0 {
}
}
#[repr(C)]
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
pub struct CONTEXT {
pub P1Home: u64,
pub P2Home: u64,
Expand Down Expand Up @@ -1067,30 +1067,30 @@ pub struct CONTEXT {
pub LastExceptionToRip: u64,
pub LastExceptionFromRip: u64,
}
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
impl Copy for CONTEXT {}
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
impl Clone for CONTEXT {
fn clone(&self) -> Self {
*self
}
}
#[repr(C)]
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
pub union CONTEXT_0 {
pub FltSave: XSAVE_FORMAT,
pub Anonymous: CONTEXT_0_0,
}
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
impl Copy for CONTEXT_0 {}
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
impl Clone for CONTEXT_0 {
fn clone(&self) -> Self {
*self
}
}
#[repr(C)]
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
pub struct CONTEXT_0_0 {
pub Header: [M128A; 2],
pub Legacy: [M128A; 8],
Expand All @@ -1111,9 +1111,9 @@ pub struct CONTEXT_0_0 {
pub Xmm14: M128A,
pub Xmm15: M128A,
}
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
impl Copy for CONTEXT_0_0 {}
#[cfg(target_arch = "x86_64")]
#[cfg(any(target_arch = "arm64ec", target_arch = "x86_64"))]
impl Clone for CONTEXT_0_0 {
fn clone(&self) -> Self {
*self
Expand Down Expand Up @@ -3339,7 +3339,7 @@ pub const FILE_WRITE_EA: FILE_ACCESS_RIGHTS = 16u32;
pub const FILE_WRITE_THROUGH: NTCREATEFILE_CREATE_OPTIONS = 2u32;
pub const FIONBIO: i32 = -2147195266i32;
#[repr(C)]
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
pub struct FLOATING_SAVE_AREA {
pub ControlWord: u32,
pub StatusWord: u32,
Expand All @@ -3351,9 +3351,9 @@ pub struct FLOATING_SAVE_AREA {
pub RegisterArea: [u8; 80],
pub Cr0NpxState: u32,
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
impl Copy for FLOATING_SAVE_AREA {}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
impl Clone for FLOATING_SAVE_AREA {
fn clone(&self) -> Self {
*self
Expand Down Expand Up @@ -4106,7 +4106,7 @@ impl Clone for WSABUF {
}
}
#[repr(C)]
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
pub struct WSADATA {
pub wVersion: u16,
pub wHighVersion: u16,
Expand All @@ -4116,9 +4116,9 @@ pub struct WSADATA {
pub szDescription: [i8; 257],
pub szSystemStatus: [i8; 129],
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
impl Copy for WSADATA {}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
impl Clone for WSADATA {
fn clone(&self) -> Self {
*self
Expand Down Expand Up @@ -4286,7 +4286,7 @@ pub const WSA_SECURE_HOST_NOT_FOUND: WSA_ERROR = 11032i32;
pub const WSA_WAIT_EVENT_0: WSA_ERROR = 0i32;
pub const WSA_WAIT_IO_COMPLETION: WSA_ERROR = 192i32;
#[repr(C)]
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
pub struct XSAVE_FORMAT {
pub ControlWord: u16,
pub StatusWord: u16,
Expand All @@ -4305,9 +4305,9 @@ pub struct XSAVE_FORMAT {
pub XmmRegisters: [M128A; 16],
pub Reserved4: [u8; 96],
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
impl Copy for XSAVE_FORMAT {}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "x86_64"))]
impl Clone for XSAVE_FORMAT {
fn clone(&self) -> Self {
*self
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/pal/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ pub fn abort_internal() -> ! {
core::arch::asm!("int $$0x29", in("ecx") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
} else if #[cfg(all(target_arch = "arm", target_feature = "thumb-mode"))] {
core::arch::asm!(".inst 0xDEFB", in("r0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
} else if #[cfg(target_arch = "aarch64")] {
} else if #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] {
core::arch::asm!("brk 0xF003", in("x0") c::FAST_FAIL_FATAL_APP_EXIT, options(noreturn, nostack));
} else {
core::intrinsics::abort();
Expand Down
55 changes: 38 additions & 17 deletions src/doc/rustc/src/platform-support/arm64ec-pc-windows-msvc.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,69 @@ applications on AArch64 Windows 11. See <https://learn.microsoft.com/en-us/windo

## Requirements

Target only supports cross-compilation, `core` and `alloc` are supported but
`std` is not.

Builds Arm64EC static and dynamic libraries and executables which can be run on
AArch64 Windows 11 devices. Arm64EC static libraries can also be linked into
Arm64X dynamic libraries and executables.

Uses `arm64ec` as its `target_arch` - code built for Arm64EC must be compatible
with x86_64 code (e.g., same structure layouts, function signatures, etc.) but
use AArch64 intrinsics.

Only supported backend is LLVM 18 (or above).
Only supported backend is LLVM 18 or above:
* 18.1.0 added initial support for Arm64EC.
* 18.1.2 fixed import library generation (required for `raw-dylib` support).
* 18.1.4 fixed linking issue for some intrinsics implemented in
`compiler_builtins`.

### Reusing code from other architectures - x86_64 or AArch64?

Arm64EC uses `arm64ec` as its `target_arch`, but it is possible to reuse
existing architecture-specific code in most cases. The best mental model for
deciding which architecture to reuse is to is to think of Arm64EC as an x86_64
process that happens to use the AArch64 instruction set (with some caveats) and
has a completely custom ABI.

To put this in practice:
* Arm64EC interacts with the operating system, other processes and other DLLs as
x86_64.
- For example, [in `backtrace`](https://github.com/rust-lang/backtrace-rs/commit/ef39a7d7da58b4cae8c8f3fc67a8300fd8d2d0d9)
we use the x86_64 versions of `CONTEXT` and `RtlVirtualUnwind`.
- If you are configuring a search path to find DLLs (e.g., to load plugins or
addons into your application), you should use the same path as the x86_64
version of your application, not the AArch64 path (since Arm64EC (i.e.,
x86_64) processes cannot load native AArch64 DLLs).
* Arm64EC uses AArch64 intrinsics.
- For example, <https://github.com/rust-lang/portable-simd/commit/ca4033f49b1f6019561b8b161b4097b4a07f2e1b>
and <https://github.com/rust-lang/stdarch/commit/166ef7ba22b6a1d908d4b29a36e68ceca324808a>.
* Assembly for AArch64 might be reusable for Arm64EC, but there are many
caveats. For full details see [Microsoft's documentation on the Arm64EC ABI](https://learn.microsoft.com/en-us/windows/arm/arm64ec-abi)
but in brief:
- Arm64EC uses a subset of AArch64 registers.
- Arm64EC uses a different name mangling scheme than AArch64.
- Arm64EC requires entry and exit thunks be generated for some functions.
- Indirect calls must be done via a call checker.
- Control Flow Guard and stack checks use different functions than AArch64.

## Building the target

You can build Rust with support for the targets by adding it to the `target`
list in `config.toml` and disabling `std`:
list in `config.toml`:

```toml
[build]
target = [ "arm64ec-pc-windows-msvc" ]

[target.arm64ec-pc-windows-msvc]
no-std = true
```

## Building Rust programs

Rust does not yet ship pre-compiled artifacts for this target. To compile for
this target, you will either need to build Rust with the target enabled (see
"Building the target" above), or build your own copy of `core` by using
`build-std` or similar.
"Building the target" above), or build your own copy using `build-std` or
similar.

## Testing

Tests can be run on AArch64 Windows 11 devices.

Since this is a `no_std` target, the Rust test suite is not supported.

## Cross-compilation toolchains and C code

C code can be built using the Arm64-targetting MSVC toolchain.
C code can be built using the Arm64-targetting MSVC or Clang toolchain.

To compile:

Expand Down
2 changes: 1 addition & 1 deletion src/tools/generate-windows-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021"

[dependencies.windows-bindgen]
version = "0.55.0"
version = "0.56.0"

0 comments on commit c5de414

Please sign in to comment.