Rollup merge of #141569 - workingjubilee:canonicalize-abi, r=bjorn3

Replace ad-hoc ABI "adjustments" with an `AbiMap` to `CanonAbi`

Our `conv_from_spec_abi`, `adjust_abi`, and `is_abi_supported` combine to give us a very confusing way of reasoning about what _actual_ calling convention we want to lower our code to and whether we want to compile the resulting code at all. Instead of leaving this code as a miniature adventure game in which someone tries to combine stateful mutations into a Rube Goldberg machine that will let them escape the maze and arrive at the promised land of codegen, we let `AbiMap` devour this complexity. Once you have an `AbiMap`, you can answer which `ExternAbi`s will lower to what `CanonAbi`s (and whether they will lower at all).

Removed:
- `conv_from_spec_abi` replaced by `AbiMap::canonize_abi`
- `adjust_abi` replaced by same
- `Conv::PreserveAll` as unused
- `Conv::Cold` as unused
- `enum Conv` replaced by `enum CanonAbi`

target-spec.json changes:
- If you have a target-spec.json then now your "entry-abi" key will be specified in terms of one of the `"{abi}"` strings Rust recognizes, e.g.
```json
    "entry-abi": "C",
    "entry-abi": "win64",
    "entry-abi": "aapcs",
```
This commit is contained in:
Matthias Krüger
2025-06-03 21:53:36 +02:00
committed by GitHub
48 changed files with 877 additions and 786 deletions

View File

@@ -1,6 +1,6 @@
use rustc_abi::{HasDataLayout, TyAbiInterface};
use rustc_abi::{ArmCall, CanonAbi, HasDataLayout, TyAbiInterface};
use crate::callconv::{ArgAbi, Conv, FnAbi, Reg, RegKind, Uniform};
use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
use crate::spec::HasTargetSpec;
fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
@@ -90,7 +90,7 @@ where
// If this is a target with a hard-float ABI, and the function is not explicitly
// `extern "aapcs"`, then we must use the VFP registers for homogeneous aggregates.
let vfp = cx.target_spec().llvm_target.ends_with("hf")
&& fn_abi.conv != Conv::ArmAapcs
&& fn_abi.conv != CanonAbi::Arm(ArmCall::Aapcs)
&& !fn_abi.c_variadic;
if !fn_abi.ret.is_ignore() {

View File

@@ -1,13 +1,12 @@
use std::fmt::Display;
use std::str::FromStr;
use std::{fmt, iter};
use rustc_abi::{
AddressSpace, Align, BackendRepr, ExternAbi, HasDataLayout, Primitive, Reg, RegKind, Scalar,
Size, TyAbiInterface, TyAndLayout,
AddressSpace, Align, BackendRepr, CanonAbi, ExternAbi, HasDataLayout, Primitive, Reg, RegKind,
Scalar, Size, TyAbiInterface, TyAndLayout,
};
use rustc_macros::HashStable_Generic;
pub use crate::spec::AbiMap;
use crate::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, RustcAbi, WasmCAbi};
mod aarch64;
@@ -529,41 +528,6 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
pub enum Conv {
// General language calling conventions, for which every target
// should have its own backend (e.g. LLVM) support.
C,
Rust,
Cold,
PreserveMost,
PreserveAll,
// Target-specific calling conventions.
ArmAapcs,
CCmseNonSecureCall,
CCmseNonSecureEntry,
Msp430Intr,
GpuKernel,
X86Fastcall,
X86Intr,
X86Stdcall,
X86ThisCall,
X86VectorCall,
X86_64SysV,
X86_64Win64,
AvrInterrupt,
AvrNonBlockingInterrupt,
RiscvInterrupt { kind: RiscvInterruptKind },
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
pub enum RiscvInterruptKind {
Machine,
@@ -605,7 +569,7 @@ pub struct FnAbi<'a, Ty> {
/// This can be used to know whether an argument is variadic or not.
pub fixed_count: u32,
/// The calling convention of this function.
pub conv: Conv,
pub conv: CanonAbi,
/// Indicates if an unwind may happen across a call to this function.
pub can_unwind: bool,
}
@@ -696,7 +660,6 @@ impl<'a, Ty> FnAbi<'a, Ty> {
"sparc" => sparc::compute_abi_info(cx, self),
"sparc64" => sparc64::compute_abi_info(cx, self),
"nvptx64" => {
let abi = cx.target_spec().adjust_abi(abi, self.c_variadic);
if abi == ExternAbi::PtxKernel || abi == ExternAbi::GpuKernel {
nvptx64::compute_ptx_kernel_abi_info(cx, self)
} else {
@@ -863,70 +826,6 @@ impl<'a, Ty> FnAbi<'a, Ty> {
}
}
impl FromStr for Conv {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"C" => Ok(Conv::C),
"Rust" => Ok(Conv::Rust),
"RustCold" => Ok(Conv::Rust),
"ArmAapcs" => Ok(Conv::ArmAapcs),
"CCmseNonSecureCall" => Ok(Conv::CCmseNonSecureCall),
"CCmseNonSecureEntry" => Ok(Conv::CCmseNonSecureEntry),
"Msp430Intr" => Ok(Conv::Msp430Intr),
"X86Fastcall" => Ok(Conv::X86Fastcall),
"X86Intr" => Ok(Conv::X86Intr),
"X86Stdcall" => Ok(Conv::X86Stdcall),
"X86ThisCall" => Ok(Conv::X86ThisCall),
"X86VectorCall" => Ok(Conv::X86VectorCall),
"X86_64SysV" => Ok(Conv::X86_64SysV),
"X86_64Win64" => Ok(Conv::X86_64Win64),
"GpuKernel" => Ok(Conv::GpuKernel),
"AvrInterrupt" => Ok(Conv::AvrInterrupt),
"AvrNonBlockingInterrupt" => Ok(Conv::AvrNonBlockingInterrupt),
"RiscvInterrupt(machine)" => {
Ok(Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine })
}
"RiscvInterrupt(supervisor)" => {
Ok(Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor })
}
_ => Err(format!("'{s}' is not a valid value for entry function call convention.")),
}
}
}
fn conv_to_externabi(conv: &Conv) -> ExternAbi {
match conv {
Conv::C => ExternAbi::C { unwind: false },
Conv::Rust => ExternAbi::Rust,
Conv::PreserveMost => ExternAbi::RustCold,
Conv::ArmAapcs => ExternAbi::Aapcs { unwind: false },
Conv::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
Conv::CCmseNonSecureEntry => ExternAbi::CCmseNonSecureEntry,
Conv::Msp430Intr => ExternAbi::Msp430Interrupt,
Conv::GpuKernel => ExternAbi::GpuKernel,
Conv::X86Fastcall => ExternAbi::Fastcall { unwind: false },
Conv::X86Intr => ExternAbi::X86Interrupt,
Conv::X86Stdcall => ExternAbi::Stdcall { unwind: false },
Conv::X86ThisCall => ExternAbi::Thiscall { unwind: false },
Conv::X86VectorCall => ExternAbi::Vectorcall { unwind: false },
Conv::X86_64SysV => ExternAbi::SysV64 { unwind: false },
Conv::X86_64Win64 => ExternAbi::Win64 { unwind: false },
Conv::AvrInterrupt => ExternAbi::AvrInterrupt,
Conv::AvrNonBlockingInterrupt => ExternAbi::AvrNonBlockingInterrupt,
Conv::RiscvInterrupt { kind: RiscvInterruptKind::Machine } => ExternAbi::RiscvInterruptM,
Conv::RiscvInterrupt { kind: RiscvInterruptKind::Supervisor } => ExternAbi::RiscvInterruptS,
Conv::Cold | Conv::PreserveAll => unreachable!(),
}
}
impl Display for Conv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", conv_to_externabi(self))
}
}
// Some types are used a lot. Make sure they don't unintentionally get bigger.
#[cfg(target_pointer_width = "64")]
mod size_asserts {

View File

@@ -92,38 +92,6 @@ impl<A: ToJson> ToJson for Option<A> {
}
}
impl ToJson for crate::callconv::Conv {
fn to_json(&self) -> Json {
let buf: String;
let s = match self {
Self::C => "C",
Self::Rust => "Rust",
Self::Cold => "Cold",
Self::PreserveMost => "PreserveMost",
Self::PreserveAll => "PreserveAll",
Self::ArmAapcs => "ArmAapcs",
Self::CCmseNonSecureCall => "CCmseNonSecureCall",
Self::CCmseNonSecureEntry => "CCmseNonSecureEntry",
Self::Msp430Intr => "Msp430Intr",
Self::X86Fastcall => "X86Fastcall",
Self::X86Intr => "X86Intr",
Self::X86Stdcall => "X86Stdcall",
Self::X86ThisCall => "X86ThisCall",
Self::X86VectorCall => "X86VectorCall",
Self::X86_64SysV => "X86_64SysV",
Self::X86_64Win64 => "X86_64Win64",
Self::GpuKernel => "GpuKernel",
Self::AvrInterrupt => "AvrInterrupt",
Self::AvrNonBlockingInterrupt => "AvrNonBlockingInterrupt",
Self::RiscvInterrupt { kind } => {
buf = format!("RiscvInterrupt({})", kind.as_str());
&buf
}
};
Json::String(s.to_owned())
}
}
impl ToJson for TargetMetadata {
fn to_json(&self) -> Json {
json!({
@@ -140,3 +108,9 @@ impl ToJson for rustc_abi::Endian {
self.as_str().to_json()
}
}
impl ToJson for rustc_abi::CanonAbi {
fn to_json(&self) -> Json {
self.to_string().to_json()
}
}

View File

@@ -0,0 +1,187 @@
use rustc_abi::{ArmCall, CanonAbi, ExternAbi, InterruptKind, X86Call};
use crate::spec::Target;
/// Mapping for ExternAbi to CanonAbi according to a Target
///
/// A maybe-transitional structure circa 2025 for hosting future experiments in
/// encapsulating arch-specific ABI lowering details to make them more testable.
#[derive(Clone, Debug)]
pub struct AbiMap {
arch: Arch,
os: OsKind,
}
#[derive(Copy, Clone, Debug)]
pub enum AbiMapping {
/// this ABI is exactly mapped for this platform
Direct(CanonAbi),
/// we don't yet warn on this, but we will
Deprecated(CanonAbi),
Invalid,
}
impl AbiMapping {
pub fn into_option(self) -> Option<CanonAbi> {
match self {
Self::Direct(abi) | Self::Deprecated(abi) => Some(abi),
Self::Invalid => None,
}
}
pub fn unwrap(self) -> CanonAbi {
self.into_option().unwrap()
}
pub fn is_mapped(self) -> bool {
self.into_option().is_some()
}
}
impl AbiMap {
pub fn from_target(target: &Target) -> Self {
// the purpose of this little exercise is to force listing what affects these mappings
let arch = match &*target.arch {
"aarch64" => Arch::Aarch64,
"amdgpu" => Arch::Amdgpu,
"arm" if target.llvm_target.starts_with("thumbv8m") => Arch::Arm(ArmVer::ThumbV8M),
"arm" => Arch::Arm(ArmVer::Other),
"avr" => Arch::Avr,
"msp430" => Arch::Msp430,
"nvptx64" => Arch::Nvptx,
"riscv32" | "riscv64" => Arch::Riscv,
"x86" => Arch::X86,
"x86_64" => Arch::X86_64,
_ => Arch::Other,
};
let os = if target.is_like_windows { OsKind::Windows } else { OsKind::Other };
AbiMap { arch, os }
}
pub fn canonize_abi(&self, extern_abi: ExternAbi, has_c_varargs: bool) -> AbiMapping {
let AbiMap { os, arch } = *self;
let canon_abi = match (extern_abi, arch) {
// infallible lowerings
(ExternAbi::C { .. }, _) => CanonAbi::C,
(ExternAbi::Rust | ExternAbi::RustCall, _) => CanonAbi::Rust,
(ExternAbi::Unadjusted, _) => CanonAbi::C,
(ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust,
(ExternAbi::RustCold, _) => CanonAbi::RustCold,
(ExternAbi::System { .. }, Arch::X86) if os == OsKind::Windows && !has_c_varargs => {
CanonAbi::X86(X86Call::Stdcall)
}
(ExternAbi::System { .. }, _) => CanonAbi::C,
// fallible lowerings
(ExternAbi::EfiApi, Arch::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs),
(ExternAbi::EfiApi, Arch::X86_64) => CanonAbi::X86(X86Call::Win64),
(ExternAbi::EfiApi, Arch::Aarch64 | Arch::Riscv | Arch::X86) => CanonAbi::C,
(ExternAbi::EfiApi, _) => return AbiMapping::Invalid,
(ExternAbi::Aapcs { .. }, Arch::Arm(..)) => CanonAbi::Arm(ArmCall::Aapcs),
(ExternAbi::Aapcs { .. }, _) => return AbiMapping::Invalid,
(ExternAbi::CCmseNonSecureCall, Arch::Arm(ArmVer::ThumbV8M)) => {
CanonAbi::Arm(ArmCall::CCmseNonSecureCall)
}
(ExternAbi::CCmseNonSecureEntry, Arch::Arm(ArmVer::ThumbV8M)) => {
CanonAbi::Arm(ArmCall::CCmseNonSecureEntry)
}
(ExternAbi::CCmseNonSecureCall | ExternAbi::CCmseNonSecureEntry, ..) => {
return AbiMapping::Invalid;
}
(ExternAbi::Cdecl { .. }, Arch::X86) => CanonAbi::C,
(ExternAbi::Cdecl { .. }, _) => return AbiMapping::Deprecated(CanonAbi::C),
(ExternAbi::Fastcall { .. }, Arch::X86) => CanonAbi::X86(X86Call::Fastcall),
(ExternAbi::Fastcall { .. }, _) if os == OsKind::Windows => {
return AbiMapping::Deprecated(CanonAbi::C);
}
(ExternAbi::Fastcall { .. }, _) => return AbiMapping::Invalid,
(ExternAbi::Stdcall { .. }, Arch::X86) => CanonAbi::X86(X86Call::Stdcall),
(ExternAbi::Stdcall { .. }, _) if os == OsKind::Windows => {
return AbiMapping::Deprecated(CanonAbi::C);
}
(ExternAbi::Stdcall { .. }, _) => return AbiMapping::Invalid,
(ExternAbi::Thiscall { .. }, Arch::X86) => CanonAbi::X86(X86Call::Thiscall),
(ExternAbi::Thiscall { .. }, _) => return AbiMapping::Invalid,
(ExternAbi::Vectorcall { .. }, Arch::X86 | Arch::X86_64) => {
CanonAbi::X86(X86Call::Vectorcall)
}
(ExternAbi::Vectorcall { .. }, _) if os == OsKind::Windows => {
return AbiMapping::Deprecated(CanonAbi::C);
}
(ExternAbi::Vectorcall { .. }, _) => return AbiMapping::Invalid,
(ExternAbi::SysV64 { .. }, Arch::X86_64) => CanonAbi::X86(X86Call::SysV64),
(ExternAbi::Win64 { .. }, Arch::X86_64) => CanonAbi::X86(X86Call::Win64),
(ExternAbi::SysV64 { .. } | ExternAbi::Win64 { .. }, _) => return AbiMapping::Invalid,
(ExternAbi::PtxKernel, Arch::Nvptx) => CanonAbi::GpuKernel,
(ExternAbi::GpuKernel, Arch::Amdgpu | Arch::Nvptx) => CanonAbi::GpuKernel,
(ExternAbi::PtxKernel | ExternAbi::GpuKernel, _) => return AbiMapping::Invalid,
(ExternAbi::AvrInterrupt, Arch::Avr) => CanonAbi::Interrupt(InterruptKind::Avr),
(ExternAbi::AvrNonBlockingInterrupt, Arch::Avr) => {
CanonAbi::Interrupt(InterruptKind::AvrNonBlocking)
}
(ExternAbi::Msp430Interrupt, Arch::Msp430) => {
CanonAbi::Interrupt(InterruptKind::Msp430)
}
(ExternAbi::RiscvInterruptM, Arch::Riscv) => {
CanonAbi::Interrupt(InterruptKind::RiscvMachine)
}
(ExternAbi::RiscvInterruptS, Arch::Riscv) => {
CanonAbi::Interrupt(InterruptKind::RiscvSupervisor)
}
(ExternAbi::X86Interrupt, Arch::X86 | Arch::X86_64) => {
CanonAbi::Interrupt(InterruptKind::X86)
}
(
ExternAbi::AvrInterrupt
| ExternAbi::AvrNonBlockingInterrupt
| ExternAbi::Msp430Interrupt
| ExternAbi::RiscvInterruptM
| ExternAbi::RiscvInterruptS
| ExternAbi::X86Interrupt,
_,
) => return AbiMapping::Invalid,
};
AbiMapping::Direct(canon_abi)
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
enum Arch {
Aarch64,
Amdgpu,
Arm(ArmVer),
Avr,
Msp430,
Nvptx,
Riscv,
X86,
X86_64,
/// Architectures which don't need other considerations for ABI lowering
Other,
}
#[derive(Debug, PartialEq, Copy, Clone)]
enum OsKind {
Windows,
Other,
}
#[derive(Debug, PartialEq, Copy, Clone)]
enum ArmVer {
ThumbV8M,
Other,
}

View File

@@ -2,10 +2,12 @@ use std::borrow::Cow;
use std::collections::BTreeMap;
use std::str::FromStr;
use rustc_abi::ExternAbi;
use serde_json::Value;
use super::{Target, TargetKind, TargetOptions, TargetWarnings};
use crate::json::{Json, ToJson};
use crate::spec::AbiMap;
impl Target {
/// Loads a target descriptor from a JSON object.
@@ -515,18 +517,6 @@ impl Target {
}
}
} );
($key_name:ident, Conv) => ( {
let name = (stringify!($key_name)).replace("_", "-");
obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
match super::Conv::from_str(s) {
Ok(c) => {
base.$key_name = c;
Some(Ok(()))
}
Err(e) => Some(Err(e))
}
})).unwrap_or(Ok(()))
} );
}
if let Some(j) = obj.remove("target-endian") {
@@ -660,9 +650,23 @@ impl Target {
key!(supports_stack_protector, bool);
key!(small_data_threshold_support, SmallDataThresholdSupport)?;
key!(entry_name);
key!(entry_abi, Conv)?;
key!(supports_xray, bool);
// we're going to run `update_from_cli`, but that won't change the target's AbiMap
// FIXME: better factor the Target definition so we enforce this on a type level
let abi_map = AbiMap::from_target(&base);
if let Some(abi_str) = obj.remove("entry-abi") {
if let Json::String(abi_str) = abi_str {
match abi_str.parse::<ExternAbi>() {
Ok(abi) => base.options.entry_abi = abi_map.canonize_abi(abi, false).unwrap(),
Err(_) => return Err(format!("{abi_str} is not a valid ExternAbi")),
}
} else {
incorrect_type.push("entry-abi".to_owned())
}
}
base.update_from_cli();
base.check_consistency(TargetKind::Json)?;

View File

@@ -43,7 +43,7 @@ use std::str::FromStr;
use std::{fmt, io};
use rustc_abi::{
Align, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors,
Align, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors,
};
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_fs_util::try_canonicalize;
@@ -53,15 +53,16 @@ use rustc_span::{Symbol, kw, sym};
use serde_json::Value;
use tracing::debug;
use crate::callconv::Conv;
use crate::json::{Json, ToJson};
use crate::spec::crt_objects::CrtObjects;
pub mod crt_objects;
mod abi_map;
mod base;
mod json;
pub use abi_map::AbiMap;
pub use base::apple;
pub use base::avr::ef_avr_arch;
@@ -2655,9 +2656,9 @@ pub struct TargetOptions {
/// Default value is "main"
pub entry_name: StaticCow<str>,
/// The ABI of entry function.
/// Default value is `Conv::C`, i.e. C call convention
pub entry_abi: Conv,
/// The ABI of the entry function.
/// Default value is `CanonAbi::C`
pub entry_abi: CanonAbi,
/// Whether the target supports XRay instrumentation.
pub supports_xray: bool,
@@ -2888,7 +2889,7 @@ impl Default for TargetOptions {
generate_arange_section: true,
supports_stack_protector: true,
entry_name: "main".into(),
entry_abi: Conv::C,
entry_abi: CanonAbi::C,
supports_xray: false,
small_data_threshold_support: SmallDataThresholdSupport::DefaultForArch,
}
@@ -2914,114 +2915,9 @@ impl DerefMut for Target {
}
impl Target {
/// Given a function ABI, turn it into the correct ABI for this target.
pub fn adjust_abi(&self, abi: ExternAbi, c_variadic: bool) -> ExternAbi {
use ExternAbi::*;
match abi {
// On Windows, `extern "system"` behaves like msvc's `__stdcall`.
// `__stdcall` only applies on x86 and on non-variadic functions:
// https://learn.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-170
System { unwind } => {
if self.is_like_windows && self.arch == "x86" && !c_variadic {
Stdcall { unwind }
} else {
C { unwind }
}
}
EfiApi => {
if self.arch == "arm" {
Aapcs { unwind: false }
} else if self.arch == "x86_64" {
Win64 { unwind: false }
} else {
C { unwind: false }
}
}
// See commentary in `is_abi_supported`.
Stdcall { unwind } | Thiscall { unwind } | Fastcall { unwind } => {
if self.arch == "x86" { abi } else { C { unwind } }
}
Vectorcall { unwind } => {
if ["x86", "x86_64"].contains(&&*self.arch) {
abi
} else {
C { unwind }
}
}
// The Windows x64 calling convention we use for `extern "Rust"`
// <https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions#register-volatility-and-preservation>
// expects the callee to save `xmm6` through `xmm15`, but `PreserveMost`
// (that we use by default for `extern "rust-cold"`) doesn't save any of those.
// So to avoid bloating callers, just use the Rust convention here.
RustCold if self.is_like_windows && self.arch == "x86_64" => Rust,
abi => abi,
}
}
pub fn is_abi_supported(&self, abi: ExternAbi) -> bool {
use ExternAbi::*;
match abi {
Rust | C { .. } | System { .. } | RustCall | Unadjusted | Cdecl { .. } | RustCold => {
true
}
EfiApi => {
["arm", "aarch64", "riscv32", "riscv64", "x86", "x86_64"].contains(&&self.arch[..])
}
X86Interrupt => ["x86", "x86_64"].contains(&&self.arch[..]),
Aapcs { .. } => "arm" == self.arch,
CCmseNonSecureCall | CCmseNonSecureEntry => {
["thumbv8m.main-none-eabi", "thumbv8m.main-none-eabihf", "thumbv8m.base-none-eabi"]
.contains(&&self.llvm_target[..])
}
Win64 { .. } | SysV64 { .. } => self.arch == "x86_64",
PtxKernel => self.arch == "nvptx64",
GpuKernel => ["amdgpu", "nvptx64"].contains(&&self.arch[..]),
Msp430Interrupt => self.arch == "msp430",
RiscvInterruptM | RiscvInterruptS => ["riscv32", "riscv64"].contains(&&self.arch[..]),
AvrInterrupt | AvrNonBlockingInterrupt => self.arch == "avr",
Thiscall { .. } => self.arch == "x86",
// On windows these fall-back to platform native calling convention (C) when the
// architecture is not supported.
//
// This is I believe a historical accident that has occurred as part of Microsoft
// striving to allow most of the code to "just" compile when support for 64-bit x86
// was added and then later again, when support for ARM architectures was added.
//
// This is well documented across MSDN. Support for this in Rust has been added in
// #54576. This makes much more sense in context of Microsoft's C++ than it does in
// Rust, but there isn't much leeway remaining here to change it back at the time this
// comment has been written.
//
// Following are the relevant excerpts from the MSDN documentation.
//
// > The __vectorcall calling convention is only supported in native code on x86 and
// x64 processors that include Streaming SIMD Extensions 2 (SSE2) and above.
// > ...
// > On ARM machines, __vectorcall is accepted and ignored by the compiler.
//
// -- https://docs.microsoft.com/en-us/cpp/cpp/vectorcall?view=msvc-160
//
// > On ARM and x64 processors, __stdcall is accepted and ignored by the compiler;
//
// -- https://docs.microsoft.com/en-us/cpp/cpp/stdcall?view=msvc-160
//
// > In most cases, keywords or compiler switches that specify an unsupported
// > convention on a particular platform are ignored, and the platform default
// > convention is used.
//
// -- https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions
Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } if self.is_like_windows => true,
// Outside of Windows we want to only support these calling conventions for the
// architectures for which these calling conventions are actually well defined.
Stdcall { .. } | Fastcall { .. } if self.arch == "x86" => true,
Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => true,
// Reject these calling conventions everywhere else.
Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => false,
}
let abi_map = AbiMap::from_target(self);
abi_map.canonize_abi(abi, false).is_mapped()
}
/// Minimum integer size in bits that this target can perform atomic

View File

@@ -5,7 +5,8 @@
// The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with
// LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features.
use crate::callconv::Conv;
use rustc_abi::{CanonAbi, X86Call};
use crate::spec::{RustcAbi, Target, TargetMetadata, base};
pub(crate) fn target() -> Target {
@@ -13,7 +14,7 @@ pub(crate) fn target() -> Target {
base.cpu = "x86-64".into();
base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.entry_abi = Conv::X86_64Win64;
base.entry_abi = CanonAbi::X86(X86Call::Win64);
// We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
// enable these CPU features explicitly before their first use, otherwise their instructions