rustc_codegen_llvm/
typetree.rs1use std::ffi::{CString, c_char};
2
3use rustc_ast::expand::typetree::{FncTree, Kind, TypeTree as RustTypeTree};
4
5use crate::attributes;
6use crate::context::FullCx;
7use crate::llvm::{self, EnzymeWrapper, Value};
8
9fn to_enzyme_typetree(
10 rust_typetree: &RustTypeTree,
11 _data_layout: &str,
12 llcx: &llvm::Context,
13) -> (llvm::TypeTree, Vec<llvm::TypeTree>) {
14 let mut enzyme_tt = llvm::TypeTree::new();
15 let extra_ints = process_typetree_recursive(&mut enzyme_tt, &rust_typetree, &[], llcx);
16
17 let mut int_vec = ::alloc::vec::Vec::new()vec![];
18 for _ in 0..extra_ints {
19 let mut int_tt = llvm::TypeTree::new();
20 int_tt.insert(&[0], llvm::CConcreteType::DT_Integer, llcx);
21 int_vec.push(int_tt);
22 }
23
24 (enzyme_tt, int_vec)
25}
26
27fn process_typetree_recursive(
28 enzyme_tt: &mut llvm::TypeTree,
29 rust_typetree: &RustTypeTree,
30 parent_indices: &[i64],
31 llcx: &llvm::Context,
32) -> u32 {
33 let mut extra_ints = 0;
34 for rust_type in &rust_typetree.0 {
35 let concrete_type = match rust_type.kind {
36 Kind::Anything => llvm::CConcreteType::DT_Anything,
37 Kind::Integer => llvm::CConcreteType::DT_Integer,
38 Kind::Pointer => llvm::CConcreteType::DT_Pointer,
39 Kind::RustSlice => llvm::CConcreteType::DT_Pointer,
40 Kind::Half => llvm::CConcreteType::DT_Half,
41 Kind::Float => llvm::CConcreteType::DT_Float,
42 Kind::Double => llvm::CConcreteType::DT_Double,
43 Kind::F128 => llvm::CConcreteType::DT_FP128,
44 Kind::Unknown => llvm::CConcreteType::DT_Unknown,
45 };
46
47 let mut indices = parent_indices.to_vec();
48 if !parent_indices.is_empty() {
49 indices.push(rust_type.offset as i64);
50 } else if rust_type.offset == -1 {
51 indices.push(-1);
52 } else {
53 indices.push(rust_type.offset as i64);
54 }
55
56 enzyme_tt.insert(&indices, concrete_type, llcx);
57
58 if #[allow(non_exhaustive_omitted_patterns)] match rust_type.kind {
Kind::RustSlice => true,
_ => false,
}matches!(rust_type.kind, Kind::RustSlice) {
59 extra_ints += 1;
61 }
62
63 if #[allow(non_exhaustive_omitted_patterns)] match rust_type.kind {
Kind::Pointer | Kind::RustSlice => true,
_ => false,
}matches!(rust_type.kind, Kind::Pointer | Kind::RustSlice)
64 && !rust_type.child.0.is_empty()
65 {
66 process_typetree_recursive(enzyme_tt, &rust_type.child, &indices, llcx);
67 }
68 }
69 extra_ints
70}
71
72enum TTLocation {
74 Definition,
75 Callsite,
76}
77
78pub(crate) fn add_tt<'tcx, 'll>(cx: &FullCx<'ll, 'tcx>, fn_def: &'ll Value, tt: FncTree) {
79 let tcx = cx.tcx;
83 if !tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) {
84 return;
85 }
86 if tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::NoTT) {
87 return;
88 }
89
90 let llmod = cx.llmod;
91 let llcx = cx.llcx;
92 let inputs = tt.args;
93 let ret_tt: RustTypeTree = tt.ret;
94
95 let llvm_data_layout: *const c_char = unsafe { llvm::LLVMGetDataLayoutStr(&*llmod) };
96 let llvm_data_layout =
97 std::str::from_utf8(unsafe { std::ffi::CStr::from_ptr(llvm_data_layout) }.to_bytes())
98 .expect("got a non-UTF8 data-layout from LLVM");
99
100 let attr_name = "enzyme_type";
101 let c_attr_name = CString::new(attr_name).unwrap();
102
103 let tt_location: TTLocation =
104 if llvm::LLVMRustIsCall(fn_def) { TTLocation::Callsite } else { TTLocation::Definition };
105
106 let mut offset = 0;
107 for (i, input) in inputs.iter().enumerate() {
108 let (enzyme_tt, extra_ints) = to_enzyme_typetree(&input, llvm_data_layout, llcx);
109
110 {
116 let enzyme_wrapper = EnzymeWrapper::get_instance();
117 let c_str = enzyme_wrapper.tree_to_cstr(enzyme_tt.inner);
118
119 let attr = llvm::CreateAttrStringValueFromCStr(llcx, &c_attr_name, &c_str);
120 let arg_pos = llvm::AttributePlace::Argument(i as u32 + offset);
121 match tt_location {
124 TTLocation::Definition => {
125 attributes::apply_to_llfn(fn_def, arg_pos, &[attr]);
126 }
127 TTLocation::Callsite => {
128 attributes::apply_to_callsite(fn_def, arg_pos, &[attr]);
129 }
130 }
131 enzyme_wrapper.tree_to_string_free(c_str.as_ptr());
132 for v in &extra_ints {
133 offset += 1;
134 let c_str = enzyme_wrapper.tree_to_cstr(v.inner);
135 let int_attr = llvm::CreateAttrStringValueFromCStr(llcx, &c_attr_name, &c_str);
136 let arg_pos = llvm::AttributePlace::Argument(i as u32 + offset);
137 match tt_location {
138 TTLocation::Definition => {
139 attributes::apply_to_llfn(fn_def, arg_pos, &[int_attr]);
140 }
141 TTLocation::Callsite => {
142 attributes::apply_to_callsite(fn_def, arg_pos, &[int_attr]);
143 }
144 }
145 enzyme_wrapper.tree_to_string_free(c_str.as_ptr());
146 }
147 }
148 }
149 if #[allow(non_exhaustive_omitted_patterns)] match tt_location {
TTLocation::Definition => true,
_ => false,
}matches!(tt_location, TTLocation::Definition) {
152 let expected = offset as usize + inputs.len();
153 let actual = llvm::count_params(fn_def) as usize;
154 if expected != actual {
155 tcx.dcx().warn(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("autodiff type-tree failure. We expected {0} LLVM argument(s), but the generated LLVM function has {1} parameter(s)",
expected, actual))
})format!(
156 "autodiff type-tree failure. We expected {expected} LLVM argument(s), \
157 but the generated LLVM function has {actual} parameter(s)"
158 ));
159 }
160 }
161
162 let (enzyme_tt, _extra_ints) = to_enzyme_typetree(&ret_tt, llvm_data_layout, llcx);
165 if ret_tt != RustTypeTree::new() {
166 let enzyme_wrapper = EnzymeWrapper::get_instance();
167 let c_str = enzyme_wrapper.tree_to_cstr(enzyme_tt.inner);
168 let ret_attr = llvm::CreateAttrStringValueFromCStr(llcx, &c_attr_name, &c_str);
169 let arg_pos = llvm::AttributePlace::ReturnValue;
170 match tt_location {
171 TTLocation::Definition => {
172 attributes::apply_to_llfn(fn_def, arg_pos, &[ret_attr]);
173 }
174 TTLocation::Callsite => {
175 attributes::apply_to_callsite(fn_def, arg_pos, &[ret_attr]);
176 }
177 }
178 enzyme_wrapper.tree_to_string_free(c_str.as_ptr());
179 }
180}