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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
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_);
}
if task.is_async {
// generate preamble for async task
let context = &task.context;
let attrs = &task.attrs;
let cfgs = &task.cfgs;
let stmts = &task.stmts;
let locals_pat = locals_pat.iter();
let locals_pat2 = locals_pat.clone();
// TODO, should we require the user to give <'static>
// (not feasible today, as RTIC lends out a non static lifetime of cx)
user_tasks.push(quote!(
#(#attrs)*
#(#cfgs)*
#[allow(non_snake_case)]
fn #name(#(#locals_pat,)* #context: #name::Context #(,#inputs)*) {
type F = impl Future + 'static;
fn create(cx: #name::Context<'static>) -> F {
task(cx)
}
static mut TASK: Task<F> = Task::new();
unsafe {
match TASK {
Task::Idle | Task::Done(_) => {
// TODO, soundness - reject tasks with &mut T resources?
TASK.spawn(|| create(mem::transmute(cx)));
}
_ => {}
};
TASK.poll(|| {
// TODO, should we panic here on error?
let _ = foo::spawn();
});
match TASK {
Task::Done(ref _r) => {
// TODO, how to deal with return value?
}
_ => {}
}
}
#(#attrs)*
#(#cfgs)*
#[allow(non_snake_case)]
// TODO, should we require the user to give <'static>
// (now we auto generate it)
async fn task(#(#locals_pat2,)* #context: #name::Context<'static> #(,#inputs)*) {
use rtic::Mutex as _;
#(#stmts)*
}
}
))
} else {
// emit task with attributes if not extern
if !&task.is_extern {
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)
}
|