1use rustc_ast::ast;
2use rustc_ast::token::{Delimiter, Token, TokenKind};
3use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
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.iter().any(|o| #[allow(non_exhaustive_omitted_patterns)] match o {
Offload::Device(_) => true,
_ => false,
}matches!(o, 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(
63 ecx: &mut ExtCtxt<'_>,
64 expand_span: Span,
65 _meta_item: &ast::MetaItem,
66 item: Annotatable,
67) -> Vec<Annotatable> {
68 let dcx = ecx.sess.dcx();
69
70 let Some((vis, sig, ident, generics, body)) = extract_fn(&item) else {
71 dcx.emit_err(diagnostics::AutoDiffInvalidApplication { span: item.span() });
72 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
73 };
74
75 let span = ecx.with_def_site_ctxt(expand_span);
76
77 let mut device_fn = Box::new(ast::Fn {
79 defaultness: ast::Defaultness::Implicit,
80 sig: sig.clone(),
81 ident,
82 generics: generics.clone(),
83 contract: None,
84 body,
85 define_opaque: None,
86 eii_impl: None,
87 });
88
89 let extern_gpu_kernel = ast::Extern::from_abi(
90 Some(ast::StrLit {
91 symbol: sym::gpu_kernel,
92 suffix: None,
93 symbol_unescaped: sym::gpu_kernel,
94 style: ast::StrStyle::Cooked,
95 span,
96 }),
97 span,
98 );
99 device_fn.sig.header.ext = extern_gpu_kernel;
100 device_fn.sig.header.safety = ast::Safety::Unsafe(span);
101
102 let rustc_offload_kernel_attr =
104 Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_offload_kernel)));
105 let rustc_offload_kernel = outer_normal_attr(
106 &rustc_offload_kernel_attr,
107 ecx.sess.psess.attr_id_generator.mk_attr_id(),
108 span,
109 );
110
111 let device_item = {
112 let mut item =
113 ecx.item(span, {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(rustc_offload_kernel.clone());
vec
}thin_vec![rustc_offload_kernel.clone()], ast::ItemKind::Fn(device_fn));
114 item.vis = vis.clone();
115 Annotatable::Item(item)
116 };
117
118 let macro_expr = ecx.expr_macro_call(
120 span,
121 ecx.macro_call(
122 span,
123 ecx.path_global(
124 span,
125 [sym::std, sym::unimplemented].map(|s| Ident::new(s, span)).to_vec(),
126 ),
127 Delimiter::Parenthesis,
128 TokenStream::default(),
129 ),
130 );
131 let stmt = ecx.stmt_expr(macro_expr);
132 let body = ecx.block(span, {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(stmt);
vec
}thin_vec![stmt]);
133
134 let mut host_fn = Box::new(ast::Fn {
136 defaultness: ast::Defaultness::Implicit,
137 sig: sig.clone(),
138 ident,
139 generics: generics.clone(),
140 contract: None,
141 body: Some(body),
142 define_opaque: None,
143 eii_impl: None,
144 });
145
146 for param in host_fn.sig.decl.inputs.iter_mut() {
147 param.pat = Box::new(ecx.pat_wild(param.pat.span));
148 }
149
150 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(
152 Token::new(TokenKind::Ident(sym::never, false.into()), span),
153 Spacing::Joint,
154 )];
155
156 let never_arg = ast::DelimArgs {
157 dspan: DelimSpan::from_single(span),
158 delim: Delimiter::Parenthesis,
159 tokens: TokenStream::from_iter(ts),
160 };
161
162 let inline_item = ast::AttrItem {
163 unsafety: ast::Safety::Default,
164 path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)),
165 args: ast::AttrArgs::Delimited(never_arg),
166 span: DUMMY_SP,
167 };
168 let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None });
169
170 let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id();
171 let inline_never = outer_normal_attr(&inline_never_attr, new_id, span);
172
173 let host_item = {
174 let mut item = ecx.item(
175 span,
176 {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(rustc_offload_kernel);
vec.push(inline_never);
vec
}thin_vec![rustc_offload_kernel, inline_never],
177 ast::ItemKind::Fn(host_fn),
178 );
179 item.vis = vis.clone();
180 Annotatable::Item(item)
181 };
182
183 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] }
184}