diff options
Diffstat (limited to 'compiler/rustc_target/src')
222 files changed, 13300 insertions, 0 deletions
| diff --git a/compiler/rustc_target/src/abi/call/aarch64.rs b/compiler/rustc_target/src/abi/call/aarch64.rs new file mode 100644 index 00000000000..1ab7722edab --- /dev/null +++ b/compiler/rustc_target/src/abi/call/aarch64.rs @@ -0,0 +1,106 @@ +use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; + +fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { + let size = arg.layout.size; + + // Ensure we have at most four uniquely addressable members. + if size > unit.size.checked_mul(4, cx).unwrap() { + return None; + } + + let valid_unit = match unit.kind { + RegKind::Integer => false, + RegKind::Float => true, + RegKind::Vector => size.bits() == 64 || size.bits() == 128, + }; + + valid_unit.then_some(Uniform { unit, total: size }) + }) +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !ret.layout.is_aggregate() { + ret.extend_integer_width_to(32); + return; + } + if let Some(uniform) = is_homogeneous_aggregate(cx, ret) { + ret.cast_to(uniform); + return; + } + let size = ret.layout.size; + let bits = size.bits(); + if bits <= 128 { + let unit = if bits <= 8 { + Reg::i8() + } else if bits <= 16 { + Reg::i16() + } else if bits <= 32 { + Reg::i32() + } else { + Reg::i64() + }; + + ret.cast_to(Uniform { unit, total: size }); + return; + } + ret.make_indirect(); +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !arg.layout.is_aggregate() { + arg.extend_integer_width_to(32); + return; + } + if let Some(uniform) = is_homogeneous_aggregate(cx, arg) { + arg.cast_to(uniform); + return; + } + let size = arg.layout.size; + let bits = size.bits(); + if bits <= 128 { + let unit = if bits <= 8 { + Reg::i8() + } else if bits <= 16 { + Reg::i16() + } else if bits <= 32 { + Reg::i32() + } else { + Reg::i64() + }; + + arg.cast_to(Uniform { unit, total: size }); + return; + } + arg.make_indirect(); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/amdgpu.rs b/compiler/rustc_target/src/abi/call/amdgpu.rs new file mode 100644 index 00000000000..0b4f279fece --- /dev/null +++ b/compiler/rustc_target/src/abi/call/amdgpu.rs @@ -0,0 +1,35 @@ +use crate::abi::call::{ArgAbi, FnAbi}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; + +fn classify_ret<'a, Ty, C>(_cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + ret.extend_integer_width_to(32); +} + +fn classify_arg<'a, Ty, C>(_cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + arg.extend_integer_width_to(32); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/arm.rs b/compiler/rustc_target/src/abi/call/arm.rs new file mode 100644 index 00000000000..26fed3bae4e --- /dev/null +++ b/compiler/rustc_target/src/abi/call/arm.rs @@ -0,0 +1,104 @@ +use crate::abi::call::{ArgAbi, Conv, FnAbi, Reg, RegKind, Uniform}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; +use crate::spec::HasTargetSpec; + +fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { + let size = arg.layout.size; + + // Ensure we have at most four uniquely addressable members. + if size > unit.size.checked_mul(4, cx).unwrap() { + return None; + } + + let valid_unit = match unit.kind { + RegKind::Integer => false, + RegKind::Float => true, + RegKind::Vector => size.bits() == 64 || size.bits() == 128, + }; + + valid_unit.then_some(Uniform { unit, total: size }) + }) +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, vfp: bool) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !ret.layout.is_aggregate() { + ret.extend_integer_width_to(32); + return; + } + + if vfp { + if let Some(uniform) = is_homogeneous_aggregate(cx, ret) { + ret.cast_to(uniform); + return; + } + } + + let size = ret.layout.size; + let bits = size.bits(); + if bits <= 32 { + let unit = if bits <= 8 { + Reg::i8() + } else if bits <= 16 { + Reg::i16() + } else { + Reg::i32() + }; + ret.cast_to(Uniform { unit, total: size }); + return; + } + ret.make_indirect(); +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, vfp: bool) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !arg.layout.is_aggregate() { + arg.extend_integer_width_to(32); + return; + } + + if vfp { + if let Some(uniform) = is_homogeneous_aggregate(cx, arg) { + arg.cast_to(uniform); + return; + } + } + + let align = arg.layout.align.abi.bytes(); + let total = arg.layout.size; + arg.cast_to(Uniform { unit: if align <= 4 { Reg::i32() } else { Reg::i64() }, total }); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout + HasTargetSpec, +{ + // 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.c_variadic; + + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret, vfp); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg, vfp); + } +} diff --git a/compiler/rustc_target/src/abi/call/avr.rs b/compiler/rustc_target/src/abi/call/avr.rs new file mode 100644 index 00000000000..c1f7a1e3af5 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/avr.rs @@ -0,0 +1,59 @@ +//! LLVM-frontend specific AVR calling convention implementation. +//! +//! # Current calling convention ABI +//! +//! Inherited from Clang's `clang::DefaultABIInfo` implementation - self described +//! as +//! +//! > the default implementation for ABI specific details. This implementation +//! > provides information which results in +//! > self-consistent and sensible LLVM IR generation, but does not +//! > conform to any particular ABI. +//! > +//! > - Doxygen Doxumentation of `clang::DefaultABIInfo` +//! +//! This calling convention may not match AVR-GCC in all cases. +//! +//! In the future, an AVR-GCC compatible argument classification ABI should be +//! adopted in both Rust and Clang. +//! +//! *NOTE*: Currently, this module implements the same calling convention +//! that clang with AVR currently does - the default, simple, unspecialized +//! ABI implementation available to all targets. This ABI is not +//! binary-compatible with AVR-GCC. Once LLVM [PR46140](https://bugs.llvm.org/show_bug.cgi?id=46140) +//! is completed, this module should be updated to match so that both Clang +//! and Rust emit code to the same AVR-GCC compatible ABI. +//! +//! In particular, both Clang and Rust may not have the same semantics +//! when promoting arguments to indirect references as AVR-GCC. It is important +//! to note that the core AVR ABI implementation within LLVM itself is ABI +//! compatible with AVR-GCC - Rust and AVR-GCC only differ in the small amount +//! of compiler frontend specific calling convention logic implemented here. + +use crate::abi::call::{ArgAbi, FnAbi}; + +fn classify_ret_ty<Ty>(ret: &mut ArgAbi<'_, Ty>) { + if ret.layout.is_aggregate() { + ret.make_indirect(); + } +} + +fn classify_arg_ty<Ty>(arg: &mut ArgAbi<'_, Ty>) { + if arg.layout.is_aggregate() { + arg.make_indirect(); + } +} + +pub fn compute_abi_info<Ty>(fty: &mut FnAbi<'_, Ty>) { + if !fty.ret.is_ignore() { + classify_ret_ty(&mut fty.ret); + } + + for arg in &mut fty.args { + if arg.is_ignore() { + continue; + } + + classify_arg_ty(arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/hexagon.rs b/compiler/rustc_target/src/abi/call/hexagon.rs new file mode 100644 index 00000000000..8028443b8a6 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/hexagon.rs @@ -0,0 +1,30 @@ +use crate::abi::call::{ArgAbi, FnAbi}; + +fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 { + ret.make_indirect(); + } else { + ret.extend_integer_width_to(32); + } +} + +fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 { + arg.make_indirect(); + } else { + arg.extend_integer_width_to(32); + } +} + +pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/mips.rs b/compiler/rustc_target/src/abi/call/mips.rs new file mode 100644 index 00000000000..733a7328bd3 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/mips.rs @@ -0,0 +1,54 @@ +use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::abi::{HasDataLayout, LayoutOf, Size, TyAndLayoutMethods}; + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty> + HasDataLayout, +{ + if !ret.layout.is_aggregate() { + ret.extend_integer_width_to(32); + } else { + ret.make_indirect(); + *offset += cx.data_layout().pointer_size; + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size) +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty> + HasDataLayout, +{ + let dl = cx.data_layout(); + let size = arg.layout.size; + let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; + + if arg.layout.is_aggregate() { + arg.cast_to(Uniform { unit: Reg::i32(), total: size }); + if !offset.is_aligned(align) { + arg.pad_with(Reg::i32()); + } + } else { + arg.extend_integer_width_to(32); + } + + *offset = offset.align_to(align) + size.align_to(align); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>) +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty> + HasDataLayout, +{ + let mut offset = Size::ZERO; + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret, &mut offset); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg, &mut offset); + } +} diff --git a/compiler/rustc_target/src/abi/call/mips64.rs b/compiler/rustc_target/src/abi/call/mips64.rs new file mode 100644 index 00000000000..917dd104d14 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/mips64.rs @@ -0,0 +1,160 @@ +use crate::abi::call::{ArgAbi, ArgAttribute, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; +use crate::abi::{self, HasDataLayout, LayoutOf, Size, TyAndLayout, TyAndLayoutMethods}; + +fn extend_integer_width_mips<Ty>(arg: &mut ArgAbi<'_, Ty>, bits: u64) { + // Always sign extend u32 values on 64-bit mips + if let abi::Abi::Scalar(ref scalar) = arg.layout.abi { + if let abi::Int(i, signed) = scalar.value { + if !signed && i.size().bits() == 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.set(ArgAttribute::SExt); + return; + } + } + } + } + + arg.extend_integer_width_to(bits); +} + +fn float_reg<'a, Ty, C>(cx: &C, ret: &ArgAbi<'a, Ty>, i: usize) -> Option<Reg> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + match ret.layout.field(cx, i).abi { + abi::Abi::Scalar(ref scalar) => match scalar.value { + abi::F32 => Some(Reg::f32()), + abi::F64 => Some(Reg::f64()), + _ => None, + }, + _ => None, + } +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !ret.layout.is_aggregate() { + extend_integer_width_mips(ret, 64); + return; + } + + let size = ret.layout.size; + let bits = size.bits(); + if bits <= 128 { + // Unlike other architectures which return aggregates in registers, MIPS n64 limits the + // use of float registers to structures (not unions) containing exactly one or two + // float fields. + + if let abi::FieldsShape::Arbitrary { .. } = ret.layout.fields { + if ret.layout.fields.count() == 1 { + if let Some(reg) = float_reg(cx, ret, 0) { + ret.cast_to(reg); + return; + } + } else if ret.layout.fields.count() == 2 { + if let Some(reg0) = float_reg(cx, ret, 0) { + if let Some(reg1) = float_reg(cx, ret, 1) { + ret.cast_to(CastTarget::pair(reg0, reg1)); + return; + } + } + } + } + + // Cast to a uniform int structure + ret.cast_to(Uniform { unit: Reg::i64(), total: size }); + } else { + ret.make_indirect(); + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !arg.layout.is_aggregate() { + extend_integer_width_mips(arg, 64); + return; + } + + let dl = cx.data_layout(); + let size = arg.layout.size; + let mut prefix = [None; 8]; + let mut prefix_index = 0; + + match arg.layout.fields { + abi::FieldsShape::Primitive => unreachable!(), + abi::FieldsShape::Array { .. } => { + // Arrays are passed indirectly + arg.make_indirect(); + return; + } + abi::FieldsShape::Union(_) => { + // Unions and are always treated as a series of 64-bit integer chunks + } + abi::FieldsShape::Arbitrary { .. } => { + // Structures are split up into a series of 64-bit integer chunks, but any aligned + // doubles not part of another aggregate are passed as floats. + let mut last_offset = Size::ZERO; + + for i in 0..arg.layout.fields.count() { + let field = arg.layout.field(cx, i); + let offset = arg.layout.fields.offset(i); + + // We only care about aligned doubles + if let abi::Abi::Scalar(ref scalar) = field.abi { + if let abi::F64 = scalar.value { + if offset.is_aligned(dl.f64_align.abi) { + // Insert enough integers to cover [last_offset, offset) + assert!(last_offset.is_aligned(dl.f64_align.abi)); + for _ in 0..((offset - last_offset).bits() / 64) + .min((prefix.len() - prefix_index) as u64) + { + prefix[prefix_index] = Some(RegKind::Integer); + prefix_index += 1; + } + + if prefix_index == prefix.len() { + break; + } + + prefix[prefix_index] = Some(RegKind::Float); + prefix_index += 1; + last_offset = offset + Reg::f64().size; + } + } + } + } + } + }; + + // Extract first 8 chunks as the prefix + let rest_size = size - Size::from_bytes(8) * prefix_index as u64; + arg.cast_to(CastTarget { + prefix, + prefix_chunk: Size::from_bytes(8), + rest: Uniform { unit: Reg::i64(), total: rest_size }, + }); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs new file mode 100644 index 00000000000..8f7e2bba5aa --- /dev/null +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -0,0 +1,612 @@ +use crate::abi::{self, Abi, Align, FieldsShape, Size}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; +use crate::spec::{self, HasTargetSpec}; + +mod aarch64; +mod amdgpu; +mod arm; +mod avr; +mod hexagon; +mod mips; +mod mips64; +mod msp430; +mod nvptx; +mod nvptx64; +mod powerpc; +mod powerpc64; +mod riscv; +mod s390x; +mod sparc; +mod sparc64; +mod wasm32; +mod wasm32_bindgen_compat; +mod x86; +mod x86_64; +mod x86_win64; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum PassMode { + /// Ignore the argument. + Ignore, + /// Pass the argument directly. + Direct(ArgAttributes), + /// Pass a pair's elements directly in two arguments. + Pair(ArgAttributes, ArgAttributes), + /// Pass the argument after casting it, to either + /// a single uniform or a pair of registers. + Cast(CastTarget), + /// Pass the argument indirectly via a hidden pointer. + /// The second value, if any, is for the extra data (vtable or length) + /// which indicates that it refers to an unsized rvalue. + Indirect(ArgAttributes, Option<ArgAttributes>), +} + +// Hack to disable non_upper_case_globals only for the bitflags! and not for the rest +// of this module +pub use attr_impl::ArgAttribute; + +#[allow(non_upper_case_globals)] +#[allow(unused)] +mod attr_impl { + // The subset of llvm::Attribute needed for arguments, packed into a bitfield. + bitflags::bitflags! { + #[derive(Default)] + pub struct ArgAttribute: u16 { + const ByVal = 1 << 0; + const NoAlias = 1 << 1; + const NoCapture = 1 << 2; + const NonNull = 1 << 3; + const ReadOnly = 1 << 4; + const SExt = 1 << 5; + const StructRet = 1 << 6; + const ZExt = 1 << 7; + const InReg = 1 << 8; + } + } +} + +/// A compact representation of LLVM attributes (at least those relevant for this module) +/// that can be manipulated without interacting with LLVM's Attribute machinery. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct ArgAttributes { + pub regular: ArgAttribute, + /// The minimum size of the pointee, guaranteed to be valid for the duration of the whole call + /// (corresponding to LLVM's dereferenceable and dereferenceable_or_null attributes). + pub pointee_size: Size, + pub pointee_align: Option<Align>, +} + +impl ArgAttributes { + pub fn new() -> Self { + ArgAttributes { + regular: ArgAttribute::default(), + pointee_size: Size::ZERO, + pointee_align: None, + } + } + + pub fn set(&mut self, attr: ArgAttribute) -> &mut Self { + self.regular |= attr; + self + } + + pub fn contains(&self, attr: ArgAttribute) -> bool { + self.regular.contains(attr) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum RegKind { + Integer, + Float, + Vector, +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Reg { + pub kind: RegKind, + pub size: Size, +} + +macro_rules! reg_ctor { + ($name:ident, $kind:ident, $bits:expr) => { + pub fn $name() -> Reg { + Reg { kind: RegKind::$kind, size: Size::from_bits($bits) } + } + }; +} + +impl Reg { + reg_ctor!(i8, Integer, 8); + reg_ctor!(i16, Integer, 16); + reg_ctor!(i32, Integer, 32); + reg_ctor!(i64, Integer, 64); + reg_ctor!(i128, Integer, 128); + + reg_ctor!(f32, Float, 32); + reg_ctor!(f64, Float, 64); +} + +impl Reg { + pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { + let dl = cx.data_layout(); + match self.kind { + RegKind::Integer => match self.size.bits() { + 1 => dl.i1_align.abi, + 2..=8 => dl.i8_align.abi, + 9..=16 => dl.i16_align.abi, + 17..=32 => dl.i32_align.abi, + 33..=64 => dl.i64_align.abi, + 65..=128 => dl.i128_align.abi, + _ => panic!("unsupported integer: {:?}", self), + }, + RegKind::Float => match self.size.bits() { + 32 => dl.f32_align.abi, + 64 => dl.f64_align.abi, + _ => panic!("unsupported float: {:?}", self), + }, + RegKind::Vector => dl.vector_align(self.size).abi, + } + } +} + +/// An argument passed entirely registers with the +/// same kind (e.g., HFA / HVA on PPC64 and AArch64). +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct Uniform { + pub unit: Reg, + + /// The total size of the argument, which can be: + /// * equal to `unit.size` (one scalar/vector), + /// * a multiple of `unit.size` (an array of scalar/vectors), + /// * if `unit.kind` is `Integer`, the last element + /// can be shorter, i.e., `{ i64, i64, i32 }` for + /// 64-bit integers with a total size of 20 bytes. + pub total: Size, +} + +impl From<Reg> for Uniform { + fn from(unit: Reg) -> Uniform { + Uniform { unit, total: unit.size } + } +} + +impl Uniform { + pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { + self.unit.align(cx) + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct CastTarget { + pub prefix: [Option<RegKind>; 8], + pub prefix_chunk: Size, + pub rest: Uniform, +} + +impl From<Reg> for CastTarget { + fn from(unit: Reg) -> CastTarget { + CastTarget::from(Uniform::from(unit)) + } +} + +impl From<Uniform> for CastTarget { + fn from(uniform: Uniform) -> CastTarget { + CastTarget { prefix: [None; 8], prefix_chunk: Size::ZERO, rest: uniform } + } +} + +impl CastTarget { + pub fn pair(a: Reg, b: Reg) -> CastTarget { + CastTarget { + prefix: [Some(a.kind), None, None, None, None, None, None, None], + prefix_chunk: a.size, + rest: Uniform::from(b), + } + } + + pub fn size<C: HasDataLayout>(&self, cx: &C) -> Size { + (self.prefix_chunk * self.prefix.iter().filter(|x| x.is_some()).count() as u64) + .align_to(self.rest.align(cx)) + + self.rest.total + } + + pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align { + self.prefix + .iter() + .filter_map(|x| x.map(|kind| Reg { kind, size: self.prefix_chunk }.align(cx))) + .fold(cx.data_layout().aggregate_align.abi.max(self.rest.align(cx)), |acc, align| { + acc.max(align) + }) + } +} + +/// Return value from the `homogeneous_aggregate` test function. +#[derive(Copy, Clone, Debug)] +pub enum HomogeneousAggregate { + /// Yes, all the "leaf fields" of this struct are passed in the + /// same way (specified in the `Reg` value). + Homogeneous(Reg), + + /// There are no leaf fields at all. + NoData, +} + +/// Error from the `homogeneous_aggregate` test function, indicating +/// there are distinct leaf fields passed in different ways, +/// or this is uninhabited. +#[derive(Copy, Clone, Debug)] +pub struct Heterogeneous; + +impl HomogeneousAggregate { + /// If this is a homogeneous aggregate, returns the homogeneous + /// unit, else `None`. + pub fn unit(self) -> Option<Reg> { + match self { + HomogeneousAggregate::Homogeneous(reg) => Some(reg), + HomogeneousAggregate::NoData => None, + } + } + + /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in + /// the same `struct`. Only succeeds if only one of them has any data, + /// or both units are identical. + fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> { + match (self, other) { + (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x), + + (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => { + if a != b { + return Err(Heterogeneous); + } + Ok(self) + } + } + } +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + fn is_aggregate(&self) -> bool { + match self.abi { + Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } => false, + Abi::ScalarPair(..) | Abi::Aggregate { .. } => true, + } + } + + /// Returns `Homogeneous` if this layout is an aggregate containing fields of + /// only a single type (e.g., `(u32, u32)`). Such aggregates are often + /// special-cased in ABIs. + /// + /// Note: We generally ignore fields of zero-sized type when computing + /// this value (see #56877). + /// + /// This is public so that it can be used in unit tests, but + /// should generally only be relevant to the ABI details of + /// specific targets. + pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous> + where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = Self>, + { + match self.abi { + Abi::Uninhabited => Err(Heterogeneous), + + // The primitive for this algorithm. + Abi::Scalar(ref scalar) => { + let kind = match scalar.value { + abi::Int(..) | abi::Pointer => RegKind::Integer, + abi::F32 | abi::F64 => RegKind::Float, + }; + Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size })) + } + + Abi::Vector { .. } => { + assert!(!self.is_zst()); + Ok(HomogeneousAggregate::Homogeneous(Reg { + kind: RegKind::Vector, + size: self.size, + })) + } + + Abi::ScalarPair(..) | Abi::Aggregate { .. } => { + // Helper for computing `homogeneous_aggregate`, allowing a custom + // starting offset (used below for handling variants). + let from_fields_at = + |layout: Self, + start: Size| + -> Result<(HomogeneousAggregate, Size), Heterogeneous> { + let is_union = match layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Array { count, .. } => { + assert_eq!(start, Size::ZERO); + + let result = if count > 0 { + layout.field(cx, 0).homogeneous_aggregate(cx)? + } else { + HomogeneousAggregate::NoData + }; + return Ok((result, layout.size)); + } + FieldsShape::Union(_) => true, + FieldsShape::Arbitrary { .. } => false, + }; + + let mut result = HomogeneousAggregate::NoData; + let mut total = start; + + for i in 0..layout.fields.count() { + if !is_union && total != layout.fields.offset(i) { + return Err(Heterogeneous); + } + + let field = layout.field(cx, i); + + result = result.merge(field.homogeneous_aggregate(cx)?)?; + + // Keep track of the offset (without padding). + let size = field.size; + if is_union { + total = total.max(size); + } else { + total += size; + } + } + + Ok((result, total)) + }; + + let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?; + + match &self.variants { + abi::Variants::Single { .. } => {} + abi::Variants::Multiple { variants, .. } => { + // Treat enum variants like union members. + // HACK(eddyb) pretend the `enum` field (discriminant) + // is at the start of every variant (otherwise the gap + // at the start of all variants would disqualify them). + // + // NB: for all tagged `enum`s (which include all non-C-like + // `enum`s with defined FFI representation), this will + // match the homogeneous computation on the equivalent + // `struct { tag; union { variant1; ... } }` and/or + // `union { struct { tag; variant1; } ... }` + // (the offsets of variant fields should be identical + // between the two for either to be a homogeneous aggregate). + let variant_start = total; + for variant_idx in variants.indices() { + let (variant_result, variant_total) = + from_fields_at(self.for_variant(cx, variant_idx), variant_start)?; + + result = result.merge(variant_result)?; + total = total.max(variant_total); + } + } + } + + // There needs to be no padding. + if total != self.size { + Err(Heterogeneous) + } else { + match result { + HomogeneousAggregate::Homogeneous(_) => { + assert_ne!(total, Size::ZERO); + } + HomogeneousAggregate::NoData => { + assert_eq!(total, Size::ZERO); + } + } + Ok(result) + } + } + } + } +} + +/// Information about how to pass an argument to, +/// or return a value from, a function, under some ABI. +#[derive(Debug)] +pub struct ArgAbi<'a, Ty> { + pub layout: TyAndLayout<'a, Ty>, + + /// Dummy argument, which is emitted before the real argument. + pub pad: Option<Reg>, + + pub mode: PassMode, +} + +impl<'a, Ty> ArgAbi<'a, Ty> { + pub fn new(layout: TyAndLayout<'a, Ty>) -> Self { + ArgAbi { layout, pad: None, mode: PassMode::Direct(ArgAttributes::new()) } + } + + pub fn make_indirect(&mut self) { + assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); + + // Start with fresh attributes for the pointer. + let mut attrs = ArgAttributes::new(); + + // For non-immediate arguments the callee gets its own copy of + // the value on the stack, so there are no aliases. It's also + // program-invisible so can't possibly capture + attrs.set(ArgAttribute::NoAlias).set(ArgAttribute::NoCapture).set(ArgAttribute::NonNull); + attrs.pointee_size = self.layout.size; + // FIXME(eddyb) We should be doing this, but at least on + // i686-pc-windows-msvc, it results in wrong stack offsets. + // attrs.pointee_align = Some(self.layout.align.abi); + + let extra_attrs = self.layout.is_unsized().then_some(ArgAttributes::new()); + + self.mode = PassMode::Indirect(attrs, extra_attrs); + } + + pub fn make_indirect_byval(&mut self) { + self.make_indirect(); + match self.mode { + PassMode::Indirect(ref mut attrs, _) => { + attrs.set(ArgAttribute::ByVal); + } + _ => unreachable!(), + } + } + + pub fn extend_integer_width_to(&mut self, bits: u64) { + // Only integers have signedness + if let Abi::Scalar(ref scalar) = self.layout.abi { + if let abi::Int(i, signed) = scalar.value { + if i.size().bits() < bits { + if let PassMode::Direct(ref mut attrs) = self.mode { + attrs.set(if signed { ArgAttribute::SExt } else { ArgAttribute::ZExt }); + } + } + } + } + } + + pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) { + assert_eq!(self.mode, PassMode::Direct(ArgAttributes::new())); + self.mode = PassMode::Cast(target.into()); + } + + pub fn pad_with(&mut self, reg: Reg) { + self.pad = Some(reg); + } + + pub fn is_indirect(&self) -> bool { + match self.mode { + PassMode::Indirect(..) => true, + _ => false, + } + } + + pub fn is_sized_indirect(&self) -> bool { + match self.mode { + PassMode::Indirect(_, None) => true, + _ => false, + } + } + + pub fn is_unsized_indirect(&self) -> bool { + match self.mode { + PassMode::Indirect(_, Some(_)) => true, + _ => false, + } + } + + pub fn is_ignore(&self) -> bool { + match self.mode { + PassMode::Ignore => true, + _ => false, + } + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Conv { + // General language calling conventions, for which every target + // should have its own backend (e.g. LLVM) support. + C, + Rust, + + // Target-specific calling conventions. + ArmAapcs, + + Msp430Intr, + + PtxKernel, + + X86Fastcall, + X86Intr, + X86Stdcall, + X86ThisCall, + X86VectorCall, + + X86_64SysV, + X86_64Win64, + + AmdGpuKernel, + AvrInterrupt, + AvrNonBlockingInterrupt, +} + +/// Metadata describing how the arguments to a native function +/// should be passed in order to respect the native ABI. +/// +/// I will do my best to describe this structure, but these +/// comments are reverse-engineered and may be inaccurate. -NDM +#[derive(Debug)] +pub struct FnAbi<'a, Ty> { + /// The LLVM types of each argument. + pub args: Vec<ArgAbi<'a, Ty>>, + + /// LLVM return type. + pub ret: ArgAbi<'a, Ty>, + + pub c_variadic: bool, + + /// The count of non-variadic arguments. + /// + /// Should only be different from args.len() when c_variadic is true. + /// This can be used to know whether an argument is variadic or not. + pub fixed_count: usize, + + pub conv: Conv, + + pub can_unwind: bool, +} + +impl<'a, Ty> FnAbi<'a, Ty> { + pub fn adjust_for_cabi<C>(&mut self, cx: &C, abi: spec::abi::Abi) -> Result<(), String> + where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout + HasTargetSpec, + { + match &cx.target_spec().arch[..] { + "x86" => { + let flavor = if abi == spec::abi::Abi::Fastcall { + x86::Flavor::Fastcall + } else { + x86::Flavor::General + }; + x86::compute_abi_info(cx, self, flavor); + } + "x86_64" => { + if abi == spec::abi::Abi::SysV64 { + x86_64::compute_abi_info(cx, self); + } else if abi == spec::abi::Abi::Win64 || cx.target_spec().options.is_like_windows { + x86_win64::compute_abi_info(self); + } else { + x86_64::compute_abi_info(cx, self); + } + } + "aarch64" => aarch64::compute_abi_info(cx, self), + "amdgpu" => amdgpu::compute_abi_info(cx, self), + "arm" => arm::compute_abi_info(cx, self), + "avr" => avr::compute_abi_info(self), + "mips" => mips::compute_abi_info(cx, self), + "mips64" => mips64::compute_abi_info(cx, self), + "powerpc" => powerpc::compute_abi_info(self), + "powerpc64" => powerpc64::compute_abi_info(cx, self), + "s390x" => s390x::compute_abi_info(cx, self), + "msp430" => msp430::compute_abi_info(self), + "sparc" => sparc::compute_abi_info(cx, self), + "sparc64" => sparc64::compute_abi_info(cx, self), + "nvptx" => nvptx::compute_abi_info(self), + "nvptx64" => nvptx64::compute_abi_info(self), + "hexagon" => hexagon::compute_abi_info(self), + "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), + "wasm32" if cx.target_spec().target_os != "emscripten" => { + wasm32_bindgen_compat::compute_abi_info(self) + } + "wasm32" | "asmjs" => wasm32::compute_abi_info(cx, self), + a => return Err(format!("unrecognized arch \"{}\" in target specification", a)), + } + + if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); + } + + Ok(()) + } +} diff --git a/compiler/rustc_target/src/abi/call/msp430.rs b/compiler/rustc_target/src/abi/call/msp430.rs new file mode 100644 index 00000000000..3004bb9ff5d --- /dev/null +++ b/compiler/rustc_target/src/abi/call/msp430.rs @@ -0,0 +1,39 @@ +// Reference: MSP430 Embedded Application Binary Interface +// http://www.ti.com/lit/an/slaa534/slaa534.pdf + +use crate::abi::call::{ArgAbi, FnAbi}; + +// 3.5 Structures or Unions Passed and Returned by Reference +// +// "Structures (including classes) and unions larger than 32 bits are passed and +// returned by reference. To pass a structure or union by reference, the caller +// places its address in the appropriate location: either in a register or on +// the stack, according to its position in the argument list. (..)" +fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 32 { + ret.make_indirect(); + } else { + ret.extend_integer_width_to(16); + } +} + +fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 32 { + arg.make_indirect(); + } else { + arg.extend_integer_width_to(16); + } +} + +pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/nvptx.rs b/compiler/rustc_target/src/abi/call/nvptx.rs new file mode 100644 index 00000000000..693337f0e52 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/nvptx.rs @@ -0,0 +1,33 @@ +// Reference: PTX Writer's Guide to Interoperability +// http://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability + +use crate::abi::call::{ArgAbi, FnAbi}; + +fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 32 { + ret.make_indirect(); + } else { + ret.extend_integer_width_to(32); + } +} + +fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 32 { + arg.make_indirect(); + } else { + arg.extend_integer_width_to(32); + } +} + +pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/nvptx64.rs b/compiler/rustc_target/src/abi/call/nvptx64.rs new file mode 100644 index 00000000000..b9c9296dbac --- /dev/null +++ b/compiler/rustc_target/src/abi/call/nvptx64.rs @@ -0,0 +1,33 @@ +// Reference: PTX Writer's Guide to Interoperability +// http://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability + +use crate::abi::call::{ArgAbi, FnAbi}; + +fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { + if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 { + ret.make_indirect(); + } else { + ret.extend_integer_width_to(64); + } +} + +fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { + if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 { + arg.make_indirect(); + } else { + arg.extend_integer_width_to(64); + } +} + +pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/powerpc.rs b/compiler/rustc_target/src/abi/call/powerpc.rs new file mode 100644 index 00000000000..27a5c6d2fc6 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/powerpc.rs @@ -0,0 +1,30 @@ +use crate::abi::call::{ArgAbi, FnAbi}; + +fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { + if ret.layout.is_aggregate() { + ret.make_indirect(); + } else { + ret.extend_integer_width_to(32); + } +} + +fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { + if arg.layout.is_aggregate() { + arg.make_indirect(); + } else { + arg.extend_integer_width_to(32); + } +} + +pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs new file mode 100644 index 00000000000..b740707320f --- /dev/null +++ b/compiler/rustc_target/src/abi/call/powerpc64.rs @@ -0,0 +1,141 @@ +// FIXME: +// Alignment of 128 bit types is not currently handled, this will +// need to be fixed when PowerPC vector support is added. + +use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; +use crate::abi::{Endian, HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; +use crate::spec::HasTargetSpec; + +#[derive(Debug, Clone, Copy, PartialEq)] +enum ABI { + ELFv1, // original ABI used for powerpc64 (big-endian) + ELFv2, // newer ABI used for powerpc64le and musl (both endians) +} +use ABI::*; + +fn is_homogeneous_aggregate<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + abi: ABI, +) -> Option<Uniform> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { + // ELFv1 only passes one-member aggregates transparently. + // ELFv2 passes up to eight uniquely addressable members. + if (abi == ELFv1 && arg.layout.size > unit.size) + || arg.layout.size > unit.size.checked_mul(8, cx).unwrap() + { + return None; + } + + let valid_unit = match unit.kind { + RegKind::Integer => false, + RegKind::Float => true, + RegKind::Vector => arg.layout.size.bits() == 128, + }; + + valid_unit.then_some(Uniform { unit, total: arg.layout.size }) + }) +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, abi: ABI) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !ret.layout.is_aggregate() { + ret.extend_integer_width_to(64); + return; + } + + // The ELFv1 ABI doesn't return aggregates in registers + if abi == ELFv1 { + ret.make_indirect(); + return; + } + + if let Some(uniform) = is_homogeneous_aggregate(cx, ret, abi) { + ret.cast_to(uniform); + return; + } + + let size = ret.layout.size; + let bits = size.bits(); + if bits <= 128 { + let unit = if cx.data_layout().endian == Endian::Big { + Reg { kind: RegKind::Integer, size } + } else if bits <= 8 { + Reg::i8() + } else if bits <= 16 { + Reg::i16() + } else if bits <= 32 { + Reg::i32() + } else { + Reg::i64() + }; + + ret.cast_to(Uniform { unit, total: size }); + return; + } + + ret.make_indirect(); +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi: ABI) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !arg.layout.is_aggregate() { + arg.extend_integer_width_to(64); + return; + } + + if let Some(uniform) = is_homogeneous_aggregate(cx, arg, abi) { + arg.cast_to(uniform); + return; + } + + let size = arg.layout.size; + let (unit, total) = if size.bits() <= 64 { + // Aggregates smaller than a doubleword should appear in + // the least-significant bits of the parameter doubleword. + (Reg { kind: RegKind::Integer, size }, size) + } else { + // Aggregates larger than a doubleword should be padded + // at the tail to fill out a whole number of doublewords. + let reg_i64 = Reg::i64(); + (reg_i64, size.align_to(reg_i64.align(cx))) + }; + + arg.cast_to(Uniform { unit, total }); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout + HasTargetSpec, +{ + let abi = if cx.target_spec().target_env == "musl" { + ELFv2 + } else { + match cx.data_layout().endian { + Endian::Big => ELFv1, + Endian::Little => ELFv2, + } + }; + + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret, abi); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg, abi); + } +} diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs new file mode 100644 index 00000000000..2e10bed3bd4 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/riscv.rs @@ -0,0 +1,356 @@ +// Reference: RISC-V ELF psABI specification +// https://github.com/riscv/riscv-elf-psabi-doc +// +// Reference: Clang RISC-V ELF psABI lowering code +// https://github.com/llvm/llvm-project/blob/8e780252a7284be45cf1ba224cabd884847e8e92/clang/lib/CodeGen/TargetInfo.cpp#L9311-L9773 + +use crate::abi::call::{ArgAbi, ArgAttribute, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform}; +use crate::abi::{ + self, Abi, FieldsShape, HasDataLayout, LayoutOf, Size, TyAndLayout, TyAndLayoutMethods, +}; +use crate::spec::HasTargetSpec; + +#[derive(Copy, Clone)] +enum RegPassKind { + Float(Reg), + Integer(Reg), + Unknown, +} + +#[derive(Copy, Clone)] +enum FloatConv { + FloatPair(Reg, Reg), + Float(Reg), + MixedPair(Reg, Reg), +} + +#[derive(Copy, Clone)] +struct CannotUseFpConv; + +fn is_riscv_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { + match arg.layout.abi { + Abi::Vector { .. } => true, + _ => arg.layout.is_aggregate(), + } +} + +fn should_use_fp_conv_helper<'a, Ty, C>( + cx: &C, + arg_layout: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, + field1_kind: &mut RegPassKind, + field2_kind: &mut RegPassKind, +) -> Result<(), CannotUseFpConv> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>>, +{ + match arg_layout.abi { + Abi::Scalar(ref scalar) => match scalar.value { + abi::Int(..) | abi::Pointer => { + if arg_layout.size.bits() > xlen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + (RegPassKind::Float(_), RegPassKind::Unknown) => { + *field2_kind = RegPassKind::Integer(Reg { + kind: RegKind::Integer, + size: arg_layout.size, + }); + } + _ => return Err(CannotUseFpConv), + } + } + abi::F32 | abi::F64 => { + if arg_layout.size.bits() > flen { + return Err(CannotUseFpConv); + } + match (*field1_kind, *field2_kind) { + (RegPassKind::Unknown, _) => { + *field1_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + (_, RegPassKind::Unknown) => { + *field2_kind = + RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size }); + } + _ => return Err(CannotUseFpConv), + } + } + }, + Abi::Vector { .. } | Abi::Uninhabited => return Err(CannotUseFpConv), + Abi::ScalarPair(..) | Abi::Aggregate { .. } => match arg_layout.fields { + FieldsShape::Primitive => { + unreachable!("aggregates can't have `FieldsShape::Primitive`") + } + FieldsShape::Union(_) => { + if !arg_layout.is_zst() { + return Err(CannotUseFpConv); + } + } + FieldsShape::Array { count, .. } => { + for _ in 0..count { + let elem_layout = arg_layout.field(cx, 0); + should_use_fp_conv_helper( + cx, + &elem_layout, + xlen, + flen, + field1_kind, + field2_kind, + )?; + } + } + FieldsShape::Arbitrary { .. } => { + match arg_layout.variants { + abi::Variants::Multiple { .. } => return Err(CannotUseFpConv), + abi::Variants::Single { .. } => (), + } + for i in arg_layout.fields.index_by_increasing_offset() { + let field = arg_layout.field(cx, i); + should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?; + } + } + }, + } + Ok(()) +} + +fn should_use_fp_conv<'a, Ty, C>( + cx: &C, + arg: &TyAndLayout<'a, Ty>, + xlen: u64, + flen: u64, +) -> Option<FloatConv> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>>, +{ + let mut field1_kind = RegPassKind::Unknown; + let mut field2_kind = RegPassKind::Unknown; + if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() { + return None; + } + match (field1_kind, field2_kind) { + (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)), + (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)), + (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)), + _ => None, + } +} + +fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>>, +{ + if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) { + match conv { + FloatConv::Float(f) => { + arg.cast_to(f); + } + FloatConv::FloatPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + FloatConv::MixedPair(l, r) => { + arg.cast_to(CastTarget::pair(l, r)); + } + } + return false; + } + + let total = arg.layout.size; + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_riscv_aggregate(arg) { + arg.make_indirect(); + } + return true; + } + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + if is_riscv_aggregate(arg) { + if total.bits() <= xlen { + arg.cast_to(xlen_reg); + } else { + arg.cast_to(Uniform { unit: xlen_reg, total: Size::from_bits(xlen * 2) }); + } + return false; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + extend_integer_width(arg, xlen); + false +} + +fn classify_arg<'a, Ty, C>( + cx: &C, + arg: &mut ArgAbi<'a, Ty>, + xlen: u64, + flen: u64, + is_vararg: bool, + avail_gprs: &mut u64, + avail_fprs: &mut u64, +) where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>>, +{ + if !is_vararg { + match should_use_fp_conv(cx, &arg.layout, xlen, flen) { + Some(FloatConv::Float(f)) if *avail_fprs >= 1 => { + *avail_fprs -= 1; + arg.cast_to(f); + return; + } + Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => { + *avail_fprs -= 2; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => { + *avail_gprs -= 1; + *avail_fprs -= 1; + arg.cast_to(CastTarget::pair(l, r)); + return; + } + _ => (), + } + } + + let total = arg.layout.size; + let align = arg.layout.align.abi.bits(); + + // "Scalars wider than 2✕XLEN are passed by reference and are replaced in + // the argument list with the address." + // "Aggregates larger than 2✕XLEN bits are passed by reference and are + // replaced in the argument list with the address, as are C++ aggregates + // with nontrivial copy constructors, destructors, or vtables." + if total.bits() > 2 * xlen { + // We rely on the LLVM backend lowering code to lower passing a scalar larger than 2*XLEN. + if is_riscv_aggregate(arg) { + arg.make_indirect(); + } + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + let double_xlen_reg = match xlen { + 32 => Reg::i64(), + 64 => Reg::i128(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + let xlen_reg = match xlen { + 32 => Reg::i32(), + 64 => Reg::i64(), + _ => unreachable!("Unsupported XLEN: {}", xlen), + }; + + if total.bits() > xlen { + let align_regs = align > xlen; + if is_riscv_aggregate(arg) { + arg.cast_to(Uniform { + unit: if align_regs { double_xlen_reg } else { xlen_reg }, + total: Size::from_bits(xlen * 2), + }); + } + if align_regs && is_vararg { + *avail_gprs -= *avail_gprs % 2; + } + if *avail_gprs >= 2 { + *avail_gprs -= 2; + } else { + *avail_gprs = 0; + } + return; + } else if is_riscv_aggregate(arg) { + arg.cast_to(xlen_reg); + if *avail_gprs >= 1 { + *avail_gprs -= 1; + } + return; + } + + // "When passed in registers, scalars narrower than XLEN bits are widened + // according to the sign of their type up to 32 bits, then sign-extended to + // XLEN bits." + if *avail_gprs >= 1 { + extend_integer_width(arg, xlen); + *avail_gprs -= 1; + } +} + +fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) { + if let Abi::Scalar(ref scalar) = arg.layout.abi { + if let abi::Int(i, _) = scalar.value { + // 32-bit integers are always sign-extended + if i.size().bits() == 32 && xlen > 32 { + if let PassMode::Direct(ref mut attrs) = arg.mode { + attrs.set(ArgAttribute::SExt); + return; + } + } + } + } + + arg.extend_integer_width_to(xlen); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout + HasTargetSpec, +{ + let flen = match &cx.target_spec().options.llvm_abiname[..] { + "ilp32f" | "lp64f" => 32, + "ilp32d" | "lp64d" => 64, + _ => 0, + }; + let xlen = cx.data_layout().pointer_size.bits(); + + let mut avail_gprs = 8; + let mut avail_fprs = 8; + + if !fn_abi.ret.is_ignore() { + if classify_ret(cx, &mut fn_abi.ret, xlen, flen) { + avail_gprs -= 1; + } + } + + for (i, arg) in fn_abi.args.iter_mut().enumerate() { + if arg.is_ignore() { + continue; + } + classify_arg( + cx, + arg, + xlen, + flen, + i >= fn_abi.fixed_count, + &mut avail_gprs, + &mut avail_fprs, + ); + } +} diff --git a/compiler/rustc_target/src/abi/call/s390x.rs b/compiler/rustc_target/src/abi/call/s390x.rs new file mode 100644 index 00000000000..005dcc62dfd --- /dev/null +++ b/compiler/rustc_target/src/abi/call/s390x.rs @@ -0,0 +1,79 @@ +// FIXME: The assumes we're using the non-vector ABI, i.e., compiling +// for a pre-z13 machine or using -mno-vx. + +use crate::abi::call::{ArgAbi, FnAbi, Reg}; +use crate::abi::{self, HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; + +fn classify_ret<'a, Ty, C>(ret: &mut ArgAbi<'_, Ty>) +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty> + HasDataLayout, +{ + if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 { + ret.extend_integer_width_to(64); + } else { + ret.make_indirect(); + } +} + +fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + match layout.abi { + abi::Abi::Scalar(ref scalar) => scalar.value.is_float(), + abi::Abi::Aggregate { .. } => { + if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { + is_single_fp_element(cx, layout.field(cx, 0)) + } else { + false + } + } + _ => false, + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !arg.layout.is_aggregate() && arg.layout.size.bits() <= 64 { + arg.extend_integer_width_to(64); + return; + } + + if is_single_fp_element(cx, arg.layout) { + match arg.layout.size.bytes() { + 4 => arg.cast_to(Reg::f32()), + 8 => arg.cast_to(Reg::f64()), + _ => arg.make_indirect(), + } + } else { + match arg.layout.size.bytes() { + 1 => arg.cast_to(Reg::i8()), + 2 => arg.cast_to(Reg::i16()), + 4 => arg.cast_to(Reg::i32()), + 8 => arg.cast_to(Reg::i64()), + _ => arg.make_indirect(), + } + } +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/sparc.rs b/compiler/rustc_target/src/abi/call/sparc.rs new file mode 100644 index 00000000000..733a7328bd3 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/sparc.rs @@ -0,0 +1,54 @@ +use crate::abi::call::{ArgAbi, FnAbi, Reg, Uniform}; +use crate::abi::{HasDataLayout, LayoutOf, Size, TyAndLayoutMethods}; + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'_, Ty>, offset: &mut Size) +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty> + HasDataLayout, +{ + if !ret.layout.is_aggregate() { + ret.extend_integer_width_to(32); + } else { + ret.make_indirect(); + *offset += cx.data_layout().pointer_size; + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'_, Ty>, offset: &mut Size) +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty> + HasDataLayout, +{ + let dl = cx.data_layout(); + let size = arg.layout.size; + let align = arg.layout.align.max(dl.i32_align).min(dl.i64_align).abi; + + if arg.layout.is_aggregate() { + arg.cast_to(Uniform { unit: Reg::i32(), total: size }); + if !offset.is_aligned(align) { + arg.pad_with(Reg::i32()); + } + } else { + arg.extend_integer_width_to(32); + } + + *offset = offset.align_to(align) + size.align_to(align); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'_, Ty>) +where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty> + HasDataLayout, +{ + let mut offset = Size::ZERO; + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret, &mut offset); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg, &mut offset); + } +} diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs new file mode 100644 index 00000000000..a647675e073 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/sparc64.rs @@ -0,0 +1,92 @@ +// FIXME: This needs an audit for correctness and completeness. + +use crate::abi::call::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; + +fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| { + // Ensure we have at most eight uniquely addressable members. + if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() { + return None; + } + + let valid_unit = match unit.kind { + RegKind::Integer => false, + RegKind::Float => true, + RegKind::Vector => arg.layout.size.bits() == 128, + }; + + valid_unit.then_some(Uniform { unit, total: arg.layout.size }) + }) +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !ret.layout.is_aggregate() { + ret.extend_integer_width_to(64); + return; + } + + if let Some(uniform) = is_homogeneous_aggregate(cx, ret) { + ret.cast_to(uniform); + return; + } + let size = ret.layout.size; + let bits = size.bits(); + if bits <= 256 { + let unit = Reg::i64(); + ret.cast_to(Uniform { unit, total: size }); + return; + } + + // don't return aggregates in registers + ret.make_indirect(); +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !arg.layout.is_aggregate() { + arg.extend_integer_width_to(64); + return; + } + + if let Some(uniform) = is_homogeneous_aggregate(cx, arg) { + arg.cast_to(uniform); + return; + } + + let total = arg.layout.size; + if total.bits() > 128 { + arg.make_indirect(); + return; + } + + arg.cast_to(Uniform { unit: Reg::i64(), total }); +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/wasm32.rs b/compiler/rustc_target/src/abi/call/wasm32.rs new file mode 100644 index 00000000000..510f671a501 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/wasm32.rs @@ -0,0 +1,62 @@ +use crate::abi::call::{ArgAbi, FnAbi, Uniform}; +use crate::abi::{HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; + +fn unwrap_trivial_aggregate<'a, Ty, C>(cx: &C, val: &mut ArgAbi<'a, Ty>) -> bool +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if val.layout.is_aggregate() { + if let Some(unit) = val.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()) { + let size = val.layout.size; + if unit.size == size { + val.cast_to(Uniform { unit, total: size }); + return true; + } + } + } + false +} + +fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + ret.extend_integer_width_to(32); + if ret.layout.is_aggregate() { + if !unwrap_trivial_aggregate(cx, ret) { + ret.make_indirect(); + } + } +} + +fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + arg.extend_integer_width_to(32); + if arg.layout.is_aggregate() { + if !unwrap_trivial_aggregate(cx, arg) { + arg.make_indirect_byval(); + } + } +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + if !fn_abi.ret.is_ignore() { + classify_ret(cx, &mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(cx, arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs b/compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs new file mode 100644 index 00000000000..59571fd9d48 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/wasm32_bindgen_compat.rs @@ -0,0 +1,29 @@ +// This is not and has never been a correct C ABI for WebAssembly, but +// for a long time this was the C ABI that Rust used. wasm-bindgen +// depends on ABI details for this ABI and is incompatible with the +// correct C ABI, so this ABI is being kept around until wasm-bindgen +// can be fixed to work with the correct ABI. See #63649 for further +// discussion. + +use crate::abi::call::{ArgAbi, FnAbi}; + +fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) { + ret.extend_integer_width_to(32); +} + +fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { + arg.extend_integer_width_to(32); +} + +pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { + if !fn_abi.ret.is_ignore() { + classify_ret(&mut fn_abi.ret); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + classify_arg(arg); + } +} diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs new file mode 100644 index 00000000000..df3dd5d9208 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -0,0 +1,130 @@ +use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind}; +use crate::abi::{self, HasDataLayout, LayoutOf, TyAndLayout, TyAndLayoutMethods}; +use crate::spec::HasTargetSpec; + +#[derive(PartialEq)] +pub enum Flavor { + General, + Fastcall, +} + +fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + match layout.abi { + abi::Abi::Scalar(ref scalar) => scalar.value.is_float(), + abi::Abi::Aggregate { .. } => { + if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { + is_single_fp_element(cx, layout.field(cx, 0)) + } else { + false + } + } + _ => false, + } +} + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout + HasTargetSpec, +{ + if !fn_abi.ret.is_ignore() { + if fn_abi.ret.layout.is_aggregate() { + // Returning a structure. Most often, this will use + // a hidden first argument. On some platforms, though, + // small structs are returned as integers. + // + // Some links: + // http://www.angelcode.com/dev/callconv/callconv.html + // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp + let t = cx.target_spec(); + if t.options.abi_return_struct_as_int { + // According to Clang, everyone but MSVC returns single-element + // float aggregates directly in a floating-point register. + if !t.options.is_like_msvc && is_single_fp_element(cx, fn_abi.ret.layout) { + match fn_abi.ret.layout.size.bytes() { + 4 => fn_abi.ret.cast_to(Reg::f32()), + 8 => fn_abi.ret.cast_to(Reg::f64()), + _ => fn_abi.ret.make_indirect(), + } + } else { + match fn_abi.ret.layout.size.bytes() { + 1 => fn_abi.ret.cast_to(Reg::i8()), + 2 => fn_abi.ret.cast_to(Reg::i16()), + 4 => fn_abi.ret.cast_to(Reg::i32()), + 8 => fn_abi.ret.cast_to(Reg::i64()), + _ => fn_abi.ret.make_indirect(), + } + } + } else { + fn_abi.ret.make_indirect(); + } + } else { + fn_abi.ret.extend_integer_width_to(32); + } + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + if arg.layout.is_aggregate() { + arg.make_indirect_byval(); + } else { + arg.extend_integer_width_to(32); + } + } + + if flavor == Flavor::Fastcall { + // Mark arguments as InReg like clang does it, + // so our fastcall is compatible with C/C++ fastcall. + + // Clang reference: lib/CodeGen/TargetInfo.cpp + // See X86_32ABIInfo::shouldPrimitiveUseInReg(), X86_32ABIInfo::updateFreeRegs() + + // IsSoftFloatABI is only set to true on ARM platforms, + // which in turn can't be x86? + + let mut free_regs = 2; + + for arg in &mut fn_abi.args { + let attrs = match arg.mode { + PassMode::Ignore | PassMode::Indirect(_, None) => continue, + PassMode::Direct(ref mut attrs) => attrs, + PassMode::Pair(..) | PassMode::Indirect(_, Some(_)) | PassMode::Cast(_) => { + unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode) + } + }; + + // At this point we know this must be a primitive of sorts. + let unit = arg.layout.homogeneous_aggregate(cx).unwrap().unit().unwrap(); + assert_eq!(unit.size, arg.layout.size); + if unit.kind == RegKind::Float { + continue; + } + + let size_in_regs = (arg.layout.size.bits() + 31) / 32; + + if size_in_regs == 0 { + continue; + } + + if size_in_regs > free_regs { + break; + } + + free_regs -= size_in_regs; + + if arg.layout.size.bits() <= 32 && unit.kind == RegKind::Integer { + attrs.set(ArgAttribute::InReg); + } + + if free_regs == 0 { + break; + } + } + } +} diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs new file mode 100644 index 00000000000..5f154dc1bc9 --- /dev/null +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -0,0 +1,248 @@ +// The classification code for the x86_64 ABI is taken from the clay language +// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp + +use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind}; +use crate::abi::{self, Abi, HasDataLayout, LayoutOf, Size, TyAndLayout, TyAndLayoutMethods}; + +/// Classification of "eightbyte" components. +// N.B., the order of the variants is from general to specific, +// such that `unify(a, b)` is the "smaller" of `a` and `b`. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +enum Class { + Int, + Sse, + SseUp, +} + +#[derive(Clone, Copy, Debug)] +struct Memory; + +// Currently supported vector size (AVX-512). +const LARGEST_VECTOR_SIZE: usize = 512; +const MAX_EIGHTBYTES: usize = LARGEST_VECTOR_SIZE / 64; + +fn classify_arg<'a, Ty, C>( + cx: &C, + arg: &ArgAbi<'a, Ty>, +) -> Result<[Option<Class>; MAX_EIGHTBYTES], Memory> +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + fn classify<'a, Ty, C>( + cx: &C, + layout: TyAndLayout<'a, Ty>, + cls: &mut [Option<Class>], + off: Size, + ) -> Result<(), Memory> + where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, + { + if !off.is_aligned(layout.align.abi) { + if !layout.is_zst() { + return Err(Memory); + } + return Ok(()); + } + + let mut c = match layout.abi { + Abi::Uninhabited => return Ok(()), + + Abi::Scalar(ref scalar) => match scalar.value { + abi::Int(..) | abi::Pointer => Class::Int, + abi::F32 | abi::F64 => Class::Sse, + }, + + Abi::Vector { .. } => Class::Sse, + + Abi::ScalarPair(..) | Abi::Aggregate { .. } => { + for i in 0..layout.fields.count() { + let field_off = off + layout.fields.offset(i); + classify(cx, layout.field(cx, i), cls, field_off)?; + } + + match &layout.variants { + abi::Variants::Single { .. } => {} + abi::Variants::Multiple { variants, .. } => { + // Treat enum variants like union members. + for variant_idx in variants.indices() { + classify(cx, layout.for_variant(cx, variant_idx), cls, off)?; + } + } + } + + return Ok(()); + } + }; + + // Fill in `cls` for scalars (Int/Sse) and vectors (Sse). + let first = (off.bytes() / 8) as usize; + let last = ((off.bytes() + layout.size.bytes() - 1) / 8) as usize; + for cls in &mut cls[first..=last] { + *cls = Some(cls.map_or(c, |old| old.min(c))); + + // Everything after the first Sse "eightbyte" + // component is the upper half of a register. + if c == Class::Sse { + c = Class::SseUp; + } + } + + Ok(()) + } + + let n = ((arg.layout.size.bytes() + 7) / 8) as usize; + if n > MAX_EIGHTBYTES { + return Err(Memory); + } + + let mut cls = [None; MAX_EIGHTBYTES]; + classify(cx, arg.layout, &mut cls, Size::ZERO)?; + if n > 2 { + if cls[0] != Some(Class::Sse) { + return Err(Memory); + } + if cls[1..n].iter().any(|&c| c != Some(Class::SseUp)) { + return Err(Memory); + } + } else { + let mut i = 0; + while i < n { + if cls[i] == Some(Class::SseUp) { + cls[i] = Some(Class::Sse); + } else if cls[i] == Some(Class::Sse) { + i += 1; + while i != n && cls[i] == Some(Class::SseUp) { + i += 1; + } + } else { + i += 1; + } + } + } + + Ok(cls) +} + +fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg> { + if *i >= cls.len() { + return None; + } + + match cls[*i] { + None => None, + Some(Class::Int) => { + *i += 1; + Some(if size.bytes() < 8 { Reg { kind: RegKind::Integer, size } } else { Reg::i64() }) + } + Some(Class::Sse) => { + let vec_len = + 1 + cls[*i + 1..].iter().take_while(|&&c| c == Some(Class::SseUp)).count(); + *i += vec_len; + Some(if vec_len == 1 { + match size.bytes() { + 4 => Reg::f32(), + _ => Reg::f64(), + } + } else { + Reg { kind: RegKind::Vector, size: Size::from_bytes(8) * (vec_len as u64) } + }) + } + Some(c) => unreachable!("reg_component: unhandled class {:?}", c), + } +} + +fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget { + let mut i = 0; + let lo = reg_component(cls, &mut i, size).unwrap(); + let offset = Size::from_bytes(8) * (i as u64); + let mut target = CastTarget::from(lo); + if size > offset { + if let Some(hi) = reg_component(cls, &mut i, size - offset) { + target = CastTarget::pair(lo, hi); + } + } + assert_eq!(reg_component(cls, &mut i, Size::ZERO), None); + target +} + +const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9 +const MAX_SSE_REGS: usize = 8; // XMM0-7 + +pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) +where + Ty: TyAndLayoutMethods<'a, C> + Copy, + C: LayoutOf<Ty = Ty, TyAndLayout = TyAndLayout<'a, Ty>> + HasDataLayout, +{ + let mut int_regs = MAX_INT_REGS; + let mut sse_regs = MAX_SSE_REGS; + + let mut x86_64_arg_or_ret = |arg: &mut ArgAbi<'a, Ty>, is_arg: bool| { + let mut cls_or_mem = classify_arg(cx, arg); + + if is_arg { + if let Ok(cls) = cls_or_mem { + let mut needed_int = 0; + let mut needed_sse = 0; + for &c in &cls { + match c { + Some(Class::Int) => needed_int += 1, + Some(Class::Sse) => needed_sse += 1, + _ => {} + } + } + match (int_regs.checked_sub(needed_int), sse_regs.checked_sub(needed_sse)) { + (Some(left_int), Some(left_sse)) => { + int_regs = left_int; + sse_regs = left_sse; + } + _ => { + // Not enough registers for this argument, so it will be + // passed on the stack, but we only mark aggregates + // explicitly as indirect `byval` arguments, as LLVM will + // automatically put immediates on the stack itself. + if arg.layout.is_aggregate() { + cls_or_mem = Err(Memory); + } + } + } + } + } + + match cls_or_mem { + Err(Memory) => { + if is_arg { + arg.make_indirect_byval(); + } else { + // `sret` parameter thus one less integer register available + arg.make_indirect(); + // NOTE(eddyb) return is handled first, so no registers + // should've been used yet. + assert_eq!(int_regs, MAX_INT_REGS); + int_regs -= 1; + } + } + Ok(ref cls) => { + // split into sized chunks passed individually + if arg.layout.is_aggregate() { + let size = arg.layout.size; + arg.cast_to(cast_target(cls, size)) + } else { + arg.extend_integer_width_to(32); + } + } + } + }; + + if !fn_abi.ret.is_ignore() { + x86_64_arg_or_ret(&mut fn_abi.ret, false); + } + + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + x86_64_arg_or_ret(arg, true); + } +} diff --git a/compiler/rustc_target/src/abi/call/x86_win64.rs b/compiler/rustc_target/src/abi/call/x86_win64.rs new file mode 100644 index 00000000000..2aad641b1ec --- /dev/null +++ b/compiler/rustc_target/src/abi/call/x86_win64.rs @@ -0,0 +1,40 @@ +use crate::abi::call::{ArgAbi, FnAbi, Reg}; +use crate::abi::Abi; + +// Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing + +pub fn compute_abi_info<Ty>(fn_abi: &mut FnAbi<'_, Ty>) { + let fixup = |a: &mut ArgAbi<'_, Ty>| { + match a.layout.abi { + Abi::Uninhabited => {} + Abi::ScalarPair(..) | Abi::Aggregate { .. } => match a.layout.size.bits() { + 8 => a.cast_to(Reg::i8()), + 16 => a.cast_to(Reg::i16()), + 32 => a.cast_to(Reg::i32()), + 64 => a.cast_to(Reg::i64()), + _ => a.make_indirect(), + }, + Abi::Vector { .. } => { + // FIXME(eddyb) there should be a size cap here + // (probably what clang calls "illegal vectors"). + } + Abi::Scalar(_) => { + if a.layout.size.bytes() > 8 { + a.make_indirect(); + } else { + a.extend_integer_width_to(32); + } + } + } + }; + + if !fn_abi.ret.is_ignore() { + fixup(&mut fn_abi.ret); + } + for arg in &mut fn_abi.args { + if arg.is_ignore() { + continue; + } + fixup(arg); + } +} diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs new file mode 100644 index 00000000000..4b565dd246f --- /dev/null +++ b/compiler/rustc_target/src/abi/mod.rs @@ -0,0 +1,1150 @@ +pub use Integer::*; +pub use Primitive::*; + +use crate::spec::Target; + +use std::convert::{TryFrom, TryInto}; +use std::num::NonZeroUsize; +use std::ops::{Add, AddAssign, Deref, Mul, Range, RangeInclusive, Sub}; + +use rustc_index::vec::{Idx, IndexVec}; +use rustc_macros::HashStable_Generic; +use rustc_span::Span; + +pub mod call; + +/// Parsed [Data layout](http://llvm.org/docs/LangRef.html#data-layout) +/// for a target, which contains everything needed to compute layouts. +pub struct TargetDataLayout { + pub endian: Endian, + pub i1_align: AbiAndPrefAlign, + pub i8_align: AbiAndPrefAlign, + pub i16_align: AbiAndPrefAlign, + pub i32_align: AbiAndPrefAlign, + pub i64_align: AbiAndPrefAlign, + pub i128_align: AbiAndPrefAlign, + pub f32_align: AbiAndPrefAlign, + pub f64_align: AbiAndPrefAlign, + pub pointer_size: Size, + pub pointer_align: AbiAndPrefAlign, + pub aggregate_align: AbiAndPrefAlign, + + /// Alignments for vector types. + pub vector_align: Vec<(Size, AbiAndPrefAlign)>, + + pub instruction_address_space: AddressSpace, +} + +impl Default for TargetDataLayout { + /// Creates an instance of `TargetDataLayout`. + fn default() -> TargetDataLayout { + let align = |bits| Align::from_bits(bits).unwrap(); + TargetDataLayout { + endian: Endian::Big, + i1_align: AbiAndPrefAlign::new(align(8)), + i8_align: AbiAndPrefAlign::new(align(8)), + i16_align: AbiAndPrefAlign::new(align(16)), + i32_align: AbiAndPrefAlign::new(align(32)), + i64_align: AbiAndPrefAlign { abi: align(32), pref: align(64) }, + i128_align: AbiAndPrefAlign { abi: align(32), pref: align(64) }, + f32_align: AbiAndPrefAlign::new(align(32)), + f64_align: AbiAndPrefAlign::new(align(64)), + pointer_size: Size::from_bits(64), + pointer_align: AbiAndPrefAlign::new(align(64)), + aggregate_align: AbiAndPrefAlign { abi: align(0), pref: align(64) }, + vector_align: vec![ + (Size::from_bits(64), AbiAndPrefAlign::new(align(64))), + (Size::from_bits(128), AbiAndPrefAlign::new(align(128))), + ], + instruction_address_space: AddressSpace::DATA, + } + } +} + +impl TargetDataLayout { + pub fn parse(target: &Target) -> Result<TargetDataLayout, String> { + // Parse an address space index from a string. + let parse_address_space = |s: &str, cause: &str| { + s.parse::<u32>().map(AddressSpace).map_err(|err| { + format!("invalid address space `{}` for `{}` in \"data-layout\": {}", s, cause, err) + }) + }; + + // Parse a bit count from a string. + let parse_bits = |s: &str, kind: &str, cause: &str| { + s.parse::<u64>().map_err(|err| { + format!("invalid {} `{}` for `{}` in \"data-layout\": {}", kind, s, cause, err) + }) + }; + + // Parse a size string. + let size = |s: &str, cause: &str| parse_bits(s, "size", cause).map(Size::from_bits); + + // Parse an alignment string. + let align = |s: &[&str], cause: &str| { + if s.is_empty() { + return Err(format!("missing alignment for `{}` in \"data-layout\"", cause)); + } + let align_from_bits = |bits| { + Align::from_bits(bits).map_err(|err| { + format!("invalid alignment for `{}` in \"data-layout\": {}", cause, err) + }) + }; + let abi = parse_bits(s[0], "alignment", cause)?; + let pref = s.get(1).map_or(Ok(abi), |pref| parse_bits(pref, "alignment", cause))?; + Ok(AbiAndPrefAlign { abi: align_from_bits(abi)?, pref: align_from_bits(pref)? }) + }; + + let mut dl = TargetDataLayout::default(); + let mut i128_align_src = 64; + for spec in target.data_layout.split('-') { + let spec_parts = spec.split(':').collect::<Vec<_>>(); + + match &*spec_parts { + ["e"] => dl.endian = Endian::Little, + ["E"] => dl.endian = Endian::Big, + [p] if p.starts_with('P') => { + dl.instruction_address_space = parse_address_space(&p[1..], "P")? + } + ["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?, + ["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?, + ["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?, + [p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => { + dl.pointer_size = size(s, p)?; + dl.pointer_align = align(a, p)?; + } + [s, ref a @ ..] if s.starts_with('i') => { + let bits = match s[1..].parse::<u64>() { + Ok(bits) => bits, + Err(_) => { + size(&s[1..], "i")?; // For the user error. + continue; + } + }; + let a = align(a, s)?; + match bits { + 1 => dl.i1_align = a, + 8 => dl.i8_align = a, + 16 => dl.i16_align = a, + 32 => dl.i32_align = a, + 64 => dl.i64_align = a, + _ => {} + } + if bits >= i128_align_src && bits <= 128 { + // Default alignment for i128 is decided by taking the alignment of + // largest-sized i{64..=128}. + i128_align_src = bits; + dl.i128_align = a; + } + } + [s, ref a @ ..] if s.starts_with('v') => { + let v_size = size(&s[1..], "v")?; + let a = align(a, s)?; + if let Some(v) = dl.vector_align.iter_mut().find(|v| v.0 == v_size) { + v.1 = a; + continue; + } + // No existing entry, add a new one. + dl.vector_align.push((v_size, a)); + } + _ => {} // Ignore everything else. + } + } + + // Perform consistency checks against the Target information. + let endian_str = match dl.endian { + Endian::Little => "little", + Endian::Big => "big", + }; + if endian_str != target.target_endian { + return Err(format!( + "inconsistent target specification: \"data-layout\" claims \ + architecture is {}-endian, while \"target-endian\" is `{}`", + endian_str, target.target_endian + )); + } + + if dl.pointer_size.bits().to_string() != target.target_pointer_width { + return Err(format!( + "inconsistent target specification: \"data-layout\" claims \ + pointers are {}-bit, while \"target-pointer-width\" is `{}`", + dl.pointer_size.bits(), + target.target_pointer_width + )); + } + + Ok(dl) + } + + /// Returns exclusive upper bound on object size. + /// + /// The theoretical maximum object size is defined as the maximum positive `isize` value. + /// This ensures that the `offset` semantics remain well-defined by allowing it to correctly + /// index every address within an object along with one byte past the end, along with allowing + /// `isize` to store the difference between any two pointers into an object. + /// + /// The upper bound on 64-bit currently needs to be lower because LLVM uses a 64-bit integer + /// to represent object size in bits. It would need to be 1 << 61 to account for this, but is + /// currently conservatively bounded to 1 << 47 as that is enough to cover the current usable + /// address space on 64-bit ARMv8 and x86_64. + pub fn obj_size_bound(&self) -> u64 { + match self.pointer_size.bits() { + 16 => 1 << 15, + 32 => 1 << 31, + 64 => 1 << 47, + bits => panic!("obj_size_bound: unknown pointer bit size {}", bits), + } + } + + pub fn ptr_sized_integer(&self) -> Integer { + match self.pointer_size.bits() { + 16 => I16, + 32 => I32, + 64 => I64, + bits => panic!("ptr_sized_integer: unknown pointer bit size {}", bits), + } + } + + pub fn vector_align(&self, vec_size: Size) -> AbiAndPrefAlign { + for &(size, align) in &self.vector_align { + if size == vec_size { + return align; + } + } + // Default to natural alignment, which is what LLVM does. + // That is, use the size, rounded up to a power of 2. + AbiAndPrefAlign::new(Align::from_bytes(vec_size.bytes().next_power_of_two()).unwrap()) + } +} + +pub trait HasDataLayout { + fn data_layout(&self) -> &TargetDataLayout; +} + +impl HasDataLayout for TargetDataLayout { + fn data_layout(&self) -> &TargetDataLayout { + self + } +} + +/// Endianness of the target, which must match cfg(target-endian). +#[derive(Copy, Clone, PartialEq)] +pub enum Endian { + Little, + Big, +} + +/// Size of a type in bytes. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub struct Size { + raw: u64, +} + +impl Size { + pub const ZERO: Size = Size { raw: 0 }; + + #[inline] + pub fn from_bits(bits: impl TryInto<u64>) -> Size { + let bits = bits.try_into().ok().unwrap(); + // Avoid potential overflow from `bits + 7`. + Size::from_bytes(bits / 8 + ((bits % 8) + 7) / 8) + } + + #[inline] + pub fn from_bytes(bytes: impl TryInto<u64>) -> Size { + Size { raw: bytes.try_into().ok().unwrap() } + } + + #[inline] + pub fn bytes(self) -> u64 { + self.raw + } + + #[inline] + pub fn bytes_usize(self) -> usize { + self.bytes().try_into().unwrap() + } + + #[inline] + pub fn bits(self) -> u64 { + self.bytes().checked_mul(8).unwrap_or_else(|| { + panic!("Size::bits: {} bytes in bits doesn't fit in u64", self.bytes()) + }) + } + + #[inline] + pub fn bits_usize(self) -> usize { + self.bits().try_into().unwrap() + } + + #[inline] + pub fn align_to(self, align: Align) -> Size { + let mask = align.bytes() - 1; + Size::from_bytes((self.bytes() + mask) & !mask) + } + + #[inline] + pub fn is_aligned(self, align: Align) -> bool { + let mask = align.bytes() - 1; + self.bytes() & mask == 0 + } + + #[inline] + pub fn checked_add<C: HasDataLayout>(self, offset: Size, cx: &C) -> Option<Size> { + let dl = cx.data_layout(); + + let bytes = self.bytes().checked_add(offset.bytes())?; + + if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None } + } + + #[inline] + pub fn checked_mul<C: HasDataLayout>(self, count: u64, cx: &C) -> Option<Size> { + let dl = cx.data_layout(); + + let bytes = self.bytes().checked_mul(count)?; + if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None } + } +} + +// Panicking addition, subtraction and multiplication for convenience. +// Avoid during layout computation, return `LayoutError` instead. + +impl Add for Size { + type Output = Size; + #[inline] + fn add(self, other: Size) -> Size { + Size::from_bytes(self.bytes().checked_add(other.bytes()).unwrap_or_else(|| { + panic!("Size::add: {} + {} doesn't fit in u64", self.bytes(), other.bytes()) + })) + } +} + +impl Sub for Size { + type Output = Size; + #[inline] + fn sub(self, other: Size) -> Size { + Size::from_bytes(self.bytes().checked_sub(other.bytes()).unwrap_or_else(|| { + panic!("Size::sub: {} - {} would result in negative size", self.bytes(), other.bytes()) + })) + } +} + +impl Mul<Size> for u64 { + type Output = Size; + #[inline] + fn mul(self, size: Size) -> Size { + size * self + } +} + +impl Mul<u64> for Size { + type Output = Size; + #[inline] + fn mul(self, count: u64) -> Size { + match self.bytes().checked_mul(count) { + Some(bytes) => Size::from_bytes(bytes), + None => panic!("Size::mul: {} * {} doesn't fit in u64", self.bytes(), count), + } + } +} + +impl AddAssign for Size { + #[inline] + fn add_assign(&mut self, other: Size) { + *self = *self + other; + } +} + +/// Alignment of a type in bytes (always a power of two). +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub struct Align { + pow2: u8, +} + +impl Align { + pub fn from_bits(bits: u64) -> Result<Align, String> { + Align::from_bytes(Size::from_bits(bits).bytes()) + } + + pub fn from_bytes(align: u64) -> Result<Align, String> { + // Treat an alignment of 0 bytes like 1-byte alignment. + if align == 0 { + return Ok(Align { pow2: 0 }); + } + + let mut bytes = align; + let mut pow2: u8 = 0; + while (bytes & 1) == 0 { + pow2 += 1; + bytes >>= 1; + } + if bytes != 1 { + return Err(format!("`{}` is not a power of 2", align)); + } + if pow2 > 29 { + return Err(format!("`{}` is too large", align)); + } + + Ok(Align { pow2 }) + } + + pub fn bytes(self) -> u64 { + 1 << self.pow2 + } + + pub fn bits(self) -> u64 { + self.bytes() * 8 + } + + /// Computes the best alignment possible for the given offset + /// (the largest power of two that the offset is a multiple of). + /// + /// N.B., for an offset of `0`, this happens to return `2^64`. + pub fn max_for_offset(offset: Size) -> Align { + Align { pow2: offset.bytes().trailing_zeros() as u8 } + } + + /// Lower the alignment, if necessary, such that the given offset + /// is aligned to it (the offset is a multiple of the alignment). + pub fn restrict_for_offset(self, offset: Size) -> Align { + self.min(Align::max_for_offset(offset)) + } +} + +/// A pair of alignments, ABI-mandated and preferred. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Encodable, Decodable)] +#[derive(HashStable_Generic)] +pub struct AbiAndPrefAlign { + pub abi: Align, + pub pref: Align, +} + +impl AbiAndPrefAlign { + pub fn new(align: Align) -> AbiAndPrefAlign { + AbiAndPrefAlign { abi: align, pref: align } + } + + pub fn min(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign { + AbiAndPrefAlign { abi: self.abi.min(other.abi), pref: self.pref.min(other.pref) } + } + + pub fn max(self, other: AbiAndPrefAlign) -> AbiAndPrefAlign { + AbiAndPrefAlign { abi: self.abi.max(other.abi), pref: self.pref.max(other.pref) } + } +} + +/// Integers, also used for enum discriminants. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, HashStable_Generic)] +pub enum Integer { + I8, + I16, + I32, + I64, + I128, +} + +impl Integer { + pub fn size(self) -> Size { + match self { + I8 => Size::from_bytes(1), + I16 => Size::from_bytes(2), + I32 => Size::from_bytes(4), + I64 => Size::from_bytes(8), + I128 => Size::from_bytes(16), + } + } + + pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAndPrefAlign { + let dl = cx.data_layout(); + + match self { + I8 => dl.i8_align, + I16 => dl.i16_align, + I32 => dl.i32_align, + I64 => dl.i64_align, + I128 => dl.i128_align, + } + } + + /// Finds the smallest Integer type which can represent the signed value. + pub fn fit_signed(x: i128) -> Integer { + match x { + -0x0000_0000_0000_0080..=0x0000_0000_0000_007f => I8, + -0x0000_0000_0000_8000..=0x0000_0000_0000_7fff => I16, + -0x0000_0000_8000_0000..=0x0000_0000_7fff_ffff => I32, + -0x8000_0000_0000_0000..=0x7fff_ffff_ffff_ffff => I64, + _ => I128, + } + } + + /// Finds the smallest Integer type which can represent the unsigned value. + pub fn fit_unsigned(x: u128) -> Integer { + match x { + 0..=0x0000_0000_0000_00ff => I8, + 0..=0x0000_0000_0000_ffff => I16, + 0..=0x0000_0000_ffff_ffff => I32, + 0..=0xffff_ffff_ffff_ffff => I64, + _ => I128, + } + } + + /// Finds the smallest integer with the given alignment. + pub fn for_align<C: HasDataLayout>(cx: &C, wanted: Align) -> Option<Integer> { + let dl = cx.data_layout(); + + for &candidate in &[I8, I16, I32, I64, I128] { + if wanted == candidate.align(dl).abi && wanted.bytes() == candidate.size().bytes() { + return Some(candidate); + } + } + None + } + + /// Find the largest integer with the given alignment or less. + pub fn approximate_align<C: HasDataLayout>(cx: &C, wanted: Align) -> Integer { + let dl = cx.data_layout(); + + // FIXME(eddyb) maybe include I128 in the future, when it works everywhere. + for &candidate in &[I64, I32, I16] { + if wanted >= candidate.align(dl).abi && wanted.bytes() >= candidate.size().bytes() { + return candidate; + } + } + I8 + } +} + +/// Fundamental unit of memory access and layout. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum Primitive { + /// The `bool` is the signedness of the `Integer` type. + /// + /// One would think we would not care about such details this low down, + /// but some ABIs are described in terms of C types and ISAs where the + /// integer arithmetic is done on {sign,zero}-extended registers, e.g. + /// a negative integer passed by zero-extension will appear positive in + /// the callee, and most operations on it will produce the wrong values. + Int(Integer, bool), + F32, + F64, + Pointer, +} + +impl Primitive { + pub fn size<C: HasDataLayout>(self, cx: &C) -> Size { + let dl = cx.data_layout(); + + match self { + Int(i, _) => i.size(), + F32 => Size::from_bits(32), + F64 => Size::from_bits(64), + Pointer => dl.pointer_size, + } + } + + pub fn align<C: HasDataLayout>(self, cx: &C) -> AbiAndPrefAlign { + let dl = cx.data_layout(); + + match self { + Int(i, _) => i.align(dl), + F32 => dl.f32_align, + F64 => dl.f64_align, + Pointer => dl.pointer_align, + } + } + + pub fn is_float(self) -> bool { + match self { + F32 | F64 => true, + _ => false, + } + } + + pub fn is_int(self) -> bool { + match self { + Int(..) => true, + _ => false, + } + } +} + +/// Information about one scalar component of a Rust type. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(HashStable_Generic)] +pub struct Scalar { + pub value: Primitive, + + /// Inclusive wrap-around range of valid values, that is, if + /// start > end, it represents `start..=MAX`, + /// followed by `0..=end`. + /// + /// That is, for an i8 primitive, a range of `254..=2` means following + /// sequence: + /// + /// 254 (-2), 255 (-1), 0, 1, 2 + /// + /// This is intended specifically to mirror LLVM’s `!range` metadata, + /// semantics. + // FIXME(eddyb) always use the shortest range, e.g., by finding + // the largest space between two consecutive valid values and + // taking everything else as the (shortest) valid range. + pub valid_range: RangeInclusive<u128>, +} + +impl Scalar { + pub fn is_bool(&self) -> bool { + if let Int(I8, _) = self.value { self.valid_range == (0..=1) } else { false } + } + + /// Returns the valid range as a `x..y` range. + /// + /// If `x` and `y` are equal, the range is full, not empty. + pub fn valid_range_exclusive<C: HasDataLayout>(&self, cx: &C) -> Range<u128> { + // For a (max) value of -1, max will be `-1 as usize`, which overflows. + // However, that is fine here (it would still represent the full range), + // i.e., if the range is everything. + let bits = self.value.size(cx).bits(); + assert!(bits <= 128); + let mask = !0u128 >> (128 - bits); + let start = *self.valid_range.start(); + let end = *self.valid_range.end(); + assert_eq!(start, start & mask); + assert_eq!(end, end & mask); + start..(end.wrapping_add(1) & mask) + } +} + +/// Describes how the fields of a type are located in memory. +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum FieldsShape { + /// Scalar primitives and `!`, which never have fields. + Primitive, + + /// All fields start at no offset. The `usize` is the field count. + Union(NonZeroUsize), + + /// Array/vector-like placement, with all fields of identical types. + Array { stride: Size, count: u64 }, + + /// Struct-like placement, with precomputed offsets. + /// + /// Fields are guaranteed to not overlap, but note that gaps + /// before, between and after all the fields are NOT always + /// padding, and as such their contents may not be discarded. + /// For example, enum variants leave a gap at the start, + /// where the discriminant field in the enum layout goes. + Arbitrary { + /// Offsets for the first byte of each field, + /// ordered to match the source definition order. + /// This vector does not go in increasing order. + // FIXME(eddyb) use small vector optimization for the common case. + offsets: Vec<Size>, + + /// Maps source order field indices to memory order indices, + /// depending on how the fields were reordered (if at all). + /// This is a permutation, with both the source order and the + /// memory order using the same (0..n) index ranges. + /// + /// Note that during computation of `memory_index`, sometimes + /// it is easier to operate on the inverse mapping (that is, + /// from memory order to source order), and that is usually + /// named `inverse_memory_index`. + /// + // FIXME(eddyb) build a better abstraction for permutations, if possible. + // FIXME(camlorn) also consider small vector optimization here. + memory_index: Vec<u32>, + }, +} + +impl FieldsShape { + pub fn count(&self) -> usize { + match *self { + FieldsShape::Primitive => 0, + FieldsShape::Union(count) => count.get(), + FieldsShape::Array { count, .. } => { + let usize_count = count as usize; + assert_eq!(usize_count as u64, count); + usize_count + } + FieldsShape::Arbitrary { ref offsets, .. } => offsets.len(), + } + } + + pub fn offset(&self, i: usize) -> Size { + match *self { + FieldsShape::Primitive => { + unreachable!("FieldsShape::offset: `Primitive`s have no fields") + } + FieldsShape::Union(count) => { + assert!( + i < count.get(), + "tried to access field {} of union with {} fields", + i, + count + ); + Size::ZERO + } + FieldsShape::Array { stride, count } => { + let i = u64::try_from(i).unwrap(); + assert!(i < count); + stride * i + } + FieldsShape::Arbitrary { ref offsets, .. } => offsets[i], + } + } + + pub fn memory_index(&self, i: usize) -> usize { + match *self { + FieldsShape::Primitive => { + unreachable!("FieldsShape::memory_index: `Primitive`s have no fields") + } + FieldsShape::Union(_) | FieldsShape::Array { .. } => i, + FieldsShape::Arbitrary { ref memory_index, .. } => { + let r = memory_index[i]; + assert_eq!(r as usize as u32, r); + r as usize + } + } + } + + /// Gets source indices of the fields by increasing offsets. + #[inline] + pub fn index_by_increasing_offset<'a>(&'a self) -> impl Iterator<Item = usize> + 'a { + let mut inverse_small = [0u8; 64]; + let mut inverse_big = vec![]; + let use_small = self.count() <= inverse_small.len(); + + // We have to write this logic twice in order to keep the array small. + if let FieldsShape::Arbitrary { ref memory_index, .. } = *self { + if use_small { + for i in 0..self.count() { + inverse_small[memory_index[i] as usize] = i as u8; + } + } else { + inverse_big = vec![0; self.count()]; + for i in 0..self.count() { + inverse_big[memory_index[i] as usize] = i as u32; + } + } + } + + (0..self.count()).map(move |i| match *self { + FieldsShape::Primitive | FieldsShape::Union(_) | FieldsShape::Array { .. } => i, + FieldsShape::Arbitrary { .. } => { + if use_small { + inverse_small[i] as usize + } else { + inverse_big[i] as usize + } + } + }) + } +} + +/// An identifier that specifies the address space that some operation +/// should operate on. Special address spaces have an effect on code generation, +/// depending on the target and the address spaces it implements. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct AddressSpace(pub u32); + +impl AddressSpace { + /// The default address space, corresponding to data space. + pub const DATA: Self = AddressSpace(0); +} + +/// Describes how values of the type are passed by target ABIs, +/// in terms of categories of C types there are ABI rules for. +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum Abi { + Uninhabited, + Scalar(Scalar), + ScalarPair(Scalar, Scalar), + Vector { + element: Scalar, + count: u64, + }, + Aggregate { + /// If true, the size is exact, otherwise it's only a lower bound. + sized: bool, + }, +} + +impl Abi { + /// Returns `true` if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + match *self { + Abi::Uninhabited | Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, + Abi::Aggregate { sized } => !sized, + } + } + + /// Returns `true` if this is a single signed integer scalar + pub fn is_signed(&self) -> bool { + match *self { + Abi::Scalar(ref scal) => match scal.value { + Primitive::Int(_, signed) => signed, + _ => false, + }, + _ => panic!("`is_signed` on non-scalar ABI {:?}", self), + } + } + + /// Returns `true` if this is an uninhabited type + pub fn is_uninhabited(&self) -> bool { + match *self { + Abi::Uninhabited => true, + _ => false, + } + } + + /// Returns `true` is this is a scalar type + pub fn is_scalar(&self) -> bool { + match *self { + Abi::Scalar(_) => true, + _ => false, + } + } +} + +rustc_index::newtype_index! { + pub struct VariantIdx { + derive [HashStable_Generic] + } +} + +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum Variants { + /// Single enum variants, structs/tuples, unions, and all non-ADTs. + Single { index: VariantIdx }, + + /// Enum-likes with more than one inhabited variant: each variant comes with + /// a *discriminant* (usually the same as the variant index but the user can + /// assign explicit discriminant values). That discriminant is encoded + /// as a *tag* on the machine. The layout of each variant is + /// a struct, and they all have space reserved for the tag. + /// For enums, the tag is the sole field of the layout. + Multiple { + tag: Scalar, + tag_encoding: TagEncoding, + tag_field: usize, + variants: IndexVec<VariantIdx, Layout>, + }, +} + +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub enum TagEncoding { + /// The tag directly stores the discriminant, but possibly with a smaller layout + /// (so converting the tag to the discriminant can require sign extension). + Direct, + + /// Niche (values invalid for a type) encoding the discriminant: + /// Discriminant and variant index coincide. + /// The variant `dataful_variant` contains a niche at an arbitrary + /// offset (field `tag_field` of the enum), which for a variant with + /// discriminant `d` is set to + /// `(d - niche_variants.start).wrapping_add(niche_start)`. + /// + /// For example, `Option<(usize, &T)>` is represented such that + /// `None` has a null pointer for the second tuple field, and + /// `Some` is the identity function (with a non-null reference). + Niche { + dataful_variant: VariantIdx, + niche_variants: RangeInclusive<VariantIdx>, + niche_start: u128, + }, +} + +#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub struct Niche { + pub offset: Size, + pub scalar: Scalar, +} + +impl Niche { + pub fn from_scalar<C: HasDataLayout>(cx: &C, offset: Size, scalar: Scalar) -> Option<Self> { + let niche = Niche { offset, scalar }; + if niche.available(cx) > 0 { Some(niche) } else { None } + } + + pub fn available<C: HasDataLayout>(&self, cx: &C) -> u128 { + let Scalar { value, valid_range: ref v } = self.scalar; + let bits = value.size(cx).bits(); + assert!(bits <= 128); + let max_value = !0u128 >> (128 - bits); + + // Find out how many values are outside the valid range. + let niche = v.end().wrapping_add(1)..*v.start(); + niche.end.wrapping_sub(niche.start) & max_value + } + + pub fn reserve<C: HasDataLayout>(&self, cx: &C, count: u128) -> Option<(u128, Scalar)> { + assert!(count > 0); + + let Scalar { value, valid_range: ref v } = self.scalar; + let bits = value.size(cx).bits(); + assert!(bits <= 128); + let max_value = !0u128 >> (128 - bits); + + if count > max_value { + return None; + } + + // Compute the range of invalid values being reserved. + let start = v.end().wrapping_add(1) & max_value; + let end = v.end().wrapping_add(count) & max_value; + + // If the `end` of our range is inside the valid range, + // then we ran out of invalid values. + // FIXME(eddyb) abstract this with a wraparound range type. + let valid_range_contains = |x| { + if v.start() <= v.end() { + *v.start() <= x && x <= *v.end() + } else { + *v.start() <= x || x <= *v.end() + } + }; + if valid_range_contains(end) { + return None; + } + + Some((start, Scalar { value, valid_range: *v.start()..=end })) + } +} + +#[derive(PartialEq, Eq, Hash, Debug, HashStable_Generic)] +pub struct Layout { + /// Says where the fields are located within the layout. + pub fields: FieldsShape, + + /// Encodes information about multi-variant layouts. + /// Even with `Multiple` variants, a layout still has its own fields! Those are then + /// shared between all variants. One of them will be the discriminant, + /// but e.g. generators can have more. + /// + /// To access all fields of this layout, both `fields` and the fields of the active variant + /// must be taken into account. + pub variants: Variants, + + /// The `abi` defines how this data is passed between functions, and it defines + /// value restrictions via `valid_range`. + /// + /// Note that this is entirely orthogonal to the recursive structure defined by + /// `variants` and `fields`; for example, `ManuallyDrop<Result<isize, isize>>` has + /// `Abi::ScalarPair`! So, even with non-`Aggregate` `abi`, `fields` and `variants` + /// have to be taken into account to find all fields of this layout. + pub abi: Abi, + + /// The leaf scalar with the largest number of invalid values + /// (i.e. outside of its `valid_range`), if it exists. + pub largest_niche: Option<Niche>, + + pub align: AbiAndPrefAlign, + pub size: Size, +} + +impl Layout { + pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self { + let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar.clone()); + let size = scalar.value.size(cx); + let align = scalar.value.align(cx); + Layout { + variants: Variants::Single { index: VariantIdx::new(0) }, + fields: FieldsShape::Primitive, + abi: Abi::Scalar(scalar), + largest_niche, + size, + align, + } + } +} + +/// The layout of a type, alongside the type itself. +/// Provides various type traversal APIs (e.g., recursing into fields). +/// +/// Note that the layout is NOT guaranteed to always be identical +/// to that obtained from `layout_of(ty)`, as we need to produce +/// layouts for which Rust types do not exist, such as enum variants +/// or synthetic fields of enums (i.e., discriminants) and fat pointers. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct TyAndLayout<'a, Ty> { + pub ty: Ty, + pub layout: &'a Layout, +} + +impl<'a, Ty> Deref for TyAndLayout<'a, Ty> { + type Target = &'a Layout; + fn deref(&self) -> &&'a Layout { + &self.layout + } +} + +/// Trait for context types that can compute layouts of things. +pub trait LayoutOf { + type Ty; + type TyAndLayout; + + fn layout_of(&self, ty: Self::Ty) -> Self::TyAndLayout; + fn spanned_layout_of(&self, ty: Self::Ty, _span: Span) -> Self::TyAndLayout { + self.layout_of(ty) + } +} + +/// The `TyAndLayout` above will always be a `MaybeResult<TyAndLayout<'_, Self>>`. +/// We can't add the bound due to the lifetime, but this trait is still useful when +/// writing code that's generic over the `LayoutOf` impl. +pub trait MaybeResult<T> { + type Error; + + fn from(x: Result<T, Self::Error>) -> Self; + fn to_result(self) -> Result<T, Self::Error>; +} + +impl<T> MaybeResult<T> for T { + type Error = !; + + fn from(Ok(x): Result<T, Self::Error>) -> Self { + x + } + fn to_result(self) -> Result<T, Self::Error> { + Ok(self) + } +} + +impl<T, E> MaybeResult<T> for Result<T, E> { + type Error = E; + + fn from(x: Result<T, Self::Error>) -> Self { + x + } + fn to_result(self) -> Result<T, Self::Error> { + self + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum PointerKind { + /// Most general case, we know no restrictions to tell LLVM. + Shared, + + /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + Frozen, + + /// `&mut T`, when we know `noalias` is safe for LLVM. + UniqueBorrowed, + + /// `Box<T>`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + UniqueOwned, +} + +#[derive(Copy, Clone, Debug)] +pub struct PointeeInfo { + pub size: Size, + pub align: Align, + pub safe: Option<PointerKind>, + pub address_space: AddressSpace, +} + +pub trait TyAndLayoutMethods<'a, C: LayoutOf<Ty = Self>>: Sized { + fn for_variant( + this: TyAndLayout<'a, Self>, + cx: &C, + variant_index: VariantIdx, + ) -> TyAndLayout<'a, Self>; + fn field(this: TyAndLayout<'a, Self>, cx: &C, i: usize) -> C::TyAndLayout; + fn pointee_info_at(this: TyAndLayout<'a, Self>, cx: &C, offset: Size) -> Option<PointeeInfo>; +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + pub fn for_variant<C>(self, cx: &C, variant_index: VariantIdx) -> Self + where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty>, + { + Ty::for_variant(self, cx, variant_index) + } + + /// Callers might want to use `C: LayoutOf<Ty=Ty, TyAndLayout: MaybeResult<Self>>` + /// to allow recursion (see `might_permit_zero_init` below for an example). + pub fn field<C>(self, cx: &C, i: usize) -> C::TyAndLayout + where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty>, + { + Ty::field(self, cx, i) + } + + pub fn pointee_info_at<C>(self, cx: &C, offset: Size) -> Option<PointeeInfo> + where + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty>, + { + Ty::pointee_info_at(self, cx, offset) + } +} + +impl<'a, Ty> TyAndLayout<'a, Ty> { + /// Returns `true` if the layout corresponds to an unsized type. + pub fn is_unsized(&self) -> bool { + self.abi.is_unsized() + } + + /// Returns `true` if the type is a ZST and not unsized. + pub fn is_zst(&self) -> bool { + match self.abi { + Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } => false, + Abi::Uninhabited => self.size.bytes() == 0, + Abi::Aggregate { sized } => sized && self.size.bytes() == 0, + } + } + + /// Determines if this type permits "raw" initialization by just transmuting some + /// memory into an instance of `T`. + /// `zero` indicates if the memory is zero-initialized, or alternatively + /// left entirely uninitialized. + /// This is conservative: in doubt, it will answer `true`. + /// + /// FIXME: Once we removed all the conservatism, we could alternatively + /// create an all-0/all-undef constant and run the const value validator to see if + /// this is a valid value for the given type. + pub fn might_permit_raw_init<C, E>(self, cx: &C, zero: bool) -> Result<bool, E> + where + Self: Copy, + Ty: TyAndLayoutMethods<'a, C>, + C: LayoutOf<Ty = Ty, TyAndLayout: MaybeResult<Self, Error = E>> + HasDataLayout, + { + let scalar_allows_raw_init = move |s: &Scalar| -> bool { + if zero { + let range = &s.valid_range; + // The range must contain 0. + range.contains(&0) || (*range.start() > *range.end()) // wrap-around allows 0 + } else { + // The range must include all values. `valid_range_exclusive` handles + // the wrap-around using target arithmetic; with wrap-around then the full + // range is one where `start == end`. + let range = s.valid_range_exclusive(cx); + range.start == range.end + } + }; + + // Check the ABI. + let valid = match &self.abi { + Abi::Uninhabited => false, // definitely UB + Abi::Scalar(s) => scalar_allows_raw_init(s), + Abi::ScalarPair(s1, s2) => scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2), + Abi::Vector { element: s, count } => *count == 0 || scalar_allows_raw_init(s), + Abi::Aggregate { .. } => true, // Cannot be excluded *right now*. + }; + if !valid { + // This is definitely not okay. + trace!("might_permit_raw_init({:?}, zero={}): not valid", self.layout, zero); + return Ok(false); + } + + // If we have not found an error yet, we need to recursively descend. + // FIXME(#66151): For now, we are conservative and do not do this. + Ok(true) + } +} diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs new file mode 100644 index 00000000000..e7c9edea765 --- /dev/null +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -0,0 +1,156 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + AArch64 AArch64InlineAsmRegClass { + reg, + vreg, + vreg_low16, + } +} + +impl AArch64InlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::reg => &['w', 'x'], + Self::vreg | Self::vreg_low16 => &['b', 'h', 's', 'd', 'q', 'v'], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::reg => match ty.size().bits() { + 64 => None, + _ => Some(('w', "w0")), + }, + Self::vreg | Self::vreg_low16 => match ty.size().bits() { + 8 => Some(('b', "b0")), + 16 => Some(('h', "h0")), + 32 => Some(('s', "s0")), + 64 => Some(('d', "d0")), + 128 => Some(('q', "q0")), + _ => None, + }, + } + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::reg => Some(('x', "x0")), + Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + } + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg => types! { _: I8, I16, I32, I64, F32, F64; }, + Self::vreg | Self::vreg_low16 => types! { + "fp": I8, I16, I32, I64, F32, F64, + VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2), VecF64(1), + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + }, + } + } +} + +def_regs! { + AArch64 AArch64InlineAsmReg AArch64InlineAsmRegClass { + x0: reg = ["x0", "w0"], + x1: reg = ["x1", "w1"], + x2: reg = ["x2", "w2"], + x3: reg = ["x3", "w3"], + x4: reg = ["x4", "w4"], + x5: reg = ["x5", "w5"], + x6: reg = ["x6", "w6"], + x7: reg = ["x7", "w7"], + x8: reg = ["x8", "w8"], + x9: reg = ["x9", "w9"], + x10: reg = ["x10", "w10"], + x11: reg = ["x11", "w11"], + x12: reg = ["x12", "w12"], + x13: reg = ["x13", "w13"], + x14: reg = ["x14", "w14"], + x15: reg = ["x15", "w15"], + x16: reg = ["x16", "w16"], + x17: reg = ["x17", "w17"], + x18: reg = ["x18", "w18"], + x19: reg = ["x19", "w19"], + x20: reg = ["x20", "w20"], + x21: reg = ["x21", "w21"], + x22: reg = ["x22", "w22"], + x23: reg = ["x23", "w23"], + x24: reg = ["x24", "w24"], + x25: reg = ["x25", "w25"], + x26: reg = ["x26", "w26"], + x27: reg = ["x27", "w27"], + x28: reg = ["x28", "w28"], + x30: reg = ["x30", "w30", "lr"], + v0: vreg, vreg_low16 = ["v0", "b0", "h0", "s0", "d0", "q0"], + v1: vreg, vreg_low16 = ["v1", "b1", "h1", "s1", "d1", "q1"], + v2: vreg, vreg_low16 = ["v2", "b2", "h2", "s2", "d2", "q2"], + v3: vreg, vreg_low16 = ["v3", "b3", "h3", "s3", "d3", "q3"], + v4: vreg, vreg_low16 = ["v4", "b4", "h4", "s4", "d4", "q4"], + v5: vreg, vreg_low16 = ["v5", "b5", "h5", "s5", "d5", "q5"], + v6: vreg, vreg_low16 = ["v6", "b6", "h6", "s6", "d6", "q6"], + v7: vreg, vreg_low16 = ["v7", "b7", "h7", "s7", "d7", "q7"], + v8: vreg, vreg_low16 = ["v8", "b8", "h8", "s8", "d8", "q8"], + v9: vreg, vreg_low16 = ["v9", "b9", "h9", "s9", "d9", "q9"], + v10: vreg, vreg_low16 = ["v10", "b10", "h10", "s10", "d10", "q10"], + v11: vreg, vreg_low16 = ["v11", "b11", "h11", "s11", "d11", "q11"], + v12: vreg, vreg_low16 = ["v12", "b12", "h12", "s12", "d12", "q12"], + v13: vreg, vreg_low16 = ["v13", "b13", "h13", "s13", "d13", "q13"], + v14: vreg, vreg_low16 = ["v14", "b14", "h14", "s14", "d14", "q14"], + v15: vreg, vreg_low16 = ["v15", "b15", "h15", "s15", "d15", "q15"], + v16: vreg = ["v16", "b16", "h16", "s16", "d16", "q16"], + v17: vreg = ["v17", "b17", "h17", "s17", "d17", "q17"], + v18: vreg = ["v18", "b18", "h18", "s18", "d18", "q18"], + v19: vreg = ["v19", "b19", "h19", "s19", "d19", "q19"], + v20: vreg = ["v20", "b20", "h20", "s20", "d20", "q20"], + v21: vreg = ["v21", "b21", "h21", "s21", "d21", "q21"], + v22: vreg = ["v22", "b22", "h22", "s22", "d22", "q22"], + v23: vreg = ["v23", "b23", "h23", "s23", "d23", "q23"], + v24: vreg = ["v24", "b24", "h24", "s24", "d24", "q24"], + v25: vreg = ["v25", "b25", "h25", "s25", "d25", "q25"], + v26: vreg = ["v26", "b26", "h26", "s26", "d26", "q26"], + v27: vreg = ["v27", "b27", "h27", "s27", "d27", "q27"], + v28: vreg = ["v28", "b28", "h28", "s28", "d28", "q28"], + v29: vreg = ["v29", "b29", "h29", "s29", "d29", "q29"], + v30: vreg = ["v30", "b30", "h30", "s30", "d30", "q30"], + v31: vreg = ["v31", "b31", "h31", "s31", "d31", "q31"], + #error = ["x29", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["sp", "wsp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["xzr", "wzr"] => + "the zero register cannot be used as an operand for inline asm", + } +} + +impl AArch64InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + let (prefix, index) = if (self as u32) < Self::v0 as u32 { + (modifier.unwrap_or('x'), self as u32 - Self::x0 as u32) + } else { + (modifier.unwrap_or('v'), self as u32 - Self::v0 as u32) + }; + assert!(index < 32); + write!(out, "{}{}", prefix, index) + } +} diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs new file mode 100644 index 00000000000..85a136b94aa --- /dev/null +++ b/compiler/rustc_target/src/asm/arm.rs @@ -0,0 +1,298 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::Target; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + Arm ArmInlineAsmRegClass { + reg, + reg_thumb, + sreg, + sreg_low16, + dreg, + dreg_low16, + dreg_low8, + qreg, + qreg_low8, + qreg_low4, + } +} + +impl ArmInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::qreg | Self::qreg_low8 | Self::qreg_low4 => &['e', 'f'], + _ => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg | Self::reg_thumb => types! { _: I8, I16, I32, F32; }, + Self::sreg | Self::sreg_low16 => types! { "vfp2": I32, F32; }, + Self::dreg | Self::dreg_low16 | Self::dreg_low8 => types! { + "vfp2": I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2); + }, + Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! { + "neon": VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4); + }, + } + } +} + +// This uses the same logic as useR7AsFramePointer in LLVM +fn frame_pointer_is_r7(mut has_feature: impl FnMut(&str) -> bool, target: &Target) -> bool { + target.options.is_like_osx || (!target.options.is_like_windows && has_feature("thumb-mode")) +} + +fn frame_pointer_r11( + _arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &Target, + _allocating: bool, +) -> Result<(), &'static str> { + if !frame_pointer_is_r7(has_feature, target) { + Err("the frame pointer (r11) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +fn frame_pointer_r7( + _arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &Target, + _allocating: bool, +) -> Result<(), &'static str> { + if frame_pointer_is_r7(has_feature, target) { + Err("the frame pointer (r7) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +def_regs! { + Arm ArmInlineAsmReg ArmInlineAsmRegClass { + r0: reg, reg_thumb = ["r0", "a1"], + r1: reg, reg_thumb = ["r1", "a2"], + r2: reg, reg_thumb = ["r2", "a3"], + r3: reg, reg_thumb = ["r3", "a4"], + r4: reg, reg_thumb = ["r4", "v1"], + r5: reg, reg_thumb = ["r5", "v2"], + r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7, + r8: reg = ["r8", "v5"], + r9: reg = ["r9", "v6", "rfp"], + r10: reg = ["r10", "sl"], + r11: reg = ["r11", "fp"] % frame_pointer_r11, + r12: reg = ["r12", "ip"], + r14: reg = ["r14", "lr"], + s0: sreg, sreg_low16 = ["s0"], + s1: sreg, sreg_low16 = ["s1"], + s2: sreg, sreg_low16 = ["s2"], + s3: sreg, sreg_low16 = ["s3"], + s4: sreg, sreg_low16 = ["s4"], + s5: sreg, sreg_low16 = ["s5"], + s6: sreg, sreg_low16 = ["s6"], + s7: sreg, sreg_low16 = ["s7"], + s8: sreg, sreg_low16 = ["s8"], + s9: sreg, sreg_low16 = ["s9"], + s10: sreg, sreg_low16 = ["s10"], + s11: sreg, sreg_low16 = ["s11"], + s12: sreg, sreg_low16 = ["s12"], + s13: sreg, sreg_low16 = ["s13"], + s14: sreg, sreg_low16 = ["s14"], + s15: sreg, sreg_low16 = ["s15"], + s16: sreg = ["s16"], + s17: sreg = ["s17"], + s18: sreg = ["s18"], + s19: sreg = ["s19"], + s20: sreg = ["s20"], + s21: sreg = ["s21"], + s22: sreg = ["s22"], + s23: sreg = ["s23"], + s24: sreg = ["s24"], + s25: sreg = ["s25"], + s26: sreg = ["s26"], + s27: sreg = ["s27"], + s28: sreg = ["s28"], + s29: sreg = ["s29"], + s30: sreg = ["s30"], + s31: sreg = ["s31"], + d0: dreg, dreg_low16, dreg_low8 = ["d0"], + d1: dreg, dreg_low16, dreg_low8 = ["d1"], + d2: dreg, dreg_low16, dreg_low8 = ["d2"], + d3: dreg, dreg_low16, dreg_low8 = ["d3"], + d4: dreg, dreg_low16, dreg_low8 = ["d4"], + d5: dreg, dreg_low16, dreg_low8 = ["d5"], + d6: dreg, dreg_low16, dreg_low8 = ["d6"], + d7: dreg, dreg_low16, dreg_low8 = ["d7"], + d8: dreg, dreg_low16 = ["d8"], + d9: dreg, dreg_low16 = ["d9"], + d10: dreg, dreg_low16 = ["d10"], + d11: dreg, dreg_low16 = ["d11"], + d12: dreg, dreg_low16 = ["d12"], + d13: dreg, dreg_low16 = ["d13"], + d14: dreg, dreg_low16 = ["d14"], + d15: dreg, dreg_low16 = ["d15"], + d16: dreg = ["d16"], + d17: dreg = ["d17"], + d18: dreg = ["d18"], + d19: dreg = ["d19"], + d20: dreg = ["d20"], + d21: dreg = ["d21"], + d22: dreg = ["d22"], + d23: dreg = ["d23"], + d24: dreg = ["d24"], + d25: dreg = ["d25"], + d26: dreg = ["d26"], + d27: dreg = ["d27"], + d28: dreg = ["d28"], + d29: dreg = ["d29"], + d30: dreg = ["d30"], + d31: dreg = ["d31"], + q0: qreg, qreg_low8, qreg_low4 = ["q0"], + q1: qreg, qreg_low8, qreg_low4 = ["q1"], + q2: qreg, qreg_low8, qreg_low4 = ["q2"], + q3: qreg, qreg_low8, qreg_low4 = ["q3"], + q4: qreg, qreg_low8 = ["q4"], + q5: qreg, qreg_low8 = ["q5"], + q6: qreg, qreg_low8 = ["q6"], + q7: qreg, qreg_low8 = ["q7"], + q8: qreg = ["q8"], + q9: qreg = ["q9"], + q10: qreg = ["q10"], + q11: qreg = ["q11"], + q12: qreg = ["q12"], + q13: qreg = ["q13"], + q14: qreg = ["q14"], + q15: qreg = ["q15"], + #error = ["r6", "v3"] => + "r6 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["r13", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r15", "pc"] => + "the program pointer cannot be used as an operand for inline asm", + } +} + +impl ArmInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + // Only qreg is allowed to have modifiers. This should have been + // validated already by now. + if let Some(modifier) = modifier { + let index = self as u32 - Self::q0 as u32; + assert!(index < 16); + let index = index * 2 + (modifier == 'f') as u32; + write!(out, "d{}", index) + } else { + out.write_str(self.name()) + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(ArmInlineAsmReg)) { + cb(self); + + macro_rules! reg_conflicts { + ( + $( + $q:ident : $d0:ident $d1:ident : $s0:ident $s1:ident $s2:ident $s3:ident + ),*; + $( + $q_high:ident : $d0_high:ident $d1_high:ident + ),*; + ) => { + match self { + $( + Self::$q => { + cb(Self::$d0); + cb(Self::$d1); + cb(Self::$s0); + cb(Self::$s1); + cb(Self::$s2); + cb(Self::$s3); + } + Self::$d0 => { + cb(Self::$q); + cb(Self::$s0); + cb(Self::$s1); + } + Self::$d1 => { + cb(Self::$q); + cb(Self::$s2); + cb(Self::$s3); + } + Self::$s0 | Self::$s1 => { + cb(Self::$q); + cb(Self::$d0); + } + Self::$s2 | Self::$s3 => { + cb(Self::$q); + cb(Self::$d1); + } + )* + $( + Self::$q_high => { + cb(Self::$d0_high); + cb(Self::$d1_high); + } + Self::$d0_high | Self::$d1_high => { + cb(Self::$q_high); + } + )* + _ => {}, + } + }; + } + + // ARM's floating-point register file is interesting in that it can be + // viewed as 16 128-bit registers, 32 64-bit registers or 32 32-bit + // registers. Because these views overlap, the registers of different + // widths will conflict (e.g. d0 overlaps with s0 and s1, and q1 + // overlaps with d2 and d3). + // + // See section E1.3.1 of the ARM Architecture Reference Manual for + // ARMv8-A for more details. + reg_conflicts! { + q0 : d0 d1 : s0 s1 s2 s3, + q1 : d2 d3 : s4 s5 s6 s7, + q2 : d4 d5 : s8 s9 s10 s11, + q3 : d6 d7 : s12 s13 s14 s15, + q4 : d8 d9 : s16 s17 s18 s19, + q5 : d10 d11 : s20 s21 s22 s23, + q6 : d12 d13 : s24 s25 s26 s27, + q7 : d14 d15 : s28 s29 s30 s31; + q8 : d16 d17, + q9 : d18 d19, + q10 : d20 d21, + q11 : d22 d23, + q12 : d24 d25, + q13 : d26 d27, + q14 : d28 d29, + q15 : d30 d31; + } + } +} diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs new file mode 100644 index 00000000000..d41941d0b4c --- /dev/null +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -0,0 +1,93 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + Hexagon HexagonInlineAsmRegClass { + reg, + } +} + +impl HexagonInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg => types! { _: I8, I16, I32, F32; }, + } + } +} + +def_regs! { + Hexagon HexagonInlineAsmReg HexagonInlineAsmRegClass { + r0: reg = ["r0"], + r1: reg = ["r1"], + r2: reg = ["r2"], + r3: reg = ["r3"], + r4: reg = ["r4"], + r5: reg = ["r5"], + r6: reg = ["r6"], + r7: reg = ["r7"], + r8: reg = ["r8"], + r9: reg = ["r9"], + r10: reg = ["r10"], + r11: reg = ["r11"], + r12: reg = ["r12"], + r13: reg = ["r13"], + r14: reg = ["r14"], + r15: reg = ["r15"], + r16: reg = ["r16"], + r17: reg = ["r17"], + r18: reg = ["r18"], + r19: reg = ["r19"], + r20: reg = ["r20"], + r21: reg = ["r21"], + r22: reg = ["r22"], + r23: reg = ["r23"], + r24: reg = ["r24"], + r25: reg = ["r25"], + r26: reg = ["r26"], + r27: reg = ["r27"], + r28: reg = ["r28"], + #error = ["r29", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["r30", "fr"] => + "the frame register cannot be used as an operand for inline asm", + #error = ["r31", "lr"] => + "the link register cannot be used as an operand for inline asm", + } +} + +impl HexagonInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + out.write_str(self.name()) + } + + pub fn overlapping_regs(self, mut _cb: impl FnMut(HexagonInlineAsmReg)) {} +} diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs new file mode 100644 index 00000000000..c22644bf813 --- /dev/null +++ b/compiler/rustc_target/src/asm/mod.rs @@ -0,0 +1,549 @@ +use crate::abi::Size; +use crate::spec::Target; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_macros::HashStable_Generic; +use rustc_span::Symbol; +use std::fmt; +use std::str::FromStr; + +#[macro_use] +macro_rules! def_reg_class { + ($arch:ident $arch_regclass:ident { + $( + $class:ident, + )* + }) => { + #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_regclass { + $($class,)* + } + + impl $arch_regclass { + pub fn name(self) -> &'static str { + match self { + $(Self::$class => stringify!($class),)* + } + } + + pub fn parse(_arch: super::InlineAsmArch, name: &str) -> Result<Self, &'static str> { + match name { + $( + stringify!($class) => Ok(Self::$class), + )* + _ => Err("unknown register class"), + } + } + } + + pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>, + > { + use rustc_data_structures::fx::{FxHashMap, FxHashSet}; + use super::InlineAsmRegClass; + let mut map = FxHashMap::default(); + $( + map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default()); + )* + map + } + } +} + +#[macro_use] +macro_rules! def_regs { + ($arch:ident $arch_reg:ident $arch_regclass:ident { + $( + $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?, + )* + $( + #error = [$($bad_reg:literal),+] => $error:literal, + )* + }) => { + #[allow(unreachable_code)] + #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] + #[allow(non_camel_case_types)] + pub enum $arch_reg { + $($reg,)* + } + + impl $arch_reg { + pub fn name(self) -> &'static str { + match self { + $(Self::$reg => $reg_name,)* + } + } + + pub fn reg_class(self) -> $arch_regclass { + match self { + $(Self::$reg => $arch_regclass::$class,)* + } + } + + pub fn parse( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + _target: &crate::spec::Target, + name: &str, + ) -> Result<Self, &'static str> { + match name { + $( + $($alias)|* | $reg_name => { + $($filter(_arch, &mut _has_feature, _target, false)?;)? + Ok(Self::$reg) + } + )* + $( + $($bad_reg)|* => Err($error), + )* + _ => Err("unknown register"), + } + } + } + + pub(super) fn fill_reg_map( + _arch: super::InlineAsmArch, + mut _has_feature: impl FnMut(&str) -> bool, + _target: &crate::spec::Target, + _map: &mut rustc_data_structures::fx::FxHashMap< + super::InlineAsmRegClass, + rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>, + >, + ) { + #[allow(unused_imports)] + use super::{InlineAsmReg, InlineAsmRegClass}; + $( + if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true { + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + $( + if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { + set.insert(InlineAsmReg::$arch($arch_reg::$reg)); + } + )* + } + )* + } + } +} + +#[macro_use] +macro_rules! types { + ( + $(_ : $($ty:expr),+;)? + $($feature:literal: $($ty2:expr),+;)* + ) => { + { + use super::InlineAsmType::*; + &[ + $($( + ($ty, None), + )*)? + $($( + ($ty2, Some($feature)), + )*)* + ] + } + }; +} + +mod aarch64; +mod arm; +mod hexagon; +mod nvptx; +mod riscv; +mod x86; + +pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; +pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; +pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; +pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; +pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; +pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmArch { + X86, + X86_64, + Arm, + AArch64, + RiscV32, + RiscV64, + Nvptx64, + Hexagon, +} + +impl FromStr for InlineAsmArch { + type Err = (); + + fn from_str(s: &str) -> Result<InlineAsmArch, ()> { + match s { + "x86" => Ok(Self::X86), + "x86_64" => Ok(Self::X86_64), + "arm" => Ok(Self::Arm), + "aarch64" => Ok(Self::AArch64), + "riscv32" => Ok(Self::RiscV32), + "riscv64" => Ok(Self::RiscV64), + "nvptx64" => Ok(Self::Nvptx64), + "hexagon" => Ok(Self::Hexagon), + _ => Err(()), + } + } +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] +pub enum InlineAsmReg { + X86(X86InlineAsmReg), + Arm(ArmInlineAsmReg), + AArch64(AArch64InlineAsmReg), + RiscV(RiscVInlineAsmReg), + Nvptx(NvptxInlineAsmReg), + Hexagon(HexagonInlineAsmReg), +} + +impl InlineAsmReg { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::Hexagon(r) => r.name(), + } + } + + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()), + Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), + Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), + Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), + Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), + } + } + + pub fn parse( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &Target, + name: Symbol, + ) -> Result<Self, &'static str> { + // FIXME: use direct symbol comparison for register names + // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. + let name = name.as_str(); + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::Arm => { + Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::AArch64 => { + Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::Nvptx64 => { + Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + InlineAsmArch::Hexagon => { + Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, &name)?) + } + }) + } + + // NOTE: This function isn't used at the moment, but is needed to support + // falling back to an external assembler. + pub fn emit( + self, + out: &mut dyn fmt::Write, + arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + match self { + Self::X86(r) => r.emit(out, arch, modifier), + Self::Arm(r) => r.emit(out, arch, modifier), + Self::AArch64(r) => r.emit(out, arch, modifier), + Self::RiscV(r) => r.emit(out, arch, modifier), + Self::Hexagon(r) => r.emit(out, arch, modifier), + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { + match self { + Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))), + Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), + Self::AArch64(_) => cb(self), + Self::RiscV(_) => cb(self), + Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), + } + } +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] +pub enum InlineAsmRegClass { + X86(X86InlineAsmRegClass), + Arm(ArmInlineAsmRegClass), + AArch64(AArch64InlineAsmRegClass), + RiscV(RiscVInlineAsmRegClass), + Nvptx(NvptxInlineAsmRegClass), + Hexagon(HexagonInlineAsmRegClass), +} + +impl InlineAsmRegClass { + pub fn name(self) -> &'static str { + match self { + Self::X86(r) => r.name(), + Self::Arm(r) => r.name(), + Self::AArch64(r) => r.name(), + Self::RiscV(r) => r.name(), + Self::Nvptx(r) => r.name(), + Self::Hexagon(r) => r.name(), + } + } + + /// Returns a suggested register class to use for this type. This is called + /// after type checking via `supported_types` fails to give a better error + /// message to the user. + pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> { + match self { + Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86), + Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), + Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), + Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), + Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), + Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), + } + } + + /// Returns a suggested template modifier to use for this type and an + /// example of a register named formatted with it. + /// + /// Such suggestions are useful if a type smaller than the full register + /// size is used and a modifier can be used to point to the subregister of + /// the correct size. + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.suggest_modifier(arch, ty), + Self::Arm(r) => r.suggest_modifier(arch, ty), + Self::AArch64(r) => r.suggest_modifier(arch, ty), + Self::RiscV(r) => r.suggest_modifier(arch, ty), + Self::Nvptx(r) => r.suggest_modifier(arch, ty), + Self::Hexagon(r) => r.suggest_modifier(arch, ty), + } + } + + /// Returns the default modifier for this register and an example of a + /// register named formatted with it. + /// + /// This is only needed when the register class can suggest a modifier, so + /// that the user can be shown how to get the default behavior without a + /// warning. + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::X86(r) => r.default_modifier(arch), + Self::Arm(r) => r.default_modifier(arch), + Self::AArch64(r) => r.default_modifier(arch), + Self::RiscV(r) => r.default_modifier(arch), + Self::Nvptx(r) => r.default_modifier(arch), + Self::Hexagon(r) => r.default_modifier(arch), + } + } + + /// Returns a list of supported types for this register class, each with a + /// options target feature required to use this type. + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::X86(r) => r.supported_types(arch), + Self::Arm(r) => r.supported_types(arch), + Self::AArch64(r) => r.supported_types(arch), + Self::RiscV(r) => r.supported_types(arch), + Self::Nvptx(r) => r.supported_types(arch), + Self::Hexagon(r) => r.supported_types(arch), + } + } + + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> { + // FIXME: use direct symbol comparison for register class names + name.with(|name| { + Ok(match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + Self::X86(X86InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::AArch64 => { + Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Hexagon => { + Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) + } + }) + }) + } + + /// Returns the list of template modifiers that can be used with this + /// register class. + pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { + match self { + Self::X86(r) => r.valid_modifiers(arch), + Self::Arm(r) => r.valid_modifiers(arch), + Self::AArch64(r) => r.valid_modifiers(arch), + Self::RiscV(r) => r.valid_modifiers(arch), + Self::Nvptx(r) => r.valid_modifiers(arch), + Self::Hexagon(r) => r.valid_modifiers(arch), + } + } +} + +#[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)] +pub enum InlineAsmRegOrRegClass { + Reg(InlineAsmReg), + RegClass(InlineAsmRegClass), +} + +impl InlineAsmRegOrRegClass { + pub fn reg_class(self) -> InlineAsmRegClass { + match self { + Self::Reg(r) => r.reg_class(), + Self::RegClass(r) => r, + } + } +} + +impl fmt::Display for InlineAsmRegOrRegClass { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Reg(r) => write!(f, "\"{}\"", r.name()), + Self::RegClass(r) => f.write_str(r.name()), + } + } +} + +/// Set of types which can be used with a particular register class. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum InlineAsmType { + I8, + I16, + I32, + I64, + I128, + F32, + F64, + VecI8(u64), + VecI16(u64), + VecI32(u64), + VecI64(u64), + VecI128(u64), + VecF32(u64), + VecF64(u64), +} + +impl InlineAsmType { + pub fn is_integer(self) -> bool { + match self { + Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 => true, + _ => false, + } + } + + pub fn size(self) -> Size { + Size::from_bytes(match self { + Self::I8 => 1, + Self::I16 => 2, + Self::I32 => 4, + Self::I64 => 8, + Self::I128 => 16, + Self::F32 => 4, + Self::F64 => 8, + Self::VecI8(n) => n * 1, + Self::VecI16(n) => n * 2, + Self::VecI32(n) => n * 4, + Self::VecI64(n) => n * 8, + Self::VecI128(n) => n * 16, + Self::VecF32(n) => n * 4, + Self::VecF64(n) => n * 8, + }) + } +} + +impl fmt::Display for InlineAsmType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Self::I8 => f.write_str("i8"), + Self::I16 => f.write_str("i16"), + Self::I32 => f.write_str("i32"), + Self::I64 => f.write_str("i64"), + Self::I128 => f.write_str("i128"), + Self::F32 => f.write_str("f32"), + Self::F64 => f.write_str("f64"), + Self::VecI8(n) => write!(f, "i8x{}", n), + Self::VecI16(n) => write!(f, "i16x{}", n), + Self::VecI32(n) => write!(f, "i32x{}", n), + Self::VecI64(n) => write!(f, "i64x{}", n), + Self::VecI128(n) => write!(f, "i128x{}", n), + Self::VecF32(n) => write!(f, "f32x{}", n), + Self::VecF64(n) => write!(f, "f64x{}", n), + } + } +} + +/// Returns the full set of allocatable registers for a given architecture. +/// +/// The registers are structured as a map containing the set of allocatable +/// registers in each register class. A particular register may be allocatable +/// from multiple register classes, in which case it will appear multiple times +/// in the map. +// NOTE: This function isn't used at the moment, but is needed to support +// falling back to an external assembler. +pub fn allocatable_registers( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &crate::spec::Target, +) -> FxHashMap<InlineAsmRegClass, FxHashSet<InlineAsmReg>> { + match arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + let mut map = x86::regclass_map(); + x86::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::Arm => { + let mut map = arm::regclass_map(); + arm::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::AArch64 => { + let mut map = aarch64::regclass_map(); + aarch64::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + let mut map = riscv::regclass_map(); + riscv::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::Nvptx64 => { + let mut map = nvptx::regclass_map(); + nvptx::fill_reg_map(arch, has_feature, target, &mut map); + map + } + InlineAsmArch::Hexagon => { + let mut map = hexagon::regclass_map(); + hexagon::fill_reg_map(arch, has_feature, target, &mut map); + map + } + } +} diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs new file mode 100644 index 00000000000..43d16ae0f5d --- /dev/null +++ b/compiler/rustc_target/src/asm/nvptx.rs @@ -0,0 +1,49 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; + +def_reg_class! { + Nvptx NvptxInlineAsmRegClass { + reg16, + reg32, + reg64, + } +} + +impl NvptxInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg16 => types! { _: I8, I16; }, + Self::reg32 => types! { _: I8, I16, I32, F32; }, + Self::reg64 => types! { _: I8, I16, I32, F32, I64, F64; }, + } + } +} + +def_regs! { + // Registers in PTX are declared in the assembly. + // There are no predefined registers that one can use. + Nvptx NvptxInlineAsmReg NvptxInlineAsmRegClass {} +} diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs new file mode 100644 index 00000000000..ced7483b005 --- /dev/null +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -0,0 +1,147 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::Target; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + RiscV RiscVInlineAsmRegClass { + reg, + freg, + } +} + +impl RiscVInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg => { + if arch == InlineAsmArch::RiscV64 { + types! { _: I8, I16, I32, I64, F32, F64; } + } else { + types! { _: I8, I16, I32, F32; } + } + } + Self::freg => types! { "f": F32; "d": F64; }, + } + } +} + +fn not_e( + _arch: InlineAsmArch, + mut has_feature: impl FnMut(&str) -> bool, + _target: &Target, + _allocating: bool, +) -> Result<(), &'static str> { + if has_feature("e") { + Err("register can't be used with the `e` target feature") + } else { + Ok(()) + } +} + +def_regs! { + RiscV RiscVInlineAsmReg RiscVInlineAsmRegClass { + x1: reg = ["x1", "ra"], + x5: reg = ["x5", "t0"], + x6: reg = ["x6", "t1"], + x7: reg = ["x7", "t2"], + x9: reg = ["x9", "s1"], + x10: reg = ["x10", "a0"], + x11: reg = ["x11", "a1"], + x12: reg = ["x12", "a2"], + x13: reg = ["x13", "a3"], + x14: reg = ["x14", "a4"], + x15: reg = ["x15", "a5"], + x16: reg = ["x16", "a6"] % not_e, + x17: reg = ["x17", "a7"] % not_e, + x18: reg = ["x18", "s2"] % not_e, + x19: reg = ["x19", "s3"] % not_e, + x20: reg = ["x20", "s4"] % not_e, + x21: reg = ["x21", "s5"] % not_e, + x22: reg = ["x22", "s6"] % not_e, + x23: reg = ["x23", "s7"] % not_e, + x24: reg = ["x24", "s8"] % not_e, + x25: reg = ["x25", "s9"] % not_e, + x26: reg = ["x26", "s10"] % not_e, + x27: reg = ["x27", "s11"] % not_e, + x28: reg = ["x28", "t3"] % not_e, + x29: reg = ["x29", "t4"] % not_e, + x30: reg = ["x30", "t5"] % not_e, + x31: reg = ["x31", "t6"] % not_e, + f0: freg = ["f0", "ft0"], + f1: freg = ["f1", "ft1"], + f2: freg = ["f2", "ft2"], + f3: freg = ["f3", "ft3"], + f4: freg = ["f4", "ft4"], + f5: freg = ["f5", "ft5"], + f6: freg = ["f6", "ft6"], + f7: freg = ["f7", "ft7"], + f8: freg = ["f8", "fs0"], + f9: freg = ["f9", "fs1"], + f10: freg = ["f10", "fa0"], + f11: freg = ["f11", "fa1"], + f12: freg = ["f12", "fa2"], + f13: freg = ["f13", "fa3"], + f14: freg = ["f14", "fa4"], + f15: freg = ["f15", "fa5"], + f16: freg = ["f16", "fa6"], + f17: freg = ["f17", "fa7"], + f18: freg = ["f18", "fs2"], + f19: freg = ["f19", "fs3"], + f20: freg = ["f20", "fs4"], + f21: freg = ["f21", "fs5"], + f22: freg = ["f22", "fs6"], + f23: freg = ["f23", "fs7"], + f24: freg = ["f24", "fs8"], + f25: freg = ["f25", "fs9"], + f26: freg = ["f26", "fs10"], + f27: freg = ["f27", "fs11"], + f28: freg = ["f28", "ft8"], + f29: freg = ["f29", "ft9"], + f30: freg = ["f30", "ft10"], + f31: freg = ["f31", "ft11"], + #error = ["x8", "s0", "fp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["x2", "sp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["x3", "gp"] => + "the global pointer cannot be used as an operand for inline asm", + #error = ["x4", "tp"] => + "the thread pointer cannot be used as an operand for inline asm" , + #error = ["x0", "zero"] => + "the zero register cannot be used as an operand for inline asm", + } +} + +impl RiscVInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option<char>, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs new file mode 100644 index 00000000000..0f62c19e1a3 --- /dev/null +++ b/compiler/rustc_target/src/asm/x86.rs @@ -0,0 +1,427 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::Target; +use rustc_macros::HashStable_Generic; +use std::fmt; + +def_reg_class! { + X86 X86InlineAsmRegClass { + reg, + reg_abcd, + reg_byte, + xmm_reg, + ymm_reg, + zmm_reg, + kreg, + } +} + +impl X86InlineAsmRegClass { + pub fn valid_modifiers(self, arch: super::InlineAsmArch) -> &'static [char] { + match self { + Self::reg => { + if arch == InlineAsmArch::X86_64 { + &['l', 'x', 'e', 'r'] + } else { + &['x', 'e'] + } + } + Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + &['l', 'h', 'x', 'e', 'r'] + } else { + &['l', 'h', 'x', 'e'] + } + } + Self::reg_byte => &[], + Self::xmm_reg | Self::ymm_reg | Self::zmm_reg => &['x', 'y', 'z'], + Self::kreg => &[], + } + } + + pub fn suggest_class(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> { + match self { + Self::reg | Self::reg_abcd if ty.size().bits() == 8 => Some(Self::reg_byte), + _ => None, + } + } + + pub fn suggest_modifier( + self, + arch: InlineAsmArch, + ty: InlineAsmType, + ) -> Option<(char, &'static str)> { + match self { + Self::reg => match ty.size().bits() { + 16 => Some(('x', "ax")), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + _ => None, + }, + Self::reg_abcd => match ty.size().bits() { + 16 => Some(('x', "ax")), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + _ => None, + }, + Self::reg_byte => None, + Self::xmm_reg => None, + Self::ymm_reg => match ty.size().bits() { + 256 => None, + _ => Some(('x', "xmm0")), + }, + Self::zmm_reg => match ty.size().bits() { + 512 => None, + 256 => Some(('y', "ymm0")), + _ => Some(('x', "xmm0")), + }, + Self::kreg => None, + } + } + + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + match self { + Self::reg | Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + Some(('r', "rax")) + } else { + Some(('e', "eax")) + } + } + Self::reg_byte => None, + Self::xmm_reg => Some(('x', "xmm0")), + Self::ymm_reg => Some(('y', "ymm0")), + Self::zmm_reg => Some(('z', "zmm0")), + Self::kreg => None, + } + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + match self { + Self::reg | Self::reg_abcd => { + if arch == InlineAsmArch::X86_64 { + types! { _: I16, I32, I64, F32, F64; } + } else { + types! { _: I16, I32, F32; } + } + } + Self::reg_byte => types! { _: I8; }, + Self::xmm_reg => types! { + "sse": I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2); + }, + Self::ymm_reg => types! { + "avx": I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4); + }, + Self::zmm_reg => types! { + "avx512f": I32, I64, F32, F64, + VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2), + VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4), + VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF32(16), VecF64(8); + }, + Self::kreg => types! { + "avx512f": I8, I16; + "avx512bw": I32, I64; + }, + } + } +} + +fn x86_64_only( + arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + _target: &Target, + _allocating: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86 => Err("register is only available on x86_64"), + InlineAsmArch::X86_64 => Ok(()), + _ => unreachable!(), + } +} + +fn high_byte( + arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + _target: &Target, + allocating: bool, +) -> Result<(), &'static str> { + match arch { + InlineAsmArch::X86_64 if allocating => { + // The error message isn't actually used... + Err("high byte registers are not allocated by reg_byte") + } + _ => Ok(()), + } +} + +def_regs! { + X86 X86InlineAsmReg X86InlineAsmRegClass { + ax: reg, reg_abcd = ["ax", "eax", "rax"], + bx: reg, reg_abcd = ["bx", "ebx", "rbx"], + cx: reg, reg_abcd = ["cx", "ecx", "rcx"], + dx: reg, reg_abcd = ["dx", "edx", "rdx"], + si: reg = ["si", "esi", "rsi"], + di: reg = ["di", "edi", "rdi"], + r8: reg = ["r8", "r8w", "r8d"] % x86_64_only, + r9: reg = ["r9", "r9w", "r9d"] % x86_64_only, + r10: reg = ["r10", "r10w", "r10d"] % x86_64_only, + r11: reg = ["r11", "r11w", "r11d"] % x86_64_only, + r12: reg = ["r12", "r12w", "r12d"] % x86_64_only, + r13: reg = ["r13", "r13w", "r13d"] % x86_64_only, + r14: reg = ["r14", "r14w", "r14d"] % x86_64_only, + r15: reg = ["r15", "r15w", "r15d"] % x86_64_only, + al: reg_byte = ["al"], + ah: reg_byte = ["ah"] % high_byte, + bl: reg_byte = ["bl"], + bh: reg_byte = ["bh"] % high_byte, + cl: reg_byte = ["cl"], + ch: reg_byte = ["ch"] % high_byte, + dl: reg_byte = ["dl"], + dh: reg_byte = ["dh"] % high_byte, + sil: reg_byte = ["sil"] % x86_64_only, + dil: reg_byte = ["dil"] % x86_64_only, + r8b: reg_byte = ["r8b"] % x86_64_only, + r9b: reg_byte = ["r9b"] % x86_64_only, + r10b: reg_byte = ["r10b"] % x86_64_only, + r11b: reg_byte = ["r11b"] % x86_64_only, + r12b: reg_byte = ["r12b"] % x86_64_only, + r13b: reg_byte = ["r13b"] % x86_64_only, + r14b: reg_byte = ["r14b"] % x86_64_only, + r15b: reg_byte = ["r15b"] % x86_64_only, + xmm0: xmm_reg = ["xmm0"], + xmm1: xmm_reg = ["xmm1"], + xmm2: xmm_reg = ["xmm2"], + xmm3: xmm_reg = ["xmm3"], + xmm4: xmm_reg = ["xmm4"], + xmm5: xmm_reg = ["xmm5"], + xmm6: xmm_reg = ["xmm6"], + xmm7: xmm_reg = ["xmm7"], + xmm8: xmm_reg = ["xmm8"] % x86_64_only, + xmm9: xmm_reg = ["xmm9"] % x86_64_only, + xmm10: xmm_reg = ["xmm10"] % x86_64_only, + xmm11: xmm_reg = ["xmm11"] % x86_64_only, + xmm12: xmm_reg = ["xmm12"] % x86_64_only, + xmm13: xmm_reg = ["xmm13"] % x86_64_only, + xmm14: xmm_reg = ["xmm14"] % x86_64_only, + xmm15: xmm_reg = ["xmm15"] % x86_64_only, + ymm0: ymm_reg = ["ymm0"], + ymm1: ymm_reg = ["ymm1"], + ymm2: ymm_reg = ["ymm2"], + ymm3: ymm_reg = ["ymm3"], + ymm4: ymm_reg = ["ymm4"], + ymm5: ymm_reg = ["ymm5"], + ymm6: ymm_reg = ["ymm6"], + ymm7: ymm_reg = ["ymm7"], + ymm8: ymm_reg = ["ymm8"] % x86_64_only, + ymm9: ymm_reg = ["ymm9"] % x86_64_only, + ymm10: ymm_reg = ["ymm10"] % x86_64_only, + ymm11: ymm_reg = ["ymm11"] % x86_64_only, + ymm12: ymm_reg = ["ymm12"] % x86_64_only, + ymm13: ymm_reg = ["ymm13"] % x86_64_only, + ymm14: ymm_reg = ["ymm14"] % x86_64_only, + ymm15: ymm_reg = ["ymm15"] % x86_64_only, + zmm0: zmm_reg = ["zmm0"], + zmm1: zmm_reg = ["zmm1"], + zmm2: zmm_reg = ["zmm2"], + zmm3: zmm_reg = ["zmm3"], + zmm4: zmm_reg = ["zmm4"], + zmm5: zmm_reg = ["zmm5"], + zmm6: zmm_reg = ["zmm6"], + zmm7: zmm_reg = ["zmm7"], + zmm8: zmm_reg = ["zmm8"] % x86_64_only, + zmm9: zmm_reg = ["zmm9"] % x86_64_only, + zmm10: zmm_reg = ["zmm10"] % x86_64_only, + zmm11: zmm_reg = ["zmm11"] % x86_64_only, + zmm12: zmm_reg = ["zmm12"] % x86_64_only, + zmm13: zmm_reg = ["zmm13"] % x86_64_only, + zmm14: zmm_reg = ["zmm14"] % x86_64_only, + zmm15: zmm_reg = ["zmm15"] % x86_64_only, + zmm16: zmm_reg = ["zmm16", "xmm16", "ymm16"] % x86_64_only, + zmm17: zmm_reg = ["zmm17", "xmm17", "ymm17"] % x86_64_only, + zmm18: zmm_reg = ["zmm18", "xmm18", "ymm18"] % x86_64_only, + zmm19: zmm_reg = ["zmm19", "xmm19", "ymm19"] % x86_64_only, + zmm20: zmm_reg = ["zmm20", "xmm20", "ymm20"] % x86_64_only, + zmm21: zmm_reg = ["zmm21", "xmm21", "ymm21"] % x86_64_only, + zmm22: zmm_reg = ["zmm22", "xmm22", "ymm22"] % x86_64_only, + zmm23: zmm_reg = ["zmm23", "xmm23", "ymm23"] % x86_64_only, + zmm24: zmm_reg = ["zmm24", "xmm24", "ymm24"] % x86_64_only, + zmm25: zmm_reg = ["zmm25", "xmm25", "ymm25"] % x86_64_only, + zmm26: zmm_reg = ["zmm26", "xmm26", "ymm26"] % x86_64_only, + zmm27: zmm_reg = ["zmm27", "xmm27", "ymm27"] % x86_64_only, + zmm28: zmm_reg = ["zmm28", "xmm28", "ymm28"] % x86_64_only, + zmm29: zmm_reg = ["zmm29", "xmm29", "ymm29"] % x86_64_only, + zmm30: zmm_reg = ["zmm30", "xmm30", "ymm30"] % x86_64_only, + zmm31: zmm_reg = ["zmm31", "xmm31", "ymm31"] % x86_64_only, + k1: kreg = ["k1"], + k2: kreg = ["k2"], + k3: kreg = ["k3"], + k4: kreg = ["k4"], + k5: kreg = ["k5"], + k6: kreg = ["k6"], + k7: kreg = ["k7"], + #error = ["bp", "bpl", "ebp", "rbp"] => + "the frame pointer cannot be used as an operand for inline asm", + #error = ["sp", "spl", "esp", "rsp"] => + "the stack pointer cannot be used as an operand for inline asm", + #error = ["ip", "eip", "rip"] => + "the instruction pointer cannot be used as an operand for inline asm", + #error = ["st", "st(0)", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)"] => + "x87 registers are not currently supported as operands for inline asm", + #error = ["mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7"] => + "MMX registers are not currently supported as operands for inline asm", + #error = ["k0"] => + "the k0 AVX mask register cannot be used as an operand for inline asm", + } +} + +impl X86InlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + arch: InlineAsmArch, + modifier: Option<char>, + ) -> fmt::Result { + let reg_default_modifier = match arch { + InlineAsmArch::X86 => 'e', + InlineAsmArch::X86_64 => 'r', + _ => unreachable!(), + }; + if self as u32 <= Self::dx as u32 { + let root = ['a', 'b', 'c', 'd'][self as usize - Self::ax as usize]; + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}l", root), + 'h' => write!(out, "{}h", root), + 'x' => write!(out, "{}x", root), + 'e' => write!(out, "e{}x", root), + 'r' => write!(out, "r{}x", root), + _ => unreachable!(), + } + } else if self as u32 <= Self::di as u32 { + let root = self.name(); + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}l", root), + 'x' => write!(out, "{}", root), + 'e' => write!(out, "e{}", root), + 'r' => write!(out, "r{}", root), + _ => unreachable!(), + } + } else if self as u32 <= Self::r15 as u32 { + let root = self.name(); + match modifier.unwrap_or(reg_default_modifier) { + 'l' => write!(out, "{}b", root), + 'x' => write!(out, "{}w", root), + 'e' => write!(out, "{}d", root), + 'r' => out.write_str(root), + _ => unreachable!(), + } + } else if self as u32 <= Self::r15b as u32 { + out.write_str(self.name()) + } else if self as u32 <= Self::xmm15 as u32 { + let prefix = modifier.unwrap_or('x'); + let index = self as u32 - Self::xmm0 as u32; + write!(out, "{}{}", prefix, index) + } else if self as u32 <= Self::ymm15 as u32 { + let prefix = modifier.unwrap_or('y'); + let index = self as u32 - Self::ymm0 as u32; + write!(out, "{}{}", prefix, index) + } else if self as u32 <= Self::zmm31 as u32 { + let prefix = modifier.unwrap_or('z'); + let index = self as u32 - Self::zmm0 as u32; + write!(out, "{}{}", prefix, index) + } else { + out.write_str(self.name()) + } + } + + pub fn overlapping_regs(self, mut cb: impl FnMut(X86InlineAsmReg)) { + macro_rules! reg_conflicts { + ( + $( + $w:ident : $l:ident $h:ident + ),*; + $( + $w2:ident : $l2:ident + ),*; + $( + $x:ident : $y:ident : $z:ident + ),*; + ) => { + match self { + $( + Self::$w => { + cb(Self::$w); + cb(Self::$l); + cb(Self::$h); + } + Self::$l => { + cb(Self::$w); + cb(Self::$l); + } + Self::$h => { + cb(Self::$w); + cb(Self::$h); + } + )* + $( + Self::$w2 | Self::$l2 => { + cb(Self::$w2); + cb(Self::$l2); + } + )* + $( + Self::$x | Self::$y | Self::$z => { + cb(Self::$x); + cb(Self::$y); + cb(Self::$z); + } + )* + r => cb(r), + } + }; + } + + // XMM*, YMM* and ZMM* are all different views of the same register. + // + // See section 15.5 of the combined Intel® 64 and IA-32 Architectures + // Software Developer’s Manual for more details. + // + // We don't need to specify conflicts for [x,y,z]mm[16-31] since these + // registers are only available with AVX-512, so we just specify them + // as aliases directly. + reg_conflicts! { + ax : al ah, + bx : bl bh, + cx : cl ch, + dx : dl dh; + si : sil, + di : dil, + r8 : r8b, + r9 : r9b, + r10 : r10b, + r11 : r11b, + r12 : r12b, + r13 : r13b, + r14 : r14b, + r15 : r15b; + xmm0 : ymm0 : zmm0, + xmm1 : ymm1 : zmm1, + xmm2 : ymm2 : zmm2, + xmm3 : ymm3 : zmm3, + xmm4 : ymm4 : zmm4, + xmm5 : ymm5 : zmm5, + xmm6 : ymm6 : zmm6, + xmm7 : ymm7 : zmm7, + xmm8 : ymm8 : zmm8, + xmm9 : ymm9 : zmm9, + xmm10 : ymm10 : zmm10, + xmm11 : ymm11 : zmm11, + xmm12 : ymm12 : zmm12, + xmm13 : ymm13 : zmm13, + xmm14 : ymm14 : zmm14, + xmm15 : ymm15 : zmm15; + } + } +} diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs new file mode 100644 index 00000000000..5788e1e8385 --- /dev/null +++ b/compiler/rustc_target/src/lib.rs @@ -0,0 +1,32 @@ +//! Some stuff used by rustc that doesn't have many dependencies +//! +//! Originally extracted from rustc::back, which was nominally the +//! compiler 'backend', though LLVM is rustc's backend, so rustc_target +//! is really just odds-and-ends relating to code gen and linking. +//! This crate mostly exists to make rustc smaller, so we might put +//! more 'stuff' here in the future. It does not have a dependency on +//! LLVM. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")] +#![feature(bool_to_option)] +#![feature(const_fn)] +#![feature(const_panic)] +#![feature(nll)] +#![feature(never_type)] +#![feature(associated_type_bounds)] +#![feature(exhaustive_patterns)] + +#[macro_use] +extern crate rustc_macros; + +#[macro_use] +extern crate tracing; + +pub mod abi; +pub mod asm; +pub mod spec; + +/// Requirements for a `StableHashingContext` to be used in this crate. +/// This is a hack to allow using the `HashStable_Generic` derive macro +/// instead of implementing everything in librustc_middle. +pub trait HashStableContext {} diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs new file mode 100644 index 00000000000..60daf10b36a --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::apple_base::opts(); + base.cpu = "apple-a12".to_string(); + base.max_atomic_width = Some(128); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]); + + base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); + + // Clang automatically chooses a more specific target based on + // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work + // correctly, we do too. + let arch = "aarch64"; + let llvm_target = super::apple_base::macos_llvm_target(&arch); + + Ok(Target { + llvm_target, + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), + arch: arch.to_string(), + target_os: "macos".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs new file mode 100644 index 00000000000..21dcec8d5e3 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs @@ -0,0 +1,37 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::Arm64, AppleOS::iOS)?; + Ok(Target { + llvm_target: "arm64-apple-ios".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "ios".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + features: "+neon,+fp-armv8,+apple-a7".to_string(), + eliminate_frame_pointer: false, + max_atomic_width: Some(128), + unsupported_abis: super::arm_base::unsupported_abis(), + forces_embed_bitcode: true, + // Taken from a clang build on Xcode 11.4.1. + // These arguments are not actually invoked - they just have + // to look right to pass App Store validation. + bitcode_llvm_cmdline: "-triple\0\ + arm64-apple-ios11.0.0\0\ + -emit-obj\0\ + -disable-llvm-passes\0\ + -target-abi\0\ + darwinpcs\0\ + -Os\0" + .to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs new file mode 100644 index 00000000000..2b0cd6cabf8 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs @@ -0,0 +1,26 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::Arm64, AppleOS::tvOS)?; + Ok(Target { + llvm_target: "arm64-apple-tvos".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "tvos".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + features: "+neon,+fp-armv8,+apple-a7".to_string(), + eliminate_frame_pointer: false, + max_atomic_width: Some(128), + unsupported_abis: super::arm_base::unsupported_abis(), + forces_embed_bitcode: true, + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs new file mode 100644 index 00000000000..aabfe458ca3 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs @@ -0,0 +1,20 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::fuchsia_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-fuchsia".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "fuchsia".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs new file mode 100644 index 00000000000..e4ecc7ac2dc --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// See https://developer.android.com/ndk/guides/abis.html#arm64-v8a +// for target ABI requirements. + +pub fn target() -> TargetResult { + let mut base = super::android_base::opts(); + base.max_atomic_width = Some(128); + // As documented in http://developer.android.com/ndk/guides/cpu-features.html + // the neon (ASIMD) and FP must exist on all android aarch64 targets. + base.features = "+neon,+fp-armv8".to_string(); + Ok(Target { + llvm_target: "aarch64-linux-android".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "android".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs new file mode 100644 index 00000000000..8c03f1e8a7e --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_msvc_base::opts(); + base.max_atomic_width = Some(64); + base.has_elf_tls = true; + base.features = "+neon,+fp-armv8".to_string(); + + Ok(Target { + llvm_target: "aarch64-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "pc".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs new file mode 100644 index 00000000000..1278b89c7fd --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::cloudabi_base::opts(); + base.max_atomic_width = Some(128); + base.unsupported_abis = super::arm_base::unsupported_abis(); + base.linker = Some("aarch64-unknown-cloudabi-cc".to_string()); + + Ok(Target { + llvm_target: "aarch64-unknown-cloudabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "cloudabi".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs new file mode 100644 index 00000000000..5ae592c5139 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs @@ -0,0 +1,20 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::freebsd_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-unknown-freebsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "freebsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs new file mode 100644 index 00000000000..e07b8f7a756 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs @@ -0,0 +1,20 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::hermit_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-unknown-hermit".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "hermit".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs new file mode 100644 index 00000000000..036162248c7 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + target_env: "gnu".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "linux".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}_mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs new file mode 100644 index 00000000000..dc613f35d1d --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-unknown-linux-musl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + target_env: "musl".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "linux".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}_mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs new file mode 100644 index 00000000000..8c2f6fcff73 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.max_atomic_width = Some(128); + base.unsupported_abis = super::arm_base::unsupported_abis(); + + Ok(Target { + llvm_target: "aarch64-unknown-netbsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "netbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs new file mode 100644 index 00000000000..e012dce73fe --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -0,0 +1,37 @@ +// Generic AArch64 target for bare-metal code - Floating point enabled +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. +// +// For example, `-C target-cpu=cortex-a53`. + +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Result<Target, String> { + let opts = TargetOptions { + linker: Some("rust-lld".to_owned()), + features: "+strict-align,+neon,+fp-armv8".to_string(), + executables: true, + relocation_model: RelocModel::Static, + disable_redzone: true, + linker_is_gnu: true, + max_atomic_width: Some(128), + panic_strategy: PanicStrategy::Abort, + unsupported_abis: super::arm_base::unsupported_abis(), + ..Default::default() + }; + Ok(Target { + llvm_target: "aarch64-unknown-none".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: opts, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs new file mode 100644 index 00000000000..e2aa6e3b8f5 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs @@ -0,0 +1,37 @@ +// Generic AArch64 target for bare-metal code - Floating point disabled +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. +// +// For example, `-C target-cpu=cortex-a53`. + +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Result<Target, String> { + let opts = TargetOptions { + linker: Some("rust-lld".to_owned()), + features: "+strict-align,-neon,-fp-armv8".to_string(), + executables: true, + relocation_model: RelocModel::Static, + disable_redzone: true, + linker_is_gnu: true, + max_atomic_width: Some(128), + panic_strategy: PanicStrategy::Abort, + unsupported_abis: super::arm_base::unsupported_abis(), + ..Default::default() + }; + Ok(Target { + llvm_target: "aarch64-unknown-none".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: opts, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs new file mode 100644 index 00000000000..fd726c70f49 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::openbsd_base::opts(); + base.max_atomic_width = Some(128); + base.unsupported_abis = super::arm_base::unsupported_abis(); + + Ok(Target { + llvm_target: "aarch64-unknown-openbsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "openbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs b/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs new file mode 100644 index 00000000000..f347a2dba53 --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs @@ -0,0 +1,20 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::redox_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-unknown-redox".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "redox".to_string(), + target_env: "relibc".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs new file mode 100644 index 00000000000..6a8d148259a --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_uwp_msvc_base::opts(); + base.max_atomic_width = Some(64); + base.has_elf_tls = true; + + Ok(Target { + llvm_target: "aarch64-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "uwp".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs new file mode 100644 index 00000000000..05f5d7d3a8b --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs @@ -0,0 +1,20 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::vxworks_base::opts(); + base.max_atomic_width = Some(128); + + Ok(Target { + llvm_target: "aarch64-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), + arch: "aarch64".to_string(), + target_os: "vxworks".to_string(), + target_env: "gnu".to_string(), + target_vendor: "wrs".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs new file mode 100644 index 00000000000..1e45739ca22 --- /dev/null +++ b/compiler/rustc_target/src/spec/abi.rs @@ -0,0 +1,125 @@ +use std::fmt; + +use rustc_macros::HashStable_Generic; + +#[cfg(test)] +mod tests; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum Abi { + // N.B., this ordering MUST match the AbiDatas array below. + // (This is ensured by the test indices_are_correct().) + + // Multiplatform / generic ABIs + // + // These ABIs come first because every time we add a new ABI, we + // have to re-bless all the hashing tests. These are used in many + // places, so giving them stable values reduces test churn. The + // specific values are meaningless. + Rust = 0, + C = 1, + + // Single platform ABIs + Cdecl, + Stdcall, + Fastcall, + Vectorcall, + Thiscall, + Aapcs, + Win64, + SysV64, + PtxKernel, + Msp430Interrupt, + X86Interrupt, + AmdGpuKernel, + EfiApi, + AvrInterrupt, + AvrNonBlockingInterrupt, + + // Multiplatform / generic ABIs + System, + RustIntrinsic, + RustCall, + PlatformIntrinsic, + Unadjusted, +} + +#[derive(Copy, Clone)] +pub struct AbiData { + abi: Abi, + + /// Name of this ABI as we like it called. + name: &'static str, + + /// A generic ABI is supported on all platforms. + generic: bool, +} + +#[allow(non_upper_case_globals)] +const AbiDatas: &[AbiData] = &[ + // Cross-platform ABIs + AbiData { abi: Abi::Rust, name: "Rust", generic: true }, + AbiData { abi: Abi::C, name: "C", generic: true }, + // Platform-specific ABIs + AbiData { abi: Abi::Cdecl, name: "cdecl", generic: false }, + AbiData { abi: Abi::Stdcall, name: "stdcall", generic: false }, + AbiData { abi: Abi::Fastcall, name: "fastcall", generic: false }, + AbiData { abi: Abi::Vectorcall, name: "vectorcall", generic: false }, + AbiData { abi: Abi::Thiscall, name: "thiscall", generic: false }, + AbiData { abi: Abi::Aapcs, name: "aapcs", generic: false }, + AbiData { abi: Abi::Win64, name: "win64", generic: false }, + AbiData { abi: Abi::SysV64, name: "sysv64", generic: false }, + AbiData { abi: Abi::PtxKernel, name: "ptx-kernel", generic: false }, + AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt", generic: false }, + AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt", generic: false }, + AbiData { abi: Abi::AmdGpuKernel, name: "amdgpu-kernel", generic: false }, + AbiData { abi: Abi::EfiApi, name: "efiapi", generic: false }, + AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt", generic: false }, + AbiData { + abi: Abi::AvrNonBlockingInterrupt, + name: "avr-non-blocking-interrupt", + generic: false, + }, + // Cross-platform ABIs + AbiData { abi: Abi::System, name: "system", generic: true }, + AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true }, + AbiData { abi: Abi::RustCall, name: "rust-call", generic: true }, + AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic", generic: true }, + AbiData { abi: Abi::Unadjusted, name: "unadjusted", generic: true }, +]; + +/// Returns the ABI with the given name (if any). +pub fn lookup(name: &str) -> Option<Abi> { + AbiDatas.iter().find(|abi_data| name == abi_data.name).map(|&x| x.abi) +} + +pub fn all_names() -> Vec<&'static str> { + AbiDatas.iter().map(|d| d.name).collect() +} + +impl Abi { + #[inline] + pub fn index(self) -> usize { + self as usize + } + + #[inline] + pub fn data(self) -> &'static AbiData { + &AbiDatas[self.index()] + } + + pub fn name(self) -> &'static str { + self.data().name + } + + pub fn generic(self) -> bool { + self.data().generic + } +} + +impl fmt::Display for Abi { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "\"{}\"", self.name()) + } +} diff --git a/compiler/rustc_target/src/spec/abi/tests.rs b/compiler/rustc_target/src/spec/abi/tests.rs new file mode 100644 index 00000000000..8bea5e5efe3 --- /dev/null +++ b/compiler/rustc_target/src/spec/abi/tests.rs @@ -0,0 +1,27 @@ +use super::*; + +#[allow(non_snake_case)] +#[test] +fn lookup_Rust() { + let abi = lookup("Rust"); + assert!(abi.is_some() && abi.unwrap().data().name == "Rust"); +} + +#[test] +fn lookup_cdecl() { + let abi = lookup("cdecl"); + assert!(abi.is_some() && abi.unwrap().data().name == "cdecl"); +} + +#[test] +fn lookup_baz() { + let abi = lookup("baz"); + assert!(abi.is_none()); +} + +#[test] +fn indices_are_correct() { + for (i, abi_data) in AbiDatas.iter().enumerate() { + assert_eq!(i, abi_data.abi.index()); + } +} diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs new file mode 100644 index 00000000000..0ea99af83a1 --- /dev/null +++ b/compiler/rustc_target/src/spec/android_base.rs @@ -0,0 +1,16 @@ +use crate::spec::{LinkerFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut base = super::linux_base::opts(); + // Many of the symbols defined in compiler-rt are also defined in libgcc. + // Android's linker doesn't like that by default. + base.pre_link_args + .get_mut(&LinkerFlavor::Gcc) + .unwrap() + .push("-Wl,--allow-multiple-definition".to_string()); + base.is_like_android = true; + base.position_independent_executables = true; + base.has_elf_tls = false; + base.requires_uwtable = true; + base +} diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs new file mode 100644 index 00000000000..e7b565ae9ca --- /dev/null +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -0,0 +1,82 @@ +use std::env; + +use crate::spec::{LinkArgs, TargetOptions}; + +pub fn opts() -> TargetOptions { + // ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6 + // either the linker will complain if it is used or the binary will end up + // segfaulting at runtime when run on 10.6. Rust by default supports macOS + // 10.7+, but there is a standard environment variable, + // MACOSX_DEPLOYMENT_TARGET, which is used to signal targeting older + // versions of macOS. For example compiling on 10.10 with + // MACOSX_DEPLOYMENT_TARGET set to 10.6 will cause the linker to generate + // warnings about the usage of ELF TLS. + // + // Here we detect what version is being requested, defaulting to 10.7. ELF + // TLS is flagged as enabled if it looks to be supported. + let version = macos_deployment_target(); + + TargetOptions { + // macOS has -dead_strip, which doesn't rely on function_sections + function_sections: false, + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + is_like_osx: true, + has_rpath: true, + dll_prefix: "lib".to_string(), + dll_suffix: ".dylib".to_string(), + archive_format: "darwin".to_string(), + pre_link_args: LinkArgs::new(), + has_elf_tls: version >= (10, 7), + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + + // This environment variable is pretty magical but is intended for + // producing deterministic builds. This was first discovered to be used + // by the `ar` tool as a way to control whether or not mtime entries in + // the archive headers were set to zero or not. It appears that + // eventually the linker got updated to do the same thing and now reads + // this environment variable too in recent versions. + // + // For some more info see the commentary on #47086 + link_env: vec![("ZERO_AR_DATE".to_string(), "1".to_string())], + + ..Default::default() + } +} + +fn macos_deployment_target() -> (u32, u32) { + let deployment_target = env::var("MACOSX_DEPLOYMENT_TARGET").ok(); + let version = deployment_target + .as_ref() + .and_then(|s| { + let mut i = s.splitn(2, '.'); + i.next().and_then(|a| i.next().map(|b| (a, b))) + }) + .and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok()); + + version.unwrap_or((10, 7)) +} + +pub fn macos_llvm_target(arch: &str) -> String { + let (major, minor) = macos_deployment_target(); + format!("{}-apple-macosx{}.{}.0", arch, major, minor) +} + +pub fn macos_link_env_remove() -> Vec<String> { + let mut env_remove = Vec::with_capacity(2); + // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which + // may occur when we're linking a custom build script while targeting iOS for example. + if let Ok(sdkroot) = env::var("SDKROOT") { + if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform") { + env_remove.push("SDKROOT".to_string()) + } + } + // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at + // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", + // although this is apparently ignored when using the linker at "/usr/bin/ld". + env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".to_string()); + env_remove +} diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs new file mode 100644 index 00000000000..0d0a0da9d1c --- /dev/null +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -0,0 +1,151 @@ +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; +use std::env; +use std::io; +use std::path::Path; +use std::process::Command; + +use Arch::*; +#[allow(non_camel_case_types)] +#[derive(Copy, Clone)] +pub enum Arch { + Armv7, + Armv7s, + Arm64, + I386, + X86_64, + X86_64_macabi, +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone)] +pub enum AppleOS { + tvOS, + iOS, +} + +impl Arch { + pub fn to_string(self) -> &'static str { + match self { + Armv7 => "armv7", + Armv7s => "armv7s", + Arm64 => "arm64", + I386 => "i386", + X86_64 => "x86_64", + X86_64_macabi => "x86_64", + } + } +} + +pub fn get_sdk_root(sdk_name: &str) -> Result<String, String> { + // Following what clang does + // (https://github.com/llvm/llvm-project/blob/ + // 296a80102a9b72c3eda80558fb78a3ed8849b341/clang/lib/Driver/ToolChains/Darwin.cpp#L1661-L1678) + // to allow the SDK path to be set. (For clang, xcrun sets + // SDKROOT; for rustc, the user or build system can set it, or we + // can fall back to checking for xcrun on PATH.) + if let Ok(sdkroot) = env::var("SDKROOT") { + let p = Path::new(&sdkroot); + match sdk_name { + // Ignore `SDKROOT` if it's clearly set for the wrong platform. + "appletvos" + if sdkroot.contains("TVSimulator.platform") + || sdkroot.contains("MacOSX.platform") => {} + "appletvsimulator" + if sdkroot.contains("TVOS.platform") || sdkroot.contains("MacOSX.platform") => {} + "iphoneos" + if sdkroot.contains("iPhoneSimulator.platform") + || sdkroot.contains("MacOSX.platform") => {} + "iphonesimulator" + if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("MacOSX.platform") => { + } + "macosx10.15" + if sdkroot.contains("iPhoneOS.platform") + || sdkroot.contains("iPhoneSimulator.platform") => {} + // Ignore `SDKROOT` if it's not a valid path. + _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} + _ => return Ok(sdkroot), + } + } + let res = + Command::new("xcrun").arg("--show-sdk-path").arg("-sdk").arg(sdk_name).output().and_then( + |output| { + if output.status.success() { + Ok(String::from_utf8(output.stdout).unwrap()) + } else { + let error = String::from_utf8(output.stderr); + let error = format!("process exit with error: {}", error.unwrap()); + Err(io::Error::new(io::ErrorKind::Other, &error[..])) + } + }, + ); + + match res { + Ok(output) => Ok(output.trim().to_string()), + Err(e) => Err(format!("failed to get {} SDK path: {}", sdk_name, e)), + } +} + +fn build_pre_link_args(arch: Arch, os: AppleOS) -> Result<LinkArgs, String> { + let sdk_name = match (arch, os) { + (Arm64, AppleOS::tvOS) => "appletvos", + (X86_64, AppleOS::tvOS) => "appletvsimulator", + (Armv7, AppleOS::iOS) => "iphoneos", + (Armv7s, AppleOS::iOS) => "iphoneos", + (Arm64, AppleOS::iOS) => "iphoneos", + (I386, AppleOS::iOS) => "iphonesimulator", + (X86_64, AppleOS::iOS) => "iphonesimulator", + (X86_64_macabi, AppleOS::iOS) => "macosx10.15", + _ => unreachable!(), + }; + + let arch_name = arch.to_string(); + + let sdk_root = get_sdk_root(sdk_name)?; + + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + "-arch".to_string(), + arch_name.to_string(), + "-isysroot".to_string(), + sdk_root.clone(), + "-Wl,-syslibroot".to_string(), + sdk_root, + ], + ); + + Ok(args) +} + +fn target_cpu(arch: Arch) -> String { + match arch { + Armv7 => "cortex-a8", // iOS7 is supported on iPhone 4 and higher + Armv7s => "cortex-a9", + Arm64 => "apple-a7", + I386 => "yonah", + X86_64 => "core2", + X86_64_macabi => "core2", + } + .to_string() +} + +fn link_env_remove(arch: Arch) -> Vec<String> { + match arch { + Armv7 | Armv7s | Arm64 | I386 | X86_64 => vec!["MACOSX_DEPLOYMENT_TARGET".to_string()], + X86_64_macabi => vec!["IPHONEOS_DEPLOYMENT_TARGET".to_string()], + } +} + +pub fn opts(arch: Arch, os: AppleOS) -> Result<TargetOptions, String> { + let pre_link_args = build_pre_link_args(arch, os)?; + Ok(TargetOptions { + cpu: target_cpu(arch), + executables: true, + pre_link_args, + link_env_remove: link_env_remove(arch), + has_elf_tls: false, + eliminate_frame_pointer: false, + ..super::apple_base::opts() + }) +} diff --git a/compiler/rustc_target/src/spec/arm_base.rs b/compiler/rustc_target/src/spec/arm_base.rs new file mode 100644 index 00000000000..b74d80dc6bb --- /dev/null +++ b/compiler/rustc_target/src/spec/arm_base.rs @@ -0,0 +1,6 @@ +use crate::spec::abi::Abi; + +// All the calling conventions trigger an assertion(Unsupported calling convention) in llvm on arm +pub fn unsupported_abis() -> Vec<Abi> { + vec![Abi::Stdcall, Abi::Fastcall, Abi::Vectorcall, Abi::Thiscall, Abi::Win64, Abi::SysV64] +} diff --git a/compiler/rustc_target/src/spec/arm_linux_androideabi.rs b/compiler/rustc_target/src/spec/arm_linux_androideabi.rs new file mode 100644 index 00000000000..7109d043f51 --- /dev/null +++ b/compiler/rustc_target/src/spec/arm_linux_androideabi.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::android_base::opts(); + // https://developer.android.com/ndk/guides/abis.html#armeabi + base.features = "+strict-align,+v5te".to_string(); + base.max_atomic_width = Some(32); + + Ok(Target { + llvm_target: "arm-linux-androideabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "android".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs new file mode 100644 index 00000000000..2e3bad83e25 --- /dev/null +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.max_atomic_width = Some(64); + Ok(Target { + llvm_target: "arm-unknown-linux-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+strict-align,+v6".to_string(), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs new file mode 100644 index 00000000000..f8e357cce66 --- /dev/null +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.max_atomic_width = Some(64); + Ok(Target { + llvm_target: "arm-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+strict-align,+v6,+vfp2,-d32".to_string(), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs new file mode 100644 index 00000000000..75753af9f30 --- /dev/null +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + + // Most of these settings are copied from the arm_unknown_linux_gnueabi + // target. + base.features = "+strict-align,+v6".to_string(); + base.max_atomic_width = Some(64); + Ok(Target { + // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it + // to determine the calling convention and float ABI, and it doesn't + // support the "musleabi" value. + llvm_target: "arm-unknown-linux-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs new file mode 100644 index 00000000000..c74c88e3612 --- /dev/null +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + + // Most of these settings are copied from the arm_unknown_linux_gnueabihf + // target. + base.features = "+strict-align,+v6,+vfp2,-d32".to_string(); + base.max_atomic_width = Some(64); + Ok(Target { + // It's important we use "gnueabihf" and not "musleabihf" here. LLVM + // uses it to determine the calling convention and float ABI, and it + // doesn't support the "musleabihf" value. + llvm_target: "arm-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs new file mode 100644 index 00000000000..e0d1f2653ce --- /dev/null +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -0,0 +1,30 @@ +// Targets the Big endian Cortex-R4/R5 processor (ARMv7-R) + +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "armebv7r-unknown-none-eabi".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: "".to_string(), + target_vendor: "".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + executables: true, + linker: Some("rust-lld".to_owned()), + relocation_model: RelocModel::Static, + panic_strategy: PanicStrategy::Abort, + max_atomic_width: Some(32), + unsupported_abis: super::arm_base::unsupported_abis(), + emit_debug_gdb_scripts: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs new file mode 100644 index 00000000000..e2d37d45bf1 --- /dev/null +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -0,0 +1,31 @@ +// Targets the Cortex-R4F/R5F processor (ARMv7-R) + +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "armebv7r-unknown-none-eabihf".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + executables: true, + linker: Some("rust-lld".to_owned()), + relocation_model: RelocModel::Static, + panic_strategy: PanicStrategy::Abort, + features: "+vfp3,-d32,-fp16".to_string(), + max_atomic_width: Some(32), + unsupported_abis: super::arm_base::unsupported_abis(), + emit_debug_gdb_scripts: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs new file mode 100644 index 00000000000..2580e8b0f85 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = super::linux_base::opts(); + Ok(Target { + llvm_target: "armv4t-unknown-linux-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+soft-float,+strict-align".to_string(), + // Atomic operations provided by compiler-builtins + max_atomic_width: Some(32), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs new file mode 100644 index 00000000000..f28421dc775 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = super::linux_base::opts(); + Ok(Target { + llvm_target: "armv5te-unknown-linux-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+soft-float,+strict-align".to_string(), + // Atomic operations provided by compiler-builtins + max_atomic_width: Some(32), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs new file mode 100644 index 00000000000..fe1fa88883d --- /dev/null +++ b/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs @@ -0,0 +1,29 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = super::linux_musl_base::opts(); + Ok(Target { + // It's important we use "gnueabihf" and not "musleabihf" here. LLVM + // uses it to determine the calling convention and float ABI, and LLVM + // doesn't support the "musleabihf" value. + llvm_target: "armv5te-unknown-linux-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+soft-float,+strict-align".to_string(), + // Atomic operations provided by compiler-builtins + max_atomic_width: Some(32), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs new file mode 100644 index 00000000000..1e06f837997 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = super::freebsd_base::opts(); + Ok(Target { + llvm_target: "armv6-unknown-freebsd-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "freebsd".to_string(), + target_env: "gnueabihf".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+v6,+vfp2,-d32".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs new file mode 100644 index 00000000000..ef40085888c --- /dev/null +++ b/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.max_atomic_width = Some(64); + Ok(Target { + llvm_target: "armv6-unknown-netbsdelf-eabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "netbsd".to_string(), + target_env: "eabihf".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+v6,+vfp2,-d32".to_string(), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "__mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_apple_ios.rs b/compiler/rustc_target/src/spec/armv7_apple_ios.rs new file mode 100644 index 00000000000..393843526a8 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_apple_ios.rs @@ -0,0 +1,24 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::Armv7, AppleOS::iOS)?; + Ok(Target { + llvm_target: "armv7-apple-ios".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), + arch: "arm".to_string(), + target_os: "ios".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + features: "+v7,+vfp3,+neon".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs new file mode 100644 index 00000000000..38c6c31bd10 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target if is for the baseline of the Android v7a ABI +// in thumb mode. It's named armv7-* instead of thumbv7-* +// for historical reasons. See the thumbv7neon variant for +// enabling NEON. + +// See https://developer.android.com/ndk/guides/abis.html#v7a +// for target ABI requirements. + +pub fn target() -> TargetResult { + let mut base = super::android_base::opts(); + base.features = "+v7,+thumb-mode,+thumb2,+vfp3,-d32,-neon".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-march=armv7-a".to_string()); + + Ok(Target { + llvm_target: "armv7-none-linux-android".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "android".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs new file mode 100644 index 00000000000..e3f4fe0b2ef --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::cloudabi_base::opts(); + base.cpu = "cortex-a8".to_string(); + base.max_atomic_width = Some(64); + base.features = "+v7,+vfp3,+neon".to_string(); + base.unsupported_abis = super::arm_base::unsupported_abis(); + base.linker = Some("armv7-unknown-cloudabi-eabihf-cc".to_string()); + + Ok(Target { + llvm_target: "armv7-unknown-cloudabi-eabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "cloudabi".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs new file mode 100644 index 00000000000..80a9e6d7e3c --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = super::freebsd_base::opts(); + Ok(Target { + llvm_target: "armv7-unknown-freebsd-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "freebsd".to_string(), + target_env: "gnueabihf".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs new file mode 100644 index 00000000000..0f175e9aef5 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs @@ -0,0 +1,29 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target is for glibc Linux on ARMv7 without thumb-mode, NEON or +// hardfloat. + +pub fn target() -> TargetResult { + let base = super::linux_base::opts(); + Ok(Target { + llvm_target: "armv7-unknown-linux-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+v7,+thumb2,+soft-float,-neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs new file mode 100644 index 00000000000..27923457cd1 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target is for glibc Linux on ARMv7 without NEON or +// thumb-mode. See the thumbv7neon variant for enabling both. + +pub fn target() -> TargetResult { + let base = super::linux_base::opts(); + Ok(Target { + llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + // Info about features at https://wiki.debian.org/ArmHardFloatPort + features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs new file mode 100644 index 00000000000..3d1bf05237f --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs @@ -0,0 +1,34 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target is for musl Linux on ARMv7 without thumb-mode, NEON or +// hardfloat. + +pub fn target() -> TargetResult { + let base = super::linux_musl_base::opts(); + // Most of these settings are copied from the armv7_unknown_linux_gnueabi + // target. + Ok(Target { + // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it + // to determine the calling convention and float ABI, and it doesn't + // support the "musleabi" value. + llvm_target: "armv7-unknown-linux-gnueabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+v7,+thumb2,+soft-float,-neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs new file mode 100644 index 00000000000..03d7d88b0d6 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs @@ -0,0 +1,33 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target is for musl Linux on ARMv7 without thumb-mode or NEON. + +pub fn target() -> TargetResult { + let base = super::linux_musl_base::opts(); + Ok(Target { + // It's important we use "gnueabihf" and not "musleabihf" here. LLVM + // uses it to determine the calling convention and float ABI, and LLVM + // doesn't support the "musleabihf" value. + llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + // Most of these settings are copied from the armv7_unknown_linux_gnueabihf + // target. + options: TargetOptions { + features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs new file mode 100644 index 00000000000..18fc9ed2ec6 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = super::netbsd_base::opts(); + Ok(Target { + llvm_target: "armv7-unknown-netbsdelf-eabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "netbsd".to_string(), + target_env: "eabihf".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "__mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs new file mode 100644 index 00000000000..04d8702471a --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = super::vxworks_base::opts(); + Ok(Target { + llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "vxworks".to_string(), + target_env: "gnu".to_string(), + target_vendor: "wrs".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + // Info about features at https://wiki.debian.org/ArmHardFloatPort + features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs new file mode 100644 index 00000000000..1db279defff --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs @@ -0,0 +1,48 @@ +// Generic ARMv7-A target for bare-metal code - floating point disabled +// +// This is basically the `armv7-unknown-linux-gnueabi` target with some changes +// (listed below) to bring it closer to the bare-metal `thumb` & `aarch64` +// targets: +// +// - `TargetOptions.features`: added `+strict-align`. rationale: unaligned +// memory access is disabled on boot on these cores +// - linker changed to LLD. rationale: C is not strictly needed to build +// bare-metal binaries (the `gcc` linker has the advantage that it knows where C +// libraries and crt*.o are but it's not much of an advantage here); LLD is also +// faster +// - `target_os` set to `none`. rationale: matches `thumb` targets +// - `target_{env,vendor}` set to an empty string. rationale: matches `thumb` +// targets +// - `panic_strategy` set to `abort`. rationale: matches `thumb` targets +// - `relocation-model` set to `static`; also no PIE, no relro and no dynamic +// linking. rationale: matches `thumb` targets + +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Result<Target, String> { + let opts = TargetOptions { + linker: Some("rust-lld".to_owned()), + features: "+v7,+thumb2,+soft-float,-neon,+strict-align".to_string(), + executables: true, + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + unsupported_abis: super::arm_base::unsupported_abis(), + emit_debug_gdb_scripts: false, + ..Default::default() + }; + Ok(Target { + llvm_target: "armv7a-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: opts, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs new file mode 100644 index 00000000000..22c2b306b43 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -0,0 +1,36 @@ +// Generic ARMv7-A target for bare-metal code - floating point enabled (assumes +// FPU is present and emits FPU instructions) +// +// This is basically the `armv7-unknown-linux-gnueabihf` target with some +// changes (list in `armv7a_none_eabi.rs`) to bring it closer to the bare-metal +// `thumb` & `aarch64` targets. + +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Result<Target, String> { + let opts = TargetOptions { + linker: Some("rust-lld".to_owned()), + features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".to_string(), + executables: true, + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + unsupported_abis: super::arm_base::unsupported_abis(), + emit_debug_gdb_scripts: false, + ..Default::default() + }; + Ok(Target { + llvm_target: "armv7a-none-eabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: opts, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs new file mode 100644 index 00000000000..fed83997190 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -0,0 +1,30 @@ +// Targets the Little-endian Cortex-R4/R5 processor (ARMv7-R) + +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "armv7r-unknown-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: "".to_string(), + target_vendor: "".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + executables: true, + linker: Some("rust-lld".to_owned()), + relocation_model: RelocModel::Static, + panic_strategy: PanicStrategy::Abort, + max_atomic_width: Some(32), + unsupported_abis: super::arm_base::unsupported_abis(), + emit_debug_gdb_scripts: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs new file mode 100644 index 00000000000..769ac13e515 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -0,0 +1,31 @@ +// Targets the Little-endian Cortex-R4F/R5F processor (ARMv7-R) + +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "armv7r-unknown-none-eabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: "".to_string(), + target_vendor: "".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + executables: true, + linker: Some("rust-lld".to_owned()), + relocation_model: RelocModel::Static, + panic_strategy: PanicStrategy::Abort, + features: "+vfp3,-d32,-fp16".to_string(), + max_atomic_width: Some(32), + unsupported_abis: super::arm_base::unsupported_abis(), + emit_debug_gdb_scripts: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs new file mode 100644 index 00000000000..998a7b2e164 --- /dev/null +++ b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs @@ -0,0 +1,24 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::Armv7s, AppleOS::iOS)?; + Ok(Target { + llvm_target: "armv7s-apple-ios".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), + arch: "arm".to_string(), + target_os: "ios".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + features: "+v7,+vfp4,+neon".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs new file mode 100644 index 00000000000..d3dbc39c99d --- /dev/null +++ b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs @@ -0,0 +1,12 @@ +use super::{wasm32_unknown_emscripten, LinkerFlavor, Target}; + +pub fn target() -> Result<Target, String> { + let mut target = wasm32_unknown_emscripten::target()?; + target + .options + .post_link_args + .entry(LinkerFlavor::Em) + .or_default() + .extend(vec!["-s".to_string(), "WASM=0".to_string()]); + Ok(target) +} diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs new file mode 100644 index 00000000000..ff559c2bfd6 --- /dev/null +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -0,0 +1,51 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +/// A base target for AVR devices using the GNU toolchain. +/// +/// Requires GNU avr-gcc and avr-binutils on the host system. +pub fn target(target_cpu: String) -> TargetResult { + Ok(Target { + arch: "avr".to_string(), + data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".to_string(), + llvm_target: "avr-unknown-unknown".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "16".to_string(), + linker_flavor: LinkerFlavor::Gcc, + target_os: "unknown".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + target_c_int_width: 16.to_string(), + options: TargetOptions { + cpu: target_cpu.clone(), + exe_suffix: ".elf".to_string(), + + linker: Some("avr-gcc".to_owned()), + dynamic_linking: false, + executables: true, + linker_is_gnu: true, + has_rpath: false, + position_independent_executables: false, + eh_frame_header: false, + pre_link_args: vec![( + LinkerFlavor::Gcc, + vec![ + format!("-mmcu={}", target_cpu), + // We want to be able to strip as much executable code as possible + // from the linker command line, and this flag indicates to the + // linker that it can avoid linking in dynamic libraries that don't + // actually satisfy any symbols up to that point (as with many other + // resolutions the linker does). This option only applies to all + // following libraries so we're sure to pass it as one of the first + // arguments. + "-Wl,--as-needed".to_string(), + ], + )] + .into_iter() + .collect(), + late_link_args: vec![(LinkerFlavor::Gcc, vec!["-lgcc".to_owned()])] + .into_iter() + .collect(), + ..TargetOptions::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs new file mode 100644 index 00000000000..5d22598b57b --- /dev/null +++ b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs @@ -0,0 +1,5 @@ +use crate::spec::TargetResult; + +pub fn target() -> TargetResult { + super::avr_gnu_base::target("atmega328".to_owned()) +} diff --git a/compiler/rustc_target/src/spec/cloudabi_base.rs b/compiler/rustc_target/src/spec/cloudabi_base.rs new file mode 100644 index 00000000000..39039435f58 --- /dev/null +++ b/compiler/rustc_target/src/spec/cloudabi_base.rs @@ -0,0 +1,35 @@ +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions, TlsModel}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + "-Wl,-Bstatic".to_string(), + "-Wl,--no-dynamic-linker".to_string(), + "-Wl,--gc-sections".to_string(), + ], + ); + + TargetOptions { + executables: true, + target_family: None, + linker_is_gnu: true, + pre_link_args: args, + position_independent_executables: true, + // As CloudABI only supports static linkage, there is no need + // for dynamic TLS. The C library therefore does not provide + // __tls_get_addr(), which is normally used to perform dynamic + // TLS lookups by programs that make use of dlopen(). Only the + // "local-exec" and "initial-exec" TLS models can be used. + // + // "local-exec" is more efficient than "initial-exec", as the + // latter has one more level of indirection: it accesses the GOT + // (Global Offset Table) to obtain the effective address of a + // thread-local variable. Using a GOT is useful only when doing + // dynamic linking. + tls_model: TlsModel::LocalExec, + relro_level: RelroLevel::Full, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs new file mode 100644 index 00000000000..8991691a9a3 --- /dev/null +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -0,0 +1,145 @@ +//! Object files providing support for basic runtime facilities and added to the produced binaries +//! at the start and at the end of linking. +//! +//! Table of CRT objects for popular toolchains. +//! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc. +//! See https://dev.gentoo.org/~vapier/crt.txt for some more details. +//! +//! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |----------------------|------------------------|------------------------|------------------|-------------------|------| +//! | dynamic-nopic-exe | crt1, crti, crtbegin | crt1, crti, crtbegin | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-pic-exe | Scrt1, crti, crtbeginS | Scrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | static-nopic-exe | crt1, crti, crtbeginT | crt1, crti, crtbegin | crtbegin_static | crt2, crtbegin | crt1 | +//! | static-pic-exe | rcrt1, crti, crtbeginS | rcrt1, crti, crtbeginS | crtbegin_dynamic | crt2, crtbegin | crt1 | +//! | dynamic-dylib | crti, crtbeginS | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (gcc) | crti, crtbeginT | crti, crtbeginS | crtbegin_so | dllcrt2, crtbegin | - | +//! | static-dylib (clang) | crti, crtbeginT | N/A | crtbegin_static | dllcrt2, crtbegin | - | +//! +//! | Post-link CRT objects | glibc | musl | bionic | mingw | wasi | +//! |-----------------------|---------------|---------------|----------------|--------|------| +//! | dynamic-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - | +//! | dynamic-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - | +//! | static-nopic-exe | crtend, crtn | crtend, crtn | crtend_android | crtend | - | +//! | static-pic-exe | crtendS, crtn | crtendS, crtn | crtend_android | crtend | - | +//! | dynamic-dylib | crtendS, crtn | crtendS, crtn | crtend_so | crtend | - | +//! | static-dylib (gcc) | crtend, crtn | crtendS, crtn | crtend_so | crtend | - | +//! | static-dylib (clang) | crtendS, crtn | N/A | crtend_so | crtend | - | +//! +//! Use cases for rustc linking the CRT objects explicitly: +//! - rustc needs to add its own Rust-specific objects (mingw is the example) +//! - gcc wrapper cannot be used for some reason and linker like ld or lld is used directly. +//! - gcc wrapper pulls wrong CRT objects (e.g. from glibc when we are targeting musl). +//! +//! In general it is preferable to rely on the target's native toolchain to pull the objects. +//! However, for some targets (musl, mingw) rustc historically provides a more self-contained +//! installation not requiring users to install the native target's toolchain. +//! In that case rustc distributes the objects as a part of the target's Rust toolchain +//! and falls back to linking with them manually. +//! Unlike native toolchains, rustc only currently adds the libc's objects during linking, +//! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710) +//! when linking in self-contained mode. + +use crate::spec::LinkOutputKind; +use rustc_serialize::json::{Json, ToJson}; +use std::collections::BTreeMap; +use std::str::FromStr; + +pub type CrtObjects = BTreeMap<LinkOutputKind, Vec<String>>; + +pub(super) fn new(obj_table: &[(LinkOutputKind, &[&str])]) -> CrtObjects { + obj_table.iter().map(|(z, k)| (*z, k.iter().map(|b| b.to_string()).collect())).collect() +} + +pub(super) fn all(obj: &str) -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &[obj]), + (LinkOutputKind::DynamicPicExe, &[obj]), + (LinkOutputKind::StaticNoPicExe, &[obj]), + (LinkOutputKind::StaticPicExe, &[obj]), + (LinkOutputKind::DynamicDylib, &[obj]), + (LinkOutputKind::StaticDylib, &[obj]), + ]) +} + +pub(super) fn pre_musl_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o"]), + (LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt1.o", "crti.o"]), + (LinkOutputKind::StaticPicExe, &["rcrt1.o", "crti.o"]), + (LinkOutputKind::DynamicDylib, &["crti.o"]), + (LinkOutputKind::StaticDylib, &["crti.o"]), + ]) +} + +pub(super) fn post_musl_fallback() -> CrtObjects { + all("crtn.o") +} + +pub(super) fn pre_mingw_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticPicExe, &["crt2.o", "rsbegin.o"]), + (LinkOutputKind::DynamicDylib, &["dllcrt2.o", "rsbegin.o"]), + (LinkOutputKind::StaticDylib, &["dllcrt2.o", "rsbegin.o"]), + ]) +} + +pub(super) fn post_mingw_fallback() -> CrtObjects { + all("rsend.o") +} + +pub(super) fn pre_mingw() -> CrtObjects { + all("rsbegin.o") +} + +pub(super) fn post_mingw() -> CrtObjects { + all("rsend.o") +} + +pub(super) fn pre_wasi_fallback() -> CrtObjects { + new(&[ + (LinkOutputKind::DynamicNoPicExe, &["crt1.o"]), + (LinkOutputKind::DynamicPicExe, &["crt1.o"]), + (LinkOutputKind::StaticNoPicExe, &["crt1.o"]), + (LinkOutputKind::StaticPicExe, &["crt1.o"]), + ]) +} + +pub(super) fn post_wasi_fallback() -> CrtObjects { + new(&[]) +} + +/// Which logic to use to determine whether to fall back to the "self-contained" mode or not. +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CrtObjectsFallback { + Musl, + Mingw, + Wasm, +} + +impl FromStr for CrtObjectsFallback { + type Err = (); + + fn from_str(s: &str) -> Result<CrtObjectsFallback, ()> { + Ok(match s { + "musl" => CrtObjectsFallback::Musl, + "mingw" => CrtObjectsFallback::Mingw, + "wasm" => CrtObjectsFallback::Wasm, + _ => return Err(()), + }) + } +} + +impl ToJson for CrtObjectsFallback { + fn to_json(&self) -> Json { + match *self { + CrtObjectsFallback::Musl => "musl", + CrtObjectsFallback::Mingw => "mingw", + CrtObjectsFallback::Wasm => "wasm", + } + .to_json() + } +} diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs new file mode 100644 index 00000000000..c7062e1ca51 --- /dev/null +++ b/compiler/rustc_target/src/spec/dragonfly_base.rs @@ -0,0 +1,29 @@ +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + // GNU-style linkers will use this to omit linking to libraries + // which don't actually fulfill any relocations, but only for + // libraries which follow this flag. Thus, use it before + // specifying libraries to link to. + "-Wl,--as-needed".to_string(), + // Always enable NX protection when it is available + "-Wl,-z,noexecstack".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + linker_is_gnu: true, + has_rpath: true, + pre_link_args: args, + position_independent_executables: true, + relro_level: RelroLevel::Full, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs new file mode 100644 index 00000000000..d2a087ab62f --- /dev/null +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -0,0 +1,31 @@ +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + // GNU-style linkers will use this to omit linking to libraries + // which don't actually fulfill any relocations, but only for + // libraries which follow this flag. Thus, use it before + // specifying libraries to link to. + "-Wl,--as-needed".to_string(), + // Always enable NX protection when it is available + "-Wl,-z,noexecstack".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + linker_is_gnu: true, + has_rpath: true, + pre_link_args: args, + position_independent_executables: true, + eliminate_frame_pointer: false, // FIXME 43575 + relro_level: RelroLevel::Full, + abi_return_struct_as_int: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs new file mode 100644 index 00000000000..6f432dc1171 --- /dev/null +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -0,0 +1,42 @@ +use crate::spec::{crt_objects, LinkArgs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Lld(LldFlavor::Ld), + vec![ + "--build-id".to_string(), + "--hash-style=gnu".to_string(), + "-z".to_string(), + "max-page-size=4096".to_string(), + "-z".to_string(), + "now".to_string(), + "-z".to_string(), + "rodynamic".to_string(), + "-z".to_string(), + "separate-loadable-segments".to_string(), + "--pack-dyn-relocs=relr".to_string(), + ], + ); + + TargetOptions { + linker: Some("rust-lld".to_owned()), + lld_flavor: LldFlavor::Ld, + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + is_like_fuchsia: true, + linker_is_gnu: true, + has_rpath: false, + pre_link_args, + pre_link_objects: crt_objects::new(&[ + (LinkOutputKind::DynamicNoPicExe, &["Scrt1.o"]), + (LinkOutputKind::DynamicPicExe, &["Scrt1.o"]), + (LinkOutputKind::StaticNoPicExe, &["Scrt1.o"]), + (LinkOutputKind::StaticPicExe, &["Scrt1.o"]), + ]), + position_independent_executables: true, + has_elf_tls: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs new file mode 100644 index 00000000000..3d7ae6c302d --- /dev/null +++ b/compiler/rustc_target/src/spec/haiku_base.rs @@ -0,0 +1,13 @@ +use crate::spec::{RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + TargetOptions { + dynamic_linking: true, + executables: true, + has_rpath: false, + target_family: Some("unix".to_string()), + relro_level: RelroLevel::Full, + linker_is_gnu: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs new file mode 100644 index 00000000000..e063c94cf2c --- /dev/null +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy}; +use crate::spec::{RelocModel, TargetOptions, TlsModel}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Lld(LldFlavor::Ld), + vec!["--build-id".to_string(), "--hash-style=gnu".to_string(), "--Bstatic".to_string()], + ); + + TargetOptions { + linker: Some("rust-lld".to_owned()), + executables: true, + has_elf_tls: true, + linker_is_gnu: true, + pre_link_args, + panic_strategy: PanicStrategy::Abort, + position_independent_executables: true, + static_position_independent_executables: true, + relocation_model: RelocModel::Pic, + target_family: None, + tls_model: TlsModel::InitialExec, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/hermit_kernel_base.rs b/compiler/rustc_target/src/spec/hermit_kernel_base.rs new file mode 100644 index 00000000000..01b9f75637f --- /dev/null +++ b/compiler/rustc_target/src/spec/hermit_kernel_base.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy}; +use crate::spec::{RelocModel, TargetOptions, TlsModel}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Lld(LldFlavor::Ld), + vec!["--build-id".to_string(), "--hash-style=gnu".to_string(), "--Bstatic".to_string()], + ); + + TargetOptions { + disable_redzone: true, + linker: Some("rust-lld".to_owned()), + executables: true, + has_elf_tls: true, + linker_is_gnu: true, + pre_link_args, + panic_strategy: PanicStrategy::Abort, + position_independent_executables: true, + static_position_independent_executables: true, + relocation_model: RelocModel::Pic, + target_family: None, + tls_model: TlsModel::InitialExec, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs new file mode 100644 index 00000000000..0976acb4fb0 --- /dev/null +++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs @@ -0,0 +1,39 @@ +use crate::spec::{LinkArgs, LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "hexagonv60".to_string(); + base.max_atomic_width = Some(32); + // FIXME: HVX length defaults are per-CPU + base.features = "-small-data,+hvx-length128b".to_string(); + + base.crt_static_default = false; + base.atomic_cas = true; + base.has_rpath = true; + base.linker_is_gnu = false; + base.dynamic_linking = true; + base.executables = true; + + base.pre_link_args = LinkArgs::new(); + base.post_link_args = LinkArgs::new(); + + Ok(Target { + llvm_target: "hexagon-unknown-linux-musl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: concat!( + "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32", + ":32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32", + ":32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048", + ":2048:2048" + ) + .to_string(), + arch: "hexagon".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i386_apple_ios.rs b/compiler/rustc_target/src/spec/i386_apple_ios.rs new file mode 100644 index 00000000000..a121d49769d --- /dev/null +++ b/compiler/rustc_target/src/spec/i386_apple_ios.rs @@ -0,0 +1,21 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::I386, AppleOS::iOS)?; + Ok(Target { + llvm_target: "i386-apple-ios".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:128-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "ios".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs new file mode 100644 index 00000000000..ba712aced84 --- /dev/null +++ b/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs @@ -0,0 +1,8 @@ +use crate::spec::TargetResult; + +pub fn target() -> TargetResult { + let mut base = super::i686_pc_windows_msvc::target()?; + base.options.cpu = "pentium".to_string(); + base.llvm_target = "i586-pc-windows-msvc".to_string(); + Ok(base) +} diff --git a/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs new file mode 100644 index 00000000000..49f4f2cb6b9 --- /dev/null +++ b/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs @@ -0,0 +1,8 @@ +use crate::spec::TargetResult; + +pub fn target() -> TargetResult { + let mut base = super::i686_unknown_linux_gnu::target()?; + base.options.cpu = "pentium".to_string(); + base.llvm_target = "i586-unknown-linux-gnu".to_string(); + Ok(base) +} diff --git a/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs new file mode 100644 index 00000000000..0f2ccebd6da --- /dev/null +++ b/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs @@ -0,0 +1,8 @@ +use crate::spec::TargetResult; + +pub fn target() -> TargetResult { + let mut base = super::i686_unknown_linux_musl::target()?; + base.options.cpu = "pentium".to_string(); + base.llvm_target = "i586-unknown-linux-musl".to_string(); + Ok(base) +} diff --git a/compiler/rustc_target/src/spec/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/i686_apple_darwin.rs new file mode 100644 index 00000000000..b7a34f9740a --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_apple_darwin.rs @@ -0,0 +1,33 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::apple_base::opts(); + base.cpu = "yonah".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); + base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); + base.stack_probes = true; + base.eliminate_frame_pointer = false; + + // Clang automatically chooses a more specific target based on + // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work + // correctly, we do too. + let arch = "i686"; + let llvm_target = super::apple_base::macos_llvm_target(&arch); + + Ok(Target { + llvm_target, + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:128-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "macos".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_linux_android.rs b/compiler/rustc_target/src/spec/i686_linux_android.rs new file mode 100644 index 00000000000..79242f24026 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_linux_android.rs @@ -0,0 +1,31 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +// See https://developer.android.com/ndk/guides/abis.html#x86 +// for target ABI requirements. + +pub fn target() -> TargetResult { + let mut base = super::android_base::opts(); + + base.max_atomic_width = Some(64); + + // http://developer.android.com/ndk/guides/abis.html#x86 + base.cpu = "pentiumpro".to_string(); + base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".to_string(); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-linux-android".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "android".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs new file mode 100644 index 00000000000..33c9008bb14 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs @@ -0,0 +1,34 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_gnu_base::opts(); + base.cpu = "pentium4".to_string(); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pe".to_string()]); + base.max_atomic_width = Some(64); + base.eliminate_frame_pointer = false; // Required for backtraces + base.linker = Some("i686-w64-mingw32-gcc".to_string()); + + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + base.pre_link_args + .get_mut(&LinkerFlavor::Gcc) + .unwrap() + .push("-Wl,--large-address-aware".to_string()); + + Ok(Target { + llvm_target: "i686-pc-windows-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-f80:32-n8:16:32-a:0:32-S32" + .to_string(), + arch: "x86".to_string(), + target_os: "windows".to_string(), + target_env: "gnu".to_string(), + target_vendor: "pc".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs new file mode 100644 index 00000000000..9d0922b8ce5 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs @@ -0,0 +1,38 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_msvc_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + + let pre_link_args_msvc = vec![ + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + "/LARGEADDRESSAWARE".to_string(), + // Ensure the linker will only produce an image if it can also produce a table of + // the image's safe exception handlers. + // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers + "/SAFESEH".to_string(), + ]; + base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + base.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .extend(pre_link_args_msvc); + + Ok(Target { + llvm_target: "i686-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-f80:32-n8:16:32-a:0:32-S32" + .to_string(), + arch: "x86".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "pc".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs new file mode 100644 index 00000000000..729b1f68e00 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::cloudabi_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.linker = Some("i686-unknown-cloudabi-cc".to_string()); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-unknown-cloudabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "cloudabi".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs new file mode 100644 index 00000000000..60f2188514e --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs @@ -0,0 +1,27 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::freebsd_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + let pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + pre_link_args.push("-m32".to_string()); + pre_link_args.push("-Wl,-znotext".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-unknown-freebsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "freebsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs new file mode 100644 index 00000000000..4dc27af30da --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::haiku_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-unknown-haiku".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "haiku".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs new file mode 100644 index 00000000000..0d578f22f98 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs new file mode 100644 index 00000000000..699a0ab45e8 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs @@ -0,0 +1,40 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,-melf_i386".to_string()); + base.stack_probes = true; + + // The unwinder used by i686-unknown-linux-musl, the LLVM libunwind + // implementation, apparently relies on frame pointers existing... somehow. + // It's not clear to me why nor where this dependency is introduced, but the + // test suite does not pass with frame pointers eliminated and it passes + // with frame pointers present. + // + // If you think that this is no longer necessary, then please feel free to + // ignore! If it still passes the test suite and the bots then sounds good + // to me. + // + // This may or may not be related to this bug: + // https://llvm.org/bugs/show_bug.cgi?id=30879 + base.eliminate_frame_pointer = false; + + Ok(Target { + llvm_target: "i686-unknown-linux-musl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs new file mode 100644 index 00000000000..88b1ae7d53c --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-unknown-netbsdelf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "netbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs new file mode 100644 index 00000000000..829cd1ac1a3 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::openbsd_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-fuse-ld=lld".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-unknown-openbsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "openbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/i686_unknown_uefi.rs new file mode 100644 index 00000000000..221d5f0785c --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_unknown_uefi.rs @@ -0,0 +1,95 @@ +// This defines the ia32 target for UEFI systems as described in the UEFI specification. See the +// uefi-base module for generic UEFI options. On ia32 systems +// UEFI systems always run in protected-mode, have the interrupt-controller pre-configured and +// force a single-CPU execution. +// The cdecl ABI is used. It differs from the stdcall or fastcall ABI. +// "i686-unknown-windows" is used to get the minimal subset of windows-specific features. + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::uefi_msvc_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + + // 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 + // will trigger an exception. Rust does not inject any code that enables AVX/MMX/SSE + // instruction sets, so this must be done by the firmware. However, existing firmware is known + // to leave these uninitialized, thus triggering exceptions if we make use of them. Which is + // why we avoid them and instead use soft-floats. This is also what GRUB and friends did so + // far. + // If you initialize FP units yourself, you can override these flags with custom linker + // arguments, thus giving you access to full MMX/SSE acceleration. + base.features = "-mmx,-sse,+soft-float".to_string(); + + // Use -GNU here, because of the reason below: + // Background and Problem: + // If we use i686-unknown-windows, the LLVM IA32 MSVC generates compiler intrinsic + // _alldiv, _aulldiv, _allrem, _aullrem, _allmul, which will cause undefined symbol. + // A real issue is __aulldiv() is referred by __udivdi3() - udivmod_inner!(), from + // https://github.com/rust-lang-nursery/compiler-builtins. + // As result, rust-lld generates link error finally. + // Root-cause: + // In rust\src\llvm-project\llvm\lib\Target\X86\X86ISelLowering.cpp, + // we have below code to use MSVC intrinsics. It assumes MSVC target + // will link MSVC library. But that is NOT true in UEFI environment. + // UEFI does not link any MSVC or GCC standard library. + // if (Subtarget.isTargetKnownWindowsMSVC() || + // Subtarget.isTargetWindowsItanium()) { + // // Setup Windows compiler runtime calls. + // setLibcallName(RTLIB::SDIV_I64, "_alldiv"); + // setLibcallName(RTLIB::UDIV_I64, "_aulldiv"); + // setLibcallName(RTLIB::SREM_I64, "_allrem"); + // setLibcallName(RTLIB::UREM_I64, "_aullrem"); + // setLibcallName(RTLIB::MUL_I64, "_allmul"); + // setLibcallCallingConv(RTLIB::SDIV_I64, CallingConv::X86_StdCall); + // setLibcallCallingConv(RTLIB::UDIV_I64, CallingConv::X86_StdCall); + // setLibcallCallingConv(RTLIB::SREM_I64, CallingConv::X86_StdCall); + // setLibcallCallingConv(RTLIB::UREM_I64, CallingConv::X86_StdCall); + // setLibcallCallingConv(RTLIB::MUL_I64, CallingConv::X86_StdCall); + // } + // The compiler intrisics should be implemented by compiler-builtins. + // Unfortunately, compiler-builtins has not provided those intrinsics yet. Such as: + // i386/divdi3.S + // i386/lshrdi3.S + // i386/moddi3.S + // i386/muldi3.S + // i386/udivdi3.S + // i386/umoddi3.S + // Possible solution: + // 1. Eliminate Intrinsics generation. + // 1.1 Choose different target to bypass isTargetKnownWindowsMSVC(). + // 1.2 Remove the "Setup Windows compiler runtime calls" in LLVM + // 2. Implement Intrinsics. + // We evaluated all options. + // #2 is hard because we need implement the intrinsics (_aulldiv) generated + // from the other intrinscis (__udivdi3) implementation with the same + // functionality (udivmod_inner). If we let _aulldiv() call udivmod_inner!(), + // then we are in loop. We may have to find another way to implement udivmod_inner!(). + // #1.2 may break the existing usage. + // #1.1 seems the simplest solution today. + // The IA32 -gnu calling convention is same as the one defined in UEFI specification. + // It uses cdecl, EAX/ECX/EDX as volatile register, and EAX/EDX as return value. + // We also checked the LLVM X86TargetLowering, the differences between -gnu and -msvc + // is fmodf(f32), longjmp() and TLS. None of them impacts the UEFI code. + // As a result, we choose -gnu for i686 version before those intrisics are implemented in + // compiler-builtins. After compiler-builtins implements all required intrinsics, we may + // remove -gnu and use the default one. + Ok(Target { + llvm_target: "i686-unknown-windows-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-f80:32-n8:16:32-a:0:32-S32" + .to_string(), + target_os: "uefi".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + arch: "x86".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Link), + + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs new file mode 100644 index 00000000000..1c6d2e061bc --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs @@ -0,0 +1,33 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_uwp_gnu_base::opts(); + base.cpu = "pentium4".to_string(); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pe".to_string()]); + base.max_atomic_width = Some(64); + base.eliminate_frame_pointer = false; // Required for backtraces + + // Mark all dynamic libraries and executables as compatible with the larger 4GiB address + // space available to x86 Windows binaries on x86_64. + base.pre_link_args + .get_mut(&LinkerFlavor::Gcc) + .unwrap() + .push("-Wl,--large-address-aware".to_string()); + + Ok(Target { + llvm_target: "i686-pc-windows-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-f80:32-n8:16:32-a:0:32-S32" + .to_string(), + arch: "x86".to_string(), + target_os: "windows".to_string(), + target_env: "gnu".to_string(), + target_vendor: "uwp".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs new file mode 100644 index 00000000000..ed2dba53589 --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_uwp_msvc_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.has_elf_tls = true; + + Ok(Target { + llvm_target: "i686-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-f80:32-n8:16:32-a:0:32-S32" + .to_string(), + arch: "x86".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "uwp".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs new file mode 100644 index 00000000000..f5f66cabb2c --- /dev/null +++ b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::vxworks_base::opts(); + base.cpu = "pentium4".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "i686-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + f64:32:64-f80:32-n8:16:32-S128" + .to_string(), + arch: "x86".to_string(), + target_os: "vxworks".to_string(), + target_env: "gnu".to_string(), + target_vendor: "wrs".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs new file mode 100644 index 00000000000..214142b88fc --- /dev/null +++ b/compiler/rustc_target/src/spec/illumos_base.rs @@ -0,0 +1,49 @@ +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; +use std::default::Default; + +pub fn opts() -> TargetOptions { + let mut late_link_args = LinkArgs::new(); + late_link_args.insert( + LinkerFlavor::Gcc, + vec![ + // LLVM will insert calls to the stack protector functions + // "__stack_chk_fail" and "__stack_chk_guard" into code in native + // object files. Some platforms include these symbols directly in + // libc, but at least historically these have been provided in + // libssp.so on illumos and Solaris systems. + "-lssp".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + has_rpath: true, + target_family: Some("unix".to_string()), + is_like_solaris: true, + limit_rdylib_exports: false, // Linker doesn't support this + eliminate_frame_pointer: false, + eh_frame_header: false, + late_link_args, + + // While we support ELF TLS, rust requires a way to register + // cleanup handlers (in C, this would be something along the lines of: + // void register_callback(void (*fn)(void *), void *arg); + // (see src/libstd/sys/unix/fast_thread_local.rs) that is currently + // missing in illumos. For now at least, we must fallback to using + // pthread_{get,set}specific. + //has_elf_tls: true, + + // FIXME: Currently, rust is invoking cc to link, which ends up + // causing these to get included twice. We should eventually transition + // to having rustc invoke ld directly, in which case these will need to + // be uncommented. + // + // We want XPG6 behavior from libc and libm. See standards(5) + //pre_link_objects_exe: vec![ + // "/usr/lib/amd64/values-Xc.o".to_string(), + // "/usr/lib/amd64/values-xpg6.o".to_string(), + //], + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs new file mode 100644 index 00000000000..5caad10161d --- /dev/null +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -0,0 +1,28 @@ +use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, TargetOptions}; +//use std::process::Command; + +// Use GCC to locate code for crt* libraries from the host, not from L4Re. Note +// that a few files also come from L4Re, for these, the function shouldn't be +// used. This uses GCC for the location of the file, but GCC is required for L4Re anyway. +//fn get_path_or(filename: &str) -> String { +// let child = Command::new("gcc") +// .arg(format!("-print-file-name={}", filename)).output() +// .expect("Failed to execute GCC"); +// String::from_utf8(child.stdout) +// .expect("Couldn't read path from GCC").trim().into() +//} + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert(LinkerFlavor::Gcc, vec![]); + + TargetOptions { + executables: true, + has_elf_tls: false, + panic_strategy: PanicStrategy::Abort, + linker: Some("ld".to_string()), + pre_link_args: args, + target_family: Some("unix".to_string()), + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs new file mode 100644 index 00000000000..52892fc3592 --- /dev/null +++ b/compiler/rustc_target/src/spec/linux_base.rs @@ -0,0 +1,33 @@ +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + // We want to be able to strip as much executable code as possible + // from the linker command line, and this flag indicates to the + // linker that it can avoid linking in dynamic libraries that don't + // actually satisfy any symbols up to that point (as with many other + // resolutions the linker does). This option only applies to all + // following libraries so we're sure to pass it as one of the first + // arguments. + "-Wl,--as-needed".to_string(), + // Always enable NX protection when it is available + "-Wl,-z,noexecstack".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + linker_is_gnu: true, + has_rpath: true, + pre_link_args: args, + position_independent_executables: true, + relro_level: RelroLevel::Full, + has_elf_tls: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/linux_kernel_base.rs b/compiler/rustc_target/src/spec/linux_kernel_base.rs new file mode 100644 index 00000000000..6d929d12447 --- /dev/null +++ b/compiler/rustc_target/src/spec/linux_kernel_base.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Gcc, + vec!["-Wl,--as-needed".to_string(), "-Wl,-z,noexecstack".to_string()], + ); + + TargetOptions { + disable_redzone: true, + panic_strategy: PanicStrategy::Abort, + stack_probes: true, + eliminate_frame_pointer: false, + linker_is_gnu: true, + position_independent_executables: true, + needs_plt: true, + relro_level: RelroLevel::Full, + relocation_model: RelocModel::Static, + pre_link_args, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs new file mode 100644 index 00000000000..b90e91d2901 --- /dev/null +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -0,0 +1,17 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::TargetOptions; + +pub fn opts() -> TargetOptions { + let mut base = super::linux_base::opts(); + + base.pre_link_objects_fallback = crt_objects::pre_musl_fallback(); + base.post_link_objects_fallback = crt_objects::post_musl_fallback(); + base.crt_objects_fallback = Some(CrtObjectsFallback::Musl); + + // These targets statically link libc by default + base.crt_static_default = true; + // These targets allow the user to choose between static and dynamic linking. + base.crt_static_respected = true; + + base +} diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs new file mode 100644 index 00000000000..b2ea8a6f388 --- /dev/null +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mips64-unknown-linux-gnuabi64".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), + arch: "mips64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + // NOTE(mips64r2) matches C toolchain + cpu: "mips64r2".to_string(), + features: "+mips64r2".to_string(), + max_atomic_width: Some(64), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs new file mode 100644 index 00000000000..17584de1144 --- /dev/null +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "mips64r2".to_string(); + base.features = "+mips64r2".to_string(); + base.max_atomic_width = Some(64); + Ok(Target { + // LLVM doesn't recognize "muslabi64" yet. + llvm_target: "mips64-unknown-linux-musl".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), + arch: "mips64".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs new file mode 100644 index 00000000000..48aea4a39b0 --- /dev/null +++ b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mips64el-unknown-linux-gnuabi64".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), + arch: "mips64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + // NOTE(mips64r2) matches C toolchain + cpu: "mips64r2".to_string(), + features: "+mips64r2".to_string(), + max_atomic_width: Some(64), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs new file mode 100644 index 00000000000..c7a849a1641 --- /dev/null +++ b/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "mips64r2".to_string(); + base.features = "+mips64r2".to_string(); + base.max_atomic_width = Some(64); + Ok(Target { + // LLVM doesn't recognize "muslabi64" yet. + llvm_target: "mips64el-unknown-linux-musl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), + arch: "mips64".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs new file mode 100644 index 00000000000..e360abdb38d --- /dev/null +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mips-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + cpu: "mips32r2".to_string(), + features: "+mips32r2,+fpxx,+nooddspreg".to_string(), + max_atomic_width: Some(32), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs new file mode 100644 index 00000000000..c8d97e600d4 --- /dev/null +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "mips32r2".to_string(); + base.features = "+mips32r2,+soft-float".to_string(); + base.max_atomic_width = Some(32); + base.crt_static_default = false; + Ok(Target { + llvm_target: "mips-unknown-linux-musl".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs new file mode 100644 index 00000000000..8116b8c9cc8 --- /dev/null +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mips-unknown-linux-uclibc".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "uclibc".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + cpu: "mips32r2".to_string(), + features: "+mips32r2,+soft-float".to_string(), + max_atomic_width: Some(32), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs new file mode 100644 index 00000000000..b3bda97c8a5 --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs @@ -0,0 +1,40 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +// The PSP has custom linker requirements. +const LINKER_SCRIPT: &str = include_str!("./mipsel_sony_psp_linker_script.ld"); + +pub fn target() -> TargetResult { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["--emit-relocs".to_string()]); + + Ok(Target { + llvm_target: "mipsel-sony-psp".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "psp".to_string(), + target_env: "".to_string(), + target_vendor: "sony".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + cpu: "mips2".to_string(), + executables: true, + linker: Some("rust-lld".to_owned()), + linker_is_gnu: true, + relocation_model: RelocModel::Static, + + // PSP FPU only supports single precision floats. + features: "+single-float".to_string(), + + // PSP does not support trap-on-condition instructions. + llvm_args: vec!["-mno-check-zero-division".to_string()], + pre_link_args, + link_script: Some(LINKER_SCRIPT.to_string()), + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld b/compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld new file mode 100644 index 00000000000..1bd436d6f94 --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsel_sony_psp_linker_script.ld @@ -0,0 +1,34 @@ +ENTRY(module_start) +SECTIONS +{ + /* PRX format requires text to begin at 0 */ + .text 0 : { *(.text .text.*) } + + /* Sort stubs for convenient ordering */ + .sceStub.text : { *(.sceStub.text) *(SORT(.sceStub.text.*)) } + + /* Keep these sections around, even though they may appear unused to the linker */ + .lib.ent.top : { KEEP(*(.lib.ent.top)) } + .lib.ent : { KEEP(*(.lib.ent)) } + .lib.ent.btm : { KEEP(*(.lib.ent.btm)) } + .lib.stub.top : { KEEP(*(.lib.stub.top)) } + .lib.stub : { KEEP(*(.lib.stub)) } + .lib.stub.btm : { KEEP(*(.lib.stub.btm)) } + .eh_frame_hdr : { KEEP(*(.eh_frame_hdr)) } + + /* Add symbols for LLVM's libunwind */ + __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; + __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; + .eh_frame : + { + __eh_frame_start = .; + KEEP(*(.eh_frame)) + __eh_frame_end = .; + } + + /* These are explicitly listed to avoid being merged into .rodata */ + .rodata.sceResident : { *(.rodata.sceResident) } + .rodata.sceModuleInfo : { *(.rodata.sceModuleInfo) } + /* Sort NIDs for convenient ordering */ + .rodata.sceNid : { *(.rodata.sceNid) *(SORT(.rodata.sceNid.*)) } +} diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs new file mode 100644 index 00000000000..7e9d8cd942a --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mipsel-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + cpu: "mips32r2".to_string(), + features: "+mips32r2,+fpxx,+nooddspreg".to_string(), + max_atomic_width: Some(32), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs new file mode 100644 index 00000000000..f70cc13224f --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "mips32r2".to_string(); + base.features = "+mips32r2,+soft-float".to_string(); + base.max_atomic_width = Some(32); + base.crt_static_default = false; + Ok(Target { + llvm_target: "mipsel-unknown-linux-musl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs new file mode 100644 index 00000000000..a8152011efa --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mipsel-unknown-linux-uclibc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "uclibc".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + cpu: "mips32r2".to_string(), + features: "+mips32r2,+soft-float".to_string(), + max_atomic_width: Some(32), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs new file mode 100644 index 00000000000..36b83c63fca --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mipsisa32r6-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + cpu: "mips32r6".to_string(), + features: "+mips32r6".to_string(), + max_atomic_width: Some(32), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs new file mode 100644 index 00000000000..717ae3f1d20 --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mipsisa32r6el-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + cpu: "mips32r6".to_string(), + features: "+mips32r6".to_string(), + max_atomic_width: Some(32), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs new file mode 100644 index 00000000000..3f7d233e55f --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mipsisa64r6-unknown-linux-gnuabi64".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), + arch: "mips64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + // NOTE(mips64r6) matches C toolchain + cpu: "mips64r6".to_string(), + features: "+mips64r6".to_string(), + max_atomic_width: Some(64), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs new file mode 100644 index 00000000000..4f41b8323a9 --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "mipsisa64r6el-unknown-linux-gnuabi64".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(), + arch: "mips64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + // NOTE(mips64r6) matches C toolchain + cpu: "mips64r6".to_string(), + features: "+mips64r6".to_string(), + max_atomic_width: Some(64), + target_mcount: "_mcount".to_string(), + + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs new file mode 100644 index 00000000000..d6e8b304380 --- /dev/null +++ b/compiler/rustc_target/src/spec/mod.rs @@ -0,0 +1,1793 @@ +//! [Flexible target specification.](https://github.com/rust-lang/rfcs/pull/131) +//! +//! Rust targets a wide variety of usecases, and in the interest of flexibility, +//! allows new target triples to be defined in configuration files. Most users +//! will not need to care about these, but this is invaluable when porting Rust +//! to a new platform, and allows for an unprecedented level of control over how +//! the compiler works. +//! +//! # Using custom targets +//! +//! A target triple, as passed via `rustc --target=TRIPLE`, will first be +//! compared against the list of built-in targets. This is to ease distributing +//! rustc (no need for configuration files) and also to hold these built-in +//! targets as immutable and sacred. If `TRIPLE` is not one of the built-in +//! targets, rustc will check if a file named `TRIPLE` exists. If it does, it +//! will be loaded as the target configuration. If the file does not exist, +//! rustc will search each directory in the environment variable +//! `RUST_TARGET_PATH` for a file named `TRIPLE.json`. The first one found will +//! be loaded. If no file is found in any of those directories, a fatal error +//! will be given. +//! +//! Projects defining their own targets should use +//! `--target=path/to/my-awesome-platform.json` instead of adding to +//! `RUST_TARGET_PATH`. +//! +//! # Defining a new target +//! +//! Targets are defined using [JSON](http://json.org/). The `Target` struct in +//! this module defines the format the JSON file should take, though each +//! underscore in the field names should be replaced with a hyphen (`-`) in the +//! JSON file. Some fields are required in every target specification, such as +//! `llvm-target`, `target-endian`, `target-pointer-width`, `data-layout`, +//! `arch`, and `os`. In general, options passed to rustc with `-C` override +//! the target's settings, though `target-feature` and `link-args` will *add* +//! to the list specified by the target, rather than replace. + +use crate::spec::abi::{lookup as lookup_abi, Abi}; +use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; +use rustc_serialize::json::{Json, ToJson}; +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::{fmt, io}; + +use rustc_macros::HashStable_Generic; + +pub mod abi; +pub mod crt_objects; + +mod android_base; +mod apple_base; +mod apple_sdk_base; +mod arm_base; +mod avr_gnu_base; +mod cloudabi_base; +mod dragonfly_base; +mod freebsd_base; +mod fuchsia_base; +mod haiku_base; +mod hermit_base; +mod hermit_kernel_base; +mod illumos_base; +mod l4re_base; +mod linux_base; +mod linux_kernel_base; +mod linux_musl_base; +mod msvc_base; +mod netbsd_base; +mod openbsd_base; +mod redox_base; +mod riscv_base; +mod solaris_base; +mod thumb_base; +mod uefi_msvc_base; +mod vxworks_base; +mod wasm32_base; +mod windows_gnu_base; +mod windows_msvc_base; +mod windows_uwp_gnu_base; +mod windows_uwp_msvc_base; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum LinkerFlavor { + Em, + Gcc, + Ld, + Msvc, + Lld(LldFlavor), + PtxLinker, +} + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum LldFlavor { + Wasm, + Ld64, + Ld, + Link, +} + +impl LldFlavor { + fn from_str(s: &str) -> Option<Self> { + Some(match s { + "darwin" => LldFlavor::Ld64, + "gnu" => LldFlavor::Ld, + "link" => LldFlavor::Link, + "wasm" => LldFlavor::Wasm, + _ => return None, + }) + } +} + +impl ToJson for LldFlavor { + fn to_json(&self) -> Json { + match *self { + LldFlavor::Ld64 => "darwin", + LldFlavor::Ld => "gnu", + LldFlavor::Link => "link", + LldFlavor::Wasm => "wasm", + } + .to_json() + } +} + +impl ToJson for LinkerFlavor { + fn to_json(&self) -> Json { + self.desc().to_json() + } +} +macro_rules! flavor_mappings { + ($((($($flavor:tt)*), $string:expr),)*) => ( + impl LinkerFlavor { + pub const fn one_of() -> &'static str { + concat!("one of: ", $($string, " ",)*) + } + + pub fn from_str(s: &str) -> Option<Self> { + Some(match s { + $($string => $($flavor)*,)* + _ => return None, + }) + } + + pub fn desc(&self) -> &str { + match *self { + $($($flavor)* => $string,)* + } + } + } + ) +} + +flavor_mappings! { + ((LinkerFlavor::Em), "em"), + ((LinkerFlavor::Gcc), "gcc"), + ((LinkerFlavor::Ld), "ld"), + ((LinkerFlavor::Msvc), "msvc"), + ((LinkerFlavor::PtxLinker), "ptx-linker"), + ((LinkerFlavor::Lld(LldFlavor::Wasm)), "wasm-ld"), + ((LinkerFlavor::Lld(LldFlavor::Ld64)), "ld64.lld"), + ((LinkerFlavor::Lld(LldFlavor::Ld)), "ld.lld"), + ((LinkerFlavor::Lld(LldFlavor::Link)), "lld-link"), +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] +pub enum PanicStrategy { + Unwind, + Abort, +} + +impl PanicStrategy { + pub fn desc(&self) -> &str { + match *self { + PanicStrategy::Unwind => "unwind", + PanicStrategy::Abort => "abort", + } + } +} + +impl ToJson for PanicStrategy { + fn to_json(&self) -> Json { + match *self { + PanicStrategy::Abort => "abort".to_json(), + PanicStrategy::Unwind => "unwind".to_json(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable)] +pub enum RelroLevel { + Full, + Partial, + Off, + None, +} + +impl RelroLevel { + pub fn desc(&self) -> &str { + match *self { + RelroLevel::Full => "full", + RelroLevel::Partial => "partial", + RelroLevel::Off => "off", + RelroLevel::None => "none", + } + } +} + +impl FromStr for RelroLevel { + type Err = (); + + fn from_str(s: &str) -> Result<RelroLevel, ()> { + match s { + "full" => Ok(RelroLevel::Full), + "partial" => Ok(RelroLevel::Partial), + "off" => Ok(RelroLevel::Off), + "none" => Ok(RelroLevel::None), + _ => Err(()), + } + } +} + +impl ToJson for RelroLevel { + fn to_json(&self) -> Json { + match *self { + RelroLevel::Full => "full".to_json(), + RelroLevel::Partial => "partial".to_json(), + RelroLevel::Off => "off".to_json(), + RelroLevel::None => "None".to_json(), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable)] +pub enum MergeFunctions { + Disabled, + Trampolines, + Aliases, +} + +impl MergeFunctions { + pub fn desc(&self) -> &str { + match *self { + MergeFunctions::Disabled => "disabled", + MergeFunctions::Trampolines => "trampolines", + MergeFunctions::Aliases => "aliases", + } + } +} + +impl FromStr for MergeFunctions { + type Err = (); + + fn from_str(s: &str) -> Result<MergeFunctions, ()> { + match s { + "disabled" => Ok(MergeFunctions::Disabled), + "trampolines" => Ok(MergeFunctions::Trampolines), + "aliases" => Ok(MergeFunctions::Aliases), + _ => Err(()), + } + } +} + +impl ToJson for MergeFunctions { + fn to_json(&self) -> Json { + match *self { + MergeFunctions::Disabled => "disabled".to_json(), + MergeFunctions::Trampolines => "trampolines".to_json(), + MergeFunctions::Aliases => "aliases".to_json(), + } + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum RelocModel { + Static, + Pic, + DynamicNoPic, + Ropi, + Rwpi, + RopiRwpi, +} + +impl FromStr for RelocModel { + type Err = (); + + fn from_str(s: &str) -> Result<RelocModel, ()> { + Ok(match s { + "static" => RelocModel::Static, + "pic" => RelocModel::Pic, + "dynamic-no-pic" => RelocModel::DynamicNoPic, + "ropi" => RelocModel::Ropi, + "rwpi" => RelocModel::Rwpi, + "ropi-rwpi" => RelocModel::RopiRwpi, + _ => return Err(()), + }) + } +} + +impl ToJson for RelocModel { + fn to_json(&self) -> Json { + match *self { + RelocModel::Static => "static", + RelocModel::Pic => "pic", + RelocModel::DynamicNoPic => "dynamic-no-pic", + RelocModel::Ropi => "ropi", + RelocModel::Rwpi => "rwpi", + RelocModel::RopiRwpi => "ropi-rwpi", + } + .to_json() + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum CodeModel { + Tiny, + Small, + Kernel, + Medium, + Large, +} + +impl FromStr for CodeModel { + type Err = (); + + fn from_str(s: &str) -> Result<CodeModel, ()> { + Ok(match s { + "tiny" => CodeModel::Tiny, + "small" => CodeModel::Small, + "kernel" => CodeModel::Kernel, + "medium" => CodeModel::Medium, + "large" => CodeModel::Large, + _ => return Err(()), + }) + } +} + +impl ToJson for CodeModel { + fn to_json(&self) -> Json { + match *self { + CodeModel::Tiny => "tiny", + CodeModel::Small => "small", + CodeModel::Kernel => "kernel", + CodeModel::Medium => "medium", + CodeModel::Large => "large", + } + .to_json() + } +} + +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum TlsModel { + GeneralDynamic, + LocalDynamic, + InitialExec, + LocalExec, +} + +impl FromStr for TlsModel { + type Err = (); + + fn from_str(s: &str) -> Result<TlsModel, ()> { + Ok(match s { + // Note the difference "general" vs "global" difference. The model name is "general", + // but the user-facing option name is "global" for consistency with other compilers. + "global-dynamic" => TlsModel::GeneralDynamic, + "local-dynamic" => TlsModel::LocalDynamic, + "initial-exec" => TlsModel::InitialExec, + "local-exec" => TlsModel::LocalExec, + _ => return Err(()), + }) + } +} + +impl ToJson for TlsModel { + fn to_json(&self) -> Json { + match *self { + TlsModel::GeneralDynamic => "global-dynamic", + TlsModel::LocalDynamic => "local-dynamic", + TlsModel::InitialExec => "initial-exec", + TlsModel::LocalExec => "local-exec", + } + .to_json() + } +} + +/// Everything is flattened to a single enum to make the json encoding/decoding less annoying. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub enum LinkOutputKind { + /// Dynamically linked non position-independent executable. + DynamicNoPicExe, + /// Dynamically linked position-independent executable. + DynamicPicExe, + /// Statically linked non position-independent executable. + StaticNoPicExe, + /// Statically linked position-independent executable. + StaticPicExe, + /// Regular dynamic library ("dynamically linked"). + DynamicDylib, + /// Dynamic library with bundled libc ("statically linked"). + StaticDylib, +} + +impl LinkOutputKind { + fn as_str(&self) -> &'static str { + match self { + LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe", + LinkOutputKind::DynamicPicExe => "dynamic-pic-exe", + LinkOutputKind::StaticNoPicExe => "static-nopic-exe", + LinkOutputKind::StaticPicExe => "static-pic-exe", + LinkOutputKind::DynamicDylib => "dynamic-dylib", + LinkOutputKind::StaticDylib => "static-dylib", + } + } + + pub(super) fn from_str(s: &str) -> Option<LinkOutputKind> { + Some(match s { + "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, + "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, + "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, + "static-pic-exe" => LinkOutputKind::StaticPicExe, + "dynamic-dylib" => LinkOutputKind::DynamicDylib, + "static-dylib" => LinkOutputKind::StaticDylib, + _ => return None, + }) + } +} + +impl fmt::Display for LinkOutputKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_str()) + } +} + +pub enum LoadTargetError { + BuiltinTargetNotFound(String), + Other(String), +} + +pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<String>>; +pub type TargetResult = Result<Target, String>; + +macro_rules! supported_targets { + ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { + $(mod $module;)+ + + /// List of supported targets + const TARGETS: &[&str] = &[$($($triple),+),+]; + + fn load_specific(target: &str) -> Result<Target, LoadTargetError> { + match target { + $( + $($triple)|+ => { + let mut t = $module::target() + .map_err(LoadTargetError::Other)?; + t.options.is_builtin = true; + + // round-trip through the JSON parser to ensure at + // run-time that the parser works correctly + t = Target::from_json(t.to_json()) + .map_err(LoadTargetError::Other)?; + debug!("got builtin target: {:?}", t); + Ok(t) + }, + )+ + _ => Err(LoadTargetError::BuiltinTargetNotFound( + format!("Unable to find target: {}", target))) + } + } + + pub fn get_targets() -> impl Iterator<Item = String> { + TARGETS.iter().filter_map(|t| -> Option<String> { + load_specific(t) + .and(Ok(t.to_string())) + .ok() + }) + } + + #[cfg(test)] + mod tests { + mod tests_impl; + + // Cannot put this into a separate file without duplication, make an exception. + $( + #[test] // `#[test]` + fn $module() { + tests_impl::test_target(super::$module::target()); + } + )+ + } + }; +} + +supported_targets! { + ("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu), + ("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32), + ("i686-unknown-linux-gnu", i686_unknown_linux_gnu), + ("i586-unknown-linux-gnu", i586_unknown_linux_gnu), + ("mips-unknown-linux-gnu", mips_unknown_linux_gnu), + ("mips64-unknown-linux-gnuabi64", mips64_unknown_linux_gnuabi64), + ("mips64el-unknown-linux-gnuabi64", mips64el_unknown_linux_gnuabi64), + ("mipsisa32r6-unknown-linux-gnu", mipsisa32r6_unknown_linux_gnu), + ("mipsisa32r6el-unknown-linux-gnu", mipsisa32r6el_unknown_linux_gnu), + ("mipsisa64r6-unknown-linux-gnuabi64", mipsisa64r6_unknown_linux_gnuabi64), + ("mipsisa64r6el-unknown-linux-gnuabi64", mipsisa64r6el_unknown_linux_gnuabi64), + ("mipsel-unknown-linux-gnu", mipsel_unknown_linux_gnu), + ("powerpc-unknown-linux-gnu", powerpc_unknown_linux_gnu), + ("powerpc-unknown-linux-gnuspe", powerpc_unknown_linux_gnuspe), + ("powerpc-unknown-linux-musl", powerpc_unknown_linux_musl), + ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu), + ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl), + ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), + ("powerpc64le-unknown-linux-musl", powerpc64le_unknown_linux_musl), + ("s390x-unknown-linux-gnu", s390x_unknown_linux_gnu), + ("sparc-unknown-linux-gnu", sparc_unknown_linux_gnu), + ("sparc64-unknown-linux-gnu", sparc64_unknown_linux_gnu), + ("arm-unknown-linux-gnueabi", arm_unknown_linux_gnueabi), + ("arm-unknown-linux-gnueabihf", arm_unknown_linux_gnueabihf), + ("arm-unknown-linux-musleabi", arm_unknown_linux_musleabi), + ("arm-unknown-linux-musleabihf", arm_unknown_linux_musleabihf), + ("armv4t-unknown-linux-gnueabi", armv4t_unknown_linux_gnueabi), + ("armv5te-unknown-linux-gnueabi", armv5te_unknown_linux_gnueabi), + ("armv5te-unknown-linux-musleabi", armv5te_unknown_linux_musleabi), + ("armv7-unknown-linux-gnueabi", armv7_unknown_linux_gnueabi), + ("armv7-unknown-linux-gnueabihf", armv7_unknown_linux_gnueabihf), + ("thumbv7neon-unknown-linux-gnueabihf", thumbv7neon_unknown_linux_gnueabihf), + ("thumbv7neon-unknown-linux-musleabihf", thumbv7neon_unknown_linux_musleabihf), + ("armv7-unknown-linux-musleabi", armv7_unknown_linux_musleabi), + ("armv7-unknown-linux-musleabihf", armv7_unknown_linux_musleabihf), + ("aarch64-unknown-linux-gnu", aarch64_unknown_linux_gnu), + ("aarch64-unknown-linux-musl", aarch64_unknown_linux_musl), + ("x86_64-unknown-linux-musl", x86_64_unknown_linux_musl), + ("i686-unknown-linux-musl", i686_unknown_linux_musl), + ("i586-unknown-linux-musl", i586_unknown_linux_musl), + ("mips-unknown-linux-musl", mips_unknown_linux_musl), + ("mipsel-unknown-linux-musl", mipsel_unknown_linux_musl), + ("mips64-unknown-linux-muslabi64", mips64_unknown_linux_muslabi64), + ("mips64el-unknown-linux-muslabi64", mips64el_unknown_linux_muslabi64), + ("hexagon-unknown-linux-musl", hexagon_unknown_linux_musl), + + ("mips-unknown-linux-uclibc", mips_unknown_linux_uclibc), + ("mipsel-unknown-linux-uclibc", mipsel_unknown_linux_uclibc), + + ("i686-linux-android", i686_linux_android), + ("x86_64-linux-android", x86_64_linux_android), + ("arm-linux-androideabi", arm_linux_androideabi), + ("armv7-linux-androideabi", armv7_linux_androideabi), + ("thumbv7neon-linux-androideabi", thumbv7neon_linux_androideabi), + ("aarch64-linux-android", aarch64_linux_android), + + ("x86_64-linux-kernel", x86_64_linux_kernel), + + ("aarch64-unknown-freebsd", aarch64_unknown_freebsd), + ("armv6-unknown-freebsd", armv6_unknown_freebsd), + ("armv7-unknown-freebsd", armv7_unknown_freebsd), + ("i686-unknown-freebsd", i686_unknown_freebsd), + ("powerpc64-unknown-freebsd", powerpc64_unknown_freebsd), + ("x86_64-unknown-freebsd", x86_64_unknown_freebsd), + + ("x86_64-unknown-dragonfly", x86_64_unknown_dragonfly), + + ("aarch64-unknown-openbsd", aarch64_unknown_openbsd), + ("i686-unknown-openbsd", i686_unknown_openbsd), + ("sparc64-unknown-openbsd", sparc64_unknown_openbsd), + ("x86_64-unknown-openbsd", x86_64_unknown_openbsd), + + ("aarch64-unknown-netbsd", aarch64_unknown_netbsd), + ("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf), + ("armv7-unknown-netbsd-eabihf", armv7_unknown_netbsd_eabihf), + ("i686-unknown-netbsd", i686_unknown_netbsd), + ("powerpc-unknown-netbsd", powerpc_unknown_netbsd), + ("sparc64-unknown-netbsd", sparc64_unknown_netbsd), + ("x86_64-unknown-netbsd", x86_64_unknown_netbsd), + ("x86_64-rumprun-netbsd", x86_64_rumprun_netbsd), + + ("i686-unknown-haiku", i686_unknown_haiku), + ("x86_64-unknown-haiku", x86_64_unknown_haiku), + + ("aarch64-apple-darwin", aarch64_apple_darwin), + ("x86_64-apple-darwin", x86_64_apple_darwin), + ("i686-apple-darwin", i686_apple_darwin), + + ("aarch64-fuchsia", aarch64_fuchsia), + ("x86_64-fuchsia", x86_64_fuchsia), + + ("avr-unknown-gnu-atmega328", avr_unknown_gnu_atmega328), + + ("x86_64-unknown-l4re-uclibc", x86_64_unknown_l4re_uclibc), + + ("aarch64-unknown-redox", aarch64_unknown_redox), + ("x86_64-unknown-redox", x86_64_unknown_redox), + + ("i386-apple-ios", i386_apple_ios), + ("x86_64-apple-ios", x86_64_apple_ios), + ("aarch64-apple-ios", aarch64_apple_ios), + ("armv7-apple-ios", armv7_apple_ios), + ("armv7s-apple-ios", armv7s_apple_ios), + ("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi), + ("aarch64-apple-tvos", aarch64_apple_tvos), + ("x86_64-apple-tvos", x86_64_apple_tvos), + + ("armebv7r-none-eabi", armebv7r_none_eabi), + ("armebv7r-none-eabihf", armebv7r_none_eabihf), + ("armv7r-none-eabi", armv7r_none_eabi), + ("armv7r-none-eabihf", armv7r_none_eabihf), + + // `x86_64-pc-solaris` is an alias for `x86_64_sun_solaris` for backwards compatibility reasons. + // (See <https://github.com/rust-lang/rust/issues/40531>.) + ("x86_64-sun-solaris", "x86_64-pc-solaris", x86_64_sun_solaris), + ("sparcv9-sun-solaris", sparcv9_sun_solaris), + + ("x86_64-unknown-illumos", x86_64_unknown_illumos), + + ("x86_64-pc-windows-gnu", x86_64_pc_windows_gnu), + ("i686-pc-windows-gnu", i686_pc_windows_gnu), + ("i686-uwp-windows-gnu", i686_uwp_windows_gnu), + ("x86_64-uwp-windows-gnu", x86_64_uwp_windows_gnu), + + ("aarch64-pc-windows-msvc", aarch64_pc_windows_msvc), + ("aarch64-uwp-windows-msvc", aarch64_uwp_windows_msvc), + ("x86_64-pc-windows-msvc", x86_64_pc_windows_msvc), + ("x86_64-uwp-windows-msvc", x86_64_uwp_windows_msvc), + ("i686-pc-windows-msvc", i686_pc_windows_msvc), + ("i686-uwp-windows-msvc", i686_uwp_windows_msvc), + ("i586-pc-windows-msvc", i586_pc_windows_msvc), + ("thumbv7a-pc-windows-msvc", thumbv7a_pc_windows_msvc), + ("thumbv7a-uwp-windows-msvc", thumbv7a_uwp_windows_msvc), + + ("asmjs-unknown-emscripten", asmjs_unknown_emscripten), + ("wasm32-unknown-emscripten", wasm32_unknown_emscripten), + ("wasm32-unknown-unknown", wasm32_unknown_unknown), + ("wasm32-wasi", wasm32_wasi), + + ("thumbv6m-none-eabi", thumbv6m_none_eabi), + ("thumbv7m-none-eabi", thumbv7m_none_eabi), + ("thumbv7em-none-eabi", thumbv7em_none_eabi), + ("thumbv7em-none-eabihf", thumbv7em_none_eabihf), + ("thumbv8m.base-none-eabi", thumbv8m_base_none_eabi), + ("thumbv8m.main-none-eabi", thumbv8m_main_none_eabi), + ("thumbv8m.main-none-eabihf", thumbv8m_main_none_eabihf), + + ("armv7a-none-eabi", armv7a_none_eabi), + ("armv7a-none-eabihf", armv7a_none_eabihf), + + ("msp430-none-elf", msp430_none_elf), + + ("aarch64-unknown-cloudabi", aarch64_unknown_cloudabi), + ("armv7-unknown-cloudabi-eabihf", armv7_unknown_cloudabi_eabihf), + ("i686-unknown-cloudabi", i686_unknown_cloudabi), + ("x86_64-unknown-cloudabi", x86_64_unknown_cloudabi), + + ("aarch64-unknown-hermit", aarch64_unknown_hermit), + ("x86_64-unknown-hermit", x86_64_unknown_hermit), + ("x86_64-unknown-hermit-kernel", x86_64_unknown_hermit_kernel), + + ("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf), + ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf), + ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf), + ("riscv64imac-unknown-none-elf", riscv64imac_unknown_none_elf), + ("riscv64gc-unknown-none-elf", riscv64gc_unknown_none_elf), + ("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu), + + ("aarch64-unknown-none", aarch64_unknown_none), + ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), + + ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), + + ("x86_64-unknown-uefi", x86_64_unknown_uefi), + ("i686-unknown-uefi", i686_unknown_uefi), + + ("nvptx64-nvidia-cuda", nvptx64_nvidia_cuda), + + ("i686-wrs-vxworks", i686_wrs_vxworks), + ("x86_64-wrs-vxworks", x86_64_wrs_vxworks), + ("armv7-wrs-vxworks-eabihf", armv7_wrs_vxworks_eabihf), + ("aarch64-wrs-vxworks", aarch64_wrs_vxworks), + ("powerpc-wrs-vxworks", powerpc_wrs_vxworks), + ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe), + ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks), + + ("mipsel-sony-psp", mipsel_sony_psp), + ("thumbv4t-none-eabi", thumbv4t_none_eabi), +} + +/// Everything `rustc` knows about how to compile for a specific target. +/// +/// Every field here must be specified, and has no default value. +#[derive(PartialEq, Clone, Debug)] +pub struct Target { + /// Target triple to pass to LLVM. + pub llvm_target: String, + /// String to use as the `target_endian` `cfg` variable. + pub target_endian: String, + /// String to use as the `target_pointer_width` `cfg` variable. + pub target_pointer_width: String, + /// Width of c_int type + pub target_c_int_width: String, + /// OS name to use for conditional compilation. + pub target_os: String, + /// Environment name to use for conditional compilation. + pub target_env: String, + /// Vendor name to use for conditional compilation. + pub target_vendor: String, + /// Architecture to use for ABI considerations. Valid options include: "x86", + /// "x86_64", "arm", "aarch64", "mips", "powerpc", "powerpc64", and others. + pub arch: String, + /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. + pub data_layout: String, + /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed + /// on the command line. + pub linker_flavor: LinkerFlavor, + /// Optional settings with defaults. + pub options: TargetOptions, +} + +pub trait HasTargetSpec { + fn target_spec(&self) -> &Target; +} + +impl HasTargetSpec for Target { + fn target_spec(&self) -> &Target { + self + } +} + +/// Optional aspects of a target specification. +/// +/// This has an implementation of `Default`, see each field for what the default is. In general, +/// these try to take "minimal defaults" that don't assume anything about the runtime they run in. +#[derive(PartialEq, Clone, Debug)] +pub struct TargetOptions { + /// Whether the target is built-in or loaded from a custom target specification. + pub is_builtin: bool, + + /// Linker to invoke + pub linker: Option<String>, + + /// LLD flavor used if `lld` (or `rust-lld`) is specified as a linker + /// without clarifying its flavor in any way. + pub lld_flavor: LldFlavor, + + /// Linker arguments that are passed *before* any user-defined libraries. + pub pre_link_args: LinkArgs, + /// Objects to link before and after all other object code. + pub pre_link_objects: CrtObjects, + pub post_link_objects: CrtObjects, + /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the + /// target's native gcc and fall back to the "self-contained" mode and pull them manually. + /// See `crt_objects.rs` for some more detailed documentation. + pub pre_link_objects_fallback: CrtObjects, + pub post_link_objects_fallback: CrtObjects, + /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. + pub crt_objects_fallback: Option<CrtObjectsFallback>, + + /// Linker arguments that are unconditionally passed after any + /// user-defined but before post-link objects. Standard platform + /// libraries that should be always be linked to, usually go here. + pub late_link_args: LinkArgs, + /// Linker arguments used in addition to `late_link_args` if at least one + /// Rust dependency is dynamically linked. + pub late_link_args_dynamic: LinkArgs, + /// Linker arguments used in addition to `late_link_args` if aall Rust + /// dependencies are statically linked. + pub late_link_args_static: LinkArgs, + /// Linker arguments that are unconditionally passed *after* any + /// user-defined libraries. + pub post_link_args: LinkArgs, + /// Optional link script applied to `dylib` and `executable` crate types. + /// This is a string containing the script, not a path. Can only be applied + /// to linkers where `linker_is_gnu` is true. + pub link_script: Option<String>, + + /// Environment variables to be set for the linker invocation. + pub link_env: Vec<(String, String)>, + /// Environment variables to be removed for the linker invocation. + pub link_env_remove: Vec<String>, + + /// Extra arguments to pass to the external assembler (when used) + pub asm_args: Vec<String>, + + /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults + /// to "generic". + pub cpu: String, + /// Default target features to pass to LLVM. These features will *always* be + /// passed, and cannot be disabled even via `-C`. Corresponds to `llc + /// -mattr=$features`. + pub features: String, + /// Whether dynamic linking is available on this target. Defaults to false. + pub dynamic_linking: bool, + /// If dynamic linking is available, whether only cdylibs are supported. + pub only_cdylib: bool, + /// Whether executables are available on this target. iOS, for example, only allows static + /// libraries. Defaults to false. + pub executables: bool, + /// Relocation model to use in object file. Corresponds to `llc + /// -relocation-model=$relocation_model`. Defaults to `Pic`. + pub relocation_model: RelocModel, + /// Code model to use. Corresponds to `llc -code-model=$code_model`. + /// Defaults to `None` which means "inherited from the base LLVM target". + pub code_model: Option<CodeModel>, + /// TLS model to use. Options are "global-dynamic" (default), "local-dynamic", "initial-exec" + /// and "local-exec". This is similar to the -ftls-model option in GCC/Clang. + pub tls_model: TlsModel, + /// Do not emit code that uses the "red zone", if the ABI has one. Defaults to false. + pub disable_redzone: bool, + /// Eliminate frame pointers from stack frames if possible. Defaults to true. + pub eliminate_frame_pointer: bool, + /// Emit each function in its own section. Defaults to true. + pub function_sections: bool, + /// String to prepend to the name of every dynamic library. Defaults to "lib". + pub dll_prefix: String, + /// String to append to the name of every dynamic library. Defaults to ".so". + pub dll_suffix: String, + /// String to append to the name of every executable. + pub exe_suffix: String, + /// String to prepend to the name of every static library. Defaults to "lib". + pub staticlib_prefix: String, + /// String to append to the name of every static library. Defaults to ".a". + pub staticlib_suffix: String, + /// OS family to use for conditional compilation. Valid options: "unix", "windows". + pub target_family: Option<String>, + /// Whether the target toolchain's ABI supports returning small structs as an integer. + pub abi_return_struct_as_int: bool, + /// Whether the target toolchain is like macOS's. Only useful for compiling against iOS/macOS, + /// in particular running dsymutil and some other stuff like `-dead_strip`. Defaults to false. + pub is_like_osx: bool, + /// Whether the target toolchain is like Solaris's. + /// Only useful for compiling against Illumos/Solaris, + /// as they have a different set of linker flags. Defaults to false. + pub is_like_solaris: bool, + /// Whether the target toolchain is like Windows'. Only useful for compiling against Windows, + /// only really used for figuring out how to find libraries, since Windows uses its own + /// library naming convention. Defaults to false. + pub is_like_windows: bool, + pub is_like_msvc: bool, + /// Whether the target toolchain is like Android's. Only useful for compiling against Android. + /// Defaults to false. + pub is_like_android: bool, + /// Whether the target toolchain is like Emscripten's. Only useful for compiling with + /// Emscripten toolchain. + /// Defaults to false. + pub is_like_emscripten: bool, + /// Whether the target toolchain is like Fuchsia's. + pub is_like_fuchsia: bool, + /// Whether the linker support GNU-like arguments such as -O. Defaults to false. + pub linker_is_gnu: bool, + /// The MinGW toolchain has a known issue that prevents it from correctly + /// handling COFF object files with more than 2<sup>15</sup> sections. Since each weak + /// symbol needs its own COMDAT section, weak linkage implies a large + /// number sections that easily exceeds the given limit for larger + /// codebases. Consequently we want a way to disallow weak linkage on some + /// platforms. + pub allows_weak_linkage: bool, + /// Whether the linker support rpaths or not. Defaults to false. + pub has_rpath: bool, + /// Whether to disable linking to the default libraries, typically corresponds + /// to `-nodefaultlibs`. Defaults to true. + pub no_default_libraries: bool, + /// Dynamically linked executables can be compiled as position independent + /// if the default relocation model of position independent code is not + /// changed. This is a requirement to take advantage of ASLR, as otherwise + /// the functions in the executable are not randomized and can be used + /// during an exploit of a vulnerability in any code. + pub position_independent_executables: bool, + /// Executables that are both statically linked and position-independent are supported. + pub static_position_independent_executables: bool, + /// Determines if the target always requires using the PLT for indirect + /// library calls or not. This controls the default value of the `-Z plt` flag. + pub needs_plt: bool, + /// Either partial, full, or off. Full RELRO makes the dynamic linker + /// resolve all symbols at startup and marks the GOT read-only before + /// starting the program, preventing overwriting the GOT. + pub relro_level: RelroLevel, + /// Format that archives should be emitted in. This affects whether we use + /// LLVM to assemble an archive or fall back to the system linker, and + /// currently only "gnu" is used to fall into LLVM. Unknown strings cause + /// the system linker to be used. + pub archive_format: String, + /// Is asm!() allowed? Defaults to true. + pub allow_asm: bool, + /// Whether the runtime startup code requires the `main` function be passed + /// `argc` and `argv` values. + pub main_needs_argc_argv: bool, + + /// Flag indicating whether ELF TLS (e.g., #[thread_local]) is available for + /// this target. + pub has_elf_tls: bool, + // This is mainly for easy compatibility with emscripten. + // If we give emcc .o files that are actually .bc files it + // will 'just work'. + pub obj_is_bitcode: bool, + /// Whether the target requires that emitted object code includes bitcode. + pub forces_embed_bitcode: bool, + /// Content of the LLVM cmdline section associated with embedded bitcode. + pub bitcode_llvm_cmdline: String, + + /// Don't use this field; instead use the `.min_atomic_width()` method. + pub min_atomic_width: Option<u64>, + + /// Don't use this field; instead use the `.max_atomic_width()` method. + pub max_atomic_width: Option<u64>, + + /// Whether the target supports atomic CAS operations natively + pub atomic_cas: bool, + + /// Panic strategy: "unwind" or "abort" + pub panic_strategy: PanicStrategy, + + /// A list of ABIs unsupported by the current target. Note that generic ABIs + /// are considered to be supported on all platforms and cannot be marked + /// unsupported. + pub unsupported_abis: Vec<Abi>, + + /// Whether or not linking dylibs to a static CRT is allowed. + pub crt_static_allows_dylibs: bool, + /// Whether or not the CRT is statically linked by default. + pub crt_static_default: bool, + /// Whether or not crt-static is respected by the compiler (or is a no-op). + pub crt_static_respected: bool, + + /// Whether or not stack probes (__rust_probestack) are enabled + pub stack_probes: bool, + + /// The minimum alignment for global symbols. + pub min_global_align: Option<u64>, + + /// Default number of codegen units to use in debug mode + pub default_codegen_units: Option<u64>, + + /// Whether to generate trap instructions in places where optimization would + /// otherwise produce control flow that falls through into unrelated memory. + pub trap_unreachable: bool, + + /// This target requires everything to be compiled with LTO to emit a final + /// executable, aka there is no native linker for this target. + pub requires_lto: bool, + + /// This target has no support for threads. + pub singlethread: bool, + + /// Whether library functions call lowering/optimization is disabled in LLVM + /// for this target unconditionally. + pub no_builtins: bool, + + /// The default visibility for symbols in this target should be "hidden" + /// rather than "default" + pub default_hidden_visibility: bool, + + /// Whether a .debug_gdb_scripts section will be added to the output object file + pub emit_debug_gdb_scripts: bool, + + /// Whether or not to unconditionally `uwtable` attributes on functions, + /// typically because the platform needs to unwind for things like stack + /// unwinders. + pub requires_uwtable: bool, + + /// Whether or not SIMD types are passed by reference in the Rust ABI, + /// typically required if a target can be compiled with a mixed set of + /// target features. This is `true` by default, and `false` for targets like + /// wasm32 where the whole program either has simd or not. + pub simd_types_indirect: bool, + + /// Pass a list of symbol which should be exported in the dylib to the linker. + pub limit_rdylib_exports: bool, + + /// If set, have the linker export exactly these symbols, instead of using + /// the usual logic to figure this out from the crate itself. + pub override_export_symbols: Option<Vec<String>>, + + /// Determines how or whether the MergeFunctions LLVM pass should run for + /// this target. Either "disabled", "trampolines", or "aliases". + /// The MergeFunctions pass is generally useful, but some targets may need + /// to opt out. The default is "aliases". + /// + /// Workaround for: https://github.com/rust-lang/rust/issues/57356 + pub merge_functions: MergeFunctions, + + /// Use platform dependent mcount function + pub target_mcount: String, + + /// LLVM ABI name, corresponds to the '-mabi' parameter available in multilib C compilers + pub llvm_abiname: String, + + /// Whether or not RelaxElfRelocation flag will be passed to the linker + pub relax_elf_relocations: bool, + + /// Additional arguments to pass to LLVM, similar to the `-C llvm-args` codegen option. + pub llvm_args: Vec<String>, + + /// Whether to use legacy .ctors initialization hooks rather than .init_array. Defaults + /// to false (uses .init_array). + pub use_ctors_section: bool, + + /// Whether the linker is instructed to add a `GNU_EH_FRAME` ELF header + /// used to locate unwinding information is passed + /// (only has effect if the linker is `ld`-like). + pub eh_frame_header: bool, +} + +impl Default for TargetOptions { + /// Creates a set of "sane defaults" for any target. This is still + /// incomplete, and if used for compilation, will certainly not work. + fn default() -> TargetOptions { + TargetOptions { + is_builtin: false, + linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()), + lld_flavor: LldFlavor::Ld, + pre_link_args: LinkArgs::new(), + post_link_args: LinkArgs::new(), + link_script: None, + asm_args: Vec::new(), + cpu: "generic".to_string(), + features: String::new(), + dynamic_linking: false, + only_cdylib: false, + executables: false, + relocation_model: RelocModel::Pic, + code_model: None, + tls_model: TlsModel::GeneralDynamic, + disable_redzone: false, + eliminate_frame_pointer: true, + function_sections: true, + dll_prefix: "lib".to_string(), + dll_suffix: ".so".to_string(), + exe_suffix: String::new(), + staticlib_prefix: "lib".to_string(), + staticlib_suffix: ".a".to_string(), + target_family: None, + abi_return_struct_as_int: false, + is_like_osx: false, + is_like_solaris: false, + is_like_windows: false, + is_like_android: false, + is_like_emscripten: false, + is_like_msvc: false, + is_like_fuchsia: false, + linker_is_gnu: false, + allows_weak_linkage: true, + has_rpath: false, + no_default_libraries: true, + position_independent_executables: false, + static_position_independent_executables: false, + needs_plt: false, + relro_level: RelroLevel::None, + pre_link_objects: Default::default(), + post_link_objects: Default::default(), + pre_link_objects_fallback: Default::default(), + post_link_objects_fallback: Default::default(), + crt_objects_fallback: None, + late_link_args: LinkArgs::new(), + late_link_args_dynamic: LinkArgs::new(), + late_link_args_static: LinkArgs::new(), + link_env: Vec::new(), + link_env_remove: Vec::new(), + archive_format: "gnu".to_string(), + main_needs_argc_argv: true, + allow_asm: true, + has_elf_tls: false, + obj_is_bitcode: false, + forces_embed_bitcode: false, + bitcode_llvm_cmdline: String::new(), + min_atomic_width: None, + max_atomic_width: None, + atomic_cas: true, + panic_strategy: PanicStrategy::Unwind, + unsupported_abis: vec![], + crt_static_allows_dylibs: false, + crt_static_default: false, + crt_static_respected: false, + stack_probes: false, + min_global_align: None, + default_codegen_units: None, + trap_unreachable: true, + requires_lto: false, + singlethread: false, + no_builtins: false, + default_hidden_visibility: false, + emit_debug_gdb_scripts: true, + requires_uwtable: false, + simd_types_indirect: true, + limit_rdylib_exports: true, + override_export_symbols: None, + merge_functions: MergeFunctions::Aliases, + target_mcount: "mcount".to_string(), + llvm_abiname: "".to_string(), + relax_elf_relocations: false, + llvm_args: vec![], + use_ctors_section: false, + eh_frame_header: true, + } + } +} + +impl Target { + /// Given a function ABI, turn it into the correct ABI for this target. + pub fn adjust_abi(&self, abi: Abi) -> Abi { + match abi { + Abi::System => { + if self.options.is_like_windows && self.arch == "x86" { + Abi::Stdcall + } else { + Abi::C + } + } + // These ABI kinds are ignored on non-x86 Windows targets. + // See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions + // and the individual pages for __stdcall et al. + Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => { + if self.options.is_like_windows && self.arch != "x86" { Abi::C } else { abi } + } + Abi::EfiApi => { + if self.arch == "x86_64" { + Abi::Win64 + } else { + Abi::C + } + } + abi => abi, + } + } + + /// Minimum integer size in bits that this target can perform atomic + /// operations on. + pub fn min_atomic_width(&self) -> u64 { + self.options.min_atomic_width.unwrap_or(8) + } + + /// Maximum integer size in bits that this target can perform atomic + /// operations on. + pub fn max_atomic_width(&self) -> u64 { + self.options.max_atomic_width.unwrap_or_else(|| self.target_pointer_width.parse().unwrap()) + } + + pub fn is_abi_supported(&self, abi: Abi) -> bool { + abi.generic() || !self.options.unsupported_abis.contains(&abi) + } + + /// Loads a target descriptor from a JSON object. + pub fn from_json(obj: Json) -> TargetResult { + // While ugly, this code must remain this way to retain + // compatibility with existing JSON fields and the internal + // expected naming of the Target and TargetOptions structs. + // To ensure compatibility is retained, the built-in targets + // are round-tripped through this code to catch cases where + // the JSON parser is not updated to match the structs. + + let get_req_field = |name: &str| { + obj.find(name) + .map(|s| s.as_string()) + .and_then(|os| os.map(|s| s.to_string())) + .ok_or_else(|| format!("Field {} in target specification is required", name)) + }; + + let get_opt_field = |name: &str, default: &str| { + obj.find(name) + .and_then(|s| s.as_string()) + .map(|s| s.to_string()) + .unwrap_or_else(|| default.to_string()) + }; + + let mut base = Target { + llvm_target: get_req_field("llvm-target")?, + target_endian: get_req_field("target-endian")?, + target_pointer_width: get_req_field("target-pointer-width")?, + target_c_int_width: get_req_field("target-c-int-width")?, + data_layout: get_req_field("data-layout")?, + arch: get_req_field("arch")?, + target_os: get_req_field("os")?, + target_env: get_opt_field("env", ""), + target_vendor: get_opt_field("vendor", "unknown"), + linker_flavor: LinkerFlavor::from_str(&*get_req_field("linker-flavor")?) + .ok_or_else(|| format!("linker flavor must be {}", LinkerFlavor::one_of()))?, + options: Default::default(), + }; + + macro_rules! key { + ($key_name:ident) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.find(&name).and_then(Json::as_string) { + base.options.$key_name = s.to_string(); + } + } ); + ($key_name:ident, bool) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.find(&name).and_then(Json::as_boolean) { + base.options.$key_name = s; + } + } ); + ($key_name:ident, Option<u64>) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.find(&name).and_then(Json::as_u64) { + base.options.$key_name = Some(s); + } + } ); + ($key_name:ident, MergeFunctions) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::<MergeFunctions>() { + Ok(mergefunc) => base.options.$key_name = mergefunc, + _ => return Some(Err(format!("'{}' is not a valid value for \ + merge-functions. Use 'disabled', \ + 'trampolines', or 'aliases'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, RelocModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::<RelocModel>() { + Ok(relocation_model) => base.options.$key_name = relocation_model, + _ => return Some(Err(format!("'{}' is not a valid relocation model. \ + Run `rustc --print relocation-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, CodeModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::<CodeModel>() { + Ok(code_model) => base.options.$key_name = Some(code_model), + _ => return Some(Err(format!("'{}' is not a valid code model. \ + Run `rustc --print code-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, TlsModel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::<TlsModel>() { + Ok(tls_model) => base.options.$key_name = tls_model, + _ => return Some(Err(format!("'{}' is not a valid TLS model. \ + Run `rustc --print tls-models` to \ + see the list of supported values.", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, PanicStrategy) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s { + "unwind" => base.options.$key_name = PanicStrategy::Unwind, + "abort" => base.options.$key_name = PanicStrategy::Abort, + _ => return Some(Err(format!("'{}' is not a valid value for \ + panic-strategy. Use 'unwind' or 'abort'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, RelroLevel) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::<RelroLevel>() { + Ok(level) => base.options.$key_name = level, + _ => return Some(Err(format!("'{}' is not a valid value for \ + relro-level. Use 'full', 'partial, or 'off'.", + s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, list) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(v) = obj.find(&name).and_then(Json::as_array) { + base.options.$key_name = v.iter() + .map(|a| a.as_string().unwrap().to_string()) + .collect(); + } + } ); + ($key_name:ident, opt_list) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(v) = obj.find(&name).and_then(Json::as_array) { + base.options.$key_name = Some(v.iter() + .map(|a| a.as_string().unwrap().to_string()) + .collect()); + } + } ); + ($key_name:ident, optional) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.find(&name[..]) { + base.options.$key_name = o + .as_string() + .map(|s| s.to_string() ); + } + } ); + ($key_name:ident, LldFlavor) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + if let Some(flavor) = LldFlavor::from_str(&s) { + base.options.$key_name = flavor; + } else { + return Some(Err(format!( + "'{}' is not a valid value for lld-flavor. \ + Use 'darwin', 'gnu', 'link' or 'wasm.", + s))) + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, LinkerFlavor) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().map(|s| { + LinkerFlavor::from_str(&s).ok_or_else(|| { + Err(format!("'{}' is not a valid value for linker-flavor. \ + Use 'em', 'gcc', 'ld' or 'msvc.", s)) + }) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, crt_objects_fallback) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match s.parse::<CrtObjectsFallback>() { + Ok(fallback) => base.options.$key_name = Some(fallback), + _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ + Use 'musl', 'mingw' or 'wasm'", s))), + } + Some(Ok(())) + })).unwrap_or(Ok(())) + } ); + ($key_name:ident, link_objects) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(val) = obj.find(&name[..]) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per CRT object kind.", name))?; + let mut args = CrtObjects::new(); + for (k, v) in obj { + let kind = LinkOutputKind::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for CRT object kind. \ + Use '(dynamic,static)-(nopic,pic)-exe' or \ + '(dynamic,static)-dylib'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_string().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_owned()) + }) + .collect::<Result<Vec<_>, String>>()?; + + args.insert(kind, v); + } + base.options.$key_name = args; + } + } ); + ($key_name:ident, link_args) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(val) = obj.find(&name[..]) { + let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ + JSON object with fields per linker-flavor.", name))?; + let mut args = LinkArgs::new(); + for (k, v) in obj { + let flavor = LinkerFlavor::from_str(&k).ok_or_else(|| { + format!("{}: '{}' is not a valid value for linker-flavor. \ + Use 'em', 'gcc', 'ld' or 'msvc'", name, k) + })?; + + let v = v.as_array().ok_or_else(|| + format!("{}.{}: expected a JSON array", name, k) + )?.iter().enumerate() + .map(|(i,s)| { + let s = s.as_string().ok_or_else(|| + format!("{}.{}[{}]: expected a JSON string", name, k, i))?; + Ok(s.to_owned()) + }) + .collect::<Result<Vec<_>, String>>()?; + + args.insert(flavor, v); + } + base.options.$key_name = args; + } + } ); + ($key_name:ident, env) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(a) = obj.find(&name[..]).and_then(|o| o.as_array()) { + for o in a { + if let Some(s) = o.as_string() { + let p = s.split('=').collect::<Vec<_>>(); + if p.len() == 2 { + let k = p[0].to_string(); + let v = p[1].to_string(); + base.options.$key_name.push((k, v)); + } + } + } + } + } ); + } + + key!(is_builtin, bool); + key!(linker, optional); + key!(lld_flavor, LldFlavor)?; + key!(pre_link_objects, link_objects); + key!(post_link_objects, link_objects); + key!(pre_link_objects_fallback, link_objects); + key!(post_link_objects_fallback, link_objects); + key!(crt_objects_fallback, crt_objects_fallback)?; + key!(pre_link_args, link_args); + key!(late_link_args, link_args); + key!(late_link_args_dynamic, link_args); + key!(late_link_args_static, link_args); + key!(post_link_args, link_args); + key!(link_script, optional); + key!(link_env, env); + key!(link_env_remove, list); + key!(asm_args, list); + key!(cpu); + key!(features); + key!(dynamic_linking, bool); + key!(only_cdylib, bool); + key!(executables, bool); + key!(relocation_model, RelocModel)?; + key!(code_model, CodeModel)?; + key!(tls_model, TlsModel)?; + key!(disable_redzone, bool); + key!(eliminate_frame_pointer, bool); + key!(function_sections, bool); + key!(dll_prefix); + key!(dll_suffix); + key!(exe_suffix); + key!(staticlib_prefix); + key!(staticlib_suffix); + key!(target_family, optional); + key!(abi_return_struct_as_int, bool); + key!(is_like_osx, bool); + key!(is_like_solaris, bool); + key!(is_like_windows, bool); + key!(is_like_msvc, bool); + key!(is_like_emscripten, bool); + key!(is_like_android, bool); + key!(is_like_fuchsia, bool); + key!(linker_is_gnu, bool); + key!(allows_weak_linkage, bool); + key!(has_rpath, bool); + key!(no_default_libraries, bool); + key!(position_independent_executables, bool); + key!(static_position_independent_executables, bool); + key!(needs_plt, bool); + key!(relro_level, RelroLevel)?; + key!(archive_format); + key!(allow_asm, bool); + key!(main_needs_argc_argv, bool); + key!(has_elf_tls, bool); + key!(obj_is_bitcode, bool); + key!(forces_embed_bitcode, bool); + key!(bitcode_llvm_cmdline); + key!(max_atomic_width, Option<u64>); + key!(min_atomic_width, Option<u64>); + key!(atomic_cas, bool); + key!(panic_strategy, PanicStrategy)?; + key!(crt_static_allows_dylibs, bool); + key!(crt_static_default, bool); + key!(crt_static_respected, bool); + key!(stack_probes, bool); + key!(min_global_align, Option<u64>); + key!(default_codegen_units, Option<u64>); + key!(trap_unreachable, bool); + key!(requires_lto, bool); + key!(singlethread, bool); + key!(no_builtins, bool); + key!(default_hidden_visibility, bool); + key!(emit_debug_gdb_scripts, bool); + key!(requires_uwtable, bool); + key!(simd_types_indirect, bool); + key!(limit_rdylib_exports, bool); + key!(override_export_symbols, opt_list); + key!(merge_functions, MergeFunctions)?; + key!(target_mcount); + key!(llvm_abiname); + key!(relax_elf_relocations, bool); + key!(llvm_args, list); + key!(use_ctors_section, bool); + key!(eh_frame_header, bool); + + // NB: The old name is deprecated, but support for it is retained for + // compatibility. + for name in ["abi-blacklist", "unsupported-abis"].iter() { + if let Some(array) = obj.find(name).and_then(Json::as_array) { + for name in array.iter().filter_map(|abi| abi.as_string()) { + match lookup_abi(name) { + Some(abi) => { + if abi.generic() { + return Err(format!( + "The ABI \"{}\" is considered to be supported on all \ + targets and cannot be marked unsupported", + abi + )); + } + + base.options.unsupported_abis.push(abi) + } + None => { + return Err(format!( + "Unknown ABI \"{}\" in target specification", + name + )); + } + } + } + } + } + + Ok(base) + } + + /// Search RUST_TARGET_PATH for a JSON file specifying the given target + /// triple. Note that it could also just be a bare filename already, so also + /// check for that. If one of the hardcoded targets we know about, just + /// return it directly. + /// + /// The error string could come from any of the APIs called, including + /// filesystem access and JSON decoding. + pub fn search(target_triple: &TargetTriple) -> Result<Target, String> { + use rustc_serialize::json; + use std::env; + use std::fs; + + fn load_file(path: &Path) -> Result<Target, String> { + let contents = fs::read(path).map_err(|e| e.to_string())?; + let obj = json::from_reader(&mut &contents[..]).map_err(|e| e.to_string())?; + Target::from_json(obj) + } + + match *target_triple { + TargetTriple::TargetTriple(ref target_triple) => { + // check if triple is in list of supported targets + match load_specific(target_triple) { + Ok(t) => return Ok(t), + Err(LoadTargetError::BuiltinTargetNotFound(_)) => (), + Err(LoadTargetError::Other(e)) => return Err(e), + } + + // search for a file named `target_triple`.json in RUST_TARGET_PATH + let path = { + let mut target = target_triple.to_string(); + target.push_str(".json"); + PathBuf::from(target) + }; + + let target_path = env::var_os("RUST_TARGET_PATH").unwrap_or_default(); + + // FIXME 16351: add a sane default search path? + + for dir in env::split_paths(&target_path) { + let p = dir.join(&path); + if p.is_file() { + return load_file(&p); + } + } + Err(format!("Could not find specification for target {:?}", target_triple)) + } + TargetTriple::TargetPath(ref target_path) => { + if target_path.is_file() { + return load_file(&target_path); + } + Err(format!("Target path {:?} is not a valid file", target_path)) + } + } + } +} + +impl ToJson for Target { + fn to_json(&self) -> Json { + let mut d = BTreeMap::new(); + let default: TargetOptions = Default::default(); + + macro_rules! target_val { + ($attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + d.insert(name, self.$attr.to_json()); + }}; + ($attr:ident, $key_name:expr) => {{ + let name = $key_name; + d.insert(name.to_string(), self.$attr.to_json()); + }}; + } + + macro_rules! target_option_val { + ($attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != self.options.$attr { + d.insert(name, self.options.$attr.to_json()); + } + }}; + ($attr:ident, $key_name:expr) => {{ + let name = $key_name; + if default.$attr != self.options.$attr { + d.insert(name.to_string(), self.options.$attr.to_json()); + } + }}; + (link_args - $attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != self.options.$attr { + let obj = self + .options + .$attr + .iter() + .map(|(k, v)| (k.desc().to_owned(), v.clone())) + .collect::<BTreeMap<_, _>>(); + d.insert(name, obj.to_json()); + } + }}; + (env - $attr:ident) => {{ + let name = (stringify!($attr)).replace("_", "-"); + if default.$attr != self.options.$attr { + let obj = self + .options + .$attr + .iter() + .map(|&(ref k, ref v)| k.clone() + "=" + &v) + .collect::<Vec<_>>(); + d.insert(name, obj.to_json()); + } + }}; + } + + target_val!(llvm_target); + target_val!(target_endian); + target_val!(target_pointer_width); + target_val!(target_c_int_width); + target_val!(arch); + target_val!(target_os, "os"); + target_val!(target_env, "env"); + target_val!(target_vendor, "vendor"); + target_val!(data_layout); + target_val!(linker_flavor); + + target_option_val!(is_builtin); + target_option_val!(linker); + target_option_val!(lld_flavor); + target_option_val!(pre_link_objects); + target_option_val!(post_link_objects); + target_option_val!(pre_link_objects_fallback); + target_option_val!(post_link_objects_fallback); + target_option_val!(crt_objects_fallback); + target_option_val!(link_args - pre_link_args); + target_option_val!(link_args - late_link_args); + target_option_val!(link_args - late_link_args_dynamic); + target_option_val!(link_args - late_link_args_static); + target_option_val!(link_args - post_link_args); + target_option_val!(link_script); + target_option_val!(env - link_env); + target_option_val!(link_env_remove); + target_option_val!(asm_args); + target_option_val!(cpu); + target_option_val!(features); + target_option_val!(dynamic_linking); + target_option_val!(only_cdylib); + target_option_val!(executables); + target_option_val!(relocation_model); + target_option_val!(code_model); + target_option_val!(tls_model); + target_option_val!(disable_redzone); + target_option_val!(eliminate_frame_pointer); + target_option_val!(function_sections); + target_option_val!(dll_prefix); + target_option_val!(dll_suffix); + target_option_val!(exe_suffix); + target_option_val!(staticlib_prefix); + target_option_val!(staticlib_suffix); + target_option_val!(target_family); + target_option_val!(abi_return_struct_as_int); + target_option_val!(is_like_osx); + target_option_val!(is_like_solaris); + target_option_val!(is_like_windows); + target_option_val!(is_like_msvc); + target_option_val!(is_like_emscripten); + target_option_val!(is_like_android); + target_option_val!(is_like_fuchsia); + target_option_val!(linker_is_gnu); + target_option_val!(allows_weak_linkage); + target_option_val!(has_rpath); + target_option_val!(no_default_libraries); + target_option_val!(position_independent_executables); + target_option_val!(static_position_independent_executables); + target_option_val!(needs_plt); + target_option_val!(relro_level); + target_option_val!(archive_format); + target_option_val!(allow_asm); + target_option_val!(main_needs_argc_argv); + target_option_val!(has_elf_tls); + target_option_val!(obj_is_bitcode); + target_option_val!(forces_embed_bitcode); + target_option_val!(bitcode_llvm_cmdline); + target_option_val!(min_atomic_width); + target_option_val!(max_atomic_width); + target_option_val!(atomic_cas); + target_option_val!(panic_strategy); + target_option_val!(crt_static_allows_dylibs); + target_option_val!(crt_static_default); + target_option_val!(crt_static_respected); + target_option_val!(stack_probes); + target_option_val!(min_global_align); + target_option_val!(default_codegen_units); + target_option_val!(trap_unreachable); + target_option_val!(requires_lto); + target_option_val!(singlethread); + target_option_val!(no_builtins); + target_option_val!(default_hidden_visibility); + target_option_val!(emit_debug_gdb_scripts); + target_option_val!(requires_uwtable); + target_option_val!(simd_types_indirect); + target_option_val!(limit_rdylib_exports); + target_option_val!(override_export_symbols); + target_option_val!(merge_functions); + target_option_val!(target_mcount); + target_option_val!(llvm_abiname); + target_option_val!(relax_elf_relocations); + target_option_val!(llvm_args); + target_option_val!(use_ctors_section); + target_option_val!(eh_frame_header); + + if default.unsupported_abis != self.options.unsupported_abis { + d.insert( + "unsupported-abis".to_string(), + self.options + .unsupported_abis + .iter() + .map(|&name| Abi::name(name).to_json()) + .collect::<Vec<_>>() + .to_json(), + ); + } + + Json::Object(d) + } +} + +/// Either a target triple string or a path to a JSON file. +#[derive(PartialEq, Clone, Debug, Hash, Encodable, Decodable)] +pub enum TargetTriple { + TargetTriple(String), + TargetPath(PathBuf), +} + +impl TargetTriple { + /// Creates a target triple from the passed target triple string. + pub fn from_triple(triple: &str) -> Self { + TargetTriple::TargetTriple(triple.to_string()) + } + + /// Creates a target triple from the passed target path. + pub fn from_path(path: &Path) -> Result<Self, io::Error> { + let canonicalized_path = path.canonicalize()?; + Ok(TargetTriple::TargetPath(canonicalized_path)) + } + + /// Returns a string triple for this target. + /// + /// If this target is a path, the file name (without extension) is returned. + pub fn triple(&self) -> &str { + match *self { + TargetTriple::TargetTriple(ref triple) => triple, + TargetTriple::TargetPath(ref path) => path + .file_stem() + .expect("target path must not be empty") + .to_str() + .expect("target path must be valid unicode"), + } + } + + /// Returns an extended string triple for this target. + /// + /// If this target is a path, a hash of the path is appended to the triple returned + /// by `triple()`. + pub fn debug_triple(&self) -> String { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let triple = self.triple(); + if let TargetTriple::TargetPath(ref path) = *self { + let mut hasher = DefaultHasher::new(); + path.hash(&mut hasher); + let hash = hasher.finish(); + format!("{}-{}", triple, hash) + } else { + triple.to_owned() + } + } +} + +impl fmt::Display for TargetTriple { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.debug_triple()) + } +} diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs new file mode 100644 index 00000000000..f75697996ac --- /dev/null +++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs @@ -0,0 +1,64 @@ +use crate::spec::{LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "msp430-none-elf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "16".to_string(), + target_c_int_width: "16".to_string(), + data_layout: "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16".to_string(), + arch: "msp430".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + executables: true, + + // The LLVM backend currently can't generate object files. To + // workaround this LLVM generates assembly files which then we feed + // to gcc to get object files. For this reason we have a hard + // dependency on this specific gcc. + asm_args: vec!["-mcpu=msp430".to_string()], + linker: Some("msp430-elf-gcc".to_string()), + + // There are no atomic CAS instructions available in the MSP430 + // instruction set, and the LLVM backend doesn't currently support + // compiler fences so the Atomic* API is missing on this target. + // When the LLVM backend gains support for compile fences uncomment + // the `singlethread: true` line and set `max_atomic_width` to + // `Some(16)`. + max_atomic_width: Some(0), + atomic_cas: false, + // singlethread: true, + + // Because these devices have very little resources having an + // unwinder is too onerous so we default to "abort" because the + // "unwind" strategy is very rare. + panic_strategy: PanicStrategy::Abort, + + // Similarly, one almost always never wants to use relocatable + // code because of the extra costs it involves. + relocation_model: RelocModel::Static, + + // Right now we invoke an external assembler and this isn't + // compatible with multiple codegen units, and plus we probably + // don't want to invoke that many gcc instances. + default_codegen_units: Some(1), + + // Since MSP430 doesn't meaningfully support faulting on illegal + // instructions, LLVM generates a call to abort() function instead + // of a trap instruction. Such calls are 4 bytes long, and that is + // too much overhead for such small target. + trap_unreachable: false, + + // See the thumb_base.rs file for an explanation of this value + emit_debug_gdb_scripts: false, + + eh_frame_header: false, + + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs new file mode 100644 index 00000000000..f57ef87cf12 --- /dev/null +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -0,0 +1,31 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let pre_link_args_msvc = vec![ + // Suppress the verbose logo and authorship debugging output, which would needlessly + // clog any log files. + "/NOLOGO".to_string(), + // Tell the compiler that non-code sections can be marked as non-executable, + // including stack pages. + // UEFI is fully compatible to non-executable data pages. + // In fact, firmware might enforce this, so we better let the linker know about this, + // so it will fail if the compiler ever tries placing code on the stack + // (e.g., trampoline constructs and alike). + "/NXCOMPAT".to_string(), + ]; + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert(LinkerFlavor::Msvc, pre_link_args_msvc.clone()); + pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), pre_link_args_msvc); + + TargetOptions { + executables: true, + is_like_windows: true, + is_like_msvc: true, + lld_flavor: LldFlavor::Link, + pre_link_args, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs new file mode 100644 index 00000000000..988346af2d7 --- /dev/null +++ b/compiler/rustc_target/src/spec/netbsd_base.rs @@ -0,0 +1,29 @@ +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + // GNU-style linkers will use this to omit linking to libraries + // which don't actually fulfill any relocations, but only for + // libraries which follow this flag. Thus, use it before + // specifying libraries to link to. + "-Wl,--as-needed".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + linker_is_gnu: true, + no_default_libraries: false, + has_rpath: true, + pre_link_args: args, + position_independent_executables: true, + relro_level: RelroLevel::Full, + use_ctors_section: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs new file mode 100644 index 00000000000..0c8f2a34301 --- /dev/null +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs @@ -0,0 +1,75 @@ +use crate::spec::abi::Abi; +use crate::spec::{ + LinkerFlavor, MergeFunctions, PanicStrategy, Target, TargetOptions, TargetResult, +}; + +pub fn target() -> TargetResult { + Ok(Target { + arch: "nvptx64".to_string(), + data_layout: "e-i64:64-i128:128-v16:16-v32:32-n16:32:64".to_string(), + llvm_target: "nvptx64-nvidia-cuda".to_string(), + + target_os: "cuda".to_string(), + target_vendor: "nvidia".to_string(), + target_env: String::new(), + + linker_flavor: LinkerFlavor::PtxLinker, + + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + + options: TargetOptions { + // The linker can be installed from `crates.io`. + linker: Some("rust-ptx-linker".to_string()), + + // With `ptx-linker` approach, it can be later overridden via link flags. + cpu: "sm_30".to_string(), + + // FIXME: create tests for the atomics. + max_atomic_width: Some(64), + + // Unwinding on CUDA is neither feasible nor useful. + panic_strategy: PanicStrategy::Abort, + + // Needed to use `dylib` and `bin` crate types and the linker. + dynamic_linking: true, + executables: true, + + // Avoid using dylib because it contain metadata not supported + // by LLVM NVPTX backend. + only_cdylib: true, + + // Let the `ptx-linker` to handle LLVM lowering into MC / assembly. + obj_is_bitcode: true, + + // Convenient and predicable naming scheme. + dll_prefix: "".to_string(), + dll_suffix: ".ptx".to_string(), + exe_suffix: ".ptx".to_string(), + + // Disable MergeFunctions LLVM optimisation pass because it can + // produce kernel functions that call other kernel functions. + // This behavior is not supported by PTX ISA. + merge_functions: MergeFunctions::Disabled, + + // FIXME: enable compilation tests for the target and + // create the tests for this. + unsupported_abis: vec![ + Abi::Cdecl, + Abi::Stdcall, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall, + Abi::Aapcs, + Abi::Win64, + Abi::SysV64, + Abi::Msp430Interrupt, + Abi::X86Interrupt, + Abi::AmdGpuKernel, + ], + + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs new file mode 100644 index 00000000000..cadd14df693 --- /dev/null +++ b/compiler/rustc_target/src/spec/openbsd_base.rs @@ -0,0 +1,31 @@ +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + // GNU-style linkers will use this to omit linking to libraries + // which don't actually fulfill any relocations, but only for + // libraries which follow this flag. Thus, use it before + // specifying libraries to link to. + "-Wl,--as-needed".to_string(), + // Always enable NX protection when it is available + "-Wl,-z,noexecstack".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + linker_is_gnu: true, + has_rpath: true, + abi_return_struct_as_int: true, + pre_link_args: args, + position_independent_executables: true, + eliminate_frame_pointer: false, // FIXME 43575 + relro_level: RelroLevel::Full, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs new file mode 100644 index 00000000000..60c15d6b7d2 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::freebsd_base::opts(); + base.cpu = "ppc64".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "powerpc64-unknown-freebsd".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64".to_string(), + arch: "powerpc64".to_string(), + target_os: "freebsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs new file mode 100644 index 00000000000..5306d905c5d --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "ppc64".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + // ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO + // for now. https://github.com/rust-lang/rust/pull/43170#issuecomment-315411474 + base.relro_level = RelroLevel::Partial; + + Ok(Target { + llvm_target: "powerpc64-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64".to_string(), + arch: "powerpc64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs new file mode 100644 index 00000000000..c3b956effae --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "ppc64".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "powerpc64-unknown-linux-musl".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64".to_string(), + arch: "powerpc64".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs new file mode 100644 index 00000000000..e00a927c3a4 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::vxworks_base::opts(); + base.cpu = "ppc64".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "powerpc64-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64".to_string(), + arch: "powerpc64".to_string(), + target_os: "vxworks".to_string(), + target_env: "gnu".to_string(), + target_vendor: "wrs".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs new file mode 100644 index 00000000000..90737994612 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "ppc64le".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "powerpc64le-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i64:64-n32:64".to_string(), + arch: "powerpc64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs new file mode 100644 index 00000000000..1a1fccfab02 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "ppc64le".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "powerpc64le-unknown-linux-musl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-i64:64-n32:64".to_string(), + arch: "powerpc64".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs new file mode 100644 index 00000000000..2d4c5986637 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.max_atomic_width = Some(32); + + Ok(Target { + llvm_target: "powerpc-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs new file mode 100644 index 00000000000..fabc4313bee --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string()); + base.max_atomic_width = Some(32); + + Ok(Target { + llvm_target: "powerpc-unknown-linux-gnuspe".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs new file mode 100644 index 00000000000..240cbcbfe6e --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.max_atomic_width = Some(32); + + Ok(Target { + llvm_target: "powerpc-unknown-linux-musl".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs new file mode 100644 index 00000000000..6ca7053ced5 --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.max_atomic_width = Some(32); + + Ok(Target { + llvm_target: "powerpc-unknown-netbsd".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "netbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs new file mode 100644 index 00000000000..2211dc29a5d --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::vxworks_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string()); + base.max_atomic_width = Some(32); + + Ok(Target { + llvm_target: "powerpc-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "vxworks".to_string(), + target_env: "gnu".to_string(), + target_vendor: "wrs".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { features: "+secure-plt".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs new file mode 100644 index 00000000000..b10182c09ad --- /dev/null +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::vxworks_base::opts(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string()); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string()); + base.max_atomic_width = Some(32); + + Ok(Target { + llvm_target: "powerpc-unknown-linux-gnuspe".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), + arch: "powerpc".to_string(), + target_os: "vxworks".to_string(), + target_env: "gnu".to_string(), + target_vendor: "wrs".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + // feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2 + features: "+secure-plt,+msync".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs new file mode 100644 index 00000000000..18cafe654d1 --- /dev/null +++ b/compiler/rustc_target/src/spec/redox_base.rs @@ -0,0 +1,35 @@ +use crate::spec::{LinkArgs, LinkerFlavor, RelroLevel, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + // We want to be able to strip as much executable code as possible + // from the linker command line, and this flag indicates to the + // linker that it can avoid linking in dynamic libraries that don't + // actually satisfy any symbols up to that point (as with many other + // resolutions the linker does). This option only applies to all + // following libraries so we're sure to pass it as one of the first + // arguments. + "-Wl,--as-needed".to_string(), + // Always enable NX protection when it is available + "-Wl,-z,noexecstack".to_string(), + ], + ); + + TargetOptions { + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + linker_is_gnu: true, + has_rpath: true, + pre_link_args: args, + position_independent_executables: true, + relro_level: RelroLevel::Full, + has_elf_tls: true, + crt_static_default: true, + crt_static_respected: true, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs new file mode 100644 index 00000000000..5b5e342000b --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -0,0 +1,32 @@ +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), + llvm_target: "riscv32".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + arch: "riscv32".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + linker: Some("rust-lld".to_string()), + cpu: "generic-rv32".to_string(), + max_atomic_width: Some(0), + atomic_cas: false, + features: String::new(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs new file mode 100644 index 00000000000..4cef5c42d8d --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs @@ -0,0 +1,32 @@ +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), + llvm_target: "riscv32".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + arch: "riscv32".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + linker: Some("rust-lld".to_string()), + cpu: "generic-rv32".to_string(), + max_atomic_width: Some(32), + atomic_cas: true, + features: "+m,+a,+c".to_string(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs new file mode 100644 index 00000000000..8ad563e441d --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs @@ -0,0 +1,32 @@ +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), + llvm_target: "riscv32".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + arch: "riscv32".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + linker: Some("rust-lld".to_string()), + cpu: "generic-rv32".to_string(), + max_atomic_width: Some(0), + atomic_cas: false, + features: "+m,+c".to_string(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs new file mode 100644 index 00000000000..f7a93c916d1 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs @@ -0,0 +1,25 @@ +use crate::spec::{CodeModel, LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "riscv64-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + target_env: "gnu".to_string(), + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(), + arch: "riscv64".to_string(), + target_os: "linux".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { + unsupported_abis: super::riscv_base::unsupported_abis(), + code_model: Some(CodeModel::Medium), + cpu: "generic-rv64".to_string(), + features: "+m,+a,+f,+d,+c".to_string(), + llvm_abiname: "lp64d".to_string(), + max_atomic_width: Some(64), + ..super::linux_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs new file mode 100644 index 00000000000..3aeb3f3ca72 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs @@ -0,0 +1,33 @@ +use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; +use crate::spec::{Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(), + llvm_target: "riscv64".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + arch: "riscv64".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + linker: Some("rust-lld".to_string()), + cpu: "generic-rv64".to_string(), + max_atomic_width: Some(64), + atomic_cas: true, + features: "+m,+a,+f,+d,+c".to_string(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + code_model: Some(CodeModel::Medium), + emit_debug_gdb_scripts: false, + unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs new file mode 100644 index 00000000000..d8144964dc9 --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs @@ -0,0 +1,33 @@ +use crate::spec::{CodeModel, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; + +pub fn target() -> TargetResult { + Ok(Target { + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(), + llvm_target: "riscv64".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + arch: "riscv64".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + linker: Some("rust-lld".to_string()), + cpu: "generic-rv64".to_string(), + max_atomic_width: Some(64), + atomic_cas: true, + features: "+m,+a,+c".to_string(), + executables: true, + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + code_model: Some(CodeModel::Medium), + emit_debug_gdb_scripts: false, + unsupported_abis: super::riscv_base::unsupported_abis(), + eh_frame_header: false, + ..Default::default() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/riscv_base.rs b/compiler/rustc_target/src/spec/riscv_base.rs new file mode 100644 index 00000000000..64cf890037e --- /dev/null +++ b/compiler/rustc_target/src/spec/riscv_base.rs @@ -0,0 +1,20 @@ +use crate::spec::abi::Abi; + +// All the calling conventions trigger an assertion(Unsupported calling +// convention) in llvm on RISCV +pub fn unsupported_abis() -> Vec<Abi> { + vec![ + Abi::Cdecl, + Abi::Stdcall, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall, + Abi::Aapcs, + Abi::Win64, + Abi::SysV64, + Abi::PtxKernel, + Abi::Msp430Interrupt, + Abi::X86Interrupt, + Abi::AmdGpuKernel, + ] +} diff --git a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs new file mode 100644 index 00000000000..f259787e1d5 --- /dev/null +++ b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs @@ -0,0 +1,27 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + // z10 is the oldest CPU supported by LLVM + base.cpu = "z10".to_string(); + // FIXME: The data_layout string below and the ABI implementation in + // cabi_s390x.rs are for now hard-coded to assume the no-vector ABI. + // Pass the -vector feature string to LLVM to respect this assumption. + base.features = "-vector".to_string(); + base.max_atomic_width = Some(64); + base.min_global_align = Some(16); + + Ok(Target { + llvm_target: "s390x-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64".to_string(), + arch: "s390x".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs new file mode 100644 index 00000000000..3d7f0034b8b --- /dev/null +++ b/compiler/rustc_target/src/spec/solaris_base.rs @@ -0,0 +1,15 @@ +use crate::spec::TargetOptions; + +pub fn opts() -> TargetOptions { + TargetOptions { + dynamic_linking: true, + executables: true, + has_rpath: true, + target_family: Some("unix".to_string()), + is_like_solaris: true, + limit_rdylib_exports: false, // Linker doesn't support this + eh_frame_header: false, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs new file mode 100644 index 00000000000..c842b22d4e1 --- /dev/null +++ b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs @@ -0,0 +1,21 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "v9".to_string(); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "sparc64-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), + arch: "sparc64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs new file mode 100644 index 00000000000..aad85e852f0 --- /dev/null +++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.cpu = "v9".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "sparc64-unknown-netbsd".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), + arch: "sparc64".to_string(), + target_os: "netbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs new file mode 100644 index 00000000000..229e0621e0d --- /dev/null +++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::openbsd_base::opts(); + base.cpu = "v9".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "sparc64-unknown-openbsd".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), + arch: "sparc64".to_string(), + target_os: "openbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs new file mode 100644 index 00000000000..162cd311a38 --- /dev/null +++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "v9".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string()); + + Ok(Target { + llvm_target: "sparc-unknown-linux-gnu".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-p:32:32-i64:64-f128:64-n32-S64".to_string(), + arch: "sparc".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs new file mode 100644 index 00000000000..acc03fd0d79 --- /dev/null +++ b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs @@ -0,0 +1,27 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::solaris_base::opts(); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); + // llvm calls this "v9" + base.cpu = "v9".to_string(); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "sparcv9-sun-solaris".to_string(), + target_endian: "big".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), + // Use "sparc64" instead of "sparcv9" here, since the former is already + // used widely in the source base. If we ever needed ABI + // differentiation from the sparc64, we could, but that would probably + // just be confusing. + arch: "sparc64".to_string(), + target_os: "solaris".to_string(), + target_env: String::new(), + target_vendor: "sun".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs new file mode 100644 index 00000000000..b2c2b8254d8 --- /dev/null +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -0,0 +1,47 @@ +use super::super::*; + +pub(super) fn test_target(target: TargetResult) { + // Grab the TargetResult struct. If we successfully retrieved + // a Target, then the test JSON encoding/decoding can run for this + // Target on this testing platform (i.e., checking the iOS targets + // only on a Mac test platform). + if let Ok(original) = target { + original.check_consistency(); + let as_json = original.to_json(); + let parsed = Target::from_json(as_json).unwrap(); + assert_eq!(original, parsed); + } +} + +impl Target { + fn check_consistency(&self) { + // Check that LLD with the given flavor is treated identically to the linker it emulates. + // If your target really needs to deviate from the rules below, except it and document the + // reasons. + assert_eq!( + self.linker_flavor == LinkerFlavor::Msvc + || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link), + self.options.lld_flavor == LldFlavor::Link, + ); + for args in &[ + &self.options.pre_link_args, + &self.options.late_link_args, + &self.options.late_link_args_dynamic, + &self.options.late_link_args_static, + &self.options.post_link_args, + ] { + assert_eq!( + args.get(&LinkerFlavor::Msvc), + args.get(&LinkerFlavor::Lld(LldFlavor::Link)), + ); + if args.contains_key(&LinkerFlavor::Msvc) { + assert_eq!(self.options.lld_flavor, LldFlavor::Link); + } + } + assert!( + (self.options.pre_link_objects_fallback.is_empty() + && self.options.post_link_objects_fallback.is_empty()) + || self.options.crt_objects_fallback.is_some() + ); + } +} diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs new file mode 100644 index 00000000000..2f7d15d5856 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -0,0 +1,57 @@ +// These `thumbv*` targets cover the ARM Cortex-M family of processors which are widely used in +// microcontrollers. Namely, all these processors: +// +// - Cortex-M0 +// - Cortex-M0+ +// - Cortex-M1 +// - Cortex-M3 +// - Cortex-M4(F) +// - Cortex-M7(F) +// - Cortex-M23 +// - Cortex-M33 +// +// We have opted for these instead of one target per processor (e.g., `cortex-m0`, `cortex-m3`, +// etc) because the differences between some processors like the cortex-m0 and cortex-m1 are almost +// non-existent from the POV of codegen so it doesn't make sense to have separate targets for them. +// And if differences exist between two processors under the same target, rustc flags can be used to +// optimize for one processor or the other. +// +// Also, we have not chosen a single target (`arm-none-eabi`) like GCC does because this makes +// difficult to integrate Rust code and C code. Targeting the Cortex-M4 requires different gcc flags +// than the ones you would use for the Cortex-M0 and with a single target it'd be impossible to +// differentiate one processor from the other. +// +// About arm vs thumb in the name. The Cortex-M devices only support the Thumb instruction set, +// which is more compact (higher code density), and not the ARM instruction set. That's why LLVM +// triples use thumb instead of arm. We follow suit because having thumb in the name let us +// differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of +// build scripts / gcc flags. + +use crate::spec::{PanicStrategy, RelocModel, TargetOptions}; + +pub fn opts() -> TargetOptions { + // See rust-lang/rfcs#1645 for a discussion about these defaults + TargetOptions { + executables: true, + // In most cases, LLD is good enough + linker: Some("rust-lld".to_string()), + // Because these devices have very little resources having an unwinder is too onerous so we + // default to "abort" because the "unwind" strategy is very rare. + panic_strategy: PanicStrategy::Abort, + // Similarly, one almost always never wants to use relocatable code because of the extra + // costs it involves. + relocation_model: RelocModel::Static, + unsupported_abis: super::arm_base::unsupported_abis(), + // When this section is added a volatile load to its start address is also generated. This + // volatile load is a footgun as it can end up loading an invalid memory address, depending + // on how the user set up their linker scripts. This section adds pretty printer for stuff + // like std::Vec, which is not that used in no-std context, so it's best to left it out + // until we figure a way to add the pretty printers without requiring a volatile load cf. + // rust-lang/rust#44993. + emit_debug_gdb_scripts: false, + // LLVM is eager to trash the link register when calling `noreturn` functions, which + // breaks debugging. Preserve LR by default to prevent that from happening. + eliminate_frame_pointer: false, + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs new file mode 100644 index 00000000000..a8c78f057fc --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -0,0 +1,62 @@ +//! Targets the ARMv4T, with code as `t32` code by default. +//! +//! Primarily of use for the GBA, but usable with other devices too. +//! +//! Please ping @Lokathor if changes are needed. +//! +//! This target profile assumes that you have the ARM binutils in your path (specifically the linker, `arm-none-eabi-ld`). They can be obtained for free for all major OSes from the ARM developer's website, and they may also be available in your system's package manager. Unfortunately, the standard linker that Rust uses (`lld`) only supports as far back as `ARMv5TE`, so we must use the GNU `ld` linker. +//! +//! **Important:** This target profile **does not** specify a linker script. You just get the default link script when you build a binary for this target. The default link script is very likely wrong, so you should use `-Clink-arg=-Tmy_script.ld` to override that with a correct linker script. + +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv4t-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "none".to_string(), + target_env: "".to_string(), + target_vendor: "".to_string(), + arch: "arm".to_string(), + /* Data layout args are '-' separated: + * little endian + * stack is 64-bit aligned (EABI) + * pointers are 32-bit + * i64 must be 64-bit aligned (EABI) + * mangle names with ELF style + * native integers are 32-bit + * All other elements are default + */ + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + linker_flavor: LinkerFlavor::Ld, + options: TargetOptions { + linker: Some("arm-none-eabi-ld".to_string()), + linker_is_gnu: true, + + // extra args passed to the external assembler (assuming `arm-none-eabi-as`): + // * activate t32/a32 interworking + // * use arch ARMv4T + // * use little-endian + asm_args: vec![ + "-mthumb-interwork".to_string(), + "-march=armv4t".to_string(), + "-mlittle-endian".to_string(), + ], + + // minimum extra features, these cannot be disabled via -C + features: "+soft-float,+strict-align".to_string(), + + main_needs_argc_argv: false, + + // No thread-local storage (just use a static Cell) + has_elf_tls: false, + + // don't have atomic compare-and-swap + atomic_cas: false, + + ..super::thumb_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs new file mode 100644 index 00000000000..953d60fcc22 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs @@ -0,0 +1,28 @@ +// Targets the Cortex-M0, Cortex-M0+ and Cortex-M1 processors (ARMv6-M architecture) + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv6m-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them + // with +strict-align. + features: "+strict-align".to_string(), + // There are no atomic CAS instructions available in the instruction set of the ARMv6-M + // architecture + atomic_cas: false, + ..super::thumb_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs new file mode 100644 index 00000000000..37828026fe1 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs @@ -0,0 +1,44 @@ +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_msvc_base::opts(); + + // Prevent error LNK2013: BRANCH24(T) fixup overflow + // The LBR optimization tries to eliminate branch islands, + // but if the displacement is larger than can fit + // in the instruction, this error will occur. The linker + // should be smart enough to insert branch islands only + // where necessary, but this is not the observed behavior. + // Disabling the LBR optimization works around the issue. + let pre_link_args_msvc = "/OPT:NOLBR".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().push(pre_link_args_msvc.clone()); + base.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .push(pre_link_args_msvc); + + // FIXME(jordanrh): use PanicStrategy::Unwind when SEH is + // implemented for windows/arm in LLVM + base.panic_strategy = PanicStrategy::Abort; + + Ok(Target { + llvm_target: "thumbv7a-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "pc".to_string(), + linker_flavor: LinkerFlavor::Msvc, + + options: TargetOptions { + features: "+vfp3,+neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs new file mode 100644 index 00000000000..29a4a9875e5 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_uwp_msvc_base::opts(); + base.max_atomic_width = Some(64); + base.has_elf_tls = true; + + // FIXME(jordanrh): use PanicStrategy::Unwind when SEH is + // implemented for windows/arm in LLVM + base.panic_strategy = PanicStrategy::Abort; + + Ok(Target { + llvm_target: "thumbv7a-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "uwp".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: TargetOptions { + features: "+vfp3,+neon".to_string(), + cpu: "generic".to_string(), + unsupported_abis: super::arm_base::unsupported_abis(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs new file mode 100644 index 00000000000..9e085381953 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs @@ -0,0 +1,29 @@ +// Targets the Cortex-M4 and Cortex-M7 processors (ARMv7E-M) +// +// This target assumes that the device doesn't have a FPU (Floating Point Unit) and lowers all the +// floating point operations to software routines (intrinsics). +// +// As such, this target uses the "soft" calling convention (ABI) where floating point values are +// passed to/from subroutines via general purpose registers (R0, R1, etc.). +// +// To opt-in to hardware accelerated floating point operations, you can use, for example, +// `-C target-feature=+vfp4` or `-C target-cpu=cortex-m4`. + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv7em-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { max_atomic_width: Some(32), ..super::thumb_base::opts() }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs new file mode 100644 index 00000000000..95b9b9d5b56 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs @@ -0,0 +1,41 @@ +// Targets the Cortex-M4F and Cortex-M7F processors (ARMv7E-M) +// +// This target assumes that the device does have a FPU (Floating Point Unit) and lowers all (single +// precision) floating point operations to hardware instructions. +// +// Additionally, this target uses the "hard" floating convention (ABI) where floating point values +// are passed to/from subroutines via FPU registers (S0, S1, D0, D1, etc.). +// +// To opt into double precision hardware support, use the `-C target-feature=+fp64` flag. + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv7em-none-eabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + // `+vfp4` is the lowest common denominator between the Cortex-M4 (vfp4-16) and the + // Cortex-M7 (vfp5) + // `-d32` both the Cortex-M4 and the Cortex-M7 only have 16 double-precision registers + // available + // `-fp64` The Cortex-M4 only supports single precision floating point operations + // whereas in the Cortex-M7 double precision is optional + // + // Reference: + // ARMv7-M Architecture Reference Manual - A2.5 The optional floating-point extension + features: "+vfp4,-d32,-fp64".to_string(), + max_atomic_width: Some(32), + ..super::thumb_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs new file mode 100644 index 00000000000..528359ffad3 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs @@ -0,0 +1,20 @@ +// Targets the Cortex-M3 processor (ARMv7-M) + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv7m-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { max_atomic_width: Some(32), ..super::thumb_base::opts() }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs new file mode 100644 index 00000000000..c52f077f6f1 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target if is for the Android v7a ABI in thumb mode with +// NEON unconditionally enabled and, therefore, with 32 FPU registers +// enabled as well. See section A2.6.2 on page A2-56 in +// https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf + +// See https://developer.android.com/ndk/guides/abis.html#v7a +// for target ABI requirements. + +pub fn target() -> TargetResult { + let mut base = super::android_base::opts(); + base.features = "+v7,+thumb-mode,+thumb2,+vfp3,+neon".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-march=armv7-a".to_string()); + + Ok(Target { + llvm_target: "armv7-none-linux-android".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "android".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs new file mode 100644 index 00000000000..78936948e64 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs @@ -0,0 +1,32 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target is for glibc Linux on ARMv7 with thumb mode enabled +// (for consistency with Android and Debian-based distributions) +// and with NEON unconditionally enabled and, therefore, with 32 FPU +// registers enabled as well. See section A2.6.2 on page A2-56 in +// https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf + +pub fn target() -> TargetResult { + let base = super::linux_base::opts(); + Ok(Target { + llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: TargetOptions { + // Info about features at https://wiki.debian.org/ArmHardFloatPort + features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs new file mode 100644 index 00000000000..f759c3eeb01 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs @@ -0,0 +1,37 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +// This target is for musl Linux on ARMv7 with thumb mode enabled +// (for consistency with Android and Debian-based distributions) +// and with NEON unconditionally enabled and, therefore, with 32 FPU +// registers enabled as well. See section A2.6.2 on page A2-56 in +// https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf + +pub fn target() -> TargetResult { + let base = super::linux_musl_base::opts(); + Ok(Target { + // It's important we use "gnueabihf" and not "musleabihf" here. LLVM + // uses it to determine the calling convention and float ABI, and LLVM + // doesn't support the "musleabihf" value. + llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + // Most of these settings are copied from the thumbv7neon_unknown_linux_gnueabihf + // target. + options: TargetOptions { + features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".to_string(), + cpu: "generic".to_string(), + max_atomic_width: Some(64), + unsupported_abis: super::arm_base::unsupported_abis(), + target_mcount: "\u{1}mcount".to_string(), + ..base + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs new file mode 100644 index 00000000000..3f67c67b7bc --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs @@ -0,0 +1,26 @@ +// Targets the Cortex-M23 processor (Baseline ARMv8-M) + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv8m.base-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + // ARMv8-M baseline doesn't support unaligned loads/stores so we disable them + // with +strict-align. + features: "+strict-align".to_string(), + max_atomic_width: Some(32), + ..super::thumb_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs new file mode 100644 index 00000000000..2f8103f0c70 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs @@ -0,0 +1,21 @@ +// Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), +// without the Floating Point extension. + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv8m.main-none-eabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { max_atomic_width: Some(32), ..super::thumb_base::opts() }, + }) +} diff --git a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs new file mode 100644 index 00000000000..53a340230d6 --- /dev/null +++ b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs @@ -0,0 +1,30 @@ +// Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), +// with the Floating Point extension. + +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + Ok(Target { + llvm_target: "thumbv8m.main-none-eabihf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), + arch: "arm".to_string(), + target_os: "none".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + + options: TargetOptions { + // If the Floating Point extension is implemented in the Cortex-M33 + // processor, the Cortex-M33 Technical Reference Manual states that + // the FPU uses the FPv5 architecture, single-precision instructions + // and 16 D registers. + // These parameters map to the following LLVM features. + features: "+fp-armv8,-fp64,-d32".to_string(), + max_atomic_width: Some(32), + ..super::thumb_base::opts() + }, + }) +} diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs new file mode 100644 index 00000000000..3f7c78c8e7d --- /dev/null +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -0,0 +1,58 @@ +// This defines a base target-configuration for native UEFI systems. The UEFI specification has +// quite detailed sections on the ABI of all the supported target architectures. In almost all +// cases it simply follows what Microsoft Windows does. Hence, whenever in doubt, see the MSDN +// documentation. +// UEFI uses COFF/PE32+ format for binaries. All binaries must be statically linked. No dynamic +// linker is supported. As native to COFF, binaries are position-dependent, but will be relocated +// by the loader if the pre-chosen memory location is already in use. +// UEFI forbids running code on anything but the boot-CPU. No interrupts are allowed other than +// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all +// code runs in the same environment, no process separation is supported. + +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut base = super::msvc_base::opts(); + + let pre_link_args_msvc = vec![ + // Non-standard subsystems have no default entry-point in PE+ files. We have to define + // one. "efi_main" seems to be a common choice amongst other implementations and the + // spec. + "/entry:efi_main".to_string(), + // COFF images have a "Subsystem" field in their header, which defines what kind of + // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION, + // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION, + // which is very likely the most common option. Individual projects can override this + // with custom linker flags. + // The subsystem-type only has minor effects on the application. It defines the memory + // regions the application is loaded into (runtime-drivers need to be put into + // reserved areas), as well as whether a return from the entry-point is treated as + // exit (default for applications). + "/subsystem:efi_application".to_string(), + ]; + base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + base.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .extend(pre_link_args_msvc); + + TargetOptions { + disable_redzone: true, + exe_suffix: ".efi".to_string(), + allows_weak_linkage: false, + panic_strategy: PanicStrategy::Abort, + stack_probes: true, + singlethread: true, + linker: Some("rust-lld".to_string()), + // FIXME: This should likely be `true` inherited from `msvc_base` + // because UEFI follows Windows ABI and uses PE/COFF. + // The `false` is probably causing ABI bugs right now. + is_like_windows: false, + // FIXME: This should likely be `true` inherited from `msvc_base` + // because UEFI follows Windows ABI and uses PE/COFF. + // The `false` is probably causing ABI bugs right now. + is_like_msvc: false, + + ..base + } +} diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs new file mode 100644 index 00000000000..777bb58d7db --- /dev/null +++ b/compiler/rustc_target/src/spec/vxworks_base.rs @@ -0,0 +1,37 @@ +use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut args = LinkArgs::new(); + args.insert( + LinkerFlavor::Gcc, + vec![ + // We want to be able to strip as much executable code as possible + // from the linker command line, and this flag indicates to the + // linker that it can avoid linking in dynamic libraries that don't + // actually satisfy any symbols up to that point (as with many other + // resolutions the linker does). This option only applies to all + // following libraries so we're sure to pass it as one of the first + // arguments. + "-Wl,--as-needed".to_string(), + ], + ); + + TargetOptions { + linker: Some("wr-c++".to_string()), + exe_suffix: ".vxe".to_string(), + dynamic_linking: true, + executables: true, + target_family: Some("unix".to_string()), + linker_is_gnu: true, + has_rpath: true, + pre_link_args: args, + position_independent_executables: false, + has_elf_tls: true, + crt_static_default: true, + crt_static_respected: true, + crt_static_allows_dylibs: true, + // VxWorks needs to implement this to support profiling + target_mcount: "_mcount".to_string(), + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs new file mode 100644 index 00000000000..62fc8f06183 --- /dev/null +++ b/compiler/rustc_target/src/spec/wasm32_base.rs @@ -0,0 +1,144 @@ +use super::crt_objects::CrtObjectsFallback; +use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; +use std::collections::BTreeMap; + +pub fn options() -> TargetOptions { + let mut lld_args = Vec::new(); + let mut clang_args = Vec::new(); + let mut arg = |arg: &str| { + lld_args.push(arg.to_string()); + clang_args.push(format!("-Wl,{}", arg)); + }; + + // By default LLD only gives us one page of stack (64k) which is a + // little small. Default to a larger stack closer to other PC platforms + // (1MB) and users can always inject their own link-args to override this. + arg("-z"); + arg("stack-size=1048576"); + + // By default LLD's memory layout is: + // + // 1. First, a blank page + // 2. Next, all static data + // 3. Finally, the main stack (which grows down) + // + // This has the unfortunate consequence that on stack overflows you + // corrupt static data and can cause some exceedingly weird bugs. To + // help detect this a little sooner we instead request that the stack is + // placed before static data. + // + // This means that we'll generate slightly larger binaries as references + // to static data will take more bytes in the ULEB128 encoding, but + // stack overflow will be guaranteed to trap as it underflows instead of + // corrupting static data. + arg("--stack-first"); + + // FIXME we probably shouldn't pass this but instead pass an explicit list + // of symbols we'll allow to be undefined. We don't currently have a + // mechanism of knowing, however, which symbols are intended to be imported + // from the environment and which are intended to be imported from other + // objects linked elsewhere. This is a coarse approximation but is sure to + // hide some bugs and frustrate someone at some point, so we should ideally + // work towards a world where we can explicitly list symbols that are + // supposed to be imported and have all other symbols generate errors if + // they remain undefined. + arg("--allow-undefined"); + + // Rust code should never have warnings, and warnings are often + // indicative of bugs, let's prevent them. + arg("--fatal-warnings"); + + // LLD only implements C++-like demangling, which doesn't match our own + // mangling scheme. Tell LLD to not demangle anything and leave it up to + // us to demangle these symbols later. Currently rustc does not perform + // further demangling, but tools like twiggy and wasm-bindgen are intended + // to do so. + arg("--no-demangle"); + + // The symbol visibility story is a bit in flux right now with LLD. + // It's... not entirely clear to me what's going on, but this looks to + // make everything work when `export_symbols` isn't otherwise called for + // things like executables. + // + // This is really only here to get things working. If it can be removed and + // basic tests still work, then sounds like it should be removed! + arg("--export-dynamic"); + + let mut pre_link_args = BTreeMap::new(); + pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args); + pre_link_args.insert(LinkerFlavor::Gcc, clang_args); + + TargetOptions { + // we allow dynamic linking, but only cdylibs. Basically we allow a + // final library artifact that exports some symbols (a wasm module) but + // we don't allow intermediate `dylib` crate types + dynamic_linking: true, + only_cdylib: true, + + // This means we'll just embed a `#[start]` function in the wasm module + executables: true, + + // relatively self-explanatory! + exe_suffix: ".wasm".to_string(), + dll_prefix: String::new(), + dll_suffix: ".wasm".to_string(), + linker_is_gnu: false, + + max_atomic_width: Some(64), + + // Unwinding doesn't work right now, so the whole target unconditionally + // defaults to panic=abort. Note that this is guaranteed to change in + // the future once unwinding is implemented. Don't rely on this as we're + // basically guaranteed to change it once WebAssembly supports + // exceptions. + panic_strategy: PanicStrategy::Abort, + + // Wasm doesn't have atomics yet, so tell LLVM that we're in a single + // threaded model which will legalize atomics to normal operations. + singlethread: true, + + // no dynamic linking, no need for default visibility! + default_hidden_visibility: true, + + // Symbol visibility takes care of this for the WebAssembly. + // Additionally the only known linker, LLD, doesn't support the script + // arguments just yet + limit_rdylib_exports: false, + + // we use the LLD shipped with the Rust toolchain by default + linker: Some("rust-lld".to_owned()), + lld_flavor: LldFlavor::Wasm, + + // No need for indirection here, simd types can always be passed by + // value as the whole module either has simd or not, which is different + // from x86 (for example) where programs can have functions that don't + // enable simd features. + simd_types_indirect: false, + + pre_link_args, + + crt_objects_fallback: Some(CrtObjectsFallback::Wasm), + + // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when + // PIC code is implemented this has quite a drastric effect if it stays + // at the default, `pic`. In an effort to keep wasm binaries as minimal + // as possible we're defaulting to `static` for now, but the hope is + // that eventually we can ship a `pic`-compatible standard library which + // works with `static` as well (or works with some method of generating + // non-relative calls and such later on). + relocation_model: RelocModel::Static, + + // When the atomics feature is activated then these two keys matter, + // otherwise they're basically ignored by the standard library. In this + // mode, however, the `#[thread_local]` attribute works (i.e. + // `has_elf_tls`) and we need to get it to work by specifying + // `local-exec` as that's all that's implemented in LLVM today for wasm. + has_elf_tls: true, + tls_model: TlsModel::LocalExec, + + // gdb scripts don't work on wasm blobs + emit_debug_gdb_scripts: false, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs new file mode 100644 index 00000000000..1916639170e --- /dev/null +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -0,0 +1,44 @@ +use super::wasm32_base; +use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions}; + +pub fn target() -> Result<Target, String> { + let mut post_link_args = LinkArgs::new(); + post_link_args.insert( + LinkerFlavor::Em, + vec![ + "-s".to_string(), + "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string(), + "-s".to_string(), + "ASSERTIONS=1".to_string(), + "-s".to_string(), + "ABORTING_MALLOC=0".to_string(), + "-Wl,--fatal-warnings".to_string(), + ], + ); + + let opts = TargetOptions { + // emcc emits two files - a .js file to instantiate the wasm and supply platform + // functionality, and a .wasm file. + exe_suffix: ".js".to_string(), + linker: None, + linker_is_gnu: true, + is_like_emscripten: true, + panic_strategy: PanicStrategy::Unwind, + post_link_args, + target_family: Some("unix".to_string()), + ..wasm32_base::options() + }; + Ok(Target { + llvm_target: "wasm32-unknown-emscripten".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "emscripten".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), + arch: "wasm32".to_string(), + linker_flavor: LinkerFlavor::Em, + options: opts, + }) +} diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs new file mode 100644 index 00000000000..ded95a34d55 --- /dev/null +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -0,0 +1,46 @@ +//! A "bare wasm" target representing a WebAssembly output that makes zero +//! assumptions about its environment. +//! +//! The `wasm32-unknown-unknown` target is intended to encapsulate use cases +//! that do not rely on any imported functionality. The binaries generated are +//! entirely self-contained by default when using the standard library. Although +//! the standard library is available, most of it returns an error immediately +//! (e.g. trying to create a TCP stream or something like that). +//! +//! This target is more or less managed by the Rust and WebAssembly Working +//! Group nowadays at https://github.com/rustwasm. + +use super::wasm32_base; +use super::{LinkerFlavor, LldFlavor, Target}; + +pub fn target() -> Result<Target, String> { + let mut options = wasm32_base::options(); + let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); + + // Make sure clang uses LLD as its linker and is configured appropriately + // otherwise + clang_args.push("--target=wasm32-unknown-unknown".to_string()); + + // For now this target just never has an entry symbol no matter the output + // type, so unconditionally pass this. + clang_args.push("-Wl,--no-entry".to_string()); + options + .pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)) + .unwrap() + .push("--no-entry".to_string()); + + Ok(Target { + llvm_target: "wasm32-unknown-unknown".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "unknown".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), + arch: "wasm32".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Wasm), + options, + }) +} diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs new file mode 100644 index 00000000000..0bba7bdd473 --- /dev/null +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -0,0 +1,120 @@ +//! The `wasm32-wasi` target is a new and still (as of April 2019) an +//! experimental target. The definition in this file is likely to be tweaked +//! over time and shouldn't be relied on too much. +//! +//! The `wasi` target is a proposal to define a standardized set of syscalls +//! that WebAssembly files can interoperate with. This set of syscalls is +//! intended to empower WebAssembly binaries with native capabilities such as +//! filesystem access, network access, etc. +//! +//! You can see more about the proposal at https://wasi.dev +//! +//! The Rust target definition here is interesting in a few ways. We want to +//! serve two use cases here with this target: +//! +//! * First, we want Rust usage of the target to be as hassle-free as possible, +//! ideally avoiding the need to configure and install a local wasm32-wasi +//! toolchain. +//! +//! * Second, one of the primary use cases of LLVM's new wasm backend and the +//! wasm support in LLD is that any compiled language can interoperate with +//! any other. To that the `wasm32-wasi` target is the first with a viable C +//! standard library and sysroot common definition, so we want Rust and C/C++ +//! code to interoperate when compiled to `wasm32-unknown-unknown`. +//! +//! You'll note, however, that the two goals above are somewhat at odds with one +//! another. To attempt to solve both use cases in one go we define a target +//! that (ab)uses the `crt-static` target feature to indicate which one you're +//! in. +//! +//! ## No interop with C required +//! +//! By default the `crt-static` target feature is enabled, and when enabled +//! this means that the the bundled version of `libc.a` found in `liblibc.rlib` +//! is used. This isn't intended really for interoperation with a C because it +//! may be the case that Rust's bundled C library is incompatible with a +//! foreign-compiled C library. In this use case, though, we use `rust-lld` and +//! some copied crt startup object files to ensure that you can download the +//! wasi target for Rust and you're off to the races, no further configuration +//! necessary. +//! +//! All in all, by default, no external dependencies are required. You can +//! compile `wasm32-wasi` binaries straight out of the box. You can't, however, +//! reliably interoperate with C code in this mode (yet). +//! +//! ## Interop with C required +//! +//! For the second goal we repurpose the `target-feature` flag, meaning that +//! you'll need to do a few things to have C/Rust code interoperate. +//! +//! 1. All Rust code needs to be compiled with `-C target-feature=-crt-static`, +//! indicating that the bundled C standard library in the Rust sysroot will +//! not be used. +//! +//! 2. If you're using rustc to build a linked artifact then you'll need to +//! specify `-C linker` to a `clang` binary that supports +//! `wasm32-wasi` and is configured with the `wasm32-wasi` sysroot. This +//! will cause Rust code to be linked against the libc.a that the specified +//! `clang` provides. +//! +//! 3. If you're building a staticlib and integrating Rust code elsewhere, then +//! compiling with `-C target-feature=-crt-static` is all you need to do. +//! +//! You can configure the linker via Cargo using the +//! `CARGO_TARGET_WASM32_WASI_LINKER` env var. Be sure to also set +//! `CC_wasm32-wasi` if any crates in the dependency graph are using the `cc` +//! crate. +//! +//! ## Remember, this is all in flux +//! +//! The wasi target is **very** new in its specification. It's likely going to +//! be a long effort to get it standardized and stable. We'll be following it as +//! best we can with this target. Don't start relying on too much here unless +//! you know what you're getting in to! + +use super::wasm32_base; +use super::{crt_objects, LinkerFlavor, LldFlavor, Target}; + +pub fn target() -> Result<Target, String> { + let mut options = wasm32_base::options(); + + options + .pre_link_args + .entry(LinkerFlavor::Gcc) + .or_insert(Vec::new()) + .push("--target=wasm32-wasi".to_string()); + + options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback(); + options.post_link_objects_fallback = crt_objects::post_wasi_fallback(); + + // Right now this is a bit of a workaround but we're currently saying that + // the target by default has a static crt which we're taking as a signal + // for "use the bundled crt". If that's turned off then the system's crt + // will be used, but this means that default usage of this target doesn't + // need an external compiler but it's still interoperable with an external + // compiler if configured correctly. + options.crt_static_default = true; + options.crt_static_respected = true; + + // Allow `+crt-static` to create a "cdylib" output which is just a wasm file + // without a main function. + options.crt_static_allows_dylibs = true; + + // WASI's `sys::args::init` function ignores its arguments; instead, + // `args::args()` makes the WASI API calls itself. + options.main_needs_argc_argv = false; + + Ok(Target { + llvm_target: "wasm32-wasi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + target_os: "wasi".to_string(), + target_env: String::new(), + target_vendor: String::new(), + data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), + arch: "wasm32".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Wasm), + options, + }) +} diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs new file mode 100644 index 00000000000..a864918655f --- /dev/null +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -0,0 +1,95 @@ +use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut pre_link_args = LinkArgs::new(); + pre_link_args.insert( + LinkerFlavor::Gcc, + vec![ + // Tell GCC to avoid linker plugins, because we are not bundling + // them with Windows installer, and Rust does its own LTO anyways. + "-fno-use-linker-plugin".to_string(), + // Always enable DEP (NX bit) when it is available + "-Wl,--nxcompat".to_string(), + ], + ); + + let mut late_link_args = LinkArgs::new(); + let mut late_link_args_dynamic = LinkArgs::new(); + let mut late_link_args_static = LinkArgs::new(); + // Order of `late_link_args*` was found through trial and error to work with various + // mingw-w64 versions (not tested on the CI). It's expected to change from time to time. + let mingw_libs = vec![ + "-lmsvcrt".to_string(), + "-lmingwex".to_string(), + "-lmingw32".to_string(), + // mingw's msvcrt is a weird hybrid import library and static library. + // And it seems that the linker fails to use import symbols from msvcrt + // that are required from functions in msvcrt in certain cases. For example + // `_fmode` that is used by an implementation of `__p__fmode` in x86_64. + // The library is purposely listed twice to fix that. + // + // See https://github.com/rust-lang/rust/pull/47483 for some more details. + "-lmsvcrt".to_string(), + "-luser32".to_string(), + "-lkernel32".to_string(), + ]; + late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); + late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); + let dynamic_unwind_libs = vec![ + // If any of our crates are dynamically linked then we need to use + // the shared libgcc_s-dw2-1.dll. This is required to support + // unwinding across DLL boundaries. + "-lgcc_s".to_string(), + "-lgcc".to_string(), + "-lkernel32".to_string(), + ]; + late_link_args_dynamic.insert(LinkerFlavor::Gcc, dynamic_unwind_libs.clone()); + late_link_args_dynamic.insert(LinkerFlavor::Lld(LldFlavor::Ld), dynamic_unwind_libs); + let static_unwind_libs = vec![ + // If all of our crates are statically linked then we can get away + // with statically linking the libgcc unwinding code. This allows + // binaries to be redistributed without the libgcc_s-dw2-1.dll + // dependency, but unfortunately break unwinding across DLL + // boundaries when unwinding across FFI boundaries. + "-lgcc_eh".to_string(), + "-l:libpthread.a".to_string(), + "-lgcc".to_string(), + // libpthread depends on libmsvcrt, so we need to link it *again*. + "-lmsvcrt".to_string(), + "-lkernel32".to_string(), + ]; + late_link_args_static.insert(LinkerFlavor::Gcc, static_unwind_libs.clone()); + late_link_args_static.insert(LinkerFlavor::Lld(LldFlavor::Ld), static_unwind_libs); + + TargetOptions { + // FIXME(#13846) this should be enabled for windows + function_sections: false, + linker: Some("gcc".to_string()), + dynamic_linking: true, + executables: true, + dll_prefix: String::new(), + dll_suffix: ".dll".to_string(), + exe_suffix: ".exe".to_string(), + staticlib_prefix: "lib".to_string(), + staticlib_suffix: ".a".to_string(), + target_family: Some("windows".to_string()), + is_like_windows: true, + allows_weak_linkage: false, + pre_link_args, + pre_link_objects: crt_objects::pre_mingw(), + post_link_objects: crt_objects::post_mingw(), + pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), + post_link_objects_fallback: crt_objects::post_mingw_fallback(), + crt_objects_fallback: Some(CrtObjectsFallback::Mingw), + late_link_args, + late_link_args_dynamic, + late_link_args_static, + abi_return_struct_as_int: true, + emit_debug_gdb_scripts: false, + requires_uwtable: true, + eh_frame_header: false, + + ..Default::default() + } +} diff --git a/compiler/rustc_target/src/spec/windows_msvc_base.rs b/compiler/rustc_target/src/spec/windows_msvc_base.rs new file mode 100644 index 00000000000..77171f8672e --- /dev/null +++ b/compiler/rustc_target/src/spec/windows_msvc_base.rs @@ -0,0 +1,30 @@ +use crate::spec::TargetOptions; + +pub fn opts() -> TargetOptions { + let base = super::msvc_base::opts(); + + TargetOptions { + dynamic_linking: true, + dll_prefix: String::new(), + dll_suffix: ".dll".to_string(), + exe_suffix: ".exe".to_string(), + staticlib_prefix: String::new(), + staticlib_suffix: ".lib".to_string(), + target_family: Some("windows".to_string()), + crt_static_allows_dylibs: true, + crt_static_respected: true, + requires_uwtable: true, + // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC + // as there's been trouble in the past of linking the C++ standard + // library required by LLVM. This likely needs to happen one day, but + // in general Windows is also a more controlled environment than + // Unix, so it's not necessarily as critical that this be implemented. + // + // Note that there are also some licensing worries about statically + // linking some libraries which require a specific agreement, so it may + // not ever be possible for us to pass this flag. + no_default_libraries: false, + + ..base + } +} diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs new file mode 100644 index 00000000000..fd55a0fc6a1 --- /dev/null +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -0,0 +1,36 @@ +use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let base = super::windows_gnu_base::opts(); + + // FIXME: This should be updated for the exception machinery changes from #67502 + // and inherit from `windows_gnu_base`, at least partially. + let mut late_link_args = LinkArgs::new(); + let late_link_args_dynamic = LinkArgs::new(); + let late_link_args_static = LinkArgs::new(); + let mingw_libs = vec![ + //"-lwinstorecompat".to_string(), + //"-lmingwex".to_string(), + //"-lwinstorecompat".to_string(), + "-lwinstorecompat".to_string(), + "-lruntimeobject".to_string(), + "-lsynchronization".to_string(), + "-lvcruntime140_app".to_string(), + "-lucrt".to_string(), + "-lwindowsapp".to_string(), + "-lmingwex".to_string(), + "-lmingw32".to_string(), + ]; + late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone()); + late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs.clone()); + + TargetOptions { + executables: false, + limit_rdylib_exports: false, + late_link_args, + late_link_args_dynamic, + late_link_args_static, + + ..base + } +} diff --git a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs new file mode 100644 index 00000000000..04ffa1a0add --- /dev/null +++ b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs @@ -0,0 +1,14 @@ +use crate::spec::{LinkerFlavor, LldFlavor, TargetOptions}; + +pub fn opts() -> TargetOptions { + let mut opts = super::windows_msvc_base::opts(); + + let pre_link_args_msvc = vec!["/APPCONTAINER".to_string(), "mincore.lib".to_string()]; + opts.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); + opts.pre_link_args + .get_mut(&LinkerFlavor::Lld(LldFlavor::Link)) + .unwrap() + .extend(pre_link_args_msvc); + + opts +} diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs new file mode 100644 index 00000000000..909aebec70b --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs @@ -0,0 +1,35 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::apple_base::opts(); + base.cpu = "core2".to_string(); + base.max_atomic_width = Some(128); // core2 support cmpxchg16b + base.eliminate_frame_pointer = false; + base.pre_link_args.insert( + LinkerFlavor::Gcc, + vec!["-m64".to_string(), "-arch".to_string(), "x86_64".to_string()], + ); + base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); + base.stack_probes = true; + + // Clang automatically chooses a more specific target based on + // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work + // correctly, we do too. + let arch = "x86_64"; + let llvm_target = super::apple_base::macos_llvm_target(&arch); + + Ok(Target { + llvm_target, + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: arch.to_string(), + target_os: "macos".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs new file mode 100644 index 00000000000..cfcf856836b --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs @@ -0,0 +1,20 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::X86_64, AppleOS::iOS)?; + Ok(Target { + llvm_target: "x86_64-apple-ios".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "ios".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs new file mode 100644 index 00000000000..c42d0911725 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -0,0 +1,20 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::X86_64_macabi, AppleOS::iOS)?; + Ok(Target { + llvm_target: "x86_64-apple-ios13.0-macabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "ios".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs new file mode 100644 index 00000000000..a56062c0b2b --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs @@ -0,0 +1,19 @@ +use super::apple_sdk_base::{opts, AppleOS, Arch}; +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let base = opts(Arch::X86_64, AppleOS::iOS)?; + Ok(Target { + llvm_target: "x86_64-apple-tvos".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(), + arch: "x86_64".to_string(), + target_os: "tvos".to_string(), + target_env: String::new(), + target_vendor: "apple".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs new file mode 100644 index 00000000000..3b5233a3e67 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs @@ -0,0 +1,91 @@ +use std::iter; + +use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; + +pub fn target() -> Result<Target, String> { + const PRE_LINK_ARGS: &[&str] = &[ + "--as-needed", + "-z", + "noexecstack", + "-e", + "elf_entry", + "-Bstatic", + "--gc-sections", + "-z", + "text", + "-z", + "norelro", + "--no-undefined", + "--error-unresolved-symbols", + "--no-undefined-version", + "-Bsymbolic", + "--export-dynamic", + // The following symbols are needed by libunwind, which is linked after + // libstd. Make sure they're included in the link. + "-u", + "__rust_abort", + "-u", + "__rust_c_alloc", + "-u", + "__rust_c_dealloc", + "-u", + "__rust_print_err", + "-u", + "__rust_rwlock_rdlock", + "-u", + "__rust_rwlock_unlock", + "-u", + "__rust_rwlock_wrlock", + ]; + + const EXPORT_SYMBOLS: &[&str] = &[ + "sgx_entry", + "HEAP_BASE", + "HEAP_SIZE", + "RELA", + "RELACOUNT", + "ENCLAVE_SIZE", + "CFGDATA_BASE", + "DEBUG", + "EH_FRM_HDR_OFFSET", + "EH_FRM_HDR_LEN", + "EH_FRM_OFFSET", + "EH_FRM_LEN", + "TEXT_BASE", + "TEXT_SIZE", + ]; + let opts = TargetOptions { + dynamic_linking: false, + executables: true, + linker_is_gnu: true, + linker: Some("rust-lld".to_owned()), + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Unwind, + cpu: "x86-64".into(), + features: "+rdrnd,+rdseed,+lvi-cfi,+lvi-load-hardening".into(), + llvm_args: vec!["--x86-experimental-lvi-inline-asm-hardening".into()], + position_independent_executables: true, + pre_link_args: iter::once(( + LinkerFlavor::Lld(LldFlavor::Ld), + PRE_LINK_ARGS.iter().cloned().map(String::from).collect(), + )) + .collect(), + override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(String::from).collect()), + relax_elf_relocations: true, + ..Default::default() + }; + Ok(Target { + llvm_target: "x86_64-elf".into(), + target_endian: "little".into(), + target_pointer_width: "64".into(), + target_c_int_width: "32".into(), + target_os: "unknown".into(), + target_env: "sgx".into(), + target_vendor: "fortanix".into(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .into(), + arch: "x86_64".into(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: opts, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs new file mode 100644 index 00000000000..37b6d57366c --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs @@ -0,0 +1,23 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::fuchsia_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-fuchsia".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "fuchsia".to_string(), + target_env: String::new(), + target_vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs new file mode 100644 index 00000000000..74097f5bf6f --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::android_base::opts(); + base.cpu = "x86-64".to_string(); + // https://developer.android.com/ndk/guides/abis.html#86-64 + base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-linux-android".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "android".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs b/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs new file mode 100644 index 00000000000..65bb97d84aa --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs @@ -0,0 +1,32 @@ +// This defines the amd64 target for the Linux Kernel. See the linux-kernel-base module for +// generic Linux kernel options. + +use crate::spec::{CodeModel, LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_kernel_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.features = + "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" + .to_string(); + base.code_model = Some(CodeModel::Kernel); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + + Ok(Target { + // FIXME: Some dispute, the linux-on-clang folks think this should use "Linux" + llvm_target: "x86_64-elf".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + target_os: "none".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + arch: "x86_64".to_string(), + linker_flavor: LinkerFlavor::Gcc, + + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs new file mode 100644 index 00000000000..99af483f1d4 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_gnu_base::opts(); + base.cpu = "x86-64".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pep".to_string()]); + base.max_atomic_width = Some(64); + base.linker = Some("x86_64-w64-mingw32-gcc".to_string()); + + Ok(Target { + llvm_target: "x86_64-pc-windows-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "windows".to_string(), + target_env: "gnu".to_string(), + target_vendor: "pc".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs new file mode 100644 index 00000000000..75ff6b97a2e --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs @@ -0,0 +1,23 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_msvc_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.has_elf_tls = true; + + Ok(Target { + llvm_target: "x86_64-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "pc".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs new file mode 100644 index 00000000000..fbade02c556 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs @@ -0,0 +1,30 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.cpu = "x86-64".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.linker = Some("x86_64-rumprun-netbsd-gcc".to_string()); + base.max_atomic_width = Some(64); + + base.dynamic_linking = false; + base.has_rpath = false; + base.position_independent_executables = false; + base.disable_redzone = true; + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-rumprun-netbsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "netbsd".to_string(), + target_env: String::new(), + target_vendor: "rumprun".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs new file mode 100644 index 00000000000..53f4df96518 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::solaris_base::opts(); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-pc-solaris".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "solaris".to_string(), + target_env: String::new(), + target_vendor: "sun".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs new file mode 100644 index 00000000000..dbc5f965020 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::cloudabi_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.linker = Some("x86_64-unknown-cloudabi-cc".to_string()); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-cloudabi".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "cloudabi".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs new file mode 100644 index 00000000000..fd1871b1a57 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::dragonfly_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-dragonfly".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "dragonfly".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs new file mode 100644 index 00000000000..a124f582bf3 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::freebsd_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-freebsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "freebsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs new file mode 100644 index 00000000000..51237697714 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::haiku_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); + base.stack_probes = true; + // This option is required to build executables on Haiku x86_64 + base.position_independent_executables = true; + + Ok(Target { + llvm_target: "x86_64-unknown-haiku".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "haiku".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs new file mode 100644 index 00000000000..4a526f90ed5 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::hermit_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.features = "+rdrnd,+rdseed".to_string(); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-hermit".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "hermit".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs new file mode 100644 index 00000000000..c25cd0809ee --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs @@ -0,0 +1,26 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::hermit_kernel_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.features = + "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float" + .to_string(); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-hermit".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "hermit".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs new file mode 100644 index 00000000000..2567ca47ef9 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::illumos_base::opts(); + base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string(), "-std=c99".to_string()]); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + + Ok(Target { + // LLVM does not currently have a separate illumos target, + // so we still pass Solaris to it + llvm_target: "x86_64-pc-solaris".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "illumos".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs new file mode 100644 index 00000000000..cab19f149a7 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs @@ -0,0 +1,22 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::l4re_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "x86_64-unknown-l4re-uclibc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "l4re".to_string(), + target_env: "uclibc".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Ld, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs new file mode 100644 index 00000000000..29cbb777db5 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs new file mode 100644 index 00000000000..0a37399e2fa --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs @@ -0,0 +1,29 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mx32".to_string()); + base.stack_probes = true; + base.has_elf_tls = false; + // BUG(GabrielMajeri): disabling the PLT on x86_64 Linux with x32 ABI + // breaks code gen. See LLVM bug 36743 + base.needs_plt = true; + + Ok(Target { + llvm_target: "x86_64-unknown-linux-gnux32".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ + i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "linux".to_string(), + target_env: "gnu".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs new file mode 100644 index 00000000000..3a22290da68 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::linux_musl_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + base.static_position_independent_executables = true; + + Ok(Target { + llvm_target: "x86_64-unknown-linux-musl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "linux".to_string(), + target_env: "musl".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs new file mode 100644 index 00000000000..adf09c89c42 --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::netbsd_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-netbsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "netbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs new file mode 100644 index 00000000000..dbd163db36b --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::openbsd_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-openbsd".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "openbsd".to_string(), + target_env: String::new(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs new file mode 100644 index 00000000000..3d40bafbe1f --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs @@ -0,0 +1,24 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::redox_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + + Ok(Target { + llvm_target: "x86_64-unknown-redox".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "redox".to_string(), + target_env: "relibc".to_string(), + target_vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs new file mode 100644 index 00000000000..849227a574a --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs @@ -0,0 +1,46 @@ +// This defines the amd64 target for UEFI systems as described in the UEFI specification. See the +// uefi-base module for generic UEFI options. On x86_64 systems (mostly called "x64" in the spec) +// UEFI systems always run in long-mode, have the interrupt-controller pre-configured and force a +// single-CPU execution. +// 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::spec::{CodeModel, LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::uefi_msvc_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + + // 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 + // will trigger an exception. Rust does not inject any code that enables AVX/MMX/SSE + // instruction sets, so this must be done by the firmware. However, existing firmware is known + // to leave these uninitialized, thus triggering exceptions if we make use of them. Which is + // why we avoid them and instead use soft-floats. This is also what GRUB and friends did so + // far. + // If you initialize FP units yourself, you can override these flags with custom linker + // arguments, thus giving you access to full MMX/SSE acceleration. + base.features = "-mmx,-sse,+soft-float".to_string(); + + // UEFI systems run without a host OS, hence we cannot assume any code locality. We must tell + // LLVM to expect code to reference any address in the address-space. The "large" code-model + // places no locality-restrictions, so it fits well here. + base.code_model = Some(CodeModel::Large); + + Ok(Target { + llvm_target: "x86_64-unknown-windows".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + target_os: "uefi".to_string(), + target_env: "".to_string(), + target_vendor: "unknown".to_string(), + arch: "x86_64".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Link), + + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs new file mode 100644 index 00000000000..3bd18f23f6f --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_uwp_gnu_base::opts(); + base.cpu = "x86-64".to_string(); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.pre_link_args + .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pep".to_string()]); + base.max_atomic_width = Some(64); + + Ok(Target { + llvm_target: "x86_64-pc-windows-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "windows".to_string(), + target_env: "gnu".to_string(), + target_vendor: "uwp".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs new file mode 100644 index 00000000000..258df010aae --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs @@ -0,0 +1,23 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::windows_uwp_msvc_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.has_elf_tls = true; + + Ok(Target { + llvm_target: "x86_64-pc-windows-msvc".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "windows".to_string(), + target_env: "msvc".to_string(), + target_vendor: "uwp".to_string(), + linker_flavor: LinkerFlavor::Msvc, + options: base, + }) +} diff --git a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs new file mode 100644 index 00000000000..f1e27f4d8be --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs @@ -0,0 +1,25 @@ +use crate::spec::{LinkerFlavor, Target, TargetResult}; + +pub fn target() -> TargetResult { + let mut base = super::vxworks_base::opts(); + base.cpu = "x86-64".to_string(); + base.max_atomic_width = Some(64); + base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); + base.stack_probes = true; + base.disable_redzone = true; + + Ok(Target { + llvm_target: "x86_64-unknown-linux-gnu".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "64".to_string(), + target_c_int_width: "32".to_string(), + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .to_string(), + arch: "x86_64".to_string(), + target_os: "vxworks".to_string(), + target_env: "gnu".to_string(), + target_vendor: "wrs".to_string(), + linker_flavor: LinkerFlavor::Gcc, + options: base, + }) +} | 
