.
[phd-thesis.git] / top / imp.tex
1 \documentclass[../thesis.tex]{subfiles}
2
3 \input{subfilepreamble}
4
5 \begin{document}
6 \input{subfileprefix}
7 \chapter{Implementation}%
8 \label{chp:implementation}
9 \begin{chapterabstract}
10 \noindent This chapter shows the implementation of the \gls{MTASK} system by:
11 \begin{itemize}
12 \item shows the implementation of the byte code compiler for \gls{MTASK}'s \gls{TOP} language;
13 \item gives details of the implementation of \gls{MTASK}'s \gls{TOP} engine that executes the \gls{MTASK} tasks on the microcontroller;
14 \item and explains how the integration of \gls{MTASK} tasks and \glspl{SDS} is implemented both on the server and on the device.
15 \end{itemize}
16 \end{chapterabstract}
17
18 Microcontrollers usually have flash-based program memory which wears out fairly quick.
19 For example, the atmega328p in the \gls{ARDUINO} UNO is rated for 10000 write cycles.
20 While this sounds like a lot, if new tasks are sent to the device every minute or so, a lifetime of not even seven days is guaranteed.
21 Hence, for dynamic applications, generating code at run-time for interpretation on the device is necessary.
22 This byte code is then interpreted on MCUs with very little memory and processing power and thus save precious write cycles of the program memory.
23 precious write cycles of the program memory.
24
25 In order to provide the device with the tools to interpret the byte code, it is programmed with a \gls{RTS}, a customisable domain-specific \gls{OS} that takes care of the execution of tasks but also low-level mechanisms such as the communication, multi tasking, and memory management.
26 Once the device is programmed with the \gls{MTASK} \gls{RTS}, it can continuously receive new tasks.
27
28 \subsection{Instruction set}
29 The instruction set is a fairly standard stack machine instruction set extended with special \gls{TOP} instructions.
30 \Cref{lst:instruction_type} shows the \gls{CLEAN} type representing the instruction set of which \cref{tbl:instr_task} gives detailed semantics.
31 Type synonyms are used to provide insight on the arguments of the instructions.
32 One notable instruction is the \cleaninline{MkTask} instruction, it allocates and initialises a task tree node and pushes a pointer to it on the stack.
33
34 \begin{lstClean}[caption={The type housing the instruction set.},label={lst:instruction_type}]
35 :: ArgWidth :== UInt8 :: ReturnWidth :== UInt8
36 :: Depth :== UInt8 :: Num :== UInt8
37 :: SdsId :== UInt8 :: JumpLabel =: JL UInt16
38
39 //** Datatype housing all instructions
40 :: BCInstr
41 //Return instructions
42 //Jumps
43 = BCJumpF JumpLabel | BCJump JumpLabel | BCLabel JumpLabel | BCJumpSR ArgWidth JumpLabel
44 | BCReturn ReturnWidth ArgWidth | BCTailcall ArgWidth ArgWidth JumpLabel
45 //Arguments
46 | BCArgs ArgWidth ArgWidth
47 //Task node creation and refinement
48 | BCMkTask BCTaskType | BCTuneRateMs | BCTuneRateSec
49 //Task value ops
50 | BCIsStable | BCIsUnstable | BCIsNoValue | BCIsValue
51 //Stack ops
52 | BCPush String255 | BCPop Num | BCRot Depth Num | BCDup | BCPushPtrs
53 //Casting
54 | BCItoR | BCItoL | BCRtoI | ...
55 // arith
56 | BCAddI | BCSubI | ...
57 ...
58
59 //** Datatype housing all task types
60 :: BCTaskType
61 = BCStableNode ArgWidth | ArgWidth
62 // Pin io
63 | BCReadD | BCWriteD | BCReadA | BCWriteA | BCPinMode
64 // Interrupts
65 | BCInterrupt
66 // Repeat
67 | BCRepeat
68 // Delay
69 | BCDelay | BCDelayUntil //* Only for internal use
70 // Parallel
71 | BCTAnd | BCTOr
72 //Step
73 | BCStep ArgWidth JumpLabel
74 //Sds ops
75 | BCSdsGet SdsId | BCSdsSet SdsId | BCSdsUpd SdsId JumpLabel
76 // Rate limiter
77 | BCRateLimit
78 ////Peripherals
79 //DHT
80 | BCDHTTemp UInt8 | BCDHTHumid UInt8
81 ...
82 \end{lstClean}
83
84 \subsection{Compiler}
85 The bytecode compiler interpretation for the \gls{MTASK} language is implemented as a monad stack containing a writer monad and a state monad.
86 The writer monad is used to generate code snippets locally without having to store them in the monadic values.
87 The state monad accumulates the code, and stores the stateful data the compiler requires.
88 \Cref{lst:compiler_state} shows the data type for the state, storing:
89 function the compiler currently is in;
90 code of the main expression;
91 context (see \todo{insert ref to compilation rules step here});
92 code for the functions;
93 next fresh label;
94 a list of all the used \glspl{SDS}, either local \glspl{SDS} containing the initial value (\cleaninline{Left}) or lifted \glspl{SDS} (see \cref{sec:liftsds}) containing a reference to the associated \gls{ITASK} \gls{SDS};
95 and finally there is a list of peripherals used.
96
97 \begin{lstClean}[label={lst:compiler_state},caption={\Gls{MTASK}'s byte code compiler type}]
98 :: BCInterpret a :== StateT BCState (WriterT [BCInstr] Identity) a
99 :: BCState =
100 { bcs_infun :: JumpLabel
101 , bcs_mainexpr :: [BCInstr]
102 , bcs_context :: [BCInstr]
103 , bcs_functions :: Map JumpLabel BCFunction
104 , bcs_freshlabel :: JumpLabel
105 , bcs_sdses :: [Either String255 MTLens]
106 , bcs_hardware :: [BCPeripheral]
107 }
108 :: BCFunction =
109 { bcf_instructions :: [BCInstr]
110 , bcf_argwidth :: UInt8
111 , bcf_returnwidth :: UInt8
112 }
113 \end{lstClean}
114
115 Executing the compiler is done by providing an initial state.
116 After compilation, several post-processing steps are applied to make the code suitable for the microprocessor.
117 First, in all tail call \cleaninline{BCReturn}'s are replaced by \cleaninline{BCTailCall} to implement tail call elimination.
118 Furthermore, all byte code is concatenated, resulting in one big program.
119 Many instructions have commonly used arguments so shorthands are introduced to reduce the program size.
120 For example, the \cleaninline{BCArg} instruction is often called with argument \qtyrange{0}{2} and can be replaced by the \cleaninline{BCArg0}--\cleaninline{BCArg2} shorthands.
121 Furthermore, redundant instructions (e.g.\ pop directly after push) are removed as well in order not to burden the code generation with these intricacies.
122 Finally the labels are resolved to represent actual program addresses instead of freshly generated identifiers.
123 After the byte code is ready, the lifted \glspl{SDS} are resolved to provide an initial value for them.
124 The result---byte code, \gls{SDS} specification and perpipheral specifications---are the result of the process, ready to be sent to the device.
125
126 \section{Compilation rules}
127 This section describes the compilation rules, the translation from abstract syntax to byte code.
128 The compilation scheme consists of three schemes\slash{}functions.
129 When something is surrounded by double vertical bars, e.g.\ $\stacksize{a_i}$, it denotes the number of stack cells required to store it.
130
131 Some schemes have a \emph{context} $r$ as an argument which contains information about the location of the arguments in scope.
132 More information is given in the schemes requiring such arguments.
133
134 \newcommand{\cschemeE}[2]{\mathcal{E}\llbracket#1\rrbracket~#2}
135 \newcommand{\cschemeF}[1]{\mathcal{F}\llbracket#1\rrbracket}
136 \newcommand{\cschemeS}[3]{\mathcal{S}\llbracket#1\rrbracket~#2~#3}
137 \begin{table}
138 \centering
139 \begin{tabularx}{\linewidth}{l X}
140 \toprule
141 Scheme & Description\\
142 \midrule
143 $\cschemeE{e}{r}$ & Produces the value of expression $e$ given the context $r$ and pushes it on the stack.
144 The result can be a basic value or a pointer to a task.\\
145 $\cschemeF{e}$ & Generates the bytecode for functions.\\
146 $\cschemeS{e}{r}{w} $ & Generates the function for the step continuation given the context $r$ and the width $w$ of the left-hand side task value.\\
147 \bottomrule
148 \end{tabularx}
149 \end{table}
150
151 \subsection{Expressions}
152 Almost all expression constructions are compiled using $\mathcal{E}$.
153 The argument of $\mathcal{E}$ is the context (see \cref{ssec:functions}).
154 Values are always placed on the stack; tuples and other compound data types are unpacked.
155 Function calls, function arguments and tasks are also compiled using $\mathcal{E}$ but their compilations is explained later.
156
157 \begin{align*}
158 \cschemeE{\text{\cleaninline{lit}}~e}{r} & = \text{\cleaninline{BCPush (bytecode e)}};\\
159 \cschemeE{e_1\mathbin{\text{\cleaninline{+.}}}e_2}{r} & = \cschemeE{e_1}{r};
160 \cschemeE{e_2}{r};
161 \text{\cleaninline{BCAdd}};\\
162 {} & \text{\emph{Similar for other binary operators}}\\
163 \cschemeE{\text{\cleaninline{Not}}~e}{r} & =
164 \cschemeE{e}{r};
165 \text{\cleaninline{BCNot}};\\
166 {} & \text{\emph{Similar for other unary operators}}\\
167 \cschemeE{\text{\cleaninline{If}}~e_1~e_2~e_3}{r} & =
168 \cschemeE{e_1}{r};
169 \text{\cleaninline{BCJmpF}}\enskip l_{else}; \mathbin{\phantom{=}} \cschemeE{e_2}{r}; \text{\cleaninline{BCJmp}}\enskip l_{endif};\\
170 {} & \mathbin{\phantom{=}} \text{\cleaninline{BCLabel}}\enskip l_{else}; \cschemeE{e_3}{r}; \mathbin{\phantom{=}} \text{\cleaninline{BCLabel}}\enskip l_{endif};\\
171 {} & \text{\emph{Where $l_{else}$ and $l_{endif}$ are fresh labels}}\\
172 \cschemeE{\text{\cleaninline{tupl}}~e_1~e_2}{r} & =
173 \cschemeE{e_1}{r};
174 \cschemeE{e_2}{r};\\
175 {} & \text{\emph{Similar for other unboxed compound data types}}\\
176 \cschemeE{\text{\cleaninline{first}}~e}{r} & =
177 \cschemeE{e}{r};
178 \text{\cleaninline{BCPop}}\enskip w;\\
179 {} & \text{\emph{Where $w$ is the width of the left value and}}\\
180 {} & \text{\emph{similar for other unboxed compound data types}}\\
181 \cschemeE{\text{\cleaninline{second}}\enskip e}{r} & =
182 \cschemeE{e}{r};
183 \text{\cleaninline{BCRot}}\enskip w_1\enskip (w_1+w_2);
184 \text{\cleaninline{BCPop}}\enskip w_2;\\
185 {} & \text{\emph{Where $w_1$ is the width of the left and, $w_2$ of the right value}}\\
186 {} & \text{\emph{similar for other unboxed compound data types}}\\
187 \end{align*}
188
189 Translating $\mathcal{E}$ to \gls{CLEAN} code is very straightforward, it basically means executing the monad.
190 Almost always, the type of the interpretation is not used, i.e.\ it is a phantom type.
191 To still have the functions return the correct type, the \cleaninline{tell`}\footnote{\cleaninline{tell` :: [BCInstr] -> BCInterpret a}} helper is used.
192 This function is similar to the writer monad's \cleaninline{tell} function but is casted to the correct type.
193 \Cref{lst:imp_arith} shows the implementation for the arithmetic and conditional expressions.
194 Note that $r$, the context, is not an explicit argument but stored in the state.
195
196 \begin{lstClean}[caption={Interpretation implementation for the arithmetic and conditional classes.},label={lst:imp_arith}]
197 instance expr BCInterpret where
198 lit t = tell` [BCPush (toByteCode{|*|} t)]
199 (+.) a b = a >>| b >>| tell` [BCAdd]
200 ...
201 If c t e = freshlabel >>= \elselabel->freshlabel >>= \endiflabel->
202 c >>| tell` [BCJumpF elselabel] >>|
203 t >>| tell` [BCJump endiflabel,BCLabel elselabel] >>|
204 e >>| tell` [BCLabel endiflabel]
205 \end{lstClean}
206
207 \subsection{Functions}
208 Compiling functions occurs in $\mathcal{F}$, which generates bytecode for the complete program by iterating over the functions and ending with the main expression.
209 When compiling the body of the function, the arguments of the function are added to the context so that the addresses can be determined when referencing arguments.
210 The main expression is a special case of $\mathcal{F}$ since it neither has arguments nor something to continue.
211 Therefore, it is just compiled using $\mathcal{E}$.
212
213 \begin{align*}
214 \cschemeF{main=m} & =
215 \cschemeE{m}{[]};\\
216 \cschemeF{f~a_0 \ldots a_n = b~\text{\cleaninline{In}}~m} & =
217 \text{\cleaninline{BCLabel}}~f; \cschemeE{b}{[\langle f, i\rangle, i\in \{(\Sigma^n_{i=0}\stacksize{a_i})..0\}]};\\
218 {} & \mathbin{\phantom{=}} \text{\cleaninline{BCReturn}}~\stacksize{b}~n; \cschemeF{m};\\
219 \end{align*}
220
221 A function call starts by pushing the stack and frame pointer, and making space for the program counter (\cref{lst:funcall_pushptrs}) followed by evaluating the arguments in reverse order (\cref{lst:funcall_args}).
222 On executing \cleaninline{BCJumpSR}, the program counter is set and the interpreter jumps to the function (\cref{lst:funcall_jumpsr}).
223 When the function returns, the return value overwrites the old pointers and the arguments.
224 This occurs right after a \cleaninline{BCReturn} (\cref{lst:funcall_ret}).
225 Putting the arguments on top of pointers and not reserving space for the return value uses little space and facilitates tail call optimization.
226
227 \begin{figure}
228 \begin{subfigure}{.24\linewidth}
229 \centering
230 \includestandalone{memory1}
231 \caption{\cleaninline{BCPushPtrs}}\label{lst:funcall_pushptrs}
232 \end{subfigure}
233 \begin{subfigure}{.24\linewidth}
234 \centering
235 \includestandalone{memory2}
236 \caption{Arguments}\label{lst:funcall_args}
237 \end{subfigure}
238 \begin{subfigure}{.24\linewidth}
239 \centering
240 \includestandalone{memory3}
241 \caption{\cleaninline{BCJumpSR}}\label{lst:funcall_jumpsr}
242 \end{subfigure}
243 \begin{subfigure}{.24\linewidth}
244 \centering
245 \includestandalone{memory4}
246 \caption{\cleaninline{BCReturn}}\label{lst:funcall_ret}
247 \end{subfigure}
248 \caption{The stack layout during function calls.}%
249 \end{figure}
250
251 Calling a function and referencing function arguments are an extension to $\mathcal{E}$ as shown below.
252 Arguments may be at different places on the stack at different times (see \cref{ssec:step}) and therefore the exact location always has to be determined from the context using \cleaninline{findarg}\footnote{\cleaninline{findarg [l`:r] l = if (l == l`) 0 (1 + findarg r l)}}.
253 Compiling argument $a_{f^i}$, the $i$th argument in function $f$, consists of traversing all positions in the current context.
254 Arguments wider than one stack cell are fetched in reverse to preserve the order.
255
256 \begin{align*}
257 \cschemeE{f(a_0, \ldots, a_n)}{r} & =
258 \text{\cleaninline{BCPushPtrs}}; \cschemeE{a_n}{r}; \cschemeE{a_{\ldots}}{r}; \cschemeE{a_0}{r}; \text{\cleaninline{BCJumpSR}}~n~f;\\
259 \cschemeE{a_{f^i}}{r} & =
260 \text{\cleaninline{BCArg}~findarg}(r, f, i)~\text{for all}~i\in\{w\ldots v\};\\
261 {} & v = \Sigma^{i-1}_{j=0}\stacksize{a_{f^j}}~\text{ and }~ w = v + \stacksize{a_{f^i}}\\
262 \end{align*}
263
264 Translating the compilation schemes for functions to Clean is not as straightforward as other schemes due to the nature of shallow embedding.\todo{deze \P{} moet ge\-\"up\-da\-ted worden}
265 The \cleaninline{fun} class has a single function with a single argument.
266 This argument is a Clean function that---when given a callable Clean function representing the mTask function---will produce \cleaninline{main} and a callable function.
267 To compile this, the argument must be called with a function representing a function call in mTask.
268 \Cref{lst:fun_imp} shows the implementation for this as Clean code.
269 To uniquely identify the function, a fresh label is generated.
270 The function is then called with the \cleaninline{callFunction} helper function that generates the instructions that correspond to calling the function.
271 That is, it pushes the pointers, compiles the arguments, and writes the \cleaninline{JumpSR} instruction.
272 The resulting structure (\cleaninline{g In m}) contains a function representing the mTask function (\cleaninline{g}) and the \cleaninline{main} structure to continue with.
273 To get the actual function, \cleaninline{g} must be called with representations for the argument, i.e.\ using \cleaninline{findarg} for all arguments.
274 The arguments are added to the context and \cleaninline{liftFunction} is called with the label, the argument width and the compiler.
275 This function executes the compiler, decorates the instructions with a label and places them in the function dictionary together with the metadata such as the argument width.
276 After lifting the function, the context is cleared again and compilation continues with the rest of the program.
277
278 \begin{lstClean}[label={lst:fun_imp},caption={The backend implementation for functions.}]
279 instance fun (BCInterpret a) BCInterpret | type a where
280 fun def = {main=freshlabel >>= \funlabel->
281 let (g In m) = def \a->callFunction funlabel (toByteWidth a) [a]
282 argwidth = toByteWidth (argOf g)
283 in addToCtx funlabel zero argwidth
284 >>| infun funlabel
285 (liftFunction funlabel argwidth
286 (g (retrieveArgs funlabel zero argwidth)
287 ) ?None)
288 >>| clearCtx >>| m.main
289 }
290
291 argOf :: ((m a) -> b) a -> UInt8 | toByteWidth a
292 callFunction :: JumpLabel UInt8 [BCInterpret b] -> BCInterpret c | ...
293 liftFunction :: JumpLabel UInt8 (BCInterpret a) (?UInt8) -> BCInterpret ()
294 \end{lstClean}
295
296 \subsection{Tasks}
297 Task trees are created with the \cleaninline{BCMkTask} instruction that allocates a node and pushes it to the stack.
298 It pops arguments from the stack according to the given task type.
299 The following extension of $\mathcal{E}$ shows this compilation scheme (except for the step combinator, explained in \cref{ssec:step}).
300
301 \begin{align*}
302 \cschemeE{\text{\cleaninline{rtrn}}~e}{r} & =
303 \cschemeE{e}{r};
304 \text{\cleaninline{BCMkTask BCStable}}_{\stacksize{e}};\\
305 \cschemeE{\text{\cleaninline{unstable}}~e}{r} & =
306 \cschemeE{e}{r};
307 \text{\cleaninline{BCMkTask BCUnstable}}_{\stacksize{e}};\\
308 \cschemeE{\text{\cleaninline{readA}}~e}{r} & =
309 \cschemeE{e}{r};
310 \text{\cleaninline{BCMkTask BCReadA}};\\
311 \cschemeE{\text{\cleaninline{writeA}}~e_1~e_2}{r} & =
312 \cschemeE{e_1}{r};
313 \cschemeE{e_2}{r};
314 \text{\cleaninline{BCMkTask BCWriteA}};\\
315 \cschemeE{\text{\cleaninline{readD}}~e}{r} & =
316 \cschemeE{e}{r};
317 \text{\cleaninline{BCMkTask BCReadD}};\\
318 \cschemeE{\text{\cleaninline{writeD}}~e_1~e_2}{r} & =
319 \cschemeE{e_1}{r};
320 \cschemeE{e_2}{r};
321 \text{\cleaninline{BCMkTask BCWriteD}};\\
322 \cschemeE{\text{\cleaninline{delay}}~e}{r} & =
323 \cschemeE{e}{r};
324 \text{\cleaninline{BCMkTask BCDelay}};\\
325 \cschemeE{\text{\cleaninline{rpeat}}~e}{r} & =
326 \cschemeE{e}{r};
327 \text{\cleaninline{BCMkTask BCRepeat}};\\
328 \cschemeE{e_1\text{\cleaninline{.\|\|.}}e_2}{r} & =
329 \cschemeE{e_1}{r};
330 \cschemeE{e_2}{r};
331 \text{\cleaninline{BCMkTask BCOr}};\\
332 \cschemeE{e_1\text{\cleaninline{.&&.}}e_2}{r} & =
333 \cschemeE{e_1}{r};
334 \cschemeE{e_2}{r};
335 \text{\cleaninline{BCMkTask BCAnd}};\\
336 \end{align*}
337
338 This simply translates to Clean code by writing the correct \cleaninline{BCMkTask} instruction as exemplified in \cref{lst:imp_ret}.
339
340 \begin{lstClean}[caption={The backend implementation for \cleaninline{rtrn}.},label={lst:imp_ret}]
341 instance rtrn BCInterpret
342 where
343 rtrn m = m >>| tell` [BCMkTask (bcstable m)]
344 \end{lstClean}
345
346 \subsection{Step combinator}\label{ssec:step}
347 The \cleaninline{step} construct is a special type of task because the task value of the left-hand side may change over time.
348 Therefore, the continuation tasks on the right-hand side are \emph{observing} this task value and acting upon it.
349 In the compilation scheme, all continuations are first converted to a single function that has two arguments: the stability of the task and its value.
350 This function either returns a pointer to a task tree or fails (denoted by $\bot$).
351 It is special because in the generated function, the task value of a task can actually be inspected.
352 Furthermore, it is a lazy node in the task tree: the right-hand side may yield a new task tree after several rewrite steps (i.e.\ it is allowed to create infinite task trees using step combinators).
353 The function is generated using the $\mathcal{S}$ scheme that requires two arguments: the context $r$ and the width of the left-hand side so that it can determine the position of the stability which is added as an argument to the function.
354 The resulting function is basically a list of if-then-else constructions to check all predicates one by one.
355 Some optimization is possible here but has currently not been implemented.
356
357 \begin{align*}
358 \cschemeE{t_1\text{\cleaninline{>>*.}}t_2}{r} & =
359 \cschemeE{a_{f^i}}{r}, \langle f, i\rangle\in r;
360 \text{\cleaninline{BCMkTask}}~\text{\cleaninline{BCStable}}_{\stacksize{r}}; \cschemeE{t_1}{r};\\
361 {} & \mathbin{\phantom{=}} \text{\cleaninline{BCMkTask}}~\text{\cleaninline{BCAnd}}; \text{\cleaninline{BCMkTask}}~(\text{\cleaninline{BCStep}}~(\cschemeS{t_2}{(r + [\langle l_s, i\rangle])}{\stacksize{t_1}}));\\
362 \end{align*}
363
364 \begin{align*}
365 \cschemeS{[]}{r}{w} & =
366 \text{\cleaninline{BCPush}}~\bot;\\
367 \cschemeS{\text{\cleaninline{IfValue}}~f~t:cs}{r}{w} & =
368 \text{\cleaninline{BCArg}} (\stacksize{r} + w);
369 \text{\cleaninline{BCIsNoValue}};\\
370 {} & \mathbin{\phantom{=}} \cschemeE{f}{r};
371 \text{\cleaninline{BCAnd}};\\
372 {} & \mathbin{\phantom{=}} \text{\cleaninline{BCJmpF}}~l_1;\\
373 {} & \mathbin{\phantom{=}} \cschemeE{t}{r};
374 \text{\cleaninline{BCJmp}}~l_2;\\
375 {} & \mathbin{\phantom{=}} \text{\cleaninline{BCLabel}}~l_1;
376 \cschemeS{cs}{r}{w};\\
377 {} & \mathbin{\phantom{=}} \text{\cleaninline{BCLabel}}~l_2;\\
378 {} & \text{\emph{Where $l_1$ and $l_2$ are fresh labels}}\\
379 {} & \text{\emph{Similar for \cleaninline{IfStable} and \cleaninline{IfUnstable}}}\\
380 \end{align*}
381
382 First the context is evaluated.
383 The context contains arguments from functions and steps that need to be preserved after rewriting.
384 The evaluated context is combined with the left-hand side task value by means of a \cleaninline{.&&.} combinator to store it in the task tree so that it is available after a rewrite.
385 This means that the task tree is be transformed as follows:
386
387 \begin{lstClean}
388 t1 >>= \v1->t2 >>= \v2->t3 >>= ...
389 //is transformed to
390 t1 >>= \v1->rtrn v1 .&&. t2 >>= \v2->rtrn (v1, v2) .&&. t3 >>= ...
391 \end{lstClean}
392
393 The translation to \gls{CLEAN} is given in \cref{lst:imp_seq}.
394
395 \begin{lstClean}[caption={Backend implementation for the step class.},label={lst:imp_seq}]
396 instance step BCInterpret where
397 (>>*.) lhs cont
398 //Fetch a fresh label and fetch the context
399 = freshlabel >>= \funlab->gets (\s->s.bcs_context)
400 //Generate code for lhs
401 >>= \ctx->lhs
402 //Possibly add the context
403 >>| tell` (if (ctx =: []) []
404 //The context is just the arguments up till now in reverse
405 ( [BCArg (UInt8 i)\\i<-reverse (indexList ctx)]
406 ++ map BCMkTask (bcstable (UInt8 (length ctx)))
407 ++ [BCMkTask BCTAnd]
408 ))
409 //Increase the context
410 >>| addToCtx funlab zero lhswidth
411 //Lift the step function
412 >>| liftFunction funlab
413 //Width of the arguments is the width of the lhs plus the
414 //stability plus the context
415 (one + lhswidth + (UInt8 (length ctx)))
416 //Body label ctx width continuations
417 (contfun funlab (UInt8 (length ctx)))
418 //Return width (always 1, a task pointer)
419 (Just one)
420 >>| modify (\s->{s & bcs_context=ctx})
421 >>| tell` [BCMkTask (instr rhswidth funlab)]
422
423 toContFun :: JumpLabel UInt8 -> BCInterpret a
424 toContFun steplabel contextwidth
425 = foldr tcf (tell` [BCPush fail]) cont
426 where
427 tcf (IfStable f t)
428 = If ((stability >>| tell` [BCIsStable]) &. f val)
429 (t val >>| tell` [])
430 ...
431 stability = tell` [BCArg (lhswidth + contextwidth)]
432 val = retrieveArgs steplabel zero lhswidth
433 \end{lstClean}
434
435 \subsection{\texorpdfstring{\Glspl{SDS}}{Shared data sources}}
436 The compilation scheme for \gls{SDS} definitions is a trivial extension to $\mathcal{F}$ since there is no code generated as seen below.
437
438 \begin{align*}
439 \cschemeF{\text{\cleaninline{sds}}~x=i~\text{\cleaninline{In}}~m} & =
440 \cschemeF{m};\\
441 \end{align*}
442
443 The \gls{SDS} access tasks have a compilation scheme similar to other tasks (see \cref{ssec:scheme_tasks}).
444 The \cleaninline{getSds} task just pushes a task tree node with the \gls{SDS} identifier embedded.
445 The \cleaninline{setSds} task evaluates the value, lifts that value to a task tree node and creates an \gls{SDS} set node.
446
447 \begin{align*}
448 \cschemeE{\text{\cleaninline{getSds}}~s}{r} & =
449 \text{\cleaninline{BCMkTask}} (\text{\cleaninline{BCSdsGet}} s);\\
450 \cschemeE{\text{\cleaninline{setSds}}~s~e}{r} & =
451 \cschemeE{e}{r};
452 \text{\cleaninline{BCMkTask BCStable}}_{\stacksize{e}};\\
453 {} & \mathbin{\phantom{=}} \text{\cleaninline{BCMkTask}} (\text{\cleaninline{BCSdsSet}} s);\\
454 \end{align*}
455
456 While there is no code generated in the definition, the byte code compiler is storing the \gls{SDS} data in the \cleaninline{bcs_sdses} field in the compilation state.
457 The \glspl{SDS} are typed as functions in the host language so an argument for this function must be created that represents the \gls{SDS} on evaluation.
458 For this, an \cleaninline{BCInterpret} is created that emits this identifier.
459 When passing it to the function, the initial value of the \gls{SDS} is returned.
460 This initial value is stored as a byte code encoded value in the state and the compiler continues with the rest of the program.
461
462 Compiling \cleaninline{getSds} is a matter of executing the \cleaninline{BCInterpret} representing the \gls{SDS}, which yields the identifier that can be embedded in the instruction.
463 Setting the \gls{SDS} is similar: the identifier is retrieved and the value is written to put in a task tree so that the resulting task can remember the value it has written.
464 Lifted SDSs are compiled in a very similar way.\todo{deze \P{} moet naar integration?}
465 The only difference is that there is no initial value but an iTasks SDS when executing the Clean function.
466 A lens on this SDS converting \cleaninline{a} from the \cleaninline{Shared a} to a \cleaninline{String255}---a bytecode encoded version---is stored in the state.
467 The encoding and decoding itself is unsafe when used directly but the type system of the language and the abstractions make it safe.
468 Upon sending the mTask task to the device, the initial values of the lifted SDSs are fetched to complete the SDS specification.
469
470 % VimTeX: SynIgnore on
471 \begin{lstClean}[caption={Backend implementation for the SDS classes.},label={lst:comp_sds}]
472 :: Sds a = Sds Int
473 instance sds BCInterpret where
474 sds def = {main = freshsds >>= \sdsi->
475 let sds = modify (\s->{s & bcs_sdses=put sdsi
476 (Left (toByteCode t)) s.bcs_sdses})
477 >>| pure (Sds sdsi)
478 (t In e) = def sds
479 in e.main}
480 getSds f = f >>= \(Sds i)-> tell` [BCMkTask (BCSdsGet (fromInt i))]
481 setSds f v = f >>= \(Sds i)->v >>| tell`
482 ( map BCMkTask (bcstable (byteWidth v))
483 ++ [BCMkTask (BCSdsSet (fromInt i))])
484 \end{lstClean}
485 % VimTeX: SynIgnore off
486
487 \section{\texorpdfstring{\Glsxtrlong{RTS}}{Run time system}}
488
489 The \gls{RTS} is designed to run on systems with as little as \qty{2}{\kibi\byte} of \gls{RAM}.
490 Aggressive memory management is therefore vital.
491 Not all firmwares for microprocessors support heaps and---when they do---allocation often leaves holes when not used in a \emph{last in first} out strategy.
492 Therefore the \gls{RTS} uses a chunk of memory in the global data segment with its own memory manager tailored to the needs of \gls{MTASK}.
493 The size of this block can be changed in the configuration of the \gls{RTS} if necessary.
494 On an \gls{ARDUINO} UNO ---equipped with \qty{2}{\kibi\byte} of \gls{RAM}--- this size is about \qty{1500}{\byte}.
495
496 In memory, task data grows from the bottom up and an interpreter stack is located directly on top of it growing in the same direction.
497 As a consequence, the stack moves when a new task is received.
498 This never happens within execution because communication is always processed before execution.
499 Values in the interpreter are always stored on the stack, even tuples.
500 Task trees grow from the top down as in a heap.
501 This approach allows for flexible ratios, i.e.\ many tasks and small trees or few tasks and big trees.
502
503 The event loop of the \gls{RTS} is executed repeatedly and consists of three distinct phases.
504 \todo{plaa\-tje van me\-mo\-ry hier}
505 \todo{pseu\-do\-code hier van de ex\-e\-cu\-tie}
506
507 %TODO evt subsubsections verwijderen
508 \subsection{Communication}
509 In the first phase, the communication channels are processed.
510 The messages announcing \gls{SDS} updates are applied immediately, the initialization of new tasks is delayed until the next phase.
511
512 \subsection{Execution}
513 The second phase consists of executing tasks.
514 The \gls{RTS} executes all tasks in a round robin fashion.
515 If a task is not initialized yet, the bytecode of the main function is interpreted to produce the initial task tree.
516 The rewriting engine uses the interpreter when needed, e.g.\ to calculate the step continuations.
517 The rewriter and the interpreter use the same stack to store intermediate values.
518 Rewriting steps are small so that interleaving results in seemingly parallel execution.
519 In this phase new task tree nodes may be allocated.
520 Both rewriting and initialization are atomic operations in the sense that no processing on SDSs is done other than SDS operations from the task itself.
521 The host is notified if a task value is changed after a rewrite step.
522
523 \subsection{Memory management}
524 The third and final phase is memory management.
525 Stable tasks, and unreachable task tree nodes are removed.
526 If a task is to be removed, tasks with higher memory addresses are moved down.
527 For task trees---stored in the heap---the \gls{RTS} already marks tasks and task trees as trash during rewriting so the heap can be compacted in a single pass.
528 This is possible because there is no sharing or cycles in task trees and nodes contain pointers pointers to their parent.
529
530 \input{subfilepostamble}
531 \end{document}