\section{Large expression trees}
Expressions of the form $((a \star b) \star (c \star d)) \star ((e \star f) \star (g \star h))$ that cannot be rearranged to shallower trees (especially if $a..g$ have side effects that influence each other) require much temporary storage. This may be solved in one of three ways:
\begin{enumerate}
    \item always push a sub-expression to the stack and pop sub-expressions when the main expression can reduce them (stack machine)
    \item increase the number of registers to fit all sub-expression results
    \item treat expressions as functions so the implementation of function calls will store the sub-expressions
\end{enumerate}
A combination of solutions is also possible. For example, store sub-expression results in registers until all registers are in use, then start pushing results to the stack. Or alternatively, always push sub-expression results to the stack and remove redundant push-pop instructions during an optimization pass.

\paragraph{Solution}
The second solution is chosen for its simplicity (it cannot interfere with stack allocations), performance (no extra instructions for pushing and popping) and the fact that the ILOC VM supports an infinite number of registers.



\section{Function calls within parameters}
Each function call requires an Activation Record (\emph{AR} or stack frame). A straightforward way of allocating new ARs is allocation at a static offset relative to the current AR. This may lead to problems with function calls of the form \verb|f(g(h))| or \verb|f(a, g(h))|: the AR of \verb|f| must not be overwritten during the call to \verb|g|.\\
There are multiple solutions to this problem:
\begin{enumerate}
    \item dynamically allocating the AR: this requires a dynamic allocator
    \item generating different offsets for function calls within the parameter of a function call: e.g. the AR of \verb|h| in \verb|f(g(h()))| must be allocated at \verb|ARP+f_AR+g_AR|
    \item allocating the AR \emph{after} evaluating its parameters: this requires a temporary storage for the parameters
\end{enumerate}

\paragraph{Solution}
Initially, the third option was chosen for its ease of implementation. This was implemented by first pushing each parameter value to the stack after evaluation. Then the AR for the outer function call was allocated and the parameter values were popped into the AR.\\

Later, with the implementation of a dynamic allocator, the first option was chosen. The AR is now dynamically allocated and a reference to the AR is saved in a (reserved) register, with each parameter being stored in the AR immediately after evaluating.



\section{Register saves}
When calling a function, some registers must be saved in order to not overwrite and lose their value when executing the function. This can be done either by the calling function (\emph{caller-saves}) or the called function (\emph{callee-saves}).

\paragraph{Solution}
The choice was made to use caller-saves. Moreover, the generator keeps track of registers that are in use in order to keep the number of saved registers to a minimum.\\

Registers are saved by pushing them on the stack before a call and popping them from the stack after a call. There is no dedicated register save area in the AR.



\section{Closures (for function passing)}
\label{problem-closures}
Fully correct function passing (i.e. closures) presents three problems:
\begin{enumerate}
    \item keeping track of functions being passed around: the context of a function must be preserved, i.e. the parent ARs, in order to access non-local variables.
    \item keeping track of active ARs: an AR must only be deallocated when no function can access it anymore.
    \item allocating ARs: since an AR may remain allocated after the function call has ended, ARs cannot be simply pushed to and popped from the stack.
\end{enumerate}

One way to keep track of the parent AR of a function, is to add an extra field for function values, turning function types into a tuple \verb|(call address,parent AR)| reference instead of a direct call address value.\\

One way to decide which ARs must be kept and which can be deallocated, is to have a reference count (\emph{RC}) for each AR. When a function reference is used, all its parent ARs have their RC incremented. When a function reference is discarded, all its parent ARs have their RC decremented.\\
Another way using RCs is to only change the RC of the direct parent AR. When a function reference is used, the direct parent's AR has its RC incremented. When a function reference is discarded, the parent AR has its RC decremented. However, if the parent AR is freed in the process, it must decrement the RC of \emph{its} parent and so on. See \cref{future-work} for more discussion about deallocating reference types.\\

\paragraph{Solutions}
To address the dynamic allocation, a simple allocator with reference counting was made, see \nameref{memlib}.\\
Function data becomes a tuple \verb|(call address, parent AR)|. This may be extended in the future to \verb|(call address, AR)| for continuations.\\
Keeping track of active ARs is solved by incrementing and decrementing the whole chain of parent ARs. This solution was chosen because of its simplicity.


\section{Local data size for reassigned functions}
\label{problems-reassigned-closures}
Neither the type, scope nor entry point of a function specify the local data size of a function. This poses a problem when a function call is made to an assigned/reassigned function.
This can be solved in two ways:
\begin{enumerate}
    \item resize the AR during the prologue of a call or allocate another block of memory for local data
    \item store the required size of the AR in the function reference
\end{enumerate}

\paragraph{Solution}
The second solution was chosen because it was easiest to implement and most perfomant. Resizing the AR would require a reallocate function in ILOC, while putting local data in another memory block would require an extra level of indirection whenever a local variable is used.

