1mod llvm_enzyme {
7 use std::str::FromStr;
8 use std::string::String;
9
10 use rustc_ast::expand::autodiff_attrs::{
11 DiffActivity, DiffMode, valid_input_activity, valid_ret_activity, valid_ty_for_activity,
12 };
13 use rustc_ast::token::{IdentKind, Lit, LitKind, Token, TokenKind};
14 use rustc_ast::tokenstream::*;
15 use rustc_ast::visit::AssocCtxt::*;
16 use rustc_ast::{
17 self as ast, AnonConst, FnRetTy, FnSig, GenericArg, GenericParamKind, Generics, ItemKind,
18 MetaItemInner, PatKind, TyKind, Visibility,
19 };
20 use rustc_attr_ir::RustcAutodiff;
21 use rustc_expand::base::{Annotatable, ExtCtxt};
22 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
23 use thin_vec::{ThinVec, thin_vec};
24 use tracing::{debug, trace};
25
26 use crate::diagnostics;
27
28 pub(crate) fn outer_normal_attr(
29 kind: &Box<rustc_ast::NormalAttr>,
30 id: rustc_ast::AttrId,
31 span: Span,
32 ) -> rustc_ast::Attribute {
33 let style = rustc_ast::AttrStyle::Outer;
34 let kind = rustc_ast::AttrKind::Normal(kind.clone());
35 rustc_ast::Attribute { kind, id, style, span }
36 }
37
38 fn has_ret(ty: &FnRetTy) -> bool {
41 match ty {
42 FnRetTy::Ty(ty) => !ty.kind.is_unit(),
43 FnRetTy::Default(_) => false,
44 }
45 }
46 fn first_ident(x: &MetaItemInner) -> rustc_span::Ident {
47 if let Some(l) = x.lit() {
48 match l.kind {
49 ast::LitKind::Int(val, _) => {
50 return rustc_span::Ident::from_str(val.get().to_string().as_str());
52 }
53 _ => {}
54 }
55 }
56
57 let segments = &x.meta_item().unwrap().path.segments;
58 if !(segments.len() == 1) {
::core::panicking::panic("assertion failed: segments.len() == 1")
};assert!(segments.len() == 1);
59 segments[0].ident
60 }
61
62 fn name(x: &MetaItemInner) -> String {
63 first_ident(x).name.to_string()
64 }
65
66 fn width(x: &MetaItemInner) -> Option<u128> {
67 let lit = x.lit()?;
68 match lit.kind {
69 ast::LitKind::Int(x, _) => Some(x.get()),
70 _ => None,
71 }
72 }
73
74 fn extract_item_info(iitem: &Box<ast::Item>) -> Option<(Visibility, FnSig, Ident, Generics)> {
76 match &iitem.kind {
77 ItemKind::Fn(ast::Fn { sig, ident, generics, .. }) => {
78 Some((iitem.vis.clone(), sig.clone(), *ident, generics.clone()))
79 }
80 _ => None,
81 }
82 }
83
84 pub(crate) fn from_ast(
85 ecx: &mut ExtCtxt<'_>,
86 meta_item: &ThinVec<MetaItemInner>,
87 has_ret: bool,
88 mode: DiffMode,
89 ) -> RustcAutodiff {
90 let dcx = ecx.sess.dcx();
91
92 let mut first_activity = 1;
95
96 let width = if let [_, x, ..] = &meta_item[..]
97 && let Some(x) = width(x)
98 {
99 first_activity = 2;
100 match x.try_into() {
101 Ok(x) => x,
102 Err(_) => {
103 dcx.emit_err(diagnostics::AutoDiffInvalidWidth {
104 span: meta_item[1].span(),
105 width: x,
106 });
107 return RustcAutodiff::error();
108 }
109 }
110 } else {
111 1
112 };
113
114 let mut activities: Vec<DiffActivity> = ::alloc::vec::Vec::new()vec![];
115 let mut errors = false;
116 for x in &meta_item[first_activity..] {
117 let activity_str = name(x);
118 let res = DiffActivity::from_str(&activity_str);
119 match res {
120 Ok(x) => activities.push(x),
121 Err(_) => {
122 dcx.emit_err(diagnostics::AutoDiffUnknownActivity {
123 span: x.span(),
124 act: activity_str,
125 });
126 errors = true;
127 }
128 };
129 }
130 if errors {
131 return RustcAutodiff::error();
132 }
133
134 let (ret_activity, input_activity) = if has_ret {
137 let Some((last, rest)) = activities.split_last() else {
138 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("should not be reachable because we counted the number of activities previously")));
};unreachable!(
139 "should not be reachable because we counted the number of activities previously"
140 );
141 };
142 (last, rest)
143 } else {
144 (&DiffActivity::None, activities.as_slice())
145 };
146
147 RustcAutodiff {
148 mode,
149 width,
150 ret_activity: *ret_activity,
151 input_activity: input_activity.iter().cloned().collect(),
152 }
153 }
154
155 fn meta_item_inner_to_ts(t: &MetaItemInner, ts: &mut Vec<TokenTree>) {
156 let comma = Token::new(TokenKind::Comma, Span::default());
157 let val = first_ident(t);
158 let t = Token::from_ast_ident(val);
159 ts.push(TokenTree::Token(t, Spacing::Joint));
160 ts.push(TokenTree::Token(comma, Spacing::Alone));
161 }
162
163 pub(crate) fn expand_forward(
164 ecx: &mut ExtCtxt<'_>,
165 expand_span: Span,
166 meta_item: &ast::MetaItem,
167 item: Annotatable,
168 ) -> Vec<Annotatable> {
169 expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Forward)
170 }
171
172 pub(crate) fn expand_reverse(
173 ecx: &mut ExtCtxt<'_>,
174 expand_span: Span,
175 meta_item: &ast::MetaItem,
176 item: Annotatable,
177 ) -> Vec<Annotatable> {
178 expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Reverse)
179 }
180
181 pub(crate) fn expand_with_mode(
205 ecx: &mut ExtCtxt<'_>,
206 expand_span: Span,
207 meta_item: &ast::MetaItem,
208 mut item: Annotatable,
209 mode: DiffMode,
210 ) -> Vec<Annotatable> {
211 let dcx = ecx.sess.dcx();
212
213 let Some((vis, sig, primal, generics, is_impl)) = (match &item {
217 Annotatable::Item(iitem) => {
218 extract_item_info(iitem).map(|(v, s, p, g)| (v, s, p, g, false))
219 }
220 Annotatable::Stmt(stmt) => match &stmt.kind {
221 ast::StmtKind::Item(iitem) => {
222 extract_item_info(iitem).map(|(v, s, p, g)| (v, s, p, g, false))
223 }
224 _ => None,
225 },
226 Annotatable::AssocItem(assoc_item, _ctxt @ (Impl { of_trait: _ } | Trait)) => {
227 match &assoc_item.kind {
228 ast::AssocItemKind::Fn(ast::Fn { sig, ident, generics, .. }) => {
229 Some((assoc_item.vis.clone(), sig.clone(), *ident, generics.clone(), true))
230 }
231 _ => None,
232 }
233 }
234 _ => None,
235 }) else {
236 dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() });
237 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
238 };
239
240 let meta_item_vec: ThinVec<MetaItemInner> = match meta_item.kind {
241 ast::MetaItemKind::List(ref vec) => vec.clone(),
242 _ => {
243 dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() });
244 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
245 }
246 };
247
248 let has_ret = has_ret(&sig.decl.output);
249
250 let mut ts: Vec<TokenTree> = ::alloc::vec::Vec::new()vec![];
253 if meta_item_vec.is_empty() {
254 dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() });
256 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
257 }
258
259 let mode_symbol = match mode {
260 DiffMode::Forward => sym::Forward,
261 DiffMode::Reverse => sym::Reverse,
262 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Unsupported mode: {0:?}", mode)));
}unreachable!("Unsupported mode: {:?}", mode),
263 };
264
265 let mode_token =
267 Token::new(TokenKind::Ident(mode_symbol, IdentKind::Normal), Span::default());
268 ts.insert(0, TokenTree::Token(mode_token, Spacing::Joint));
269 ts.insert(
270 1,
271 TokenTree::Token(Token::new(TokenKind::Comma, Span::default()), Spacing::Alone),
272 );
273
274 let start_position;
277 let kind = LitKind::Integer;
278 let symbol;
279 if meta_item_vec.len() >= 2
280 && let Some(width) = width(&meta_item_vec[1])
281 {
282 start_position = 2;
283 symbol = Symbol::intern(&width.to_string());
284 } else {
285 start_position = 1;
286 symbol = sym::integer(1);
287 }
288
289 let l = Lit { kind, symbol, suffix: None };
290 let t = Token::new(TokenKind::Literal(l), Span::default());
291 let comma = Token::new(TokenKind::Comma, Span::default());
292 ts.push(TokenTree::Token(t, Spacing::Joint));
293 ts.push(TokenTree::Token(comma, Spacing::Alone));
294
295 for t in meta_item_vec.clone()[start_position..].iter() {
296 meta_item_inner_to_ts(t, &mut ts);
297 }
298
299 if !has_ret {
300 let t = Token::new(TokenKind::Ident(sym::None, IdentKind::Normal), Span::default());
303 ts.push(TokenTree::Token(t, Spacing::Joint));
304 ts.push(TokenTree::Token(comma, Spacing::Alone));
305 }
306 ts.pop();
308 let ts = TokenStream::from_iter(ts);
309
310 let x = from_ast(ecx, &meta_item_vec, has_ret, mode);
311 if !x.is_active() {
312 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
315 }
316 let span = ecx.with_def_site_ctxt(expand_span);
317
318 let d_sig = gen_enzyme_decl(ecx, &sig, &x, span);
319
320 let d_body = ecx.block(
321 span,
322 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(call_autodiff(ecx, primal, first_ident(&meta_item_vec[0]), span,
&sig, &d_sig, &generics, is_impl));
vec
}thin_vec![call_autodiff(
323 ecx,
324 primal,
325 first_ident(&meta_item_vec[0]),
326 span,
327 &sig,
328 &d_sig,
329 &generics,
330 is_impl,
331 )],
332 );
333
334 let d_fn = Box::new(ast::Fn {
336 defaultness: ast::Defaultness::Implicit,
337 sig: d_sig,
338 ident: first_ident(&meta_item_vec[0]),
339 generics,
340 contract: None,
341 body: Some(d_body),
342 define_opaque: None,
343 eii_impl: None,
344 });
345 let mut rustc_ad_attr =
346 Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff)));
347
348 let ts2 = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[TokenTree::Token(Token::new(TokenKind::Ident(sym::never,
IdentKind::Normal), span), Spacing::Joint)]))vec![TokenTree::Token(
349 Token::new(TokenKind::Ident(sym::never, IdentKind::Normal), span),
350 Spacing::Joint,
351 )];
352 let never_arg = ast::DelimArgs {
353 dspan: DelimSpan::from_single(span),
354 delim: ast::token::Delimiter::Parenthesis,
355 tokens: TokenStream::from_iter(ts2),
356 };
357 let inline_item = ast::AttrItem {
358 unsafety: ast::Safety::Default,
359 path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)),
360 args: ast::AttrArgs::Delimited(never_arg),
361 span: DUMMY_SP,
362 };
363 let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None });
364 let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
365 let attr = outer_normal_attr(&rustc_ad_attr, new_id, span);
366 let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
367 let inline_never = outer_normal_attr(&inline_never_attr, new_id, span);
368
369 fn same_attribute(attr: &ast::AttrKind, item: &ast::AttrKind) -> bool {
371 match (attr, item) {
372 (ast::AttrKind::Normal(a), ast::AttrKind::Normal(b)) => {
373 let a = &a.item.path;
374 let b = &b.item.path;
375 a.segments.iter().eq_by(&b.segments, |a, b| a.ident == b.ident)
376 }
377 _ => false,
378 }
379 }
380
381 let mut has_inline_never = false;
382
383 let orig_annotatable = match item {
385 Annotatable::Item(ref mut iitem) => {
386 if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) {
387 iitem.attrs.push(attr);
388 }
389 if iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) {
390 has_inline_never = true;
391 }
392 Annotatable::Item(iitem.clone())
393 }
394 Annotatable::AssocItem(ref mut assoc_item, ctxt @ (Impl { .. } | Trait)) => {
395 if !assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) {
396 assoc_item.attrs.push(attr);
397 }
398 if assoc_item.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) {
399 has_inline_never = true;
400 }
401 Annotatable::AssocItem(assoc_item.clone(), ctxt)
402 }
403 Annotatable::Stmt(ref mut stmt) => {
404 match stmt.kind {
405 ast::StmtKind::Item(ref mut iitem) => {
406 if !iitem.attrs.iter().any(|a| same_attribute(&a.kind, &attr.kind)) {
407 iitem.attrs.push(attr);
408 }
409 if iitem.attrs.iter().any(|a| same_attribute(&a.kind, &inline_never.kind)) {
410 has_inline_never = true;
411 }
412 }
413 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("stmt kind checked previously")));
}unreachable!("stmt kind checked previously"),
414 };
415
416 Annotatable::Stmt(stmt.clone())
417 }
418 _ => {
419 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("annotatable kind checked previously")));
}unreachable!("annotatable kind checked previously")
420 }
421 };
422 rustc_ad_attr.item.args = rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs {
424 dspan: DelimSpan::dummy(),
425 delim: rustc_ast::token::Delimiter::Parenthesis,
426 tokens: ts,
427 });
428
429 let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
430 let d_attr = outer_normal_attr(&rustc_ad_attr, new_id, span);
431
432 let mut d_attrs = {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(d_attr);
vec
}thin_vec![d_attr];
434
435 if has_inline_never {
436 d_attrs.push(inline_never);
437 }
438
439 let d_annotatable = match &item {
440 Annotatable::AssocItem(_, ctxt) => {
441 let assoc_item = ast::AssocItemKind::Fn(d_fn);
442 let d_fn = Box::new(ast::AssocItem {
443 attrs: d_attrs,
444 id: ast::DUMMY_NODE_ID,
445 span,
446 vis,
447 kind: assoc_item,
448 tokens: None,
449 });
450 Annotatable::AssocItem(d_fn, *ctxt)
451 }
452 Annotatable::Item(_) => {
453 let mut d_fn = ecx.item(span, d_attrs, ItemKind::Fn(d_fn));
454 d_fn.vis = vis;
455
456 Annotatable::Item(d_fn)
457 }
458 Annotatable::Stmt(_) => {
459 let mut d_fn = ecx.item(span, d_attrs, ItemKind::Fn(d_fn));
460 d_fn.vis = vis;
461 Annotatable::Stmt(Box::new(ecx.stmt_item(span, d_fn)))
462 }
463 _ => {
464 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("item kind checked previously")));
}unreachable!("item kind checked previously")
465 }
466 };
467
468 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[orig_annotatable, d_annotatable]))vec![orig_annotatable, d_annotatable]
469 }
470
471 fn assure_mut_ref(ty: &ast::Ty) -> ast::Ty {
474 let mut ty = ty.clone();
475 match ty.kind {
476 TyKind::Ptr(_, ref mut mutbl) => {
477 *mutbl = ast::Mutability::Mut;
478 }
479 TyKind::Ref(_, _, ref mut mutbl) => {
480 *mutbl = ast::Mutability::Mut;
481 }
482 _ => {
483 {
::core::panicking::panic_fmt(format_args!("unsupported type: {0:?}", ty));
};panic!("unsupported type: {:?}", ty);
484 }
485 }
486 ty
487 }
488
489 fn call_autodiff(
494 ecx: &ExtCtxt<'_>,
495 primal: Ident,
496 diff: Ident,
497 span: Span,
498 p_sig: &FnSig,
499 d_sig: &FnSig,
500 generics: &Generics,
501 is_impl: bool,
502 ) -> rustc_ast::Stmt {
503 let primal_path_expr = gen_turbofish_expr(ecx, primal, generics, span, is_impl);
504
505 let self_ty = || ecx.ty_path(ast::Path::from_ident(Ident::with_dummy_span(kw::SelfUpper)));
506 let fn_ptr_params: ThinVec<ast::Param> = p_sig
507 .decl
508 .inputs
509 .iter()
510 .map(|param| {
511 let ty = match ¶m.ty.kind {
512 TyKind::ImplicitSelf => self_ty(),
513 TyKind::Ref(lt, inner_ty, mutbl)
514 if #[allow(non_exhaustive_omitted_patterns)] match inner_ty.kind {
TyKind::ImplicitSelf => true,
_ => false,
}matches!(inner_ty.kind, TyKind::ImplicitSelf) =>
515 {
516 ecx.ty(span, TyKind::Ref(*lt, self_ty(), *mutbl))
517 }
518 TyKind::Ptr(inner_ty, mutbl)
519 if #[allow(non_exhaustive_omitted_patterns)] match inner_ty.kind {
TyKind::ImplicitSelf => true,
_ => false,
}matches!(inner_ty.kind, TyKind::ImplicitSelf) =>
520 {
521 ecx.ty(span, TyKind::Ptr(self_ty(), *mutbl))
522 }
523 _ => param.ty.clone(),
524 };
525 ast::Param {
526 attrs: ast::AttrVec::new(),
527 ty,
528 pat: Box::new(ecx.pat_wild(span)),
529 id: ast::DUMMY_NODE_ID,
530 span,
531 is_placeholder: false,
532 has_const_keyword: false,
533 }
534 })
535 .collect();
536 let fn_ptr_ty = ecx.ty(
537 span,
538 TyKind::FnPtr(Box::new(ast::FnPtrTy {
539 safety: p_sig.header.safety,
540 ext: p_sig.header.ext,
541 generic_params: ThinVec::new(),
542 decl: Box::new(ast::FnDecl {
543 inputs: fn_ptr_params,
544 output: p_sig.decl.output.clone(),
545 }),
546 decl_span: span,
547 })),
548 );
549 let primal_fn_ptr = ecx.expr(span, ast::ExprKind::Cast(primal_path_expr, fn_ptr_ty));
550
551 let diff_path_expr = gen_turbofish_expr(ecx, diff, generics, span, is_impl);
552
553 let tuple_expr = ecx.expr_tuple(
554 span,
555 d_sig
556 .decl
557 .inputs
558 .iter()
559 .map(|arg| match arg.pat.kind {
560 PatKind::Ident(_, ident, _) => ecx.expr_path(ecx.path_ident(span, ident)),
561 _ => ::core::panicking::panic("not implemented")unimplemented!(),
562 })
563 .collect::<ThinVec<_>>(),
564 );
565
566 let enzyme_path_idents = ecx.std_path(&[sym::intrinsics, sym::autodiff]);
567 let enzyme_path = ecx.path(span, enzyme_path_idents);
568 let call_expr = ecx.expr_call(
569 span,
570 ecx.expr_path(enzyme_path),
571 {
let len = [(), (), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(primal_fn_ptr);
vec.push(diff_path_expr);
vec.push(tuple_expr);
vec
}thin_vec![primal_fn_ptr, diff_path_expr, tuple_expr],
572 );
573
574 ecx.stmt_expr(call_expr)
575 }
576
577 fn gen_turbofish_expr(
581 ecx: &ExtCtxt<'_>,
582 ident: Ident,
583 generics: &Generics,
584 span: Span,
585 is_impl: bool,
586 ) -> Box<ast::Expr> {
587 let generic_args = generics
588 .params
589 .iter()
590 .filter_map(|p| match &p.kind {
591 GenericParamKind::Type { .. } => {
592 let path = ast::Path::from_ident(p.ident);
593 let ty = ecx.ty_path(path);
594 Some(GenericArg::Type(ty))
595 }
596 GenericParamKind::Const { .. } => {
597 let expr = ecx.expr_path(ast::Path::from_ident(p.ident));
598 let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr };
599 Some(GenericArg::Const(anon_const))
600 }
601 GenericParamKind::Lifetime => None,
602 })
603 .collect::<Vec<_>>();
604
605 let idents =
606 if is_impl { ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Ident::new(kw::SelfUpper, span), ident]))vec![Ident::new(kw::SelfUpper, span), ident] } else { ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[ident]))vec![ident] };
607 let path = ecx.path_all(span, false, idents, generic_args);
608
609 ecx.expr_path(path)
610 }
611
612 fn gen_enzyme_decl(
624 ecx: &ExtCtxt<'_>,
625 sig: &ast::FnSig,
626 x: &RustcAutodiff,
627 span: Span,
628 ) -> ast::FnSig {
629 let dcx = ecx.sess.dcx();
630 let has_ret = has_ret(&sig.decl.output);
631 let sig_args = sig.decl.inputs.len() + if has_ret { 1 } else { 0 };
632 let num_activities = x.input_activity.len() + if x.has_ret_activity() { 1 } else { 0 };
633 if sig_args != num_activities {
634 dcx.emit_err(diagnostics::AutoDiffInvalidNumberActivities {
635 span,
636 expected: sig_args,
637 found: num_activities,
638 });
639 return sig.clone();
641 }
642 if !(sig.decl.inputs.len() == x.input_activity.len()) {
::core::panicking::panic("assertion failed: sig.decl.inputs.len() == x.input_activity.len()")
};assert!(sig.decl.inputs.len() == x.input_activity.len());
643 if !(has_ret == x.has_ret_activity()) {
::core::panicking::panic("assertion failed: has_ret == x.has_ret_activity()")
};assert!(has_ret == x.has_ret_activity());
644 let mut d_decl = sig.decl.clone();
645 let mut d_inputs = ThinVec::new();
646 let mut act_ret = ThinVec::new();
647
648 let mut errors = false;
651 for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
652 if !valid_input_activity(x.mode, *activity) {
653 dcx.emit_err(diagnostics::AutoDiffInvalidApplicationModeAct {
654 span,
655 mode: x.mode.to_string(),
656 act: activity.to_string(),
657 });
658 errors = true;
659 }
660 if !valid_ty_for_activity(&arg.ty, *activity) {
661 dcx.emit_err(diagnostics::AutoDiffInvalidTypeForActivity {
662 span: arg.ty.span,
663 act: activity.to_string(),
664 });
665 errors = true;
666 }
667 }
668
669 if has_ret && !valid_ret_activity(x.mode, x.ret_activity) {
670 dcx.emit_err(diagnostics::AutoDiffInvalidRetAct {
671 span,
672 mode: x.mode.to_string(),
673 act: x.ret_activity.to_string(),
674 });
675 }
678
679 if errors {
680 return sig.clone();
682 }
683
684 let unsafe_activities = x
685 .input_activity
686 .iter()
687 .any(|&act| #[allow(non_exhaustive_omitted_patterns)] match act {
DiffActivity::DuplicatedOnly | DiffActivity::DualOnly => true,
_ => false,
}matches!(act, DiffActivity::DuplicatedOnly | DiffActivity::DualOnly));
688 for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
689 d_inputs.push(arg.clone());
690 match activity {
691 DiffActivity::Active => {
692 act_ret.push(arg.ty.clone());
693 }
695 DiffActivity::ActiveOnly => {
696 }
699 DiffActivity::Duplicated | DiffActivity::DuplicatedOnly => {
700 for i in 0..x.width {
701 let mut shadow_arg = arg.clone();
702 *shadow_arg.ty = assure_mut_ref(&arg.ty);
704 let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
705 ident.name
706 } else {
707 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs:707",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(707u32),
::tracing_core::__macro_support::Option::Some("rustc_builtin_macros::autodiff::llvm_enzyme"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("{0:#?}",
&shadow_arg.pat) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("{:#?}", &shadow_arg.pat);
708 { ::core::panicking::panic_fmt(format_args!("not an ident?")); };panic!("not an ident?");
709 };
710 let name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("d{0}_{1}", old_name, i))
})format!("d{}_{}", old_name, i);
711 let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
712 *shadow_arg.pat = ecx.pat_ident(shadow_arg.pat.span, ident);
713 d_inputs.push(shadow_arg);
714 }
715 }
716 DiffActivity::Dual
717 | DiffActivity::DualOnly
718 | DiffActivity::Dualv
719 | DiffActivity::DualvOnly => {
720 let iterations =
723 if #[allow(non_exhaustive_omitted_patterns)] match activity {
DiffActivity::Dualv | DiffActivity::DualvOnly => true,
_ => false,
}matches!(activity, DiffActivity::Dualv | DiffActivity::DualvOnly) {
724 1
725 } else {
726 x.width
727 };
728 for i in 0..iterations {
729 let mut shadow_arg = arg.clone();
730 let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
731 ident.name
732 } else {
733 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs:733",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(733u32),
::tracing_core::__macro_support::Option::Some("rustc_builtin_macros::autodiff::llvm_enzyme"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("{0:#?}",
&shadow_arg.pat) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("{:#?}", &shadow_arg.pat);
734 { ::core::panicking::panic_fmt(format_args!("not an ident?")); };panic!("not an ident?");
735 };
736 let name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("b{0}_{1}", old_name, i))
})format!("b{}_{}", old_name, i);
737 let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
738
739 *shadow_arg.pat = ecx.pat_ident(shadow_arg.pat.span, ident);
740 d_inputs.push(shadow_arg);
741 }
742 }
743 DiffActivity::Const => {
744 }
746 DiffActivity::None | DiffActivity::FakeActivitySize(_) => {
747 { ::core::panicking::panic_fmt(format_args!("Should not happen")); };panic!("Should not happen");
748 }
749 }
750 }
751
752 let active_only_ret = x.ret_activity == DiffActivity::ActiveOnly;
753 if active_only_ret {
754 if !x.mode.is_rev() {
::core::panicking::panic("assertion failed: x.mode.is_rev()")
};assert!(x.mode.is_rev());
755 }
756
757 if x.mode.is_rev() {
760 match x.ret_activity {
761 DiffActivity::Active | DiffActivity::ActiveOnly => {
762 let ty = match d_decl.output {
763 FnRetTy::Ty(ref ty) => ty.clone(),
764 FnRetTy::Default(span) => {
765 {
::core::panicking::panic_fmt(format_args!("Did not expect Default ret ty: {0:?}",
span));
};panic!("Did not expect Default ret ty: {:?}", span);
766 }
767 };
768 let name = "dret".to_string();
769 let ident = Ident::from_str_and_span(&name, ty.span);
770 let shadow_arg = ecx.param(ty.span, ident, ty);
771 d_inputs.push(shadow_arg);
772 }
773 _ => {}
774 }
775 }
776 d_decl.inputs = d_inputs;
777
778 if x.mode.is_fwd() {
779 let ty = match d_decl.output {
780 FnRetTy::Ty(ref ty) => ty.clone(),
781 FnRetTy::Default(span) => {
782 let ty = ecx.ty_unit(span);
784 d_decl.output = FnRetTy::Ty(ty.clone());
785 if !#[allow(non_exhaustive_omitted_patterns)] match x.ret_activity {
DiffActivity::None => true,
_ => false,
} {
::core::panicking::panic("assertion failed: matches!(x.ret_activity, DiffActivity::None)")
};assert!(matches!(x.ret_activity, DiffActivity::None));
786 ty
788 }
789 };
790
791 if #[allow(non_exhaustive_omitted_patterns)] match x.ret_activity {
DiffActivity::Dual | DiffActivity::Dualv => true,
_ => false,
}matches!(x.ret_activity, DiffActivity::Dual | DiffActivity::Dualv) {
792 let kind = if x.width == 1 || #[allow(non_exhaustive_omitted_patterns)] match x.ret_activity {
DiffActivity::Dualv => true,
_ => false,
}matches!(x.ret_activity, DiffActivity::Dualv) {
793 TyKind::Tup({
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(ty.clone());
vec.push(ty.clone());
vec
}thin_vec![ty.clone(), ty.clone()])
796 } else {
797 let anon_const = rustc_ast::AnonConst {
799 id: ast::DUMMY_NODE_ID,
800 value: ecx.expr_usize(span, 1 + x.width as usize),
801 };
802 TyKind::Array(ty.clone(), anon_const)
803 };
804 let ty = Box::new(rustc_ast::Ty { kind, id: ty.id, span: ty.span });
805 d_decl.output = FnRetTy::Ty(ty);
806 }
807 if #[allow(non_exhaustive_omitted_patterns)] match x.ret_activity {
DiffActivity::DualOnly | DiffActivity::DualvOnly => true,
_ => false,
}matches!(x.ret_activity, DiffActivity::DualOnly | DiffActivity::DualvOnly) {
808 if x.width > 1 {
812 let anon_const = rustc_ast::AnonConst {
813 id: ast::DUMMY_NODE_ID,
814 value: ecx.expr_usize(span, x.width as usize),
815 };
816 let kind = TyKind::Array(ty.clone(), anon_const);
817 let ty = Box::new(rustc_ast::Ty { kind, id: ty.id, span: ty.span });
818 d_decl.output = FnRetTy::Ty(ty);
819 }
820 }
821 }
822
823 d_decl.output =
825 if active_only_ret { FnRetTy::Default(span) } else { d_decl.output.clone() };
826
827 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs:827",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(827u32),
::tracing_core::__macro_support::Option::Some("rustc_builtin_macros::autodiff::llvm_enzyme"),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("act_ret: {0:?}",
act_ret) as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!("act_ret: {:?}", act_ret);
828
829 if act_ret.len() > 0 {
833 let ret_ty = match d_decl.output {
834 FnRetTy::Ty(ref ty) => {
835 if !active_only_ret {
836 act_ret.insert(0, ty.clone());
837 }
838 let kind = TyKind::Tup(act_ret);
839 Box::new(rustc_ast::Ty { kind, id: ty.id, span: ty.span })
840 }
841 FnRetTy::Default(span) => {
842 if act_ret.len() == 1 {
843 act_ret[0].clone()
844 } else {
845 let kind = TyKind::Tup(act_ret);
846 Box::new(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span })
847 }
848 }
849 };
850 d_decl.output = FnRetTy::Ty(ret_ty);
851 }
852
853 let mut d_header = sig.header;
854 if unsafe_activities {
855 d_header.safety = rustc_ast::Safety::Unsafe(span);
856 }
857 let d_sig = FnSig { header: d_header, decl: d_decl, span };
858 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs:858",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/7df81381d027710708209f36b98d11b1c88f8d2e/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(858u32),
::tracing_core::__macro_support::Option::Some("rustc_builtin_macros::autodiff::llvm_enzyme"),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Generated signature: {0:?}",
d_sig) as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!("Generated signature: {:?}", d_sig);
859 d_sig
860 }
861}
862
863pub(crate) use llvm_enzyme::{expand_forward, expand_reverse};