Skip to main content

ruda_kernel_macros/ir/
entrypoints.rs

1use core::panic;
2
3use ir::error::error_into_token_stream;
4use ir::generate::autotune::generate_autotune_key;
5use ir::parse::{
6    ruda_impl::RudaImpl,
7    ruda_trait::{RudaTrait, RudaTraitImpl},
8    helpers::{RemoveHelpers, ReplaceIndices},
9    kernel::{Launch, from_tokens},
10};
11use syn::visit_mut::VisitMut;
12
13use crate::ir::{
14    generate::{assign::generate_ruda_type_mut, into_runtime::generate_into_runtime},
15    parse::{
16        ruda_type::generate_ruda_type, derive_expand::generate_derive_expand,
17        helpers::ReplaceDefines,
18    },
19};
20
21
22/// Mark a ruda function, trait or implementation for expansion.
23///
24/// # Arguments
25/// * `launch` - generates a function to launch the kernel
26/// * `launch_unchecked` - generates a launch function without checks
27/// * `debug` - panics after generation to print the output to console
28/// * `create_dummy_kernel` - Generates a function to create a kernel without launching it. Used for
29///   testing.
30///
31/// # Trait arguments
32/// * `expand_base_traits` - base traits for the expanded "second half" of a trait with methods.
33/// * `self_type` - the type used for the `self` parameter of the expanded "second half" of a trait
34///   with methods. You shouldn't need to touch this unless you specifically need to dynamically
35///   dispatch an expanded trait.
36///
37/// # Example
38///
39/// ```ignored
40/// # use ruda_kernel_macros::ruda;
41/// #[ruda]
42/// fn my_addition(a: u32, b: u32) -> u32 {
43///     a + b
44/// }
45/// ```
46#[proc_macro_attribute]
47pub fn ruda(args: TokenStream, input: TokenStream) -> TokenStream {
48    match ruda_impl(args, input.clone()) {
49        Ok(tokens) => tokens,
50        Err(e) => error_into_token_stream(e, input.into()).into(),
51    }
52}
53
54fn ruda_impl(args: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
55    let mut item: Item = syn::parse(input)?;
56    let args = from_tokens(args.into())?;
57
58    let tokens = match item.clone() {
59        Item::Fn(kernel) => {
60            let kernel = Launch::from_item_fn(kernel, args)?;
61            RemoveHelpers.visit_item_mut(&mut item);
62            ReplaceIndices.visit_item_mut(&mut item);
63            ReplaceDefines.visit_item_mut(&mut item);
64
65            return Ok(TokenStream::from(quote! {
66                #[allow(dead_code, clippy::too_many_arguments)]
67                #item
68                #kernel
69            }));
70        }
71        Item::Trait(kernel_trait) => {
72            let is_debug = args.debug.is_present();
73            let expand_trait = RudaTrait::from_item_trait(kernel_trait, args)?;
74
75            let tokens = TokenStream::from(quote! {
76                #expand_trait
77            });
78            if is_debug {
79                panic!("{tokens}");
80            }
81            return Ok(tokens);
82        }
83        Item::Impl(item_impl) => {
84            if item_impl.trait_.is_some() {
85                let mut expand_impl = RudaTraitImpl::from_item_impl(item_impl, &args)?;
86                let expand_impl = expand_impl.to_tokens_mut();
87
88                Ok(TokenStream::from(quote! {
89                    #expand_impl
90                }))
91            } else {
92                let mut expand_impl = RudaImpl::from_item_impl(item_impl, &args)?;
93                let expand_impl = expand_impl.to_tokens_mut();
94
95                Ok(TokenStream::from(quote! {
96                    #expand_impl
97                }))
98            }
99        }
100        item => Err(syn::Error::new_spanned(
101            item,
102            "`#[ruda]` is only supported on traits and functions",
103        ))?,
104    };
105
106    if args.debug.is_present() {
107        match tokens {
108            Ok(tokens) => panic!("{tokens}"),
109            Err(err) => panic!("{err}"),
110        };
111    }
112
113    tokens
114}
115
116/// Derive macro to define a ruda type that is launched with a kernel
117#[proc_macro_derive(RudaLaunch, attributes(ruda, launch))]
118pub fn module_derive_ruda_launch(input: TokenStream) -> TokenStream {
119    gen_ruda_type(input, true)
120}
121
122/// Derive macro to define a ruda type that is not launched
123#[proc_macro_derive(RudaType, attributes(ruda))]
124pub fn module_derive_ruda_type(input: TokenStream) -> TokenStream {
125    gen_ruda_type(input, false)
126}
127
128fn gen_ruda_type(input: TokenStream, with_launch: bool) -> TokenStream {
129    let parsed = syn::parse(input);
130
131    let input = match &parsed {
132        Ok(val) => val,
133        Err(err) => return err.to_compile_error().into(),
134    };
135
136    match generate_ruda_type(input, with_launch) {
137        Ok(val) => val.into(),
138        Err(err) => err.to_compile_error().into(),
139    }
140}
141
142/// Attribute macro to define a type that can be used as a kernel comptime
143/// argument This derive Debug, Hash, `PartialEq`, Eq, Clone, Copy
144#[proc_macro_attribute]
145pub fn derive_ruda_comptime(_metadata: TokenStream, input: TokenStream) -> TokenStream {
146    let input: proc_macro2::TokenStream = input.into();
147    quote! {
148        #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
149        #input
150    }
151    .into()
152}
153
154/// Attribute macro to derive ruda traits for existing structs, without redefining that struct.
155#[proc_macro_attribute]
156pub fn derive_expand(metadata: TokenStream, input: TokenStream) -> TokenStream {
157    match generate_derive_expand(input.into(), metadata.into()) {
158        Ok(val) => val.into(),
159        Err(err) => err.to_compile_error().into(),
160    }
161}
162
163/// Mark the contents of this macro as compile time values, turning off all
164/// expansion for this code and using it verbatim
165///
166/// # Example
167/// ```ignored
168/// #use ruda_kernel_macros::ruda;
169/// #fn some_rust_function(a: u32) -> u32 {}
170/// #[ruda]
171/// fn do_stuff(input: u32) -> u32 {
172///     let comptime_value = comptime! { some_rust_function(3) };
173///     input + comptime_value
174/// }
175/// ```
176#[proc_macro]
177pub fn comptime(input: TokenStream) -> TokenStream {
178    let tokens: proc_macro2::TokenStream = input.into();
179    quote![{ #tokens }].into()
180}
181
182/// Mark the contents of this macro as an intrinsic, turning off all expansion
183/// for this code and calling it with the scope
184///
185/// # Example
186/// ```ignored
187/// #use ruda_kernel_macros::ruda;
188/// #[ruda]
189/// fn do_stuff(input: u32) -> u32 {
190///     let comptime_value = intrinsic! { |scope| u32::elem_size(scope) };
191///     input + comptime_value
192/// }
193/// ```
194#[proc_macro]
195pub fn intrinsic(_input: TokenStream) -> TokenStream {
196    let core = ir::paths::core_path();
197    quote![{ #core::unexpanded!() }].into()
198}
199
200/// Makes the function return a compile time value
201/// Useful in a ruda trait to have a part of the trait return comptime values
202///
203/// # Example
204/// ```ignored
205/// #use ruda_kernel_macros::ruda;
206/// #[ruda]
207/// fn do_stuff(#[comptime] input: u32) -> comptime_type!(u32) {
208///     input + 5   
209/// }
210/// ```
211///
212/// TODO: calling a trait method returning `comptime_type` from
213/// within another trait method does not work
214#[proc_macro]
215pub fn comptime_type(input: TokenStream) -> TokenStream {
216    let tokens: proc_macro2::TokenStream = input.into();
217    quote![ #tokens ].into()
218}
219
220/// Insert a literal comment into the kernel source code.
221///
222/// # Example
223/// ```ignored
224/// #use ruda_kernel_macros::ruda;
225/// #[ruda]
226/// fn do_stuff(input: u32) -> u32 {
227///     comment!("Add five to the input");
228///     input + 5
229/// }
230/// ```
231#[proc_macro]
232pub fn comment(input: TokenStream) -> TokenStream {
233    let tokens: proc_macro2::TokenStream = input.into();
234    quote![{ #tokens }].into()
235}
236
237/// Terminate the execution of the kernel for the current unit.
238///
239/// This terminates the execution of the unit even if nested inside many
240/// functions.
241///
242/// # Example
243/// ```ignored
244/// #use ruda_kernel_macros::ruda;
245/// #[ruda]
246/// fn stop_if_more_than_ten(input: u32)  {
247///     if input > 10 {
248///         terminate!();
249///     }
250/// }
251/// ```
252#[proc_macro]
253pub fn terminate(input: TokenStream) -> TokenStream {
254    let tokens: proc_macro2::TokenStream = input.into();
255    quote![{ #tokens }].into()
256}
257
258/// Implements display and initialization for autotune keys.
259///
260/// # Helper
261///
262/// Use the `#[autotune(anchor)]` helper attribute to anchor a numerical value.
263/// This groups multiple numerical values into the same bucket.
264///
265/// For now, only an exponential function is supported, and it can be modified
266/// with `exp`. By default, the base is '2' and there are no `min` or `max`
267/// provided.
268///
269/// # Example
270/// ```ignore
271/// #[derive(AutotuneKey, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
272/// pub struct OperationKey {
273///     #[autotune(name = "Batch Size")]
274///     batch_size: usize,
275///     channels: usize,
276///     #[autotune(anchor(exp(min = 16, max = 1024, base = 2)))]
277///     height: usize,
278///     #[autotune(anchor)]
279///     width: usize,
280/// }
281/// ```
282#[proc_macro_derive(AutotuneKey, attributes(autotune))]
283pub fn derive_autotune_key(input: TokenStream) -> TokenStream {
284    let input = syn::parse(input).unwrap();
285    match generate_autotune_key(input) {
286        Ok(tokens) => tokens.into(),
287        Err(e) => e.into_compile_error().into(),
288    }
289}
290
291/// Implements `IntoRuntime` for a `RudaType`
292#[proc_macro_derive(IntoRuntime, attributes(ruda))]
293pub fn derive_into_runtime(input: TokenStream) -> TokenStream {
294    let input = syn::parse(input).unwrap();
295    match generate_into_runtime(&input) {
296        Ok(tokens) => tokens.into(),
297        Err(e) => e.into_compile_error().into(),
298    }
299}
300
301/// Implements mutability for a `RudaType`
302#[proc_macro_derive(RudaTypeMut, attributes(ruda))]
303pub fn derive_assign(input: TokenStream) -> TokenStream {
304    let input = syn::parse(input).unwrap();
305    match generate_ruda_type_mut(&input) {
306        Ok(tokens) => tokens.into(),
307        Err(e) => e.into_compile_error().into(),
308    }
309}