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::{Lit, LitKind, Token, TokenKind};
14 use rustc_ast::tokenstream::*;
15 use rustc_ast::visit::AssocCtxt::*;
16 use rustc_ast::{
17 self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode,
18 FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind,
19 MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility,
20 };
21 use rustc_attr_ir::RustcAutodiff;
22 use rustc_expand::base::{Annotatable, ExtCtxt};
23 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
24 use thin_vec::{ThinVec, thin_vec};
25 use tracing::{debug, trace};
26
27 use crate::diagnostics;
28
29 pub(crate) fn outer_normal_attr(
30 kind: &Box<rustc_ast::NormalAttr>,
31 id: rustc_ast::AttrId,
32 span: Span,
33 ) -> rustc_ast::Attribute {
34 let style = rustc_ast::AttrStyle::Outer;
35 let kind = rustc_ast::AttrKind::Normal(kind.clone());
36 rustc_ast::Attribute { kind, id, style, span }
37 }
38
39 fn has_ret(ty: &FnRetTy) -> bool {
42 match ty {
43 FnRetTy::Ty(ty) => !ty.kind.is_unit(),
44 FnRetTy::Default(_) => false,
45 }
46 }
47 fn first_ident(x: &MetaItemInner) -> rustc_span::Ident {
48 if let Some(l) = x.lit() {
49 match l.kind {
50 ast::LitKind::Int(val, _) => {
51 return rustc_span::Ident::from_str(val.get().to_string().as_str());
53 }
54 _ => {}
55 }
56 }
57
58 let segments = &x.meta_item().unwrap().path.segments;
59 if !(segments.len() == 1) {
::core::panicking::panic("assertion failed: segments.len() == 1")
};assert!(segments.len() == 1);
60 segments[0].ident
61 }
62
63 fn name(x: &MetaItemInner) -> String {
64 first_ident(x).name.to_string()
65 }
66
67 fn width(x: &MetaItemInner) -> Option<u128> {
68 let lit = x.lit()?;
69 match lit.kind {
70 ast::LitKind::Int(x, _) => Some(x.get()),
71 _ => None,
72 }
73 }
74
75 fn extract_item_info(iitem: &Box<ast::Item>) -> Option<(Visibility, FnSig, Ident, Generics)> {
77 match &iitem.kind {
78 ItemKind::Fn(ast::Fn { sig, ident, generics, .. }) => {
79 Some((iitem.vis.clone(), sig.clone(), *ident, generics.clone()))
80 }
81 _ => None,
82 }
83 }
84
85 pub(crate) fn from_ast(
86 ecx: &mut ExtCtxt<'_>,
87 meta_item: &ThinVec<MetaItemInner>,
88 has_ret: bool,
89 mode: DiffMode,
90 ) -> RustcAutodiff {
91 let dcx = ecx.sess.dcx();
92
93 let mut first_activity = 1;
96
97 let width = if let [_, x, ..] = &meta_item[..]
98 && let Some(x) = width(x)
99 {
100 first_activity = 2;
101 match x.try_into() {
102 Ok(x) => x,
103 Err(_) => {
104 dcx.emit_err(diagnostics::AutoDiffInvalidWidth {
105 span: meta_item[1].span(),
106 width: x,
107 });
108 return RustcAutodiff::error();
109 }
110 }
111 } else {
112 1
113 };
114
115 let mut activities: Vec<DiffActivity> = ::alloc::vec::Vec::new()vec![];
116 let mut errors = false;
117 for x in &meta_item[first_activity..] {
118 let activity_str = name(x);
119 let res = DiffActivity::from_str(&activity_str);
120 match res {
121 Ok(x) => activities.push(x),
122 Err(_) => {
123 dcx.emit_err(diagnostics::AutoDiffUnknownActivity {
124 span: x.span(),
125 act: activity_str,
126 });
127 errors = true;
128 }
129 };
130 }
131 if errors {
132 return RustcAutodiff::error();
133 }
134
135 let (ret_activity, input_activity) = if has_ret {
138 let Some((last, rest)) = activities.split_last() else {
139 {
::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!(
140 "should not be reachable because we counted the number of activities previously"
141 );
142 };
143 (last, rest)
144 } else {
145 (&DiffActivity::None, activities.as_slice())
146 };
147
148 RustcAutodiff {
149 mode,
150 width,
151 ret_activity: *ret_activity,
152 input_activity: input_activity.iter().cloned().collect(),
153 }
154 }
155
156 fn meta_item_inner_to_ts(t: &MetaItemInner, ts: &mut Vec<TokenTree>) {
157 let comma: Token = Token::new(TokenKind::Comma, Span::default());
158 let val = first_ident(t);
159 let t = Token::from_ast_ident(val);
160 ts.push(TokenTree::Token(t, Spacing::Joint));
161 ts.push(TokenTree::Token(comma, Spacing::Alone));
162 }
163
164 pub(crate) fn expand_forward(
165 ecx: &mut ExtCtxt<'_>,
166 expand_span: Span,
167 meta_item: &ast::MetaItem,
168 item: Annotatable,
169 ) -> Vec<Annotatable> {
170 expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Forward)
171 }
172
173 pub(crate) fn expand_reverse(
174 ecx: &mut ExtCtxt<'_>,
175 expand_span: Span,
176 meta_item: &ast::MetaItem,
177 item: Annotatable,
178 ) -> Vec<Annotatable> {
179 expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Reverse)
180 }
181
182 pub(crate) fn expand_with_mode(
206 ecx: &mut ExtCtxt<'_>,
207 expand_span: Span,
208 meta_item: &ast::MetaItem,
209 mut item: Annotatable,
210 mode: DiffMode,
211 ) -> Vec<Annotatable> {
212 let dcx = ecx.sess.dcx();
213
214 let Some((vis, sig, primal, generics, is_impl)) = (match &item {
218 Annotatable::Item(iitem) => {
219 extract_item_info(iitem).map(|(v, s, p, g)| (v, s, p, g, false))
220 }
221 Annotatable::Stmt(stmt) => match &stmt.kind {
222 ast::StmtKind::Item(iitem) => {
223 extract_item_info(iitem).map(|(v, s, p, g)| (v, s, p, g, false))
224 }
225 _ => None,
226 },
227 Annotatable::AssocItem(assoc_item, _ctxt @ (Impl { of_trait: _ } | Trait)) => {
228 match &assoc_item.kind {
229 ast::AssocItemKind::Fn(ast::Fn { sig, ident, generics, .. }) => {
230 Some((assoc_item.vis.clone(), sig.clone(), *ident, generics.clone(), true))
231 }
232 _ => None,
233 }
234 }
235 _ => None,
236 }) else {
237 dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() });
238 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
239 };
240
241 let meta_item_vec: ThinVec<MetaItemInner> = match meta_item.kind {
242 ast::MetaItemKind::List(ref vec) => vec.clone(),
243 _ => {
244 dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() });
245 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
246 }
247 };
248
249 let has_ret = has_ret(&sig.decl.output);
250
251 let mut ts: Vec<TokenTree> = ::alloc::vec::Vec::new()vec![];
254 if meta_item_vec.is_empty() {
255 dcx.emit_err(diagnostics::AutoDiffMissingConfig { span: item.span() });
257 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
258 }
259
260 let mode_symbol = match mode {
261 DiffMode::Forward => sym::Forward,
262 DiffMode::Reverse => sym::Reverse,
263 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Unsupported mode: {0:?}", mode)));
}unreachable!("Unsupported mode: {:?}", mode),
264 };
265
266 let mode_token = Token::new(TokenKind::Ident(mode_symbol, false.into()), 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 = 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 = 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, false.into()), 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 = TokenStream::from_iter(ts);
309
310 let x: RustcAutodiff = 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: Vec<TokenTree> = ::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,
false.into()), span), Spacing::Joint)]))vec![TokenTree::Token(
349 Token::new(TokenKind::Ident(sym::never, false.into()), 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: 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: AssocItemKind = 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
462 Annotatable::Stmt(Box::new(ast::Stmt {
463 id: ast::DUMMY_NODE_ID,
464 kind: ast::StmtKind::Item(d_fn),
465 span,
466 }))
467 }
468 _ => {
469 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("item kind checked previously")));
}unreachable!("item kind checked previously")
470 }
471 };
472
473 ::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]
474 }
475
476 fn assure_mut_ref(ty: &ast::Ty) -> ast::Ty {
479 let mut ty = ty.clone();
480 match ty.kind {
481 TyKind::Ptr(ref mut mut_ty) => {
482 mut_ty.mutbl = ast::Mutability::Mut;
483 }
484 TyKind::Ref(_, ref mut mut_ty) => {
485 mut_ty.mutbl = ast::Mutability::Mut;
486 }
487 _ => {
488 {
::core::panicking::panic_fmt(format_args!("unsupported type: {0:?}", ty));
};panic!("unsupported type: {:?}", ty);
489 }
490 }
491 ty
492 }
493
494 fn call_autodiff(
499 ecx: &ExtCtxt<'_>,
500 primal: Ident,
501 diff: Ident,
502 span: Span,
503 p_sig: &FnSig,
504 d_sig: &FnSig,
505 generics: &Generics,
506 is_impl: bool,
507 ) -> rustc_ast::Stmt {
508 let primal_path_expr = gen_turbofish_expr(ecx, primal, generics, span, is_impl);
509
510 let self_ty = || ecx.ty_path(ast::Path::from_ident(Ident::with_dummy_span(kw::SelfUpper)));
511 let fn_ptr_params: ThinVec<ast::Param> = p_sig
512 .decl
513 .inputs
514 .iter()
515 .map(|param| {
516 let ty = match ¶m.ty.kind {
517 TyKind::ImplicitSelf => self_ty(),
518 TyKind::Ref(lt, mt) if #[allow(non_exhaustive_omitted_patterns)] match mt.ty.kind {
TyKind::ImplicitSelf => true,
_ => false,
}matches!(mt.ty.kind, TyKind::ImplicitSelf) => ecx
519 .ty(span, TyKind::Ref(*lt, ast::MutTy { ty: self_ty(), mutbl: mt.mutbl })),
520 TyKind::Ptr(mt) if #[allow(non_exhaustive_omitted_patterns)] match mt.ty.kind {
TyKind::ImplicitSelf => true,
_ => false,
}matches!(mt.ty.kind, TyKind::ImplicitSelf) => {
521 ecx.ty(span, TyKind::Ptr(ast::MutTy { ty: self_ty(), mutbl: mt.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 }
533 })
534 .collect();
535 let fn_ptr_ty = ecx.ty(
536 span,
537 TyKind::FnPtr(Box::new(ast::FnPtrTy {
538 safety: p_sig.header.safety,
539 ext: p_sig.header.ext,
540 generic_params: ThinVec::new(),
541 decl: Box::new(ast::FnDecl {
542 inputs: fn_ptr_params,
543 output: p_sig.decl.output.clone(),
544 }),
545 decl_span: span,
546 })),
547 );
548 let primal_fn_ptr = ecx.expr(span, ast::ExprKind::Cast(primal_path_expr, fn_ptr_ty));
549
550 let diff_path_expr = gen_turbofish_expr(ecx, diff, generics, span, is_impl);
551
552 let tuple_expr = ecx.expr_tuple(
553 span,
554 d_sig
555 .decl
556 .inputs
557 .iter()
558 .map(|arg| match arg.pat.kind {
559 PatKind::Ident(_, ident, _) => ecx.expr_path(ecx.path_ident(span, ident)),
560 _ => ::core::panicking::panic("not implemented")unimplemented!(),
561 })
562 .collect::<ThinVec<_>>(),
563 );
564
565 let enzyme_path_idents = ecx.std_path(&[sym::intrinsics, sym::autodiff]);
566 let enzyme_path = ecx.path(span, enzyme_path_idents);
567 let call_expr = ecx.expr_call(
568 span,
569 ecx.expr_path(enzyme_path),
570 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[primal_fn_ptr, diff_path_expr, tuple_expr]))vec![primal_fn_ptr, diff_path_expr, tuple_expr].into(),
571 );
572
573 ecx.stmt_expr(call_expr)
574 }
575
576 fn gen_turbofish_expr(
580 ecx: &ExtCtxt<'_>,
581 ident: Ident,
582 generics: &Generics,
583 span: Span,
584 is_impl: bool,
585 ) -> Box<ast::Expr> {
586 let generic_args = generics
587 .params
588 .iter()
589 .filter_map(|p| match &p.kind {
590 GenericParamKind::Type { .. } => {
591 let path = ast::Path::from_ident(p.ident);
592 let ty = ecx.ty_path(path);
593 Some(AngleBracketedArg::Arg(GenericArg::Type(ty)))
594 }
595 GenericParamKind::Const { .. } => {
596 let expr = ecx.expr_path(ast::Path::from_ident(p.ident));
597 let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr };
598 Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const)))
599 }
600 GenericParamKind::Lifetime => None,
601 })
602 .collect::<ThinVec<_>>();
603
604 let args: AngleBracketedArgs = AngleBracketedArgs { span, args: generic_args };
605
606 let segment = PathSegment {
607 ident,
608 id: ast::DUMMY_NODE_ID,
609 args: Some(Box::new(GenericArgs::AngleBracketed(args))),
610 };
611
612 let segments = if is_impl {
613 {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(PathSegment {
ident: Ident::from_str("Self"),
id: ast::DUMMY_NODE_ID,
args: None,
});
vec.push(segment);
vec
}thin_vec![
614 PathSegment { ident: Ident::from_str("Self"), id: ast::DUMMY_NODE_ID, args: None },
615 segment,
616 ]
617 } else {
618 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(segment);
vec
}thin_vec![segment]
619 };
620
621 let path = Path { span, segments };
622
623 ecx.expr_path(path)
624 }
625
626 fn gen_enzyme_decl(
638 ecx: &ExtCtxt<'_>,
639 sig: &ast::FnSig,
640 x: &RustcAutodiff,
641 span: Span,
642 ) -> ast::FnSig {
643 let dcx = ecx.sess.dcx();
644 let has_ret = has_ret(&sig.decl.output);
645 let sig_args = sig.decl.inputs.len() + if has_ret { 1 } else { 0 };
646 let num_activities = x.input_activity.len() + if x.has_ret_activity() { 1 } else { 0 };
647 if sig_args != num_activities {
648 dcx.emit_err(diagnostics::AutoDiffInvalidNumberActivities {
649 span,
650 expected: sig_args,
651 found: num_activities,
652 });
653 return sig.clone();
655 }
656 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());
657 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());
658 let mut d_decl = sig.decl.clone();
659 let mut d_inputs = Vec::new();
660 let mut new_inputs = Vec::new();
661 let mut idents = Vec::new();
662 let mut act_ret = ThinVec::new();
663
664 let mut errors = false;
667 for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
668 if !valid_input_activity(x.mode, *activity) {
669 dcx.emit_err(diagnostics::AutoDiffInvalidApplicationModeAct {
670 span,
671 mode: x.mode.to_string(),
672 act: activity.to_string(),
673 });
674 errors = true;
675 }
676 if !valid_ty_for_activity(&arg.ty, *activity) {
677 dcx.emit_err(diagnostics::AutoDiffInvalidTypeForActivity {
678 span: arg.ty.span,
679 act: activity.to_string(),
680 });
681 errors = true;
682 }
683 }
684
685 if has_ret && !valid_ret_activity(x.mode, x.ret_activity) {
686 dcx.emit_err(diagnostics::AutoDiffInvalidRetAct {
687 span,
688 mode: x.mode.to_string(),
689 act: x.ret_activity.to_string(),
690 });
691 }
694
695 if errors {
696 return sig.clone();
698 }
699
700 let unsafe_activities = x
701 .input_activity
702 .iter()
703 .any(|&act| #[allow(non_exhaustive_omitted_patterns)] match act {
DiffActivity::DuplicatedOnly | DiffActivity::DualOnly => true,
_ => false,
}matches!(act, DiffActivity::DuplicatedOnly | DiffActivity::DualOnly));
704 for (arg, activity) in sig.decl.inputs.iter().zip(x.input_activity.iter()) {
705 d_inputs.push(arg.clone());
706 match activity {
707 DiffActivity::Active => {
708 act_ret.push(arg.ty.clone());
709 }
711 DiffActivity::ActiveOnly => {
712 }
715 DiffActivity::Duplicated | DiffActivity::DuplicatedOnly => {
716 for i in 0..x.width {
717 let mut shadow_arg = arg.clone();
718 *shadow_arg.ty = assure_mut_ref(&arg.ty);
720 let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
721 ident.name
722 } else {
723 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs:723",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(723u32),
::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);
724 { ::core::panicking::panic_fmt(format_args!("not an ident?")); };panic!("not an ident?");
725 };
726 let name: String = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("d{0}_{1}", old_name, i))
})format!("d{}_{}", old_name, i);
727 new_inputs.push(name.clone());
728 let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
729 *shadow_arg.pat = ast::Pat {
730 id: ast::DUMMY_NODE_ID,
731 kind: PatKind::Ident(BindingMode::NONE, ident, None),
732 span: shadow_arg.pat.span,
733 };
734 d_inputs.push(shadow_arg.clone());
735 }
736 }
737 DiffActivity::Dual
738 | DiffActivity::DualOnly
739 | DiffActivity::Dualv
740 | DiffActivity::DualvOnly => {
741 let iterations =
744 if #[allow(non_exhaustive_omitted_patterns)] match activity {
DiffActivity::Dualv | DiffActivity::DualvOnly => true,
_ => false,
}matches!(activity, DiffActivity::Dualv | DiffActivity::DualvOnly) {
745 1
746 } else {
747 x.width
748 };
749 for i in 0..iterations {
750 let mut shadow_arg = arg.clone();
751 let old_name = if let PatKind::Ident(_, ident, _) = arg.pat.kind {
752 ident.name
753 } else {
754 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs:754",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(754u32),
::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);
755 { ::core::panicking::panic_fmt(format_args!("not an ident?")); };panic!("not an ident?");
756 };
757 let name: String = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("b{0}_{1}", old_name, i))
})format!("b{}_{}", old_name, i);
758 new_inputs.push(name.clone());
759 let ident = Ident::from_str_and_span(&name, shadow_arg.pat.span);
760 *shadow_arg.pat = ast::Pat {
761 id: ast::DUMMY_NODE_ID,
762 kind: PatKind::Ident(BindingMode::NONE, ident, None),
763 span: shadow_arg.pat.span,
764 };
765 d_inputs.push(shadow_arg.clone());
766 }
767 }
768 DiffActivity::Const => {
769 }
771 DiffActivity::None | DiffActivity::FakeActivitySize(_) => {
772 { ::core::panicking::panic_fmt(format_args!("Should not happen")); };panic!("Should not happen");
773 }
774 }
775 if let PatKind::Ident(_, ident, _) = arg.pat.kind {
776 idents.push(ident);
777 } else {
778 { ::core::panicking::panic_fmt(format_args!("not an ident?")); };panic!("not an ident?");
779 }
780 }
781
782 let active_only_ret = x.ret_activity == DiffActivity::ActiveOnly;
783 if active_only_ret {
784 if !x.mode.is_rev() {
::core::panicking::panic("assertion failed: x.mode.is_rev()")
};assert!(x.mode.is_rev());
785 }
786
787 if x.mode.is_rev() {
790 match x.ret_activity {
791 DiffActivity::Active | DiffActivity::ActiveOnly => {
792 let ty = match d_decl.output {
793 FnRetTy::Ty(ref ty) => ty.clone(),
794 FnRetTy::Default(span) => {
795 {
::core::panicking::panic_fmt(format_args!("Did not expect Default ret ty: {0:?}",
span));
};panic!("Did not expect Default ret ty: {:?}", span);
796 }
797 };
798 let name = "dret".to_string();
799 let ident = Ident::from_str_and_span(&name, ty.span);
800 let shadow_arg = ast::Param {
801 attrs: ThinVec::new(),
802 ty: ty.clone(),
803 pat: Box::new(ast::Pat {
804 id: ast::DUMMY_NODE_ID,
805 kind: PatKind::Ident(BindingMode::NONE, ident, None),
806 span: ty.span,
807 }),
808 id: ast::DUMMY_NODE_ID,
809 span: ty.span,
810 is_placeholder: false,
811 };
812 d_inputs.push(shadow_arg);
813 new_inputs.push(name);
814 }
815 _ => {}
816 }
817 }
818 d_decl.inputs = d_inputs.into();
819
820 if x.mode.is_fwd() {
821 let ty = match d_decl.output {
822 FnRetTy::Ty(ref ty) => ty.clone(),
823 FnRetTy::Default(span) => {
824 let kind = TyKind::Tup(ThinVec::new());
826 let ty = Box::new(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span });
827 d_decl.output = FnRetTy::Ty(ty.clone());
828 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));
829 ty
831 }
832 };
833
834 if #[allow(non_exhaustive_omitted_patterns)] match x.ret_activity {
DiffActivity::Dual | DiffActivity::Dualv => true,
_ => false,
}matches!(x.ret_activity, DiffActivity::Dual | DiffActivity::Dualv) {
835 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) {
836 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()])
839 } else {
840 let anon_const = rustc_ast::AnonConst {
842 id: ast::DUMMY_NODE_ID,
843 value: ecx.expr_usize(span, 1 + x.width as usize),
844 };
845 TyKind::Array(ty.clone(), anon_const)
846 };
847 let ty = Box::new(rustc_ast::Ty { kind, id: ty.id, span: ty.span });
848 d_decl.output = FnRetTy::Ty(ty);
849 }
850 if #[allow(non_exhaustive_omitted_patterns)] match x.ret_activity {
DiffActivity::DualOnly | DiffActivity::DualvOnly => true,
_ => false,
}matches!(x.ret_activity, DiffActivity::DualOnly | DiffActivity::DualvOnly) {
851 if x.width > 1 {
855 let anon_const = rustc_ast::AnonConst {
856 id: ast::DUMMY_NODE_ID,
857 value: ecx.expr_usize(span, x.width as usize),
858 };
859 let kind = TyKind::Array(ty.clone(), anon_const);
860 let ty = Box::new(rustc_ast::Ty { kind, id: ty.id, span: ty.span });
861 d_decl.output = FnRetTy::Ty(ty);
862 }
863 }
864 }
865
866 d_decl.output =
868 if active_only_ret { FnRetTy::Default(span) } else { d_decl.output.clone() };
869
870 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs:870",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(870u32),
::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);
871
872 if act_ret.len() > 0 {
876 let ret_ty = match d_decl.output {
877 FnRetTy::Ty(ref ty) => {
878 if !active_only_ret {
879 act_ret.insert(0, ty.clone());
880 }
881 let kind = TyKind::Tup(act_ret);
882 Box::new(rustc_ast::Ty { kind, id: ty.id, span: ty.span })
883 }
884 FnRetTy::Default(span) => {
885 if act_ret.len() == 1 {
886 act_ret[0].clone()
887 } else {
888 let kind = TyKind::Tup(act_ret);
889 Box::new(rustc_ast::Ty { kind, id: ast::DUMMY_NODE_ID, span })
890 }
891 }
892 };
893 d_decl.output = FnRetTy::Ty(ret_ty);
894 }
895
896 let mut d_header = sig.header;
897 if unsafe_activities {
898 d_header.safety = rustc_ast::Safety::Unsafe(span);
899 }
900 let d_sig = FnSig { header: d_header, decl: d_decl, span };
901 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs:901",
"rustc_builtin_macros::autodiff::llvm_enzyme",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/2e2b193f8ada105f27608b7be81c293e0d7292cb/compiler/rustc_builtin_macros/src/autodiff.rs"),
::tracing_core::__macro_support::Option::Some(901u32),
::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);
902 d_sig
903 }
904}
905
906pub(crate) use llvm_enzyme::{expand_forward, expand_reverse};