aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen/spawn.rs
blob: 6cad809b5b1de16d0229b435bd5ae7fd2df70e9e (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 std::collections::HashSet;

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use rtic_syntax::ast::App;

use crate::{
    analyze::Analysis,
    check::Extra,
    codegen::{spawn_body, util},
};

/// Generates all `${ctxt}::Spawn` methods
pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
    let mut items = vec![];

    let mut seen = HashSet::<_>::new();
    for (spawner, spawnees) in app.spawn_callers() {
        let mut methods = vec![];

        for name in spawnees {
            let spawnee = &app.software_tasks[name];
            //let receiver = spawnee.args.core;
            let cfgs = &spawnee.cfgs;
            let (args, _, untupled, ty) = util::regroup_inputs(&spawnee.inputs);
            let args = &args;

            if spawner.is_init() {
                // `init` uses a special spawn implementation; it doesn't use the `spawn_${name}`
                // functions which are shared by other contexts

                let body = spawn_body::codegen(spawner, &name, app, analysis, extra);

                let let_instant = if app.uses_schedule() {
                    let m = extra.monotonic();

                    Some(quote!(let instant = unsafe { <#m as rtic::Monotonic>::zero() };))
                } else {
                    None
                };

                let section = util::link_section("text");
                methods.push(quote!(
                    #(#cfgs)*
                    #section
                    fn #name(&self #(,#args)*) -> Result<(), #ty> {
                        #let_instant
                        #body
                    }
                ));
            } else {
                let spawn = util::spawn_ident(name);

                if !seen.contains(name) {
                    // generate a `spawn_${name}_S${sender}` function
                    seen.insert(name);

                    let instant = if app.uses_schedule() {
                        let m = extra.monotonic();

                        Some(quote!(, instant: <#m as rtic::Monotonic>::Instant))
                    } else {
                        None
                    };

                    let body = spawn_body::codegen(spawner, &name, app, analysis, extra);

                    let section = util::link_section("text");
                    items.push(quote!(
                        //#cfg_sender
                        #(#cfgs)*
                        #section
                        unsafe fn #spawn(
                            priority: &rtic::export::Priority
                            #instant
                            #(,#args)*
                        ) -> Result<(), #ty> {
                            #body
                        }
                    ));
                }

                let (let_instant, instant) = if app.uses_schedule() {
                    let m = extra.monotonic();

                    (
                        Some(if spawner.is_idle() {
                            quote!(let instant = <#m as rtic::Monotonic>::now();)
                        } else {
                            quote!(let instant = self.instant();)
                        }),
                        Some(quote!(, instant)),
                    )
                } else {
                    (None, None)
                };

                methods.push(quote!(
                    #(#cfgs)*
                    #[inline(always)]
                    fn #name(&self #(,#args)*) -> Result<(), #ty> {
                        unsafe {
                            #let_instant
                            #spawn(self.priority() #instant #(,#untupled)*)
                        }
                    }
                ));
            }
        }

        let lt = if spawner.is_init() {
            None
        } else {
            Some(quote!('a))
        };

        let spawner = spawner.ident(app);
        debug_assert!(!methods.is_empty());
        items.push(quote!(
            //#cfg_sender
            impl<#lt> #spawner::Spawn<#lt> {
                #(#methods)*
            }
        ));
    }

    items
}