Skip to content

Commit cd11b04

Browse files
Rollup merge of rust-lang#158278 - ZuseZ4:typetrees-for-enzyme2, r=oli-obk
autodiff - typtree cleanups In [some recent experiments](https://github.com/rust-lang/rust/pull/157994/changes) I managed to lower more MIR information to LLVM to help Enzyme compile a test case that was previously failing. That PR became a little bigger, and we might want to add even more code. To get started I split it out of mod.rs, since that file is already 2.4k loc. In the process, I also fix some checks and deleted `typetree_from_ty_impl` and `typetree_from_ty_inner`, since they don't do much. r? oli-obk (noticed a regression in a test, marking as draft for now.)
2 parents 86dd805 + d9038db commit cd11b04

3 files changed

Lines changed: 212 additions & 228 deletions

File tree

compiler/rustc_codegen_llvm/src/intrinsic.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use crate::errors::{
4343
AutoDiffWithoutEnable, AutoDiffWithoutLto, IntrinsicSignatureMismatch, IntrinsicWrongArch,
4444
OffloadWithoutEnable, OffloadWithoutFatLTO, UnknownIntrinsic,
4545
};
46+
use crate::intrinsic::ty::typetree::fnc_typetrees;
4647
use crate::llvm::{self, Type, Value};
4748
use crate::type_of::LayoutLlvmExt;
4849
use crate::va_arg::emit_va_arg;
@@ -1787,7 +1788,7 @@ fn codegen_autodiff<'ll, 'tcx>(
17871788
&mut diff_attrs.input_activity,
17881789
);
17891790

1790-
let fnc_tree = rustc_middle::ty::fnc_typetrees(tcx, source_fn_ptr_ty);
1791+
let fnc_tree = fnc_typetrees(tcx, source_fn_ptr_ty);
17911792

17921793
// Build body
17931794
generate_enzyme_call(

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 2 additions & 227 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ pub use intrinsic::IntrinsicDef;
2727
use rustc_abi::{
2828
Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, VariantIdx,
2929
};
30-
use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree};
3130
use rustc_ast::node_id::NodeMap;
3231
use rustc_ast::{self as ast};
3332
pub use rustc_ast_ir::{Movability, Mutability, try_visit};
@@ -67,7 +66,7 @@ pub use rustc_type_ir::solve::{CandidatePreferenceMode, SizedTraitKind, VisibleF
6766
pub use rustc_type_ir::*;
6867
#[allow(hidden_glob_reexports, unused_imports)]
6968
use rustc_type_ir::{InferCtxtLike, Interner};
70-
use tracing::{debug, instrument, trace};
69+
use tracing::{debug, instrument};
7170
pub use vtable::*;
7271

7372
pub use self::closure::{
@@ -145,6 +144,7 @@ pub mod print;
145144
pub mod relate;
146145
pub mod significant_drop_order;
147146
pub mod trait_def;
147+
pub mod typetree;
148148
pub mod util;
149149
pub mod vtable;
150150

@@ -2389,228 +2389,3 @@ pub struct DestructuredAdtConst<'tcx> {
23892389
pub variant: VariantIdx,
23902390
pub fields: &'tcx [ty::Const<'tcx>],
23912391
}
2392-
2393-
/// Generate TypeTree information for autodiff.
2394-
/// This function creates TypeTree metadata that describes the memory layout
2395-
/// of function parameters and return types for Enzyme autodiff.
2396-
pub fn fnc_typetrees<'tcx>(tcx: TyCtxt<'tcx>, fn_ty: Ty<'tcx>) -> FncTree {
2397-
// Check if TypeTrees are disabled via NoTT flag
2398-
if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) {
2399-
return FncTree { args: vec![], ret: TypeTree::new() };
2400-
}
2401-
2402-
// Check if this is actually a function type
2403-
if !fn_ty.is_fn() {
2404-
return FncTree { args: vec![], ret: TypeTree::new() };
2405-
}
2406-
2407-
// Get the function signature
2408-
let fn_sig = fn_ty.fn_sig(tcx);
2409-
let sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
2410-
2411-
// Create TypeTrees for each input parameter
2412-
let mut args = vec![];
2413-
for ty in sig.inputs().iter() {
2414-
let type_tree = typetree_from_ty(tcx, *ty);
2415-
args.push(type_tree);
2416-
}
2417-
2418-
// Create TypeTree for return type
2419-
let ret = typetree_from_ty(tcx, sig.output());
2420-
2421-
FncTree { args, ret }
2422-
}
2423-
2424-
/// Generate TypeTree for a specific type.
2425-
/// This function analyzes a Rust type and creates appropriate TypeTree metadata.
2426-
pub fn typetree_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> TypeTree {
2427-
let mut visited = Vec::new();
2428-
typetree_from_ty_inner(tcx, ty, 0, &mut visited)
2429-
}
2430-
2431-
/// Maximum recursion depth for TypeTree generation to prevent stack overflow
2432-
/// from pathological deeply nested types. Combined with cycle detection.
2433-
const MAX_TYPETREE_DEPTH: usize = 6;
2434-
2435-
/// Internal recursive function for TypeTree generation with cycle detection and depth limiting.
2436-
fn typetree_from_ty_inner<'tcx>(
2437-
tcx: TyCtxt<'tcx>,
2438-
ty: Ty<'tcx>,
2439-
depth: usize,
2440-
visited: &mut Vec<Ty<'tcx>>,
2441-
) -> TypeTree {
2442-
if depth >= MAX_TYPETREE_DEPTH {
2443-
trace!("typetree depth limit {} reached for type: {}", MAX_TYPETREE_DEPTH, ty);
2444-
return TypeTree::new();
2445-
}
2446-
2447-
if visited.contains(&ty) {
2448-
return TypeTree::new();
2449-
}
2450-
2451-
visited.push(ty);
2452-
let result = typetree_from_ty_impl(tcx, ty, depth, visited);
2453-
visited.pop();
2454-
result
2455-
}
2456-
2457-
/// Implementation of TypeTree generation logic.
2458-
fn typetree_from_ty_impl<'tcx>(
2459-
tcx: TyCtxt<'tcx>,
2460-
ty: Ty<'tcx>,
2461-
depth: usize,
2462-
visited: &mut Vec<Ty<'tcx>>,
2463-
) -> TypeTree {
2464-
typetree_from_ty_impl_inner(tcx, ty, depth, visited, false)
2465-
}
2466-
2467-
/// Internal implementation with context about whether this is for a reference target.
2468-
fn typetree_from_ty_impl_inner<'tcx>(
2469-
tcx: TyCtxt<'tcx>,
2470-
ty: Ty<'tcx>,
2471-
depth: usize,
2472-
visited: &mut Vec<Ty<'tcx>>,
2473-
is_reference_target: bool,
2474-
) -> TypeTree {
2475-
if ty.is_scalar() {
2476-
let (kind, size) = if ty.is_integral() || ty.is_char() || ty.is_bool() {
2477-
(Kind::Integer, ty.primitive_size(tcx).bytes_usize())
2478-
} else if ty.is_floating_point() {
2479-
match ty {
2480-
x if x == tcx.types.f16 => (Kind::Half, 2),
2481-
x if x == tcx.types.f32 => (Kind::Float, 4),
2482-
x if x == tcx.types.f64 => (Kind::Double, 8),
2483-
x if x == tcx.types.f128 => (Kind::F128, 16),
2484-
_ => (Kind::Integer, 0),
2485-
}
2486-
} else {
2487-
(Kind::Integer, 0)
2488-
};
2489-
2490-
// Use offset 0 for scalars that are direct targets of references (like &f64)
2491-
// Use offset -1 for scalars used directly (like function return types)
2492-
let offset = if is_reference_target && !ty.is_array() { 0 } else { -1 };
2493-
return TypeTree(vec![Type { offset, size, kind, child: TypeTree::new() }]);
2494-
}
2495-
2496-
if ty.is_ref() || ty.is_raw_ptr() || ty.is_box() {
2497-
let Some(inner_ty) = ty.builtin_deref(true) else {
2498-
return TypeTree::new();
2499-
};
2500-
2501-
let child = typetree_from_ty_impl_inner(tcx, inner_ty, depth + 1, visited, true);
2502-
return TypeTree(vec![Type {
2503-
offset: -1,
2504-
size: tcx.data_layout.pointer_size().bytes_usize(),
2505-
kind: Kind::Pointer,
2506-
child,
2507-
}]);
2508-
}
2509-
2510-
if ty.is_array() {
2511-
if let ty::Array(element_ty, len_const) = ty.kind() {
2512-
let len = len_const.try_to_target_usize(tcx).unwrap_or(0);
2513-
if len == 0 {
2514-
return TypeTree::new();
2515-
}
2516-
let element_tree =
2517-
typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false);
2518-
let mut types = Vec::new();
2519-
for elem_type in &element_tree.0 {
2520-
types.push(Type {
2521-
offset: -1,
2522-
size: elem_type.size,
2523-
kind: elem_type.kind,
2524-
child: elem_type.child.clone(),
2525-
});
2526-
}
2527-
2528-
return TypeTree(types);
2529-
}
2530-
}
2531-
2532-
if ty.is_slice() {
2533-
if let ty::Slice(element_ty) = ty.kind() {
2534-
let element_tree =
2535-
typetree_from_ty_impl_inner(tcx, *element_ty, depth + 1, visited, false);
2536-
return element_tree;
2537-
}
2538-
}
2539-
2540-
if let ty::Tuple(tuple_types) = ty.kind() {
2541-
if tuple_types.is_empty() {
2542-
return TypeTree::new();
2543-
}
2544-
2545-
let mut types = Vec::new();
2546-
let mut current_offset = 0;
2547-
2548-
for tuple_ty in tuple_types.iter() {
2549-
let element_tree =
2550-
typetree_from_ty_impl_inner(tcx, tuple_ty, depth + 1, visited, false);
2551-
2552-
let element_layout = tcx
2553-
.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(tuple_ty))
2554-
.ok()
2555-
.map(|layout| layout.size.bytes_usize())
2556-
.unwrap_or(0);
2557-
2558-
for elem_type in &element_tree.0 {
2559-
types.push(Type {
2560-
offset: if elem_type.offset == -1 {
2561-
current_offset as isize
2562-
} else {
2563-
current_offset as isize + elem_type.offset
2564-
},
2565-
size: elem_type.size,
2566-
kind: elem_type.kind,
2567-
child: elem_type.child.clone(),
2568-
});
2569-
}
2570-
2571-
current_offset += element_layout;
2572-
}
2573-
2574-
return TypeTree(types);
2575-
}
2576-
2577-
if let ty::Adt(adt_def, args) = ty.kind() {
2578-
if adt_def.is_struct() {
2579-
let struct_layout =
2580-
tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty));
2581-
if let Ok(layout) = struct_layout {
2582-
let mut types = Vec::new();
2583-
2584-
for (field_idx, field_def) in adt_def.all_fields().enumerate() {
2585-
let field_ty = field_def.ty(tcx, args);
2586-
let field_tree = typetree_from_ty_impl_inner(
2587-
tcx,
2588-
field_ty.skip_norm_wip(),
2589-
depth + 1,
2590-
visited,
2591-
false,
2592-
);
2593-
2594-
let field_offset = layout.fields.offset(field_idx).bytes_usize();
2595-
2596-
for elem_type in &field_tree.0 {
2597-
types.push(Type {
2598-
offset: if elem_type.offset == -1 {
2599-
field_offset as isize
2600-
} else {
2601-
field_offset as isize + elem_type.offset
2602-
},
2603-
size: elem_type.size,
2604-
kind: elem_type.kind,
2605-
child: elem_type.child.clone(),
2606-
});
2607-
}
2608-
}
2609-
2610-
return TypeTree(types);
2611-
}
2612-
}
2613-
}
2614-
2615-
TypeTree::new()
2616-
}

0 commit comments

Comments
 (0)