rustc_session/
code_stats.rs

1use std::cmp;
2
3use rustc_abi::{Align, Size};
4use rustc_data_structures::fx::FxHashSet;
5use rustc_data_structures::sync::Lock;
6use rustc_span::Symbol;
7
8#[derive(#[automatically_derived]
impl ::core::clone::Clone for VariantInfo {
    #[inline]
    fn clone(&self) -> VariantInfo {
        VariantInfo {
            name: ::core::clone::Clone::clone(&self.name),
            kind: ::core::clone::Clone::clone(&self.kind),
            size: ::core::clone::Clone::clone(&self.size),
            align: ::core::clone::Clone::clone(&self.align),
            fields: ::core::clone::Clone::clone(&self.fields),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for VariantInfo {
    #[inline]
    fn eq(&self, other: &VariantInfo) -> bool {
        self.size == other.size && self.align == other.align &&
                    self.name == other.name && self.kind == other.kind &&
            self.fields == other.fields
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for VariantInfo {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<Option<Symbol>>;
        let _: ::core::cmp::AssertParamIsEq<SizeKind>;
        let _: ::core::cmp::AssertParamIsEq<u64>;
        let _: ::core::cmp::AssertParamIsEq<Vec<FieldInfo>>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for VariantInfo {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        ::core::hash::Hash::hash(&self.name, state);
        ::core::hash::Hash::hash(&self.kind, state);
        ::core::hash::Hash::hash(&self.size, state);
        ::core::hash::Hash::hash(&self.align, state);
        ::core::hash::Hash::hash(&self.fields, state)
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for VariantInfo {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field5_finish(f, "VariantInfo",
            "name", &self.name, "kind", &self.kind, "size", &self.size,
            "align", &self.align, "fields", &&self.fields)
    }
}Debug)]
9pub struct VariantInfo {
10    pub name: Option<Symbol>,
11    pub kind: SizeKind,
12    pub size: u64,
13    pub align: u64,
14    pub fields: Vec<FieldInfo>,
15}
16
17#[derive(#[automatically_derived]
impl ::core::marker::Copy for SizeKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for SizeKind {
    #[inline]
    fn clone(&self) -> SizeKind { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for SizeKind {
    #[inline]
    fn eq(&self, other: &SizeKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for SizeKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for SizeKind {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for SizeKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                SizeKind::Exact => "Exact",
                SizeKind::Min => "Min",
            })
    }
}Debug)]
18pub enum SizeKind {
19    Exact,
20    Min,
21}
22
23#[derive(#[automatically_derived]
impl ::core::marker::Copy for FieldKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for FieldKind {
    #[inline]
    fn clone(&self) -> FieldKind { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for FieldKind {
    #[inline]
    fn eq(&self, other: &FieldKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for FieldKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for FieldKind {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for FieldKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                FieldKind::AdtField => "AdtField",
                FieldKind::Upvar => "Upvar",
                FieldKind::CoroutineLocal => "CoroutineLocal",
            })
    }
}Debug)]
24pub enum FieldKind {
25    AdtField,
26    Upvar,
27    CoroutineLocal,
28}
29
30impl std::fmt::Display for FieldKind {
31    fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            FieldKind::AdtField => w.write_fmt(format_args!("field"))write!(w, "field"),
34            FieldKind::Upvar => w.write_fmt(format_args!("upvar"))write!(w, "upvar"),
35            FieldKind::CoroutineLocal => w.write_fmt(format_args!("local"))write!(w, "local"),
36        }
37    }
38}
39
40#[derive(#[automatically_derived]
impl ::core::marker::Copy for FieldInfo { }Copy, #[automatically_derived]
impl ::core::clone::Clone for FieldInfo {
    #[inline]
    fn clone(&self) -> FieldInfo {
        let _: ::core::clone::AssertParamIsClone<FieldKind>;
        let _: ::core::clone::AssertParamIsClone<Symbol>;
        let _: ::core::clone::AssertParamIsClone<u64>;
        let _: ::core::clone::AssertParamIsClone<Option<Symbol>>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for FieldInfo {
    #[inline]
    fn eq(&self, other: &FieldInfo) -> bool {
        self.offset == other.offset && self.size == other.size &&
                        self.align == other.align && self.kind == other.kind &&
                self.name == other.name && self.type_name == other.type_name
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for FieldInfo {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<FieldKind>;
        let _: ::core::cmp::AssertParamIsEq<Symbol>;
        let _: ::core::cmp::AssertParamIsEq<u64>;
        let _: ::core::cmp::AssertParamIsEq<Option<Symbol>>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for FieldInfo {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        ::core::hash::Hash::hash(&self.kind, state);
        ::core::hash::Hash::hash(&self.name, state);
        ::core::hash::Hash::hash(&self.offset, state);
        ::core::hash::Hash::hash(&self.size, state);
        ::core::hash::Hash::hash(&self.align, state);
        ::core::hash::Hash::hash(&self.type_name, state)
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for FieldInfo {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        let names: &'static _ =
            &["kind", "name", "offset", "size", "align", "type_name"];
        let values: &[&dyn ::core::fmt::Debug] =
            &[&self.kind, &self.name, &self.offset, &self.size, &self.align,
                        &&self.type_name];
        ::core::fmt::Formatter::debug_struct_fields_finish(f, "FieldInfo",
            names, values)
    }
}Debug)]
41pub struct FieldInfo {
42    pub kind: FieldKind,
43    pub name: Symbol,
44    pub offset: u64,
45    pub size: u64,
46    pub align: u64,
47    /// Name of the type of this field.
48    /// Present only if the creator thought that this would be important for identifying the field,
49    /// typically because the field name is uninformative.
50    pub type_name: Option<Symbol>,
51}
52
53#[derive(#[automatically_derived]
impl ::core::marker::Copy for DataTypeKind { }Copy, #[automatically_derived]
impl ::core::clone::Clone for DataTypeKind {
    #[inline]
    fn clone(&self) -> DataTypeKind { *self }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for DataTypeKind {
    #[inline]
    fn eq(&self, other: &DataTypeKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for DataTypeKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for DataTypeKind {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for DataTypeKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                DataTypeKind::Struct => "Struct",
                DataTypeKind::Union => "Union",
                DataTypeKind::Enum => "Enum",
                DataTypeKind::Closure => "Closure",
                DataTypeKind::Coroutine => "Coroutine",
            })
    }
}Debug)]
54pub enum DataTypeKind {
55    Struct,
56    Union,
57    Enum,
58    Closure,
59    Coroutine,
60}
61
62#[derive(#[automatically_derived]
impl ::core::cmp::PartialEq for TypeSizeInfo {
    #[inline]
    fn eq(&self, other: &TypeSizeInfo) -> bool {
        self.align == other.align && self.overall_size == other.overall_size
                            && self.packed == other.packed && self.kind == other.kind &&
                    self.type_description == other.type_description &&
                self.opt_discr_size == other.opt_discr_size &&
            self.variants == other.variants
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for TypeSizeInfo {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_receiver_is_total_eq(&self) -> () {
        let _: ::core::cmp::AssertParamIsEq<DataTypeKind>;
        let _: ::core::cmp::AssertParamIsEq<String>;
        let _: ::core::cmp::AssertParamIsEq<u64>;
        let _: ::core::cmp::AssertParamIsEq<bool>;
        let _: ::core::cmp::AssertParamIsEq<Option<u64>>;
        let _: ::core::cmp::AssertParamIsEq<Vec<VariantInfo>>;
    }
}Eq, #[automatically_derived]
impl ::core::hash::Hash for TypeSizeInfo {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {
        ::core::hash::Hash::hash(&self.kind, state);
        ::core::hash::Hash::hash(&self.type_description, state);
        ::core::hash::Hash::hash(&self.align, state);
        ::core::hash::Hash::hash(&self.overall_size, state);
        ::core::hash::Hash::hash(&self.packed, state);
        ::core::hash::Hash::hash(&self.opt_discr_size, state);
        ::core::hash::Hash::hash(&self.variants, state)
    }
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for TypeSizeInfo {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        let names: &'static _ =
            &["kind", "type_description", "align", "overall_size", "packed",
                        "opt_discr_size", "variants"];
        let values: &[&dyn ::core::fmt::Debug] =
            &[&self.kind, &self.type_description, &self.align,
                        &self.overall_size, &self.packed, &self.opt_discr_size,
                        &&self.variants];
        ::core::fmt::Formatter::debug_struct_fields_finish(f, "TypeSizeInfo",
            names, values)
    }
}Debug)]
63pub struct TypeSizeInfo {
64    pub kind: DataTypeKind,
65    pub type_description: String,
66    pub align: u64,
67    pub overall_size: u64,
68    pub packed: bool,
69    pub opt_discr_size: Option<u64>,
70    pub variants: Vec<VariantInfo>,
71}
72
73#[derive(#[automatically_derived]
impl ::core::default::Default for CodeStats {
    #[inline]
    fn default() -> CodeStats {
        CodeStats { type_sizes: ::core::default::Default::default() }
    }
}Default)]
74pub struct CodeStats {
75    /// The hash set that actually holds all the type size information.
76    /// The field is public for use in external tools. See #139876.
77    pub type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
78}
79
80impl CodeStats {
81    pub fn record_type_size<S: ToString>(
82        &self,
83        kind: DataTypeKind,
84        type_desc: S,
85        align: Align,
86        overall_size: Size,
87        packed: bool,
88        opt_discr_size: Option<Size>,
89        mut variants: Vec<VariantInfo>,
90    ) {
91        // Sort variants so the largest ones are shown first. A stable sort is
92        // used here so that source code order is preserved for all variants
93        // that have the same size.
94        // Except for Coroutines, whose variants are already sorted according to
95        // their yield points in `variant_info_for_coroutine`.
96        if kind != DataTypeKind::Coroutine {
97            variants.sort_by_key(|info| cmp::Reverse(info.size));
98        }
99        let info = TypeSizeInfo {
100            kind,
101            type_description: type_desc.to_string(),
102            align: align.bytes(),
103            overall_size: overall_size.bytes(),
104            packed,
105            opt_discr_size: opt_discr_size.map(|s| s.bytes()),
106            variants,
107        };
108        self.type_sizes.borrow_mut().insert(info);
109    }
110
111    pub fn print_type_sizes(&self) {
112        let type_sizes = self.type_sizes.borrow();
113        // We will soon sort, so the initial order does not matter.
114        #[allow(rustc::potential_query_instability)]
115        let mut sorted: Vec<_> = type_sizes.iter().collect();
116
117        // Primary sort: large-to-small.
118        // Secondary sort: description (dictionary order)
119        sorted.sort_by_key(|info| (cmp::Reverse(info.overall_size), &info.type_description));
120
121        for info in sorted {
122            let TypeSizeInfo { type_description, overall_size, align, kind, variants, .. } = info;
123            {
    ::std::io::_print(format_args!("print-type-size type: `{0}`: {1} bytes, alignment: {2} bytes\n",
            type_description, overall_size, align));
};println!(
124                "print-type-size type: `{type_description}`: {overall_size} bytes, alignment: {align} bytes"
125            );
126            let indent = "    ";
127
128            let discr_size = if let Some(discr_size) = info.opt_discr_size {
129                {
    ::std::io::_print(format_args!("print-type-size {0}discriminant: {1} bytes\n",
            indent, discr_size));
};println!("print-type-size {indent}discriminant: {discr_size} bytes");
130                discr_size
131            } else {
132                0
133            };
134
135            // We start this at discr_size (rather than 0) because
136            // things like C-enums do not have variants but we still
137            // want the max_variant_size at the end of the loop below
138            // to reflect the presence of the discriminant.
139            let mut max_variant_size = discr_size;
140
141            let struct_like = match kind {
142                DataTypeKind::Struct | DataTypeKind::Closure => true,
143                DataTypeKind::Enum | DataTypeKind::Union | DataTypeKind::Coroutine => false,
144            };
145            for (i, variant_info) in variants.into_iter().enumerate() {
146                let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info;
147                let indent = if !struct_like {
148                    let name = match name.as_ref() {
149                        Some(name) => name.to_string(),
150                        None => i.to_string(),
151                    };
152                    {
    ::std::io::_print(format_args!("print-type-size {1}variant `{2}`: {0} bytes\n",
            size - discr_size, indent, name));
};println!(
153                        "print-type-size {indent}variant `{name}`: {diff} bytes",
154                        diff = size - discr_size
155                    );
156                    "        "
157                } else {
158                    if !(i < 1) { ::core::panicking::panic("assertion failed: i < 1") };assert!(i < 1);
159                    "    "
160                };
161                max_variant_size = cmp::max(max_variant_size, size);
162
163                let mut min_offset = discr_size;
164
165                // We want to print fields by increasing offset. We also want
166                // zero-sized fields before non-zero-sized fields, otherwise
167                // the loop below goes wrong; hence the `f.size` in the sort
168                // key.
169                let mut fields = fields.clone();
170                fields.sort_by_key(|f| (f.offset, f.size));
171
172                for field in fields {
173                    let FieldInfo { kind, ref name, offset, size, align, type_name } = field;
174
175                    if offset > min_offset {
176                        let pad = offset - min_offset;
177                        {
    ::std::io::_print(format_args!("print-type-size {0}padding: {1} bytes\n",
            indent, pad));
};println!("print-type-size {indent}padding: {pad} bytes");
178                    }
179
180                    if offset < min_offset {
181                        // If this happens it's probably a union.
182                        {
    ::std::io::_print(format_args!("print-type-size {0}{1} `.{2}`: {3} bytes, offset: {4} bytes, alignment: {5} bytes",
            indent, kind, name, size, offset, align));
};print!(
183                            "print-type-size {indent}{kind} `.{name}`: {size} bytes, \
184                                  offset: {offset} bytes, \
185                                  alignment: {align} bytes"
186                        );
187                    } else if info.packed || offset == min_offset {
188                        {
    ::std::io::_print(format_args!("print-type-size {0}{1} `.{2}`: {3} bytes",
            indent, kind, name, size));
};print!("print-type-size {indent}{kind} `.{name}`: {size} bytes");
189                    } else {
190                        // Include field alignment in output only if it caused padding injection
191                        {
    ::std::io::_print(format_args!("print-type-size {0}{1} `.{2}`: {3} bytes, alignment: {4} bytes",
            indent, kind, name, size, align));
};print!(
192                            "print-type-size {indent}{kind} `.{name}`: {size} bytes, \
193                                  alignment: {align} bytes"
194                        );
195                    }
196
197                    if let Some(type_name) = type_name {
198                        { ::std::io::_print(format_args!(", type: {0}\n", type_name)); };println!(", type: {type_name}");
199                    } else {
200                        { ::std::io::_print(format_args!("\n")); };println!();
201                    }
202
203                    min_offset = offset + size;
204                }
205            }
206
207            match overall_size.checked_sub(max_variant_size) {
208                None => {
    ::core::panicking::panic_fmt(format_args!("max_variant_size {0} > {1} overall_size",
            max_variant_size, overall_size));
}panic!("max_variant_size {max_variant_size} > {overall_size} overall_size"),
209                Some(diff @ 1..) => {
    ::std::io::_print(format_args!("print-type-size {0}end padding: {1} bytes\n",
            indent, diff));
}println!("print-type-size {indent}end padding: {diff} bytes"),
210                Some(0) => {}
211            }
212        }
213    }
214}