1use rustc_ast::token::{Delimiter, Token, TokenKind};
2use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
3use rustc_ast::{AttrItem, ast};
4use rustc_expand::base::{Annotatable, ExtCtxt};
5use rustc_session::config::Offload;
6use rustc_span::{DUMMY_SP, Ident, Span, sym};
7use thin_vec::thin_vec;
8
9use crate::diagnostics;
10
11fn compile_for_device(ecx: &mut ExtCtxt<'_>) -> bool {
12 ecx.sess.opts.unstable_opts.offload.contains(&Offload::Device)
13}
14
15fn outer_normal_attr(normal: &Box<ast::NormalAttr>, id: ast::AttrId, span: Span) -> ast::Attribute {
16 let style = ast::AttrStyle::Outer;
17 let kind = ast::AttrKind::Normal(normal.clone());
18 ast::Attribute { kind, id, style, span }
19}
20
21fn extract_fn(
22 item: &Annotatable,
23) -> Option<(ast::Visibility, ast::FnSig, Ident, ast::Generics, Option<Box<ast::Block>>)> {
24 match item {
25 Annotatable::Item(iitem) => match &iitem.kind {
26 ast::ItemKind::Fn(ast::Fn { sig, ident, generics, body, .. }) => {
27 Some((iitem.vis.clone(), sig.clone(), *ident, generics.clone(), body.clone()))
28 }
29 _ => None,
30 },
31 _ => None,
32 }
33}
34
35pub(crate) fn expand_kernel(
65 ecx: &mut ExtCtxt<'_>,
66 expand_span: Span,
67 _meta_item: &ast::MetaItem,
68 item: Annotatable,
69) -> Vec<Annotatable> {
70 let dcx = ecx.sess.dcx();
71
72 let Some((vis, sig, ident, generics, body)) = extract_fn(&item) else {
73 dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() });
74 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
75 };
76
77 let span = ecx.with_def_site_ctxt(expand_span);
78
79 let mut device_fn = Box::new(ast::Fn {
81 defaultness: ast::Defaultness::Implicit,
82 sig: sig.clone(),
83 ident,
84 generics: generics.clone(),
85 contract: None,
86 body,
87 define_opaque: None,
88 eii_impls: Default::default(),
89 });
90
91 let extern_gpu_kernel = ast::Extern::from_abi(
92 Some(ast::StrLit {
93 symbol: sym::gpu_kernel,
94 suffix: None,
95 symbol_unescaped: sym::gpu_kernel,
96 style: ast::StrStyle::Cooked,
97 span,
98 }),
99 span,
100 );
101 device_fn.sig.header.ext = extern_gpu_kernel;
102 device_fn.sig.header.safety = ast::Safety::Unsafe(span);
103
104 let rustc_offload_kernel_attr =
106 Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_offload_kernel)));
107 let rustc_offload_kernel = outer_normal_attr(
108 &rustc_offload_kernel_attr,
109 ecx.sess.psess.attr_id_generator.mk_attr_id(),
110 span,
111 );
112
113 let unsafe_item = AttrItem {
115 unsafety: ast::Safety::Unsafe(span),
116 path: ast::Path::from_ident(Ident::new(sym::no_mangle, span)),
117 args: ast::AttrArgs::Empty,
118 span,
119 };
120
121 let no_mangle_attr = Box::new(ast::NormalAttr { item: unsafe_item, tokens: None });
122 let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
123 let unsafe_no_mangle = outer_normal_attr(&no_mangle_attr, new_id, span);
124
125 let device_item = {
126 let mut item = ecx.item(
127 span,
128 {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(rustc_offload_kernel);
vec.push(unsafe_no_mangle);
vec
}thin_vec![rustc_offload_kernel, unsafe_no_mangle],
129 ast::ItemKind::Fn(device_fn),
130 );
131 item.vis = vis.clone();
132 Annotatable::Item(item)
133 };
134
135 let macro_expr = ecx.expr_macro_call(
137 span,
138 ecx.macro_call(
139 span,
140 ecx.path_global(
141 span,
142 [sym::std, sym::unimplemented].map(|s| Ident::new(s, span)).to_vec(),
143 ),
144 Delimiter::Parenthesis,
145 TokenStream::default(),
146 ),
147 );
148 let stmt = ecx.stmt_expr(macro_expr);
149 let body = ecx.block(span, {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(stmt);
vec
}thin_vec![stmt]);
150
151 let mut host_fn = Box::new(ast::Fn {
153 defaultness: ast::Defaultness::Implicit,
154 sig: sig.clone(),
155 ident,
156 generics: generics.clone(),
157 contract: None,
158 body: Some(body),
159 define_opaque: None,
160 eii_impls: Default::default(),
161 });
162
163 for param in host_fn.sig.decl.inputs.iter_mut() {
164 param.pat = Box::new(ecx.pat_wild(param.pat.span));
165 }
166
167 let ts: 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(
169 Token::new(TokenKind::Ident(sym::never, false.into()), span),
170 Spacing::Joint,
171 )];
172
173 let never_arg = ast::DelimArgs {
174 dspan: DelimSpan::from_single(span),
175 delim: Delimiter::Parenthesis,
176 tokens: TokenStream::from_iter(ts),
177 };
178
179 let inline_item = ast::AttrItem {
180 unsafety: ast::Safety::Default,
181 path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)),
182 args: ast::AttrArgs::Delimited(never_arg),
183 span: DUMMY_SP,
184 };
185 let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None });
186
187 let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
188 let inline_never = outer_normal_attr(&inline_never_attr, new_id, span);
189
190 let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
191 let unsafe_no_mangle = outer_normal_attr(&no_mangle_attr, new_id, span);
192
193 let host_item = {
194 let mut item =
195 ecx.item(span, {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(unsafe_no_mangle);
vec.push(inline_never);
vec
}thin_vec![unsafe_no_mangle, inline_never], ast::ItemKind::Fn(host_fn));
196 item.vis = vis.clone();
197 Annotatable::Item(item)
198 };
199
200 if compile_for_device(ecx) { ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[device_item]))vec![device_item] } else { ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[host_item]))vec![host_item] }
201}