source: doc/theses/andrew_beach_MMath/implement.tex @ 67a1c67

ADTast-experimentalpthread-emulation
Last change on this file since 67a1c67 was 814f87d, checked in by Andrew Beach <ajbeach@…>, 3 years ago

Andrew MMath: Updated thesis with Yizhou Zhang's feedback.

  • Property mode set to 100644
File size: 41.8 KB
RevLine 
[26ca815]1\chapter{Implementation}
[553f8abe]2\label{c:implement}
[26ca815]3
[d02e547]4% Local Helpers:
5\newcommand\transformline[1][becomes...]{
6  \hrulefill#1\hrulefill
7  \medskip
8}
9
[5a4f1a8]10The implementation work for this thesis covers the two components: virtual
[7eb6eb5]11system and exceptions. Each component is discussed in detail.
12
[26ca815]13\section{Virtual System}
[7eb6eb5]14\label{s:VirtualSystem}
[26ca815]15% Virtual table rules. Virtual tables, the pointer to them and the cast.
[9cdfa5fb]16While the \CFA virtual system currently has only two public features, virtual
[e8bad5c8]17cast and virtual tables,
18substantial structure is required to support them,
[df24d37]19and provide features for exception handling and the standard library.
[7eb6eb5]20
[830299f]21\subsection{Virtual Type}
[ba2e8f0]22A virtual type~(see \autoref{s:virtuals}) has a pointer to a virtual table,
23called the \emph{virtual-table pointer},
24which binds each instance of a virtual type to a virtual table.
25Internally, the field is called \snake{virtual_table}
26and is fixed after construction.
27This pointer is also the table's id and how the system accesses the
28virtual table and the virtual members there.
29It is always the first field in the
[7372065]30structure so that its location is always known.
[9d7e5cb]31
[ba2e8f0]32% We have no special rules for these constructors.
33Virtual table pointers are passed to the constructors of virtual types
34as part of field-by-field construction.
[e56eabac]35
[9cdfa5fb]36\subsection{Type ID}
37Every virtual type has a unique ID.
[ba2e8f0]38These are used in type equality, to check if the representation of two values
39are the same, and to access the type's type information.
40This uniqueness means across a program composed of multiple translation
41units (TU), not uniqueness across all programs or even across multiple
42processes on the same machine.
43
44Our approach for program uniqueness is using a static declaration for each
[9cdfa5fb]45type ID, where the run-time storage address of that variable is guaranteed to
[ba2e8f0]46be unique during program execution.
[9cdfa5fb]47The type ID storage can also be used for other purposes,
[ba2e8f0]48and is used for type information.
49
[9cdfa5fb]50The problem is that a type ID may appear in multiple TUs that compose a
51program (see \autoref{ss:VirtualTable}), so the initial solution would seem
[6aa84e0]52to be make it external in each translation unit. However, the type ID must
[ba2e8f0]53have a declaration in (exactly) one of the TUs to create the storage.
54No other declaration related to the virtual type has this property, so doing
55this through standard C declarations would require the user to do it manually.
56
[9cdfa5fb]57Instead, the linker is used to handle this problem.
[ba2e8f0]58% I did not base anything off of C++17; they are solving the same problem.
59A new feature has been added to \CFA for this purpose, the special attribute
60\snake{cfa_linkonce}, which uses the special section @.gnu.linkonce@.
[9cdfa5fb]61When used as a prefix (\eg @.gnu.linkonce.example@), the linker does
[ba2e8f0]62not combine these sections, but instead discards all but one with the same
63full name.
64
[9cdfa5fb]65So, each type ID must be given a unique section name with the \snake{linkonce}
66prefix. Luckily, \CFA already has a way to get unique names, the name mangler.
[ba2e8f0]67For example, this could be written directly in \CFA:
68\begin{cfa}
69__attribute__((cfa_linkonce)) void f() {}
70\end{cfa}
71This is translated to:
72\begin{cfa}
73__attribute__((section(".gnu.linkonce._X1fFv___1"))) void _X1fFv___1() {}
74\end{cfa}
[9cdfa5fb]75This is done internally to access the name mangler.
[ba2e8f0]76This attribute is useful for other purposes, any other place a unique
77instance required, and should eventually be made part of a public and
78stable feature in \CFA.
79
80\subsection{Type Information}
81
[9cdfa5fb]82There is data stored at the type ID's declaration, the type information.
83The type information currently is only the parent's type ID or, if the
[5a4f1a8]84type has no parent, the null pointer.
[9cdfa5fb]85The ancestors of a virtual type are found by traversing type IDs through
[5a4f1a8]86the type information.
[ba2e8f0]87An example using helper macros looks like:
88\begin{cfa}
89struct INFO_TYPE(TYPE) {
90        INFO_TYPE(PARENT) const * parent;
91};
92
93__attribute__((cfa_linkonce))
94INFO_TYPE(TYPE) const INFO_NAME(TYPE) = {
95        &INFO_NAME(PARENT),
96};
97\end{cfa}
[7372065]98
99Type information is constructed as follows:
[cd03b76d]100\begin{enumerate}[nosep]
[5a4f1a8]101\item
[ba2e8f0]102Use the type's name to generate a name for the type information structure,
103which is saved so it can be reused.
[5a4f1a8]104\item
105Generate a new structure definition to store the type
[9cdfa5fb]106information. The layout is the same in each case, just the parent's type ID,
[5a4f1a8]107but the types used change from instance to instance.
[ba2e8f0]108The generated name is used for both this structure and, if relevant, the
[5a4f1a8]109parent pointer.
[b51e389c]110If the virtual type is polymorphic then the type information structure is
[9d7e5cb]111polymorphic as well, with the same polymorphic arguments.
[5a4f1a8]112\item
[ba2e8f0]113A separate name for instances is generated from the type's name.
[5a4f1a8]114\item
[ba2e8f0]115The definition is generated and initialized.
[9cdfa5fb]116The parent ID is set to the null pointer or to the address of the parent's
[5a4f1a8]117type information instance. Name resolution handles the rest.
118\item
119\CFA's name mangler does its regular name mangling encoding the type of
[ba2e8f0]120the declaration into the instance name.
121This process gives a completely unique name
[5a4f1a8]122including different instances of the same polymorphic type.
123\end{enumerate}
[b51e389c]124
[7372065]125Writing that code manually, with helper macros for the early name mangling,
126would look like this:
127\begin{cfa}
128struct INFO_TYPE(TYPE) {
129        INFO_TYPE(PARENT) const * parent;
130};
131
132__attribute__((cfa_linkonce))
133INFO_TYPE(TYPE) const INFO_NAME(TYPE) = {
134        &INFO_NAME(PARENT),
135};
136\end{cfa}
[830299f]137
[ba2e8f0]138\begin{comment}
[5a4f1a8]139\subsubsection{\lstinline{cfa\_linkonce} Attribute}
[ba2e8f0]140% I just realized: This is an extension of the inline keyword.
[5a4f1a8]141% An extension of C's at least, it is very similar to C++'s.
[c21f5a9]142Another feature added to \CFA is a new attribute: \texttt{cfa\_linkonce}.
[5a4f1a8]143This attribute is attached to an object or function definition
144(any global declaration with a name and a type)
145allowing it to be defined multiple times.
146All matching definitions mush have the link-once attribute
147and their implementations should be identical as well.
148
149A single definition with the attribute can be included in a header
150file as if it was a forward declaration, except no definition is required.
151
[9cdfa5fb]152This technique is used for type ID instances. A link-once definition is
[5a4f1a8]153generated each time the structure is seen. This will result in multiple
154copies but the link-once attribute ensures all but one are removed for a
155unique instance.
156
157Internally, @cfa_linkonce@ is replaced with
[c21f5a9]158@section(".gnu.linkonce.NAME")@ where \texttt{NAME} is replaced by the
159mangled name of the object.
[5a4f1a8]160Any other @section@ attributes are removed from the declaration.
[c21f5a9]161The prefix \texttt{.gnu.linkonce} in section names is recognized by the
[5a4f1a8]162linker. If two of these sections appear with the same name, including
163everything that comes after the special prefix, then only one is used
164and the other is discarded.
[ba2e8f0]165\end{comment}
[c21f5a9]166
[7eb6eb5]167\subsection{Virtual Table}
[5a4f1a8]168\label{ss:VirtualTable}
[9cdfa5fb]169Each virtual type has a virtual table type that stores its type ID and
[9d7e5cb]170virtual members.
[6aa84e0]171An instance of a virtual type is bound to a virtual table instance,
172which have the values of the virtual members.
173Both the layout of the fields (in the virtual table type)
174and their value (in the virtual table instance) are decided by the rules given
[9d7e5cb]175below.
176
[cd03b76d]177The layout always comes in three parts (see \autoref{f:VirtualTableLayout}).
[9cdfa5fb]178The first section is just the type ID at the head of the table. It is always
[5a4f1a8]179there to ensure that it can be found even when the accessing code does not
180know which virtual type it has.
[9cdfa5fb]181The second section is all the virtual members of the parent, in the same
[9d7e5cb]182order as they appear in the parent's virtual table. Note that the type may
[0a55a53]183change slightly as references to the ``this" change. This is limited to
[9d7e5cb]184inside pointers/references and via function pointers so that the size (and
185hence the offsets) are the same.
186The third section is similar to the second except that it is the new virtual
187members introduced at this level in the hierarchy.
188
189\begin{figure}
[cd03b76d]190\begin{center}
[9b0bb79]191\input{vtable-layout}
[cd03b76d]192\end{center}
[9d7e5cb]193\caption{Virtual Table Layout}
194\label{f:VirtualTableLayout}
195\end{figure}
196
197The first and second sections together mean that every virtual table has a
198prefix that has the same layout and types as its parent virtual table.
[7372065]199This, combined with the fixed offset to the virtual table pointer, means that
[5a4f1a8]200for any virtual type, it is always safe to access its virtual table and,
[9cdfa5fb]201from there, it is safe to check the type ID to identify the exact type of the
[b51e389c]202underlying object, access any of the virtual members and pass the object to
[9d7e5cb]203any of the method-like virtual members.
204
[5a4f1a8]205When a virtual table is declared, the user decides where to declare it and its
[9d7e5cb]206name. The initialization of the virtual table is entirely automatic based on
207the context of the declaration.
208
[9cdfa5fb]209The type ID is always fixed, with each virtual table type having
210exactly one possible type ID.
[5a4f1a8]211The virtual members are usually filled in by type resolution.
212The best match for a given name and type at the declaration site is used.
213There are two exceptions to that rule: the @size@ field, the type's size,
[9cdfa5fb]214is set using a @sizeof@ expression, and the @align@ field, the
[5a4f1a8]215type's alignment, is set using an @alignof@ expression.
[9d7e5cb]216
[0a55a53]217Most of these tools are already inside the compiler. Using simple
[9cdfa5fb]218code transformations early on in compilation allows most of that work to be
[e8bad5c8]219handed off to the existing tools. \autoref{f:VirtualTableTransformation}
[9cdfa5fb]220shows an example transformation; this example shows an exception virtual table.
[0a55a53]221It also shows the transformation on the full declaration.
222For a forward declaration, the @extern@ keyword is preserved and the
[e8bad5c8]223initializer is not added.
224
225\begin{figure}[htb]
226\begin{cfa}
227vtable(example_type) example_name;
228\end{cfa}
229\transformline
230% Check mangling.
231\begin{cfa}
232const struct example_type_vtable example_name = {
233        .__cfavir_typeid : &__cfatid_example_type,
234        .size : sizeof(example_type),
235        .copy : copy,
236        .^?{} : ^?{},
237        .msg : msg,
238};
239\end{cfa}
240\caption{Virtual Table Transformation}
241\label{f:VirtualTableTransformation}
242\end{figure}
243
[ba2e8f0]244\subsection{Concurrency Integration}
[f28fdee]245Coroutines and threads need instances of @CoroutineCancelled@ and
[830299f]246@ThreadCancelled@ respectively to use all of their functionality. When a new
[5a4f1a8]247data type is declared with @coroutine@ or @thread@, a forward declaration for
[7eb6eb5]248the instance is created as well. The definition of the virtual table is created
249at the definition of the main function.
[c21f5a9]250
[ba2e8f0]251These transformations are shown through code re-writing in
252\autoref{f:CoroutineTypeTransformation} and
253\autoref{f:CoroutineMainTransformation}.
254Threads use the same pattern, with some names and types changed.
255In both cases, the original declaration is not modified,
[7372065]256only new ones are added.
[5a4f1a8]257
[ba2e8f0]258\begin{figure}[htb]
[c21f5a9]259\begin{cfa}
260coroutine Example {
261        // fields
[9b0bb79]262};
[c21f5a9]263\end{cfa}
264
[d02e547]265\transformline[appends...]
266
[c21f5a9]267\begin{cfa}
268__attribute__((cfa_linkonce))
269struct __cfatid_struct_CoroutineCancelled(Example)
270                __cfatid_CoroutineCancelled = {
271        &EXCEPTION_TYPE_ID,
272};
273extern CoroutineCancelled_vtable _default_vtable_object_declaration;
274extern CoroutineCancelled_vtable & _default_vtable;
275\end{cfa}
[ba2e8f0]276\caption{Coroutine Type Transformation}
277\label{f:CoroutineTypeTransformation}
[7372065]278\end{figure}
[e56eabac]279
[e8bad5c8]280\begin{figure}[htb]
[c21f5a9]281\begin{cfa}
282void main(Example & this) {
283        // body
284}
285\end{cfa}
286
[d02e547]287\transformline[appends...]
288
[c21f5a9]289\begin{cfa}
290CoroutineCancelled_vtable _default_vtable_object_declaration = {
291        __cfatid_CoroutineCancelled,
292        // Virtual member initialization.
293};
294
295CoroutineCancelled_vtable & _default_vtable =
296        &_default_vtable_object_declaration;
297\end{cfa}
[ba2e8f0]298\caption{Coroutine Main Transformation}
299\label{f:CoroutineMainTransformation}
[c21f5a9]300\end{figure}
[26ca815]301
302\subsection{Virtual Cast}
[7eb6eb5]303Virtual casts are implemented as a function call that does the subtype check
304and a C coercion-cast to do the type conversion.
305% The C-cast is just to make sure the generated code is correct so the rest of
306% the section is about that function.
[9d7e5cb]307The function is implemented in the standard library and has the following
308signature:
[7eb6eb5]309\begin{cfa}
[0c4df43]310void * __cfa__virtual_cast(
[ba2e8f0]311        struct __cfavir_type_id * parent,
312        struct __cfavir_type_id * const * child );
[7eb6eb5]313\end{cfa}
[9cdfa5fb]314The type ID for the target type of the virtual cast is passed in as
[ba2e8f0]315@parent@ and
[9d7e5cb]316the cast target is passed in as @child@.
[ba2e8f0]317The generated C code wraps both arguments and the result with type casts.
[5a4f1a8]318There is also an internal check inside the compiler to make sure that the
[9d7e5cb]319target type is a virtual type.
320% It also checks for conflicting definitions.
321
[5a4f1a8]322The virtual cast either returns the original pointer or the null pointer
323as the new type.
[9cdfa5fb]324The function does the parent check and returns the appropriate value.
325The parent check is a simple linear search of the child's ancestors using the
[9d7e5cb]326type information.
[26ca815]327
328\section{Exceptions}
[e8bad5c8]329% The implementation of exception types.
330
[9cdfa5fb]331Creating exceptions can be roughly divided into two parts:
[e8bad5c8]332the exceptions themselves and the virtual system interactions.
333
[0a55a53]334Creating an exception type is just a matter of prepending the field 
[e8bad5c8]335with the virtual table pointer to the list of the fields
336(see \autoref{f:ExceptionTypeTransformation}).
337
338\begin{figure}[htb]
339\begin{cfa}
340exception new_exception {
341        // EXISTING FIELDS
342};
343\end{cfa}
344\transformline
345\begin{cfa}
346struct new_exception {
347        struct new_exception_vtable const * virtual_table;
348        // EXISTING FIELDS
349};
350\end{cfa}
351\caption{Exception Type Transformation}
352\label{f:ExceptionTypeTransformation}
353\end{figure}
354
355The integration between exceptions and the virtual system is a bit more
356complex simply because of the nature of the virtual system prototype.
357The primary issue is that the virtual system has no way to detect when it
358should generate any of its internal types and data. This is handled by
359the exception code, which tells the virtual system when to generate
360its components.
361
362All types associated with a virtual type,
[9cdfa5fb]363the types of the virtual table and the type ID,
[e8bad5c8]364are generated when the virtual type (the exception) is first found.
[9cdfa5fb]365The type ID (the instance) is generated with the exception, if it is
[e8bad5c8]366a monomorphic type.
[9cdfa5fb]367However, if the exception is polymorphic, then a different type ID has to
[0a55a53]368be generated for every instance. In this case, generation is delayed
[e8bad5c8]369until a virtual table is created.
370% There are actually some problems with this, which is why it is not used
371% for monomorphic types.
[0a55a53]372When a virtual table is created and initialized, two functions are created
[e8bad5c8]373to fill in the list of virtual members.
[9cdfa5fb]374The first is the @copy@ function that adapts the exception's copy constructor
[e8bad5c8]375to work with pointers, avoiding some issues with the current copy constructor
376interface.
[9cdfa5fb]377Second is the @msg@ function that returns a C-string with the type's name,
[e8bad5c8]378including any polymorphic parameters.
[26ca815]379
380\section{Unwinding}
381% Adapt the unwind chapter, just describe the sections of libunwind used.
382% Mention that termination and cancellation use it. Maybe go into why
383% resumption doesn't as well.
384
[5a4f1a8]385% Many modern languages work with an internal stack that function push and pop
[7eb6eb5]386% their local data to. Stack unwinding removes large sections of the stack,
387% often across functions.
388
389Stack unwinding is the process of removing stack frames (activations) from the
[9d7e5cb]390stack. On function entry and return, unwinding is handled directly by the
391call/return code embedded in the function.
392
[ba2e8f0]393% Discussing normal stack unwinding:
[9d7e5cb]394Usually, the stack-frame size is known statically based on parameter and
[ba2e8f0]395local variable declarations. Even for a dynamic stack-size, the information
[5a4f1a8]396to determine how much of the stack has to be removed is still contained
[9d7e5cb]397within the function.
[7eb6eb5]398Allocating/deallocating stack space is usually an $O(1)$ operation achieved by
399bumping the hardware stack-pointer up or down as needed.
[5a4f1a8]400Constructing/destructing values within a stack frame has
[ba2e8f0]401a similar complexity but larger constants.
[7eb6eb5]402
[ba2e8f0]403% Discussing multiple frame stack unwinding:
[9cdfa5fb]404Unwinding across multiple stack frames is more complex, because that
[9d7e5cb]405information is no longer contained within the current function.
[0a55a53]406With separate compilation,
[ba2e8f0]407a function does not know its callers nor their frame layout.
408Even using the return address, that information is encoded in terms of
[9cdfa5fb]409actions in code, intermixed with the actions required to finish the function.
[ba2e8f0]410Without changing the main code path it is impossible to select one of those
411two groups of actions at the return site.
[7eb6eb5]412
[9cdfa5fb]413The traditional unwinding mechanism for C is implemented by saving a snapshot
414of a function's state with @setjmp@ and restoring that snapshot with
[7eb6eb5]415@longjmp@. This approach bypasses the need to know stack details by simply
[814f87d]416resetting to a snapshot of an arbitrary but existing function frame on the
[9cdfa5fb]417stack. It is up to the programmer to ensure the snapshot is valid when it is
418reset and that all required cleanup from the unwound stacks is performed.
[814f87d]419Because it does not automate or check any of this cleanup,
420it can be easy to make mistakes and always must be handled manually.
[9d7e5cb]421
[ba2e8f0]422With respect to the extra work in the surrounding code,
[9cdfa5fb]423many languages define cleanup actions that must be taken when certain
424sections of the stack are removed, such as when the storage for a variable
[ba2e8f0]425is removed from the stack, possibly requiring a destructor call,
426or when a try statement with a finally clause is
[9d7e5cb]427(conceptually) popped from the stack.
[9cdfa5fb]428None of these cases should be handled by the user -- that would contradict the
429intention of these features -- so they need to be handled automatically.
[9d7e5cb]430
[5a4f1a8]431To safely remove sections of the stack, the language must be able to find and
[9cdfa5fb]432run these cleanup actions even when removing multiple functions unknown at
[9d7e5cb]433the beginning of the unwinding.
[7eb6eb5]434
435One of the most popular tools for stack management is libunwind, a low-level
436library that provides tools for stack walking, handler execution, and
437unwinding. What follows is an overview of all the relevant features of
[814f87d]438libunwind needed for this work.
439Following that is the description of the \CFA code that uses libunwind
440to implement termination.
[7eb6eb5]441
442\subsection{libunwind Usage}
443Libunwind, accessed through @unwind.h@ on most platforms, is a C library that
[df24d37]444provides \Cpp-style stack-unwinding. Its operation is divided into two phases:
[7eb6eb5]445search and cleanup. The dynamic target search -- phase 1 -- is used to scan the
446stack and decide where unwinding should stop (but no unwinding occurs). The
447cleanup -- phase 2 -- does the unwinding and also runs any cleanup code.
448
449To use libunwind, each function must have a personality function and a Language
[830299f]450Specific Data Area (LSDA). The LSDA has the unique information for each
[7eb6eb5]451function to tell the personality function where a function is executing, its
[830299f]452current stack frame, and what handlers should be checked. Theoretically, the
[7eb6eb5]453LSDA can contain any information but conventionally it is a table with entries
[5a4f1a8]454representing regions of a function and what has to be done there during
[9d7e5cb]455unwinding. These regions are bracketed by instruction addresses. If the
[7eb6eb5]456instruction pointer is within a region's start/end, then execution is currently
457executing in that region. Regions are used to mark out the scopes of objects
[b51e389c]458with destructors and try blocks.
[7eb6eb5]459
460% Libunwind actually does very little, it simply moves down the stack from
461% function to function. Most of the actions are implemented by the personality
462% function which libunwind calls on every function. Since this is shared across
463% many functions or even every function in a language it will need a bit more
464% information.
465
466The GCC compilation flag @-fexceptions@ causes the generation of an LSDA and
[9d7e5cb]467attaches a personality function to each function.
468In plain C (which \CFA currently compiles down to) this
[830299f]469flag only handles the cleanup attribute:
[ba2e8f0]470%\label{code:cleanup}
[7eb6eb5]471\begin{cfa}
472void clean_up( int * var ) { ... }
[830299f]473int avar __attribute__(( cleanup(clean_up) ));
[7eb6eb5]474\end{cfa}
[5a4f1a8]475The attribute is used on a variable and specifies a function,
[9d7e5cb]476in this case @clean_up@, run when the variable goes out of scope.
[5a4f1a8]477This feature is enough to mimic destructors,
[ba2e8f0]478but not try statements that affect
[9d7e5cb]479the unwinding.
480
[5a4f1a8]481To get full unwinding support, all of these features must be handled directly
[0a55a53]482in assembly and assembler directives; particularly the cfi directives
[b51e389c]483\snake{.cfi_lsda} and \snake{.cfi_personality}.
[7eb6eb5]484
485\subsection{Personality Functions}
[830299f]486Personality functions have a complex interface specified by libunwind. This
[7eb6eb5]487section covers some of the important parts of the interface.
488
[5a4f1a8]489A personality function can perform different actions depending on how it is
[830299f]490called.
[9b0bb79]491\begin{lstlisting}
492typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn) (
493        _Unwind_Action action,
494        _Unwind_Exception_Class exception_class,
495        _Unwind_Exception * exception,
496        struct _Unwind_Context * context);
[26ca815]497\end{lstlisting}
[7eb6eb5]498The @action@ argument is a bitmask of possible actions:
[9d7e5cb]499\begin{enumerate}[topsep=5pt]
[7eb6eb5]500\item
501@_UA_SEARCH_PHASE@ specifies a search phase and tells the personality function
[830299f]502to check for handlers. If there is a handler in a stack frame, as defined by
[7eb6eb5]503the language, the personality function returns @_URC_HANDLER_FOUND@; otherwise
504it return @_URC_CONTINUE_UNWIND@.
505
506\item
507@_UA_CLEANUP_PHASE@ specifies a cleanup phase, where the entire frame is
508unwound and all cleanup code is run. The personality function does whatever
509cleanup the language defines (such as running destructors/finalizers) and then
510generally returns @_URC_CONTINUE_UNWIND@.
511
512\item
513\begin{sloppypar}
514@_UA_HANDLER_FRAME@ specifies a cleanup phase on a function frame that found a
515handler. The personality function must prepare to return to normal code
516execution and return @_URC_INSTALL_CONTEXT@.
517\end{sloppypar}
518
519\item
520@_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs
521the cleanup phase and uses a different means to decide when to stop
[ba2e8f0]522(see \autoref{s:ForcedUnwind}).
[7eb6eb5]523\end{enumerate}
524
525The @exception_class@ argument is a copy of the
[5a4f1a8]526\code{C}{exception}'s @exception_class@ field,
[ba2e8f0]527which is a number that identifies the EHM
[5a4f1a8]528that created the exception.
[7eb6eb5]529
[5a4f1a8]530The \code{C}{exception} argument is a pointer to a user
[9d7e5cb]531provided storage object. It has two public fields: the @exception_class@,
532which is described above, and the @exception_cleanup@ function.
[9cdfa5fb]533The cleanup function is used by the EHM to clean up the exception. If it
[9d7e5cb]534should need to be freed at an unusual time, it takes an argument that says
535why it had to be cleaned up.
[7eb6eb5]536
537The @context@ argument is a pointer to an opaque type passed to helper
538functions called inside the personality function.
539
540The return value, @_Unwind_Reason_Code@, is an enumeration of possible messages
[26ca815]541that can be passed several places in libunwind. It includes a number of
542messages for special cases (some of which should never be used by the
[9d7e5cb]543personality function) and error codes. However, unless otherwise noted, the
[5a4f1a8]544personality function always returns @_URC_CONTINUE_UNWIND@.
[26ca815]545
546\subsection{Raise Exception}
[5a4f1a8]547Raising an exception is the central function of libunwind and it performs
[7eb6eb5]548two-staged unwinding.
549\begin{cfa}
[26ca815]550_Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *);
[7eb6eb5]551\end{cfa}
552First, the function begins the search phase, calling the personality function
553of the most recent stack frame. It continues to call personality functions
554traversing the stack from newest to oldest until a function finds a handler or
[9cdfa5fb]555the end of the stack is reached. In the latter case,
556@_Unwind_RaiseException@ returns @_URC_END_OF_STACK@.
[7eb6eb5]557
[9cdfa5fb]558Second, when a handler is matched, @_Unwind_RaiseException@
559moves to the cleanup phase and walks the stack a second time.
[7eb6eb5]560Once again, it calls the personality functions of each stack frame from newest
561to oldest. This pass stops at the stack frame containing the matching handler.
[9cdfa5fb]562If that personality function has not installed a handler, it is an error.
[7eb6eb5]563
[9cdfa5fb]564If an error is encountered, @_Unwind_RaiseException@ returns either
[7eb6eb5]565@_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the
566error occurred.
[26ca815]567
568\subsection{Forced Unwind}
[7eb6eb5]569\label{s:ForcedUnwind}
570Forced Unwind is the other central function in libunwind.
571\begin{cfa}
[9d7e5cb]572_Unwind_Reason_Code _Unwind_ForcedUnwind(_Unwind_Exception *,
[7eb6eb5]573        _Unwind_Stop_Fn, void *);
574\end{cfa}
[9cdfa5fb]575It also unwinds the stack but it does not use the search phase. Instead,
576another
[830299f]577function, the stop function, is used to stop searching. The exception is the
[9cdfa5fb]578same as the one passed to @_Unwind_RaiseException@.
579The extra arguments are the stop
[7eb6eb5]580function and the stop parameter. The stop function has a similar interface as a
581personality function, except it is also passed the stop parameter.
[9b0bb79]582\begin{lstlisting}
583typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(
584        _Unwind_Action action,
585        _Unwind_Exception_Class exception_class,
586        _Unwind_Exception * exception,
587        struct _Unwind_Context * context,
588        void * stop_parameter);
[26ca815]589\end{lstlisting}
590
591The stop function is called at every stack frame before the personality
[7eb6eb5]592function is called and then once more after all frames of the stack are
593unwound.
[26ca815]594
[7eb6eb5]595Each time it is called, the stop function should return @_URC_NO_REASON@ or
596transfer control directly to other code outside of libunwind. The framework
597does not provide any assistance here.
[26ca815]598
[7eb6eb5]599\begin{sloppypar}
[830299f]600Its arguments are the same as the paired personality function. The actions
[887fc79]601\snake{_UA_CLEANUP_PHASE} and \snake{_UA_FORCE_UNWIND} are always set when it is
[7eb6eb5]602called. Beyond the libunwind standard, both GCC and Clang add an extra action
[887fc79]603on the last call at the end of the stack: \snake{_UA_END_OF_STACK}.
[7eb6eb5]604\end{sloppypar}
[26ca815]605
606\section{Exception Context}
607% Should I have another independent section?
608% There are only two things in it, top_resume and current_exception. How it is
[7eb6eb5]609% stored changes depending on whether or not the thread-library is linked.
610
611The exception context is global storage used to maintain data across different
612exception operations and to communicate among different components.
613
614Each stack must have its own exception context. In a sequential \CFA program,
615there is only one stack with a single global exception-context. However, when
[9d7e5cb]616the library @libcfathread@ is linked, there are multiple stacks and each
[7eb6eb5]617needs its own exception context.
618
[ba2e8f0]619The current exception context should be retrieved by calling the function
[887fc79]620\snake{this_exception_context}.
621For sequential execution, this function is defined as
[7eb6eb5]622a weak symbol in the \CFA system-library, @libcfa@. When a \CFA program is
623concurrent, it links with @libcfathread@, where this function is defined with a
624strong symbol replacing the sequential version.
625
[830299f]626The sequential @this_exception_context@ returns a hard-coded pointer to the
[9d7e5cb]627global exception context.
[830299f]628The concurrent version adds the exception context to the data stored at the
[9d7e5cb]629base of each stack. When @this_exception_context@ is called, it retrieves the
[830299f]630active stack and returns the address of the context saved there.
[26ca815]631
632\section{Termination}
633% Memory management & extra information, the custom function used to implement
634% catches. Talk about GCC nested functions.
635
[5a4f1a8]636\CFA termination exceptions use libunwind heavily because they match
[9d7e5cb]637\Cpp exceptions closely. The main complication for \CFA is that the
[7eb6eb5]638compiler generates C code, making it very difficult to generate the assembly to
[b51e389c]639form the LSDA for try blocks or destructors.
[26ca815]640
641\subsection{Memory Management}
[7eb6eb5]642The first step of a termination raise is to copy the exception into memory
643managed by the exception system. Currently, the system uses @malloc@, rather
[ba2e8f0]644than reserved memory or the stack top. The EHM manages
[7eb6eb5]645memory for the exception as well as memory for libunwind and the system's own
646per-exception storage.
647
[9d7e5cb]648\begin{figure}
[5a4f1a8]649\centering
[9b0bb79]650\input{exception-layout}
[9d7e5cb]651\caption{Exception Layout}
652\label{f:ExceptionLayout}
653\end{figure}
[830299f]654
[5a4f1a8]655Exceptions are stored in variable-sized blocks
656(see \autoref{f:ExceptionLayout}).
[9d7e5cb]657The first component is a fixed-sized data structure that contains the
[7eb6eb5]658information for libunwind and the exception system. The second component is an
659area of memory big enough to store the exception. Macros with pointer arthritic
660and type cast are used to move between the components or go from the embedded
[f28fdee]661@_Unwind_Exception@ to the entire node.
[26ca815]662
[5a4f1a8]663Multiple exceptions can exist at the same time because exceptions can be
[9d7e5cb]664raised inside handlers, destructors and finally blocks.
665Figure~\vref{f:MultipleExceptions} shows a program that has multiple
666exceptions active at one time.
667Each time an exception is thrown and caught the stack unwinds and the finally
[5a4f1a8]668clause runs. This handler throws another exception (until @num_exceptions@ gets
669high enough), which must be allocated. The previous exceptions may not be
[9d7e5cb]670freed because the handler/catch clause has not been run.
[5a4f1a8]671Therefore, the EHM must keep all unhandled exceptions alive
672while it allocates exceptions for new throws.
[9d7e5cb]673
674\begin{figure}
675\centering
[9b0bb79]676\newsavebox{\codeBox}
677\newsavebox{\stackBox}
678\begin{lrbox}{\codeBox}
[25d4e15]679\begin{cfa}
[9d7e5cb]680unsigned num_exceptions = 0;
681void throws() {
682    try {
683        try {
684            ++num_exceptions;
685            throw (Example){table};
686        } finally {
687            if (num_exceptions < 3) {
688                throws();
689            }
690        }
691    } catch (exception_t *) {
692        --num_exceptions;
693    }
694}
695int main() {
696    throws();
697}
[25d4e15]698\end{cfa}
[9d7e5cb]699\end{lrbox}
700
[9b0bb79]701\begin{lrbox}{\stackBox}
[9d7e5cb]702\begin{lstlisting}
[25d4e15]703| finally block (Example)
704| try block
[9b0bb79]705throws()
[25d4e15]706| finally block (Example)
707| try block
[9b0bb79]708throws()
[25d4e15]709| finally block (Example)
710| try block
[9b0bb79]711throws()
712main()
[9d7e5cb]713\end{lstlisting}
714\end{lrbox}
715
[9b0bb79]716{\usebox\codeBox}
[9d7e5cb]717\hspace{25pt}
[9b0bb79]718{\usebox\stackBox}
[9d7e5cb]719
720\caption{Multiple Exceptions}
721\label{f:MultipleExceptions}
722\end{figure}
723
[5a4f1a8]724All exceptions are stored in nodes, which are then linked together in lists
[9d7e5cb]725one list per stack, with the
[7eb6eb5]726list head stored in the exception context. Within each linked list, the most
[9cdfa5fb]727recently thrown exception is at the head, followed by older thrown
[7eb6eb5]728exceptions. This format allows exceptions to be thrown, while a different
729exception is being handled. The exception at the head of the list is currently
730being handled, while other exceptions wait for the exceptions before them to be
[5a4f1a8]731handled and removed.
[7eb6eb5]732
733The virtual members in the exception's virtual table provide the size of the
734exception, the copy function, and the free function, so they are specific to an
735exception type. The size and copy function are used immediately to copy an
[9d7e5cb]736exception into managed memory. After the exception is handled, the free
737function is used to clean up the exception and then the entire node is
[9cdfa5fb]738passed to @free@, returning the memory back to the heap.
[7eb6eb5]739
740\subsection{Try Statements and Catch Clauses}
[b51e389c]741The try statement with termination handlers is complex because it must
[ba2e8f0]742compensate for the C code-generation versus proper
[5a4f1a8]743assembly-code generated from \CFA. Libunwind
[7eb6eb5]744requires an LSDA and personality function for control to unwind across a
745function. The LSDA in particular is hard to mimic in generated C code.
746
[ba2e8f0]747The workaround is a function called \snake{__cfaehm_try_terminate} in the
748standard \CFA library. The contents of a try block and the termination
749handlers are converted into nested functions. These are then passed to the
750try terminate function and it calls them, appropriately.
[830299f]751Because this function is known and fixed (and not an arbitrary function that
[ba2e8f0]752happens to contain a try statement), its LSDA can be generated ahead
[830299f]753of time.
754
[ba2e8f0]755Both the LSDA and the personality function for \snake{__cfaehm_try_terminate}
756are set ahead of time using
[9d7e5cb]757embedded assembly. This assembly code is handcrafted using C @asm@ statements
758and contains
[ba2e8f0]759enough information for the single try statement the function represents.
[26ca815]760
761The three functions passed to try terminate are:
[7eb6eb5]762\begin{description}
[9cdfa5fb]763\item[try function:] This function is the try block. It is where all the code
[5a4f1a8]764from inside the try block is placed. It takes no parameters and has no
[7eb6eb5]765return value. This function is called during regular execution to run the try
766block.
767
768\item[match function:] This function is called during the search phase and
[830299f]769decides if a catch clause matches the termination exception. It is constructed
[7eb6eb5]770from the conditional part of each handler and runs each check, top to bottom,
[ba2e8f0]771in turn, to see if the exception matches this handler.
[9cdfa5fb]772The match is performed in two steps: first, a virtual cast is used to check
[ba2e8f0]773if the raised exception is an instance of the declared exception type or
774one of its descendant types, and then the condition is evaluated, if
775present.
776The match function takes a pointer to the exception and returns 0 if the
[9cdfa5fb]777exception is not handled here. Otherwise, the return value is the ID of the
[7eb6eb5]778handler that matches the exception.
779
[5a4f1a8]780\item[handler function:] This function handles the exception, and contains
781all the code from the handlers in the try statement, joined with a switch
782statement on the handler's id.
783It takes a
[7eb6eb5]784pointer to the exception and the handler's id and returns nothing. It is called
[5a4f1a8]785after the cleanup phase.
[7eb6eb5]786\end{description}
787All three functions are created with GCC nested functions. GCC nested functions
[9cdfa5fb]788can be used to create closures;
[ba2e8f0]789in other words,
[9cdfa5fb]790functions that can refer to variables in their lexical scope even though
[ba2e8f0]791those variables are part of a different function.
792This approach allows the functions to refer to all the
[830299f]793variables in scope for the function containing the @try@ statement. These
[7eb6eb5]794nested functions and all other functions besides @__cfaehm_try_terminate@ in
795\CFA use the GCC personality function and the @-fexceptions@ flag to generate
[9d7e5cb]796the LSDA.
797Using this pattern, \CFA implements destructors with the cleanup attribute.
[c21f5a9]798
[5a4f1a8]799\autoref{f:TerminationTransformation} shows the pattern used to transform
[ba2e8f0]800a \CFA try statement with catch clauses into the appropriate C functions.
[5a4f1a8]801
[c21f5a9]802\begin{figure}
803\begin{cfa}
804try {
805        // TRY BLOCK
806} catch (Exception1 * name1 ; check(name1)) {
807        // CATCH BLOCK 1
808} catch (Exception2 * name2) {
809        // CATCH BLOCK 2
810}
811\end{cfa}
812
[d02e547]813\transformline
[5a4f1a8]814
[c21f5a9]815\begin{cfa}
816void try(void) {
817        // TRY BLOCK
818}
819int match(exception_t * __exception_inst) {
820        {
821                Exception1 * name1;
[887fc79]822                if (name1 = (virtual Exception1 *)__exception_inst
823                                && check(name1)) {
[c21f5a9]824                        return 1;
825                }
826        }
827        {
828                Exception2 * name2;
829                if (name2 = (virtual Exception2 *)__exception_inst) {
830                        return 2;
831                }
832        }
833        return 0;
834}
835void catch(exception_t * __exception_inst, int __handler_index) {
836        switch (__handler_index) {
837        case 1:
838        {
839                Exception1 * name1 = (virtual Exception1 *)__exception_inst;
840                // CATCH BLOCK 1
841        }
842        return;
843        case 2:
844        {
845                Exception2 * name2 = (virtual Exception2 *)__exception_inst;
846                // CATCH BLOCK 2
847        }
848        return;
849        }
850}
851{
852        __cfaehm_try_terminate(try, catch, match);
853}
854\end{cfa}
855
856\caption{Termination Transformation}
857\label{f:TerminationTransformation}
858\end{figure}
[26ca815]859
860\section{Resumption}
861% The stack-local data, the linked list of nodes.
862
[5a4f1a8]863Resumption is simpler to implement than termination
[9d7e5cb]864because there is no stack unwinding.
865Instead of storing the data in a special area using assembly,
866there is just a linked list of possible handlers for each stack,
[ba2e8f0]867with each node on the list representing a try statement on the stack.
[9d7e5cb]868
869The head of the list is stored in the exception context.
[b51e389c]870The nodes are stored in order, with the more recent try statements closer
[9d7e5cb]871to the head of the list.
[5a4f1a8]872Instead of traversing the stack, resumption handling traverses the list.
[ba2e8f0]873At each node, the EHM checks to see if the try statement the node represents
[9d7e5cb]874can handle the exception. If it can, then the exception is handled and
[9cdfa5fb]875the operation finishes; otherwise, the search continues to the next node.
[b51e389c]876If the search reaches the end of the list without finding a try statement
[ba2e8f0]877with a handler clause
878that can handle the exception, the default handler is executed.
879If the default handler returns, control continues after the raise statement.
[9d7e5cb]880
[5a4f1a8]881Each node has a handler function that does most of the work.
882The handler function is passed the raised exception and returns true
883if the exception is handled and false otherwise.
884The handler function checks each of its internal handlers in order,
[9cdfa5fb]885top-to-bottom, until it finds a match. If a match is found that handler is
[5a4f1a8]886run, after which the function returns true, ignoring all remaining handlers.
887If no match is found the function returns false.
[9cdfa5fb]888The match is performed in two steps. First a virtual cast is used to see
[ba2e8f0]889if the raised exception is an instance of the declared exception type or one
[9cdfa5fb]890of its descendant types, if so, then the second step is to see if the
891exception passes the custom predicate
[ba2e8f0]892if one is defined.
893% You need to make sure the type is correct before running the predicate
894% because the predicate can depend on that.
[5a4f1a8]895
896\autoref{f:ResumptionTransformation} shows the pattern used to transform
[cd03b76d]897a \CFA try statement with catchResume clauses into the appropriate
898C functions.
[9d7e5cb]899
[c21f5a9]900\begin{figure}
901\begin{cfa}
902try {
903        // TRY BLOCK
904} catchResume (Exception1 * name1 ; check(name1)) {
905        // CATCH BLOCK 1
906} catchResume (Exception2 * name2) {
907        // CATCH BLOCK 2
908}
909\end{cfa}
910
[d02e547]911\transformline
[5a4f1a8]912
[c21f5a9]913\begin{cfa}
914bool handle(exception_t * __exception_inst) {
915        {
916                Exception1 * name1;
[887fc79]917                if (name1 = (virtual Exception1 *)__exception_inst
918                                && check(name1)) {
[c21f5a9]919                        // CATCH BLOCK 1
920                        return 1;
921                }
922        }
923        {
924                Exception2 * name2;
925                if (name2 = (virtual Exception2 *)__exception_inst) {
926                        // CATCH BLOCK 2
927                        return 2;
928                }
929        }
930        return false;
931}
932struct __try_resume_node __resume_node
933        __attribute__((cleanup( __cfaehm_try_resume_cleanup )));
934__cfaehm_try_resume_setup( &__resume_node, handler );
935\end{cfa}
936
937\caption{Resumption Transformation}
938\label{f:ResumptionTransformation}
939\end{figure}
[26ca815]940
[12b4ab4]941% Recursive Resumption Stuff:
[5a4f1a8]942\autoref{f:ResumptionMarking} shows search skipping
[9cdfa5fb]943(see \autoref{s:ResumptionMarking}), which ignores parts of
[df24d37]944the stack
[ba2e8f0]945already examined, and is accomplished by updating the front of the list as
946the search continues.
947Before the handler is called at a matching node, the head of the list
[7eb6eb5]948is updated to the next node of the current node. After the search is complete,
949successful or not, the head of the list is reset.
[5a4f1a8]950% No paragraph?
[7eb6eb5]951This mechanism means the current handler and every handler that has already
952been checked are not on the list while a handler is run. If a resumption is
[5a4f1a8]953thrown during the handling of another resumption, the active handlers and all
[ba2e8f0]954the other handlers checked up to this point are not checked again.
[5a4f1a8]955% No paragraph?
956This structure also supports new handlers added while the resumption is being
[12b4ab4]957handled. These are added to the front of the list, pointing back along the
[9cdfa5fb]958stack -- the first one points over all the checked handlers --
[5a4f1a8]959and the ordering is maintained.
[c21f5a9]960
961\begin{figure}
[ba2e8f0]962\centering
[9b0bb79]963\input{resumption-marking}
[c21f5a9]964\caption{Resumption Marking}
965\label{f:ResumptionMarking}
966\end{figure}
[7eb6eb5]967
968\label{p:zero-cost}
[5a4f1a8]969Finally, the resumption implementation has a cost for entering/exiting a try
970statement with @catchResume@ clauses, whereas a try statement with @catch@
[7eb6eb5]971clauses has zero-cost entry/exit. While resumption does not need the stack
972unwinding and cleanup provided by libunwind, it could use the search phase to
973providing zero-cost enter/exit using the LSDA. Unfortunately, there is no way
974to return from a libunwind search without installing a handler or raising an
[830299f]975error. Although workarounds might be possible, they are beyond the scope of
[7eb6eb5]976this thesis. The current resumption implementation has simplicity in its
977favour.
[26ca815]978% Seriously, just compare the size of the two chapters and then consider
979% that unwind is required knowledge for that chapter.
980
981\section{Finally}
982% Uses destructors and GCC nested functions.
[ba2e8f0]983
984%\autoref{code:cleanup}
985A finally clause is handled by converting it into a once-off destructor.
[9cdfa5fb]986The code inside the clause is placed into a GCC nested-function
[ba2e8f0]987with a unique name, and no arguments or return values.
988This nested function is
989then set as the cleanup function of an empty object that is declared at the
990beginning of a block placed around the context of the associated try
[9cdfa5fb]991statement, as shown in \autoref{f:FinallyTransformation}.
[ba2e8f0]992
993\begin{figure}
994\begin{cfa}
995try {
996        // TRY BLOCK
997} finally {
998        // FINALLY BLOCK
999}
1000\end{cfa}
1001
1002\transformline
1003
1004\begin{cfa}
1005{
1006        void finally(void *__hook){
1007                // FINALLY BLOCK
1008        }
1009        __attribute__ ((cleanup(finally)))
1010        struct __cfaehm_cleanup_hook __finally_hook;
1011        {
1012                // TRY BLOCK
1013        }
1014}
1015\end{cfa}
1016
1017\caption{Finally Transformation}
1018\label{f:FinallyTransformation}
1019\end{figure}
1020
1021The rest is handled by GCC.
1022The TRY BLOCK
1023contains the try block itself as well as all code generated for handlers.
1024Once that code has completed,
1025control exits the block and the empty object is cleaned
[7eb6eb5]1026up, which runs the function that contains the finally code.
[26ca815]1027
1028\section{Cancellation}
1029% Stack selections, the three internal unwind functions.
1030
[9cdfa5fb]1031Cancellation also uses libunwind to do its stack traversal and unwinding.
1032However, it uses a different primary function: @_Unwind_ForcedUnwind@. Details
1033of its interface can be found in Section~\vref{s:ForcedUnwind}.
[26ca815]1034
[7eb6eb5]1035The first step of cancellation is to find the cancelled stack and its type:
[7372065]1036coroutine, thread or main thread.
[5a4f1a8]1037In \CFA, a thread (the construct the user works with) is a user-level thread
1038(point of execution) paired with a coroutine, the thread's main coroutine.
1039The thread library also stores pointers to the main thread and the current
[7372065]1040thread.
[5a4f1a8]1041If the current thread's main and current coroutines are the same then the
1042current stack is a thread stack, otherwise it is a coroutine stack.
1043If the current stack is a thread stack, it is also the main thread stack
1044if and only if the main and current threads are the same.
[0c4df43]1045
[7eb6eb5]1046However, if the threading library is not linked, the sequential execution is on
1047the main stack. Hence, the entire check is skipped because the weak-symbol
[5a4f1a8]1048function is loaded. Therefore, main thread cancellation is unconditionally
[7eb6eb5]1049performed.
1050
1051Regardless of how the stack is chosen, the stop function and parameter are
1052passed to the forced-unwind function. The general pattern of all three stop
[5a4f1a8]1053functions is the same: continue unwinding until the end of stack and
[ba2e8f0]1054then perform the appropriate transfer.
[0c4df43]1055
[7eb6eb5]1056For main stack cancellation, the transfer is just a program abort.
1057
[0c4df43]1058For coroutine cancellation, the exception is stored on the coroutine's stack,
[7eb6eb5]1059and the coroutine context switches to its last resumer. The rest is handled on
[5a4f1a8]1060the backside of the resume, which checks if the resumed coroutine is
[7eb6eb5]1061cancelled. If cancelled, the exception is retrieved from the resumed coroutine,
1062and a @CoroutineCancelled@ exception is constructed and loaded with the
1063cancelled exception. It is then resumed as a regular exception with the default
1064handler coming from the context of the resumption call.
1065
1066For thread cancellation, the exception is stored on the thread's main stack and
1067then context switched to the scheduler. The rest is handled by the thread
1068joiner. When the join is complete, the joiner checks if the joined thread is
1069cancelled. If cancelled, the exception is retrieved and the joined thread, and
1070a @ThreadCancelled@ exception is constructed and loaded with the cancelled
1071exception. The default handler is passed in as a function pointer. If it is
1072null (as it is for the auto-generated joins on destructor call), the default is
1073used, which is a program abort.
1074%; which gives the required handling on implicate join.
Note: See TracBrowser for help on using the repository browser.