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:
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
187
compiler/rustc_target/src/spec/abi_map.rs
Normal file
187
compiler/rustc_target/src/spec/abi_map.rs
Normal 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,
|
||||
}
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user