rustc_target: allow unenumerated architectures

This commit is contained in:
Tamir Duberstein 2025-10-28 19:48:56 -04:00
parent 0c8533d690
commit 26b0560b6d
No known key found for this signature in database
19 changed files with 169 additions and 67 deletions

View file

@ -788,7 +788,7 @@ pub(crate) fn codegen_drop<'tcx>(
pub(crate) fn lib_call_arg_param(tcx: TyCtxt<'_>, ty: Type, is_signed: bool) -> AbiParam {
let param = AbiParam::new(ty);
if ty.is_int() && u64::from(ty.bits()) < tcx.data_layout.pointer_size().bits() {
match (tcx.sess.target.arch, tcx.sess.target.vendor.as_ref()) {
match (&tcx.sess.target.arch, tcx.sess.target.vendor.as_ref()) {
(Arch::X86_64, _) | (Arch::AArch64, "apple") => match (ty, is_signed) {
(types::I8 | types::I16, true) => param.sext(),
(types::I8 | types::I16, false) => param.uext(),

View file

@ -235,12 +235,12 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
#[cfg(feature = "master")]
fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option<FnAttribute<'gcc>> {
conv_to_fn_attribute(self.conv, cx.tcx.sess.target.arch)
conv_to_fn_attribute(self.conv, &cx.tcx.sess.target.arch)
}
}
#[cfg(feature = "master")]
pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: Arch) -> Option<FnAttribute<'gcc>> {
pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &Arch) -> Option<FnAttribute<'gcc>> {
let attribute = match conv {
CanonAbi::C | CanonAbi::Rust => return None,
CanonAbi::RustCold => FnAttribute::Cold,
@ -254,8 +254,8 @@ pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: Arch) -> Option<FnAttrib
ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"),
},
CanonAbi::GpuKernel => match arch {
Arch::AmdGpu => FnAttribute::GcnAmdGpuHsaKernel,
Arch::Nvptx64 => FnAttribute::NvptxKernel,
&Arch::AmdGpu => FnAttribute::GcnAmdGpuHsaKernel,
&Arch::Nvptx64 => FnAttribute::NvptxKernel,
arch => panic!("Arch {arch} does not support GpuKernel calling convention"),
},
// TODO(antoyo): check if those AVR attributes are mapped correctly.

View file

@ -487,7 +487,7 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
let entry_name = self.sess().target.entry_name.as_ref();
if !self.functions.borrow().contains_key(entry_name) {
#[cfg(feature = "master")]
let conv = conv_to_fn_attribute(self.sess().target.entry_abi, self.sess().target.arch);
let conv = conv_to_fn_attribute(self.sess().target.entry_abi, &self.sess().target.arch);
#[cfg(not(feature = "master"))]
let conv = None;
Some(self.declare_entry_fn(entry_name, fn_type, conv))

View file

@ -67,46 +67,46 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
// cSpell:disable
match (sess.target.arch, s) {
match (&sess.target.arch, s) {
// FIXME: seems like x87 does not exist?
(Arch::X86 | Arch::X86_64, "x87") => smallvec![],
(Arch::X86 | Arch::X86_64, "sse4.2") => smallvec!["sse4.2", "crc32"],
(Arch::X86 | Arch::X86_64, "pclmulqdq") => smallvec!["pclmul"],
(Arch::X86 | Arch::X86_64, "rdrand") => smallvec!["rdrnd"],
(Arch::X86 | Arch::X86_64, "bmi1") => smallvec!["bmi"],
(Arch::X86 | Arch::X86_64, "cmpxchg16b") => smallvec!["cx16"],
(Arch::X86 | Arch::X86_64, "avx512vaes") => smallvec!["vaes"],
(Arch::X86 | Arch::X86_64, "avx512gfni") => smallvec!["gfni"],
(Arch::X86 | Arch::X86_64, "avx512vpclmulqdq") => smallvec!["vpclmulqdq"],
(&Arch::X86 | &Arch::X86_64, "x87") => smallvec![],
(&Arch::X86 | &Arch::X86_64, "sse4.2") => smallvec!["sse4.2", "crc32"],
(&Arch::X86 | &Arch::X86_64, "pclmulqdq") => smallvec!["pclmul"],
(&Arch::X86 | &Arch::X86_64, "rdrand") => smallvec!["rdrnd"],
(&Arch::X86 | &Arch::X86_64, "bmi1") => smallvec!["bmi"],
(&Arch::X86 | &Arch::X86_64, "cmpxchg16b") => smallvec!["cx16"],
(&Arch::X86 | &Arch::X86_64, "avx512vaes") => smallvec!["vaes"],
(&Arch::X86 | &Arch::X86_64, "avx512gfni") => smallvec!["gfni"],
(&Arch::X86 | &Arch::X86_64, "avx512vpclmulqdq") => smallvec!["vpclmulqdq"],
// NOTE: seems like GCC requires 'avx512bw' for 'avx512vbmi2'.
(Arch::X86 | Arch::X86_64, "avx512vbmi2") => {
(&Arch::X86 | &Arch::X86_64, "avx512vbmi2") => {
smallvec!["avx512vbmi2", "avx512bw"]
}
// NOTE: seems like GCC requires 'avx512bw' for 'avx512bitalg'.
(Arch::X86 | Arch::X86_64, "avx512bitalg") => {
(&Arch::X86 | &Arch::X86_64, "avx512bitalg") => {
smallvec!["avx512bitalg", "avx512bw"]
}
(Arch::AArch64, "rcpc2") => smallvec!["rcpc-immo"],
(Arch::AArch64, "dpb") => smallvec!["ccpp"],
(Arch::AArch64, "dpb2") => smallvec!["ccdp"],
(Arch::AArch64, "frintts") => smallvec!["fptoint"],
(Arch::AArch64, "fcma") => smallvec!["complxnum"],
(Arch::AArch64, "pmuv3") => smallvec!["perfmon"],
(Arch::AArch64, "paca") => smallvec!["pauth"],
(Arch::AArch64, "pacg") => smallvec!["pauth"],
(&Arch::AArch64, "rcpc2") => smallvec!["rcpc-immo"],
(&Arch::AArch64, "dpb") => smallvec!["ccpp"],
(&Arch::AArch64, "dpb2") => smallvec!["ccdp"],
(&Arch::AArch64, "frintts") => smallvec!["fptoint"],
(&Arch::AArch64, "fcma") => smallvec!["complxnum"],
(&Arch::AArch64, "pmuv3") => smallvec!["perfmon"],
(&Arch::AArch64, "paca") => smallvec!["pauth"],
(&Arch::AArch64, "pacg") => smallvec!["pauth"],
// Rust ties fp and neon together. In GCC neon implicitly enables fp,
// but we manually enable neon when a feature only implicitly enables fp
(Arch::AArch64, "f32mm") => smallvec!["f32mm", "neon"],
(Arch::AArch64, "f64mm") => smallvec!["f64mm", "neon"],
(Arch::AArch64, "fhm") => smallvec!["fp16fml", "neon"],
(Arch::AArch64, "fp16") => smallvec!["fullfp16", "neon"],
(Arch::AArch64, "jsconv") => smallvec!["jsconv", "neon"],
(Arch::AArch64, "sve") => smallvec!["sve", "neon"],
(Arch::AArch64, "sve2") => smallvec!["sve2", "neon"],
(Arch::AArch64, "sve2-aes") => smallvec!["sve2-aes", "neon"],
(Arch::AArch64, "sve2-sm4") => smallvec!["sve2-sm4", "neon"],
(Arch::AArch64, "sve2-sha3") => smallvec!["sve2-sha3", "neon"],
(Arch::AArch64, "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
(&Arch::AArch64, "f32mm") => smallvec!["f32mm", "neon"],
(&Arch::AArch64, "f64mm") => smallvec!["f64mm", "neon"],
(&Arch::AArch64, "fhm") => smallvec!["fp16fml", "neon"],
(&Arch::AArch64, "fp16") => smallvec!["fullfp16", "neon"],
(&Arch::AArch64, "jsconv") => smallvec!["jsconv", "neon"],
(&Arch::AArch64, "sve") => smallvec!["sve", "neon"],
(&Arch::AArch64, "sve2") => smallvec!["sve2", "neon"],
(&Arch::AArch64, "sve2-aes") => smallvec!["sve2-aes", "neon"],
(&Arch::AArch64, "sve2-sm4") => smallvec!["sve2-sm4", "neon"],
(&Arch::AArch64, "sve2-sha3") => smallvec!["sve2-sha3", "neon"],
(&Arch::AArch64, "sve2-bitperm") => smallvec!["sve2-bitperm", "neon"],
(_, s) => smallvec![s],
}
// cSpell:enable

View file

@ -698,7 +698,7 @@ pub(crate) fn to_llvm_calling_convention(sess: &Session, abi: CanonAbi) -> llvm:
// possible to declare an `extern "custom"` block, so the backend still needs a calling
// convention for declaring foreign functions.
CanonAbi::Custom => llvm::CCallConv,
CanonAbi::GpuKernel => match sess.target.arch {
CanonAbi::GpuKernel => match &sess.target.arch {
Arch::AmdGpu => llvm::AmdgpuKernel,
Arch::Nvptx64 => llvm::PtxKernel,
arch => panic!("Architecture {arch} does not support GpuKernel calling convention"),

View file

@ -342,7 +342,7 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig {
/// Determine whether or not experimental float types are reliable based on known bugs.
fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) {
let target_arch = sess.target.arch;
let target_arch = &sess.target.arch;
let target_os = sess.target.options.os.as_ref();
let target_env = sess.target.options.env.as_ref();
let target_abi = sess.target.options.abi.as_ref();

View file

@ -118,7 +118,7 @@ pub trait ArchiveBuilderBuilder {
let exports =
items.into_iter().map(|item| item.into_coff_short_export(sess)).collect::<Vec<_>>();
let machine = match sess.target.arch {
let machine = match &sess.target.arch {
Arch::X86_64 => MachineTypes::AMD64,
Arch::X86 => MachineTypes::I386,
Arch::AArch64 => MachineTypes::ARM64,
@ -224,7 +224,7 @@ fn create_mingw_dll_import_lib(
};
// dlltool target architecture args from:
// https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69
let (dlltool_target_arch, dlltool_target_bitness) = match sess.target.arch {
let (dlltool_target_arch, dlltool_target_bitness) = match &sess.target.arch {
Arch::X86_64 => ("i386:x86-64", "--64"),
Arch::X86 => ("i386", "--32"),
Arch::AArch64 => ("arm64", "--64"),

View file

@ -983,7 +983,7 @@ impl CrateInfo {
// by the compiler, but that's ok because all this stuff is unstable anyway.
let target = &tcx.sess.target;
if !are_upstream_rust_objects_already_included(tcx.sess) {
let add_prefix = match (target.is_like_windows, target.arch) {
let add_prefix = match (target.is_like_windows, &target.arch) {
(true, Arch::X86) => |name: String, _: SymbolExportKind| format!("_{name}"),
(true, Arch::Arm64EC) => {
// Only functions are decorated for arm64ec.

View file

@ -1112,7 +1112,7 @@ pub fn build_session(
_ => CtfeBacktrace::Disabled,
});
let asm_arch = if target.allow_asm { InlineAsmArch::from_arch(target.arch) } else { None };
let asm_arch = if target.allow_asm { InlineAsmArch::from_arch(&target.arch) } else { None };
let target_filesearch =
filesearch::FileSearch::new(&sopts.search_paths, &target_tlib_path, &target);
let host_filesearch = filesearch::FileSearch::new(&sopts.search_paths, &host_tlib_path, &host);

View file

@ -245,7 +245,7 @@ pub enum InlineAsmArch {
}
impl InlineAsmArch {
pub fn from_arch(arch: Arch) -> Option<Self> {
pub fn from_arch(arch: &Arch) -> Option<Self> {
match arch {
Arch::X86 => Some(Self::X86),
Arch::X86_64 => Some(Self::X86_64),
@ -273,7 +273,7 @@ impl InlineAsmArch {
Arch::Msp430 => Some(Self::Msp430),
Arch::M68k => Some(Self::M68k),
Arch::CSky => Some(Self::CSKY),
Arch::AmdGpu | Arch::Xtensa => None,
Arch::AmdGpu | Arch::Xtensa | Arch::Unknown(_) => None,
}
}
}

View file

@ -633,7 +633,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
}
let spec = cx.target_spec();
match spec.arch {
match &spec.arch {
Arch::X86 => {
let (flavor, regparm) = match abi {
ExternAbi::Fastcall { .. } | ExternAbi::Vectorcall { .. } => {
@ -701,7 +701,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
Arch::RiscV32 | Arch::RiscV64 => riscv::compute_abi_info(cx, self),
Arch::Wasm32 | Arch::Wasm64 => wasm::compute_abi_info(cx, self),
Arch::Bpf => bpf::compute_abi_info(cx, self),
arch @ (Arch::PowerPC64LE | Arch::SpirV) => {
arch @ (Arch::PowerPC64LE | Arch::SpirV | Arch::Unknown(_)) => {
panic!("no lowering implemented for {arch}")
}
}
@ -713,7 +713,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
C: HasDataLayout + HasTargetSpec,
{
let spec = cx.target_spec();
match spec.arch {
match &spec.arch {
Arch::X86 => x86::compute_rust_abi_info(cx, self),
Arch::RiscV32 | Arch::RiscV64 => riscv::compute_rust_abi_info(cx, self),
Arch::LoongArch32 | Arch::LoongArch64 => loongarch::compute_rust_abi_info(cx, self),

View file

@ -118,6 +118,67 @@ macro_rules! target_spec_enum {
}
}
crate::target_spec_enum!(@common_impls $Name);
};
(
$( #[$attr:meta] )*
pub enum $Name:ident {
$(
$( #[$variant_attr:meta] )*
$Variant:ident = $string:literal,
)*
}
$( #[$other_variant_attr:meta] )*
other_variant = $OtherVariant:ident;
) => {
$( #[$attr] )*
#[derive(Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub enum $Name {
$(
$( #[$variant_attr:meta] )*
$Variant,
)*
$( #[$other_variant_attr] )*
$OtherVariant(crate::spec::StaticCow<str>),
}
impl schemars::JsonSchema for $Name {
fn schema_name() -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Borrowed(stringify!($Name))
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "string"
})
}
}
impl FromStr for $Name {
type Err = core::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
$( $string => Self::$Variant, )*
_ => Self::$OtherVariant(s.to_owned().into()),
})
}
}
impl $Name {
pub fn desc(&self) -> &str {
match self {
$( Self::$Variant => $string, )*
Self::$OtherVariant(name) => name.as_ref(),
}
}
}
crate::target_spec_enum!(@common_impls $Name);
};
(@common_impls $Name:ident) => {
impl crate::json::ToJson for $Name {
fn to_json(&self) -> crate::json::Json {
self.desc().to_json()

View file

@ -311,7 +311,7 @@ impl OSVersion {
/// This matches what LLVM does, see in part:
/// <https://github.com/llvm/llvm-project/blob/llvmorg-21.1.3/llvm/lib/TargetParser/Triple.cpp#L2140-L2175>
pub fn minimum_deployment_target(target: &Target) -> Self {
let (major, minor, patch) = match (&*target.os, target.arch, &*target.env) {
let (major, minor, patch) = match (&*target.os, &target.arch, &*target.env) {
("macos", crate::spec::Arch::AArch64, _) => (11, 0, 0),
("ios", crate::spec::Arch::AArch64, "macabi") => (14, 0, 0),
("ios", crate::spec::Arch::AArch64, "sim") => (14, 0, 0),

View file

@ -1881,12 +1881,18 @@ crate::target_spec_enum! {
X86_64 = "x86_64",
Xtensa = "xtensa",
}
parse_error_type = "architecture";
/// The vast majority of the time, the compiler deals with a fixed set of
/// target architectures, so it is convenient for them to be represented in
/// an enum. However, it is possible to have arbitrary values for the "arch"
/// field in a target JSON file (which can be parsed when `--target` is
/// specified). This might occur, for example, for an out-of-tree codegen
/// backend that supports an architecture that rustc currently doesn't know
/// about. This variant exists as an escape hatch for such cases.
other_variant = Unknown;
}
impl Arch {
pub const fn desc_symbol(&self) -> Symbol {
pub fn desc_symbol(&self) -> Symbol {
match self {
Self::AArch64 => sym::aarch64,
Self::AmdGpu => sym::amdgpu,
@ -1919,6 +1925,7 @@ impl Arch {
Self::X86 => sym::x86,
Self::X86_64 => sym::x86_64,
Self::Xtensa => sym::xtensa,
Self::Unknown(name) => rustc_span::Symbol::intern(name),
}
}
}
@ -3274,7 +3281,8 @@ impl Target {
| Arch::PowerPC64LE
| Arch::SpirV
| Arch::Wasm32
| Arch::Wasm64 => return None,
| Arch::Wasm64
| Arch::Unknown(_) => return None,
})
}

View file

@ -955,7 +955,7 @@ pub struct FeatureConstraints {
impl Target {
pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] {
match self.arch {
match &self.arch {
Arch::Arm => ARM_FEATURES,
Arch::AArch64 | Arch::Arm64EC => AARCH64_FEATURES,
Arch::X86 | Arch::X86_64 => X86_FEATURES,
@ -976,12 +976,13 @@ impl Target {
| Arch::Msp430
| Arch::PowerPC64LE
| Arch::SpirV
| Arch::Xtensa => &[],
| Arch::Xtensa
| Arch::Unknown(_) => &[],
}
}
pub fn features_for_correct_vector_abi(&self) -> &'static [(u64, &'static str)] {
match self.arch {
match &self.arch {
Arch::X86 | Arch::X86_64 => X86_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::AArch64 | Arch::Arm64EC => AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::Arm => ARM_FEATURES_FOR_CORRECT_VECTOR_ABI,
@ -1004,12 +1005,13 @@ impl Target {
| Arch::Msp430
| Arch::PowerPC64LE
| Arch::SpirV
| Arch::Xtensa => &[],
| Arch::Xtensa
| Arch::Unknown(_) => &[],
}
}
pub fn tied_target_features(&self) -> &'static [&'static [&'static str]] {
match self.arch {
match &self.arch {
Arch::AArch64 | Arch::Arm64EC => AARCH64_TIED_FEATURES,
_ => &[],
}
@ -1050,7 +1052,7 @@ impl Target {
// defined by target features. When that is the case, those target features must be
// "forbidden" in the list above to ensure that there is a consistent answer to the
// questions "which ABI is used".
match self.arch {
match &self.arch {
Arch::X86 => {
// We use our own ABI indicator here; LLVM does not have anything native.
// Every case should require or forbid `soft-float`!

View file

@ -1,3 +1,4 @@
use crate::json::ToJson;
use crate::spec::Target;
#[test]
@ -15,3 +16,27 @@ fn report_unused_fields() {
eprintln!("{result:#?}");
assert!(result.is_err());
}
#[test]
fn custom_arch_propagates_from_json() {
let json = r#"
{
"llvm-target": "x86_64-unknown-none-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "customarch",
"target-endian": "little",
"target-pointer-width": 64,
"os": "customos",
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"executables": true
}
"#;
rustc_span::create_session_if_not_set_then(rustc_span::edition::DEFAULT_EDITION, |_| {
let (target, warnings) = Target::from_json(json).expect("json target parses");
assert!(warnings.warning_messages().is_empty());
assert_eq!(target.arch.desc(), "customarch");
let serialized = target.to_json();
assert_eq!(serialized["arch"], "customarch");
});
}

View file

@ -20,7 +20,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// `library/std/src/sys/alloc/mod.rs` (where this is called `MIN_ALIGN`) and should
// be kept in sync.
let os = this.tcx.sess.target.os.as_ref();
let max_fundamental_align = match this.tcx.sess.target.arch {
let max_fundamental_align = match &this.tcx.sess.target.arch {
Arch::RiscV32 if matches!(os, "espidf" | "zkvm") => 4,
Arch::Xtensa if matches!(os, "espidf") => 4,
Arch::X86
@ -53,7 +53,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
| Arch::Msp430
| Arch::Nvptx64
| Arch::PowerPC64LE
| Arch::SpirV) => bug!("unsupported target architecture for malloc: `{arch}`"),
| Arch::SpirV
| Arch::Unknown(_)) => bug!("unsupported target architecture for malloc: `{arch}`"),
};
// The C standard only requires sufficient alignment for any *type* with size less than or
// equal to the size requested. Types one can define in standard C seem to never have an alignment

View file

@ -1,7 +1,7 @@
{
"llvm-target": "x86_64-unknown-none-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"arch": "tamirdarch",
"target-endian": "little",
"target-pointer-width": 64,
"os": "ericos",

View file

@ -6,14 +6,19 @@
//@ needs-llvm-components: x86
//@ compile-flags: --crate-type=lib --check-cfg=cfg() --target={{src-base}}/check-cfg/my-awesome-platform.json
#![feature(lang_items, no_core, auto_traits)]
#![feature(lang_items, no_core, auto_traits, rustc_attrs)]
#![no_core]
extern crate minicore;
use minicore::*;
#[cfg(target_os = "linux")]
fn target_os_linux() {}
#[rustc_builtin_macro]
macro_rules! compile_error {
() => {};
}
#[cfg(target_os = "ericos")]
fn target_os_ericos() {}
#[cfg(not(target_os = "ericos"))]
compile_error!("target_os from target JSON not wired through");
#[cfg(not(target_arch = "tamirdarch"))]
compile_error!("target_arch from target JSON not wired through");