Skip to main content

rustc_middle/ty/
typetree.rs

1use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree};
2use tracing::trace;
3
4use crate::ty::context::TyCtxt;
5use crate::ty::{self, Ty};
6
7/// Generate TypeTree information for autodiff.
8/// This function creates TypeTree metadata that describes the memory layout
9/// of function parameters and return types for Enzyme autodiff.
10pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree {
11    // Check if TypeTrees are disabled via NoTT flag
12    if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) {
13        return FncTree { args: ::alloc::vec::Vec::new()vec![], ret: TypeTree::new() };
14    }
15
16    // Check if this is actually a function type
17    if !fn_ty.is_fn() {
18        return FncTree { args: ::alloc::vec::Vec::new()vec![], ret: TypeTree::new() };
19    }
20
21    // Get the function signature
22    let fn_sig = fn_ty.fn_sig(tcx);
23    let sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
24
25    // Create TypeTrees for each input parameter
26    let mut args = ::alloc::vec::Vec::new()vec![];
27    for ty in sig.inputs().iter() {
28        let type_tree = typetree_from_ty(tcx, *ty);
29        args.push(type_tree);
30    }
31
32    // Create TypeTree for return type
33    let ret = typetree_from_ty(tcx, sig.output());
34
35    FncTree { args, ret }
36}
37
38/// Generate a TypeTree for a specific type.
39/// Mainly a convenience wrapper around the actual implementation.
40pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree {
41    let mut visited = Vec::new();
42    typetree_from_ty_impl_inner(tcx, ty, 0, &mut visited, false)
43}
44
45/// Maximum recursion depth for TypeTree generation to prevent stack overflow
46/// from pathological deeply nested types. Combined with cycle detection.
47const MAX_TYPETREE_DEPTH: usize = 6;
48
49/// Internal implementation with context about whether this is for a reference target.
50fn typetree_from_ty_impl_inner<'tcx>(
51    tcx: TyCtxt<'tcx>,
52    ty: Ty<'tcx>,
53    depth: usize,
54    visited: &mut Vec<Ty<'tcx>>,
55    is_reference_target: bool,
56) -> TypeTree {
57    if depth >= MAX_TYPETREE_DEPTH {
58        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_middle/src/ty/typetree.rs:58",
                        "rustc_middle::ty::typetree", ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_middle/src/ty/typetree.rs"),
                        ::tracing_core::__macro_support::Option::Some(58u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_middle::ty::typetree"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("typetree depth limit {0} reached for type: {1}",
                                                    MAX_TYPETREE_DEPTH, ty) as &dyn Value))])
            });
    } else { ; }
};trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty);
59        return TypeTree::new();
60    }
61
62    if visited.contains(&ty) {
63        return TypeTree::new();
64    }
65    visited.push(ty);
66
67    if ty.is_scalar() {
68        let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() {
69            (Kind::Integer, ty.primitive_size(tcx).bytes_usize())
70        } else if ty.is_floating_point() {
71            match ty {
72                x if x == tcx.types.f16 => (Kind::Half, 2),
73                x if x == tcx.types.f32 => (Kind::Float, 4),
74                x if x == tcx.types.f64 => (Kind::Double, 8),
75                x if x == tcx.types.f128 => (Kind::F128, 16),
76                _ => (Kind::Integer, 0),
77            }
78        } else {
79            (Kind::Integer, 0)
80        };
81
82        // Use offset 0 for scalars that are direct targets of references (like &f64)
83        // Use offset -1 for scalars used directly (like function return types)
84        let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 };
85        return TypeTree(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Type { offset, size, kind, child: TypeTree::new() }]))vec![Type { offset, size, kind, child: TypeTree::new() }]);
86    }
87
88    if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() {
89        let Some(inner_ty) = ty.builtin_deref(true) else {
90            return TypeTree::new();
91        };
92
93        let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true);
94        return TypeTree(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [Type {
                    offset: -1,
                    size: tcx.data_layout.pointer_size().bytes_usize(),
                    kind: Kind::Pointer,
                    child,
                }]))vec![Type {
95            offset: -1,
96            size: tcx.data_layout.pointer_size().bytes_usize(),
97            kind: Kind::Pointer,
98            child,
99        }]);
100    }
101
102    if ty.is_array() {
103        if let ty::Array(element_ty, len_const) = ty.kind() {
104            let len = len_const.try_to_target_usize(tcx).unwrap_or(0);
105            if len == 0 {
106                return TypeTree::new();
107            }
108            let element_tree =
109                typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false);
110            let mut types = Vec::new();
111            for elem_type in &element_tree.0 {
112                types.push(Type {
113                    offset: -1,
114                    size: elem_type.size,
115                    kind: elem_type.kind,
116                    child: elem_type.child.clone(),
117                });
118            }
119
120            return TypeTree(types);
121        }
122    }
123
124    if ty.is_slice() {
125        if let ty::Slice(element_ty) = ty.kind() {
126            let element_tree =
127                typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false);
128            return element_tree;
129        }
130    }
131
132    if let ty::Tuple(tuple_types) = ty.kind() {
133        if tuple_types.is_empty() {
134            return TypeTree::new();
135        }
136
137        let mut types = Vec::new();
138        let mut current_offset = 0;
139
140        for tuple_ty in tuple_types.iter() {
141            let element_tree =
142                typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false);
143
144            let element_layout = tcx
145                .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty))
146                .ok()
147                .map(|layout| layout.size.bytes_usize())
148                .unwrap_or(0);
149
150            for elem_type in &element_tree.0 {
151                types.push(Type {
152                    offset: if elem_type.offset == -1 {
153                        current_offset as isize
154                    } else {
155                        current_offset as isize + elem_type.offset
156                    },
157                    size: elem_type.size,
158                    kind: elem_type.kind,
159                    child: elem_type.child.clone(),
160                });
161            }
162
163            current_offset += element_layout;
164        }
165
166        return TypeTree(types);
167    }
168
169    if let ty::Adt(adt_def, args) = ty.kind() {
170        if adt_def.is_struct() {
171            let struct_layout =
172                tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty));
173            if let Ok(layout) = struct_layout {
174                let mut types = Vec::new();
175
176                for (field_idx, field_def) in adt_def.all_fields().enumerate() {
177                    let field_ty = field_def.ty(tcx, args);
178                    let field_tree = typetree_from_ty_impl_inner(
179                        tcx,
180                        field_ty.skip_norm_wip(),
181                        depth + 1,
182                        visited,
183                        false,
184                    );
185
186                    let field_offset = layout.fields.offset(field_idx).bytes_usize();
187
188                    for elem_type in &field_tree.0 {
189                        types.push(Type {
190                            offset: if elem_type.offset == -1 {
191                                field_offset as isize
192                            } else {
193                                field_offset as isize + elem_type.offset
194                            },
195                            size: elem_type.size,
196                            kind: elem_type.kind,
197                            child: elem_type.child.clone(),
198                        });
199                    }
200                }
201
202                return TypeTree(types);
203            }
204        }
205    }
206
207    TypeTree::new()
208}