aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen/software_tasks.rs
blob: dfba193bc87c6b6c8402dd84ba757172b3685032 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use rtic_syntax::{ast::App, Context};

use crate::{
    analyze::Analysis,
    check::Extra,
    codegen::{locals, module, resources_struct, util},
};

pub fn codegen(
    app: &App,
    analysis: &Analysis,
    extra: &Extra,
) -> (
    // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors
    Vec<TokenStream2>,
    // root_software_tasks -- items that must be placed in the root of the crate:
    // - `${task}Locals` structs
    // - `${task}Resources` structs
    // - `${task}` modules
    Vec<TokenStream2>,
    // user_software_tasks -- the `#[task]` functions written by the user
    Vec<TokenStream2>,
) {
    let mut mod_app = vec![];
    let mut root = vec![];
    let mut user_tasks = vec![];

    for (name, task) in &app.software_tasks {
        let inputs = &task.inputs;
        let (_, _, _, input_ty) = util::regroup_inputs(inputs);

        let cap = task.args.capacity;
        let cap_lit = util::capacity_literal(cap);
        let cap_ty = util::capacity_typenum(cap, true);

        // Create free queues and inputs / instants buffers
        let fq = util::fq_ident(name);

        let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
            (
                quote!(rtic::export::SCFQ<#cap_ty>),
                quote!(rtic::export::Queue(unsafe {
                    rtic::export::iQueue::u8_sc()
                })),
                Box::new(|| util::link_section_uninit(true)),
            )
        };
        mod_app.push(quote!(
            /// Queue version of a free-list that keeps track of empty slots in
            /// the following buffers
            static mut #fq: #fq_ty = #fq_expr;
        ));

        let elems = &(0..cap)
            .map(|_| quote!(core::mem::MaybeUninit::uninit()))
            .collect::<Vec<_>>();

        if let Some(m) = &extra.monotonic {
            let instants = util::instants_ident(name);

            let uninit = mk_uninit();
            mod_app.push(quote!(
                #uninit
                /// Buffer that holds the instants associated to the inputs of a task
                static mut #instants:
                    [core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] =
                    [#(#elems,)*];
            ));
        }

        let uninit = mk_uninit();
        let inputs_ident = util::inputs_ident(name);
        mod_app.push(quote!(
            #uninit
            /// Buffer that holds the inputs of a task
            static mut #inputs_ident: [core::mem::MaybeUninit<#input_ty>; #cap_lit] =
                [#(#elems,)*];
        ));

        // `${task}Resources`
        let mut needs_lt = false;
        if !task.args.resources.is_empty() {
            let (item, constructor) =
                resources_struct::codegen(Context::SoftwareTask(name), &mut needs_lt, app);

            root.push(item);

            mod_app.push(constructor);
        }

        // `${task}Locals`
        let mut locals_pat = None;
        if !task.locals.is_empty() {
            let (struct_, pat) = locals::codegen(Context::SoftwareTask(name), &task.locals, app);

            locals_pat = Some(pat);
            root.push(struct_);
        }

        let context = &task.context;
        let attrs = &task.attrs;
        let cfgs = &task.cfgs;
        let stmts = &task.stmts;
        let locals_pat = locals_pat.iter();
        user_tasks.push(quote!(
            #(#attrs)*
            #(#cfgs)*
            #[allow(non_snake_case)]
            fn #name(#(#locals_pat,)* #context: #name::Context #(,#inputs)*) {
                use rtic::Mutex as _;

                #(#stmts)*
            }
        ));

        root.push(module::codegen(
            Context::SoftwareTask(name),
            needs_lt,
            app,
            analysis,
            extra,
        ));
    }

    (mod_app, root, user_tasks)
}