Skip to main content

rustc_builtin_macros/
offload.rs

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
35/// The `offload_kernel` macro expands the function into two separate definitions:
36/// one on the host to handle the call, and one on the device for executing the kernel.
37///
38/// ```
39/// #[offload_kernel]
40/// fn foo(a: &[f32], b: &[f32], c: *mut f32) {
41///     *c = a[0] + b[0];
42/// }
43/// ```
44///
45/// This expands to the host-side function:
46///
47/// ```
48/// #[unsafe(no_mangle)]
49/// #[inline(never)]
50/// fn foo(_: &[f32], _: &[f32], _: *mut f32) {
51///     ::core::panicking::panic("not implemented")
52/// }
53/// ```
54///
55/// And the device-side kernel:
56///
57/// ```
58/// #[rustc_offload_kernel]
59/// #[unsafe(no_mangle)]
60/// unsafe extern "gpu-kernel" fn foo(a: &[f32], b: &[f32], c: *mut f32) {
61///     *c = a[0] + b[0];
62/// }
63/// ```
64pub(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    // device function
80    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    // rustc_offload_kernel attr
105    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    // unsafe(no_mangle) attr
114    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    // unimplemented! body
136    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    // host function
152    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    // inline(never) attr
168    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}