summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_target/src/abi/call/mod.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /compiler/rustc_target/src/abi/call/mod.rs
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_target/src/abi/call/mod.rs')
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs162
1 files changed, 130 insertions, 32 deletions
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 8fab13d5d..5efd171b9 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -2,6 +2,7 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size};
use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::{self, HasTargetSpec};
use rustc_span::Symbol;
+use std::fmt;
use std::str::FromStr;
mod aarch64;
@@ -36,23 +37,52 @@ pub enum PassMode {
Ignore,
/// Pass the argument directly.
///
- /// The argument has a layout abi of `Scalar`, `Vector` or in rare cases `Aggregate`.
+ /// The argument has a layout abi of `Scalar` or `Vector`.
+ /// Unfortunately due to past mistakes, in rare cases on wasm, it can also be `Aggregate`.
+ /// This is bad since it leaks LLVM implementation details into the ABI.
+ /// (Also see <https://github.com/rust-lang/rust/issues/115666>.)
Direct(ArgAttributes),
/// Pass a pair's elements directly in two arguments.
///
/// The argument has a layout abi of `ScalarPair`.
Pair(ArgAttributes, ArgAttributes),
- /// Pass the argument after casting it, to either a single uniform or a
- /// pair of registers. The bool indicates if a `Reg::i32()` dummy argument
- /// is emitted before the real argument.
- Cast(Box<CastTarget>, bool),
+ /// Pass the argument after casting it. See the `CastTarget` docs for details. The bool
+ /// indicates if a `Reg::i32()` dummy argument is emitted before the real argument.
+ Cast { pad_i32: bool, cast: Box<CastTarget> },
/// Pass the argument indirectly via a hidden pointer.
- /// The `extra_attrs` value, if any, is for the extra data (vtable or length)
- /// which indicates that it refers to an unsized rvalue.
- /// `on_stack` defines that the value should be passed at a fixed
- /// stack offset in accordance to the ABI rather than passed using a
- /// pointer. This corresponds to the `byval` LLVM argument attribute.
- Indirect { attrs: ArgAttributes, extra_attrs: Option<ArgAttributes>, on_stack: bool },
+ /// The `meta_attrs` value, if any, is for the metadata (vtable or length) of an unsized
+ /// argument. (This is the only mode that supports unsized arguments.)
+ /// `on_stack` defines that the value should be passed at a fixed stack offset in accordance to
+ /// the ABI rather than passed using a pointer. This corresponds to the `byval` LLVM argument
+ /// attribute (using the Rust type of this argument). `on_stack` cannot be true for unsized
+ /// arguments, i.e., when `meta_attrs` is `Some`.
+ Indirect { attrs: ArgAttributes, meta_attrs: Option<ArgAttributes>, on_stack: bool },
+}
+
+impl PassMode {
+ /// Checks if these two `PassMode` are equal enough to be considered "the same for all
+ /// function call ABIs". However, the `Layout` can also impact ABI decisions,
+ /// so that needs to be compared as well!
+ pub fn eq_abi(&self, other: &Self) -> bool {
+ match (self, other) {
+ (PassMode::Ignore, PassMode::Ignore) => true,
+ (PassMode::Direct(a1), PassMode::Direct(a2)) => a1.eq_abi(a2),
+ (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => a1.eq_abi(a2) && b1.eq_abi(b2),
+ (
+ PassMode::Cast { cast: c1, pad_i32: pad1 },
+ PassMode::Cast { cast: c2, pad_i32: pad2 },
+ ) => c1.eq_abi(c2) && pad1 == pad2,
+ (
+ PassMode::Indirect { attrs: a1, meta_attrs: None, on_stack: s1 },
+ PassMode::Indirect { attrs: a2, meta_attrs: None, on_stack: s2 },
+ ) => a1.eq_abi(a2) && s1 == s2,
+ (
+ PassMode::Indirect { attrs: a1, meta_attrs: Some(e1), on_stack: s1 },
+ PassMode::Indirect { attrs: a2, meta_attrs: Some(e2), on_stack: s2 },
+ ) => a1.eq_abi(a2) && e1.eq_abi(e2) && s1 == s2,
+ _ => false,
+ }
+ }
}
// Hack to disable non_upper_case_globals only for the bitflags! and not for the rest
@@ -127,6 +157,24 @@ impl ArgAttributes {
pub fn contains(&self, attr: ArgAttribute) -> bool {
self.regular.contains(attr)
}
+
+ /// Checks if these two `ArgAttributes` are equal enough to be considered "the same for all
+ /// function call ABIs".
+ pub fn eq_abi(&self, other: &Self) -> bool {
+ // There's only one regular attribute that matters for the call ABI: InReg.
+ // Everything else is things like noalias, dereferenceable, nonnull, ...
+ // (This also applies to pointee_size, pointee_align.)
+ if self.regular.contains(ArgAttribute::InReg) != other.regular.contains(ArgAttribute::InReg)
+ {
+ return false;
+ }
+ // We also compare the sign extension mode -- this could let the callee make assumptions
+ // about bits that conceptually were not even passed.
+ if self.arg_ext != other.arg_ext {
+ return false;
+ }
+ return true;
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
@@ -211,6 +259,13 @@ impl Uniform {
}
}
+/// Describes the type used for `PassMode::Cast`.
+///
+/// Passing arguments in this mode works as follows: the registers in the `prefix` (the ones that
+/// are `Some`) get laid out one after the other (using `repr(C)` layout rules). Then the
+/// `rest.unit` register type gets repeated often enough to cover `rest.size`. This describes the
+/// actual type used for the call; the Rust type of the argument is then transmuted to this ABI type
+/// (and all data in the padding between the registers is dropped).
#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
pub struct CastTarget {
pub prefix: [Option<Reg>; 8],
@@ -272,6 +327,14 @@ impl CastTarget {
acc.max(align)
})
}
+
+ /// Checks if these two `CastTarget` are equal enough to be considered "the same for all
+ /// function call ABIs".
+ pub fn eq_abi(&self, other: &Self) -> bool {
+ let CastTarget { prefix: prefix_l, rest: rest_l, attrs: attrs_l } = self;
+ let CastTarget { prefix: prefix_r, rest: rest_r, attrs: attrs_r } = other;
+ prefix_l == prefix_r && rest_l == rest_r && attrs_l.eq_abi(attrs_r)
+ }
}
/// Return value from the `homogeneous_aggregate` test function.
@@ -330,8 +393,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// 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).
+ /// Note: We generally ignore 1-ZST fields 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
@@ -389,12 +451,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
let mut total = start;
for i in 0..layout.fields.count() {
+ let field = layout.field(cx, i);
+ if field.is_1zst() {
+ // No data here and no impact on layout, can be ignored.
+ // (We might be able to also ignore all aligned ZST but that's less clear.)
+ continue;
+ }
+
if !is_union && total != layout.fields.offset(i) {
+ // This field isn't just after the previous one we considered, abort.
return Err(Heterogeneous);
}
- let field = layout.field(cx, i);
-
result = result.merge(field.homogeneous_aggregate(cx)?)?;
// Keep track of the offset (without padding).
@@ -458,13 +526,22 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// Information about how to pass an argument to,
/// or return a value from, a function, under some ABI.
-#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+#[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)]
pub struct ArgAbi<'a, Ty> {
pub layout: TyAndLayout<'a, Ty>,
pub mode: PassMode,
}
+// Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl.
+impl<'a, Ty: fmt::Display> fmt::Debug for ArgAbi<'a, Ty> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let ArgAbi { layout, mode } = self;
+ f.debug_struct("ArgAbi").field("layout", layout).field("mode", mode).finish()
+ }
+}
+
impl<'a, Ty> ArgAbi<'a, Ty> {
+ /// This defines the "default ABI" for that type, that is then later adjusted in `fn_abi_adjust_for_abi`.
pub fn new(
cx: &impl HasDataLayout,
layout: TyAndLayout<'a, Ty>,
@@ -478,6 +555,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)),
),
Abi::Vector { .. } => PassMode::Direct(ArgAttributes::new()),
+ // The `Aggregate` ABI should always be adjusted later.
Abi::Aggregate { .. } => PassMode::Direct(ArgAttributes::new()),
};
ArgAbi { layout, mode }
@@ -497,15 +575,15 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
attrs.pointee_size = layout.size;
attrs.pointee_align = Some(layout.align.abi);
- let extra_attrs = layout.is_unsized().then_some(ArgAttributes::new());
+ let meta_attrs = layout.is_unsized().then_some(ArgAttributes::new());
- PassMode::Indirect { attrs, extra_attrs, on_stack: false }
+ PassMode::Indirect { attrs, meta_attrs, on_stack: false }
}
pub fn make_indirect(&mut self) {
match self.mode {
PassMode::Direct(_) | PassMode::Pair(_, _) => {}
- PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: false } => return,
+ PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: false } => return,
_ => panic!("Tried to make {:?} indirect", self.mode),
}
@@ -515,7 +593,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) {
self.make_indirect();
match self.mode {
- PassMode::Indirect { ref mut attrs, extra_attrs: _, ref mut on_stack } => {
+ PassMode::Indirect { ref mut attrs, meta_attrs: _, ref mut on_stack } => {
*on_stack = true;
// Some platforms, like 32-bit x86, change the alignment of the type when passing
@@ -548,11 +626,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
}
pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) {
- self.mode = PassMode::Cast(Box::new(target.into()), false);
+ self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false };
}
pub fn cast_to_and_pad_i32<T: Into<CastTarget>>(&mut self, target: T, pad_i32: bool) {
- self.mode = PassMode::Cast(Box::new(target.into()), pad_i32);
+ self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 };
}
pub fn is_indirect(&self) -> bool {
@@ -560,16 +638,24 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
}
pub fn is_sized_indirect(&self) -> bool {
- matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ })
+ matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ })
}
pub fn is_unsized_indirect(&self) -> bool {
- matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ })
+ matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ })
}
pub fn is_ignore(&self) -> bool {
matches!(self.mode, PassMode::Ignore)
}
+
+ /// Checks if these two `ArgAbi` are equal enough to be considered "the same for all
+ /// function call ABIs".
+ pub fn eq_abi(&self, other: &Self) -> bool {
+ // Ideally we'd just compare the `mode`, but that is not enough -- for some modes LLVM will look
+ // at the type.
+ self.layout.eq_abi(&other.layout) && self.mode.eq_abi(&other.mode)
+ }
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
@@ -579,10 +665,9 @@ pub enum Conv {
C,
Rust,
- /// For things unlikely to be called, where smaller caller codegen is
- /// preferred over raw speed.
- /// Stronger than just `#[cold]` because `fn` pointers might be incompatible.
- RustCold,
+ Cold,
+ PreserveMost,
+ PreserveAll,
// Target-specific calling conventions.
ArmAapcs,
@@ -605,9 +690,7 @@ pub enum Conv {
AvrInterrupt,
AvrNonBlockingInterrupt,
- RiscvInterrupt {
- kind: RiscvInterruptKind,
- },
+ RiscvInterrupt { kind: RiscvInterruptKind },
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
@@ -630,7 +713,7 @@ impl RiscvInterruptKind {
///
/// I will do my best to describe this structure, but these
/// comments are reverse-engineered and may be inaccurate. -NDM
-#[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
+#[derive(Clone, PartialEq, Eq, Hash, HashStable_Generic)]
pub struct FnAbi<'a, Ty> {
/// The LLVM types of each argument.
pub args: Box<[ArgAbi<'a, Ty>]>,
@@ -651,6 +734,21 @@ pub struct FnAbi<'a, Ty> {
pub can_unwind: bool,
}
+// Needs to be a custom impl because of the bounds on the `TyAndLayout` debug impl.
+impl<'a, Ty: fmt::Display> fmt::Debug for FnAbi<'a, Ty> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let FnAbi { args, ret, c_variadic, fixed_count, conv, can_unwind } = self;
+ f.debug_struct("FnAbi")
+ .field("args", args)
+ .field("ret", ret)
+ .field("c_variadic", c_variadic)
+ .field("fixed_count", fixed_count)
+ .field("conv", conv)
+ .field("can_unwind", can_unwind)
+ .finish()
+ }
+}
+
/// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI.
#[derive(Copy, Clone, Debug, HashStable_Generic)]
pub enum AdjustForForeignAbiError {