Skip to main content

rustc_abi/
callconv.rs

1#[cfg(feature = "nightly")]
2use crate::{BackendRepr, FieldsShape, Primitive, Size, TyAbiInterface, TyAndLayout, Variants};
3
4mod reg;
5
6pub use reg::{Reg, RegKind};
7
8/// Return value from the `homogeneous_aggregate` test function.
9#[derive(#[automatically_derived]
impl ::core::marker::Copy for HomogeneousAggregate { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for HomogeneousAggregate { }
#[automatically_derived]
impl ::core::clone::Clone for HomogeneousAggregate {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<Reg>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for HomogeneousAggregate {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Homogeneous(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Homogeneous", &__self_0),
            Self::NoData => ::core::fmt::Formatter::write_str(f, "NoData"),
        }
    }
}Debug)]
10pub enum HomogeneousAggregate {
11    /// Yes, all the "leaf fields" of this struct are passed in the
12    /// same way (specified in the `Reg` value).
13    Homogeneous(Reg),
14
15    /// There are no leaf fields at all.
16    NoData,
17}
18
19/// Error from the `homogeneous_aggregate` test function, indicating
20/// there are distinct leaf fields passed in different ways,
21/// or this is uninhabited.
22#[derive(#[automatically_derived]
impl ::core::marker::Copy for Heterogeneous { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Heterogeneous { }
#[automatically_derived]
impl ::core::clone::Clone for Heterogeneous {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for Heterogeneous {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "Heterogeneous")
    }
}Debug)]
23pub struct Heterogeneous;
24
25impl HomogeneousAggregate {
26    /// If this is a homogeneous aggregate, returns the homogeneous
27    /// unit, else `None`.
28    pub fn unit(self) -> Option<Reg> {
29        match self {
30            HomogeneousAggregate::Homogeneous(reg) => Some(reg),
31            HomogeneousAggregate::NoData => None,
32        }
33    }
34
35    /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
36    /// the same `struct`. Only succeeds if only one of them has any data,
37    /// or both units are identical.
38    #[cfg(feature = "nightly")]
39    fn merge(self, other: HomogeneousAggregate) -> Result<HomogeneousAggregate, Heterogeneous> {
40        match (self, other) {
41            (x, HomogeneousAggregate::NoData) | (HomogeneousAggregate::NoData, x) => Ok(x),
42
43            (HomogeneousAggregate::Homogeneous(a), HomogeneousAggregate::Homogeneous(b)) => {
44                if a != b {
45                    return Err(Heterogeneous);
46                }
47                Ok(self)
48            }
49        }
50    }
51}
52
53#[cfg(feature = "nightly")]
54impl<'a, Ty> TyAndLayout<'a, Ty> {
55    /// Returns `Homogeneous` if this layout is an aggregate containing fields of
56    /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
57    /// special-cased in ABIs.
58    ///
59    /// Note: We generally ignore 1-ZST fields when computing this value (see #56877).
60    ///
61    /// Note: The fields of a union are merged as if it were a struct, so the result does not
62    /// distinguish the two. `powerpc64::is_or_contains_union` in `rustc_target` walks the layout
63    /// again to do that, mirroring the array handling here.
64    ///
65    /// This is public so that it can be used in unit tests, but
66    /// should generally only be relevant to the ABI details of
67    /// specific targets.
68    {}
#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("homogeneous_aggregate",
                                    "rustc_abi::callconv", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/75a75c3e0a67d3fa3d03982775f5bb0356e7b510/compiler/rustc_abi/src/callconv.rs"),
                                    ::tracing_core::__macro_support::Option::Some(68u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_abi::callconv"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("self")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("self");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&self)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return:
                    Result<HomogeneousAggregate, Heterogeneous> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            match self.backend_repr {
                BackendRepr::Scalar(scalar) => {
                    let kind =
                        match scalar.primitive() {
                            Primitive::Int(..) | Primitive::Pointer(_) =>
                                RegKind::Integer,
                            Primitive::Float(_) => RegKind::Float,
                        };
                    Ok(HomogeneousAggregate::Homogeneous(Reg {
                                kind,
                                size: self.size,
                            }))
                }
                BackendRepr::SimdVector { element, count: _ } => {
                    if !!self.is_zst() {
                        ::core::panicking::panic("assertion failed: !self.is_zst()")
                    };
                    Ok(HomogeneousAggregate::Homogeneous(Reg {
                                kind: RegKind::Vector {
                                    hint_vector_elem: element.primitive(),
                                },
                                size: self.size,
                            }))
                }
                BackendRepr::SimdScalableVector { .. } => {
                    {
                        ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
                                format_args!("`homogeneous_aggregate` should not be called for scalable vectors")));
                    }
                }
                BackendRepr::ScalarPair { .. } | BackendRepr::Memory {
                    sized: true } => {
                    let from_fields_at =
                        |layout: Self, start: Size|
                            -> Result<(HomogeneousAggregate, Size), Heterogeneous>
                            {
                                let is_union =
                                    match layout.fields {
                                        FieldsShape::Primitive => {
                                            {
                                                ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
                                                        format_args!("aggregates can\'t have `FieldsShape::Primitive`")));
                                            }
                                        }
                                        FieldsShape::Array { count, .. } => {
                                            {
                                                match (&start, &Size::ZERO) {
                                                    (left_val, right_val) => {
                                                        if !(*left_val == *right_val) {
                                                            let kind = ::core::panicking::AssertKind::Eq;
                                                            ::core::panicking::assert_failed(kind, &*left_val,
                                                                &*right_val, ::core::option::Option::None);
                                                        }
                                                    }
                                                }
                                            };
                                            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() {
                                    let field = layout.field(cx, i);
                                    if field.is_1zst() { continue; }
                                    if !is_union && total != layout.fields.offset(i) {
                                        return Err(Heterogeneous);
                                    }
                                    result = result.merge(field.homogeneous_aggregate(cx)?)?;
                                    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 {
                        Variants::Single { .. } | Variants::Empty => {}
                        Variants::Multiple { variants, .. } => {
                            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);
                            }
                        }
                    }
                    if total != self.size {
                        Err(Heterogeneous)
                    } else {
                        match result {
                            HomogeneousAggregate::Homogeneous(_) => {
                                {
                                    match (&total, &Size::ZERO) {
                                        (left_val, right_val) => {
                                            if *left_val == *right_val {
                                                let kind = ::core::panicking::AssertKind::Ne;
                                                ::core::panicking::assert_failed(kind, &*left_val,
                                                    &*right_val, ::core::option::Option::None);
                                            }
                                        }
                                    }
                                };
                            }
                            HomogeneousAggregate::NoData => {
                                {
                                    match (&total, &Size::ZERO) {
                                        (left_val, right_val) => {
                                            if !(*left_val == *right_val) {
                                                let kind = ::core::panicking::AssertKind::Eq;
                                                ::core::panicking::assert_failed(kind, &*left_val,
                                                    &*right_val, ::core::option::Option::None);
                                            }
                                        }
                                    }
                                };
                            }
                        }
                        Ok(result)
                    }
                }
                BackendRepr::Memory { sized: false } => Err(Heterogeneous),
            }
        }
    }
}#[tracing::instrument(skip(cx), level = "debug")]
69    pub fn homogeneous_aggregate<C>(&self, cx: &C) -> Result<HomogeneousAggregate, Heterogeneous>
70    where
71        Ty: TyAbiInterface<'a, C> + Copy,
72    {
73        match self.backend_repr {
74            // The primitive for this algorithm.
75            BackendRepr::Scalar(scalar) => {
76                let kind = match scalar.primitive() {
77                    Primitive::Int(..) | Primitive::Pointer(_) => RegKind::Integer,
78                    Primitive::Float(_) => RegKind::Float,
79                };
80                Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
81            }
82
83            BackendRepr::SimdVector { element, count: _ } => {
84                assert!(!self.is_zst());
85
86                Ok(HomogeneousAggregate::Homogeneous(Reg {
87                    kind: RegKind::Vector { hint_vector_elem: element.primitive() },
88                    size: self.size,
89                }))
90            }
91
92            BackendRepr::SimdScalableVector { .. } => {
93                unreachable!("`homogeneous_aggregate` should not be called for scalable vectors")
94            }
95
96            BackendRepr::ScalarPair { .. } | BackendRepr::Memory { sized: true } => {
97                // Helper for computing `homogeneous_aggregate`, allowing a custom
98                // starting offset (used below for handling variants).
99                let from_fields_at =
100                    |layout: Self,
101                     start: Size|
102                     -> Result<(HomogeneousAggregate, Size), Heterogeneous> {
103                        let is_union = match layout.fields {
104                            FieldsShape::Primitive => {
105                                unreachable!("aggregates can't have `FieldsShape::Primitive`")
106                            }
107                            FieldsShape::Array { count, .. } => {
108                                assert_eq!(start, Size::ZERO);
109
110                                let result = if count > 0 {
111                                    layout.field(cx, 0).homogeneous_aggregate(cx)?
112                                } else {
113                                    HomogeneousAggregate::NoData
114                                };
115                                return Ok((result, layout.size));
116                            }
117                            FieldsShape::Union(_) => true,
118                            FieldsShape::Arbitrary { .. } => false,
119                        };
120
121                        let mut result = HomogeneousAggregate::NoData;
122                        let mut total = start;
123
124                        for i in 0..layout.fields.count() {
125                            let field = layout.field(cx, i);
126                            if field.is_1zst() {
127                                // No data here and no impact on layout, can be ignored.
128                                // (We might be able to also ignore all aligned ZST but that's less clear.)
129                                continue;
130                            }
131
132                            if !is_union && total != layout.fields.offset(i) {
133                                // This field isn't just after the previous one we considered, abort.
134                                return Err(Heterogeneous);
135                            }
136
137                            result = result.merge(field.homogeneous_aggregate(cx)?)?;
138
139                            // Keep track of the offset (without padding).
140                            let size = field.size;
141                            if is_union {
142                                total = total.max(size);
143                            } else {
144                                total += size;
145                            }
146                        }
147
148                        Ok((result, total))
149                    };
150
151                let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
152
153                match &self.variants {
154                    Variants::Single { .. } | Variants::Empty => {}
155                    Variants::Multiple { variants, .. } => {
156                        // Treat enum variants like union members.
157                        // HACK(eddyb) pretend the `enum` field (discriminant)
158                        // is at the start of every variant (otherwise the gap
159                        // at the start of all variants would disqualify them).
160                        //
161                        // NB: for all tagged `enum`s (which include all non-C-like
162                        // `enum`s with defined FFI representation), this will
163                        // match the homogeneous computation on the equivalent
164                        // `struct { tag; union { variant1; ... } }` and/or
165                        // `union { struct { tag; variant1; } ... }`
166                        // (the offsets of variant fields should be identical
167                        // between the two for either to be a homogeneous aggregate).
168                        let variant_start = total;
169                        for variant_idx in variants.indices() {
170                            let (variant_result, variant_total) =
171                                from_fields_at(self.for_variant(cx, variant_idx), variant_start)?;
172
173                            result = result.merge(variant_result)?;
174                            total = total.max(variant_total);
175                        }
176                    }
177                }
178
179                // There needs to be no padding.
180                if total != self.size {
181                    Err(Heterogeneous)
182                } else {
183                    match result {
184                        HomogeneousAggregate::Homogeneous(_) => {
185                            assert_ne!(total, Size::ZERO);
186                        }
187                        HomogeneousAggregate::NoData => {
188                            assert_eq!(total, Size::ZERO);
189                        }
190                    }
191                    Ok(result)
192                }
193            }
194            BackendRepr::Memory { sized: false } => Err(Heterogeneous),
195        }
196    }
197}