Skip to content

Commit efd597e

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 e95f632 + d9038db commit efd597e

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

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

0 commit comments

Comments
 (0)