source: doc/theses/andrew_beach_MMath/implement.tex @ 34fcc13

ADTast-experimentalenumforall-pointer-decayjacob/cs343-translationpthread-emulationqualifiedEnum
Last change on this file since 34fcc13 was 34fcc13, checked in by Andrew Beach <ajbeach@…>, 3 years ago

Andrew MMath: Nope! Forgot to delete one \todo.

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