Changes in / [f99f5ba:cd70477]
- Files:
-
- 4 deleted
- 26 edited
-
.gitignore (modified) (1 diff)
-
doc/LaTeXmacros/common.tex (modified) (6 diffs)
-
doc/bibliography/pl.bib (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/.gitignore (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/existing.tex (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/features.tex (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/future.tex (modified) (1 diff)
-
doc/theses/andrew_beach_MMath/implement.tex (modified) (6 diffs)
-
doc/theses/andrew_beach_MMath/unwinding.tex (modified) (3 diffs)
-
doc/theses/andrew_beach_MMath/uw-ethesis.tex (modified) (3 diffs)
-
libcfa/src/Makefile.am (modified) (1 diff)
-
libcfa/src/bits/collection.hfa (modified) (2 diffs)
-
libcfa/src/bits/sequence.hfa (modified) (8 diffs)
-
libcfa/src/bits/weakso_locks.cfa (deleted)
-
libcfa/src/bits/weakso_locks.hfa (deleted)
-
libcfa/src/concurrency/locks.cfa (modified) (3 diffs)
-
libcfa/src/concurrency/locks.hfa (modified) (2 diffs)
-
libcfa/src/stdlib.hfa (modified) (2 diffs)
-
src/Parser/parser.yy (modified) (28 diffs)
-
tests/.expect/attributes.nast.x64.txt (modified) (2 diffs)
-
tests/.expect/attributes.nast.x86.txt (modified) (2 diffs)
-
tests/.expect/attributes.oast.x64.txt (modified) (2 diffs)
-
tests/.expect/attributes.oast.x86.txt (modified) (2 diffs)
-
tests/alloc2.cfa (modified) (1 diff)
-
tests/attributes.cfa (modified) (2 diffs)
-
tests/linking/.expect/weakso_nothd.txt (deleted)
-
tests/linking/weakso_nothd.cfa (deleted)
-
tools/prettyprinter/Makefile.am (modified) (3 diffs)
-
tools/prettyprinter/ParserTypes.h (modified) (1 diff)
-
tools/prettyprinter/parser.yy (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
.gitignore
rf99f5ba rcd70477 79 79 # generated by npm 80 80 package-lock.json 81 82 # generated by benchmark83 benchmark/Cargo.toml -
doc/LaTeXmacros/common.tex
rf99f5ba rcd70477 11 11 %% Created On : Sat Apr 9 10:06:17 2016 12 12 %% Last Modified By : Peter A. Buhr 13 %% Last Modified On : Thu Jan 28 19:01:57 202114 %% Update Count : 4 9413 %% Last Modified On : Mon Oct 5 09:34:46 2020 14 %% Update Count : 464 15 15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 16 16 … … 32 32 \setlist[enumerate]{listparindent=\parindent}% global 33 33 \setlist[enumerate,2]{leftmargin=\parindent,labelsep=*,align=parleft,label=\alph*.}% local 34 \setlist[description]{ topsep=0.5ex,itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex}34 \setlist[description]{itemsep=0pt,listparindent=\parindent,leftmargin=\parindent,labelsep=1.5ex} 35 35 36 36 % Names used in the document. … … 89 89 \newcommand{\italic}[1]{\emph{\hyperpage{#1}}} 90 90 \newcommand{\Definition}[1]{\textbf{\hyperpage{#1}}} 91 \newcommand{\see}[1]{ (see #1)}91 \newcommand{\see}[1]{\emph{see}~#1} 92 92 93 93 % Define some commands that produce formatted index entries suitable for cross-references. … … 229 229 \usepackage{listings} % format program code 230 230 \usepackage{lstlang} 231 \usepackage{calc} % latex arithmetic232 233 231 \makeatletter 232 234 233 \newcommand{\LstBasicStyle}[1]{{\lst@basicstyle{#1}}} 235 234 \newcommand{\LstKeywordStyle}[1]{{\lst@basicstyle{\lst@keywordstyle{#1}}}} … … 266 265 showlines=true, % show blank lines at end of code 267 266 aboveskip=4pt, % spacing above/below code block 268 belowskip=-2pt, 269 numberstyle=\footnotesize\sf, % numbering style 267 belowskip=3pt, 270 268 % replace/adjust listing characters that look bad in sanserif 271 269 literate={-}{\makebox[1ex][c]{\raisebox{0.4ex}{\rule{0.75ex}{0.1ex}}}}1 {^}{\raisebox{0.6ex}{$\scriptscriptstyle\land\,$}}1 … … 295 293 language=CFA, 296 294 escapechar=\$, % LaTeX escape in CFA code 297 mathescape=false, % LaTeX math escape in CFA code $...$298 295 moredelim=**[is][\color{red}]{@}{@}, % red highlighting @...@ 299 296 }% lstset -
doc/bibliography/pl.bib
rf99f5ba rcd70477 1797 1797 } 1798 1798 1799 @article{Delisle 20,1799 @article{Delisle19, 1800 1800 keywords = {concurrency, Cforall}, 1801 1801 contributer = {pabuhr@plg}, 1802 1802 author = {Thierry Delisle and Peter A. Buhr}, 1803 1803 title = {Advanced Control-flow and Concurrency in \textsf{C}$\mathbf{\forall}$}, 1804 year = 20 20,1804 year = 2019, 1805 1805 journal = spe, 1806 pages = {1-38}, 1807 note = {\href{https://doi-org.proxy.lib.uwaterloo.ca/10.1002/spe.2925}{https://\-doi-org.proxy.lib.uwaterloo.ca/\-10.1002/\-spe.2925}}, 1808 note = {}, 1806 pages = {1-33}, 1807 note = {submitted}, 1809 1808 } 1810 1809 -
doc/theses/andrew_beach_MMath/.gitignore
rf99f5ba rcd70477 3 3 4 4 # Final Files: 5 *.pdf5 thesis.pdf 6 6 7 7 # The Makefile here is not generated. -
doc/theses/andrew_beach_MMath/existing.tex
rf99f5ba rcd70477 1 \chapter{\texorpdfstring{\CFA Existing Features}{Cforall Existing Features}} 2 3 \CFA (C-for-all)~\cite{Cforall} is an open-source project extending ISO C with 4 modern safety and productivity features, while still ensuring backwards 5 compatibility with C and its programmers. \CFA is designed to have an 6 orthogonal feature-set based closely on the C programming paradigm 7 (non-object-oriented) and these features can be added incrementally to an 8 existing C code-base allowing programmers to learn \CFA on an as-needed basis. 9 10 Only those \CFA features pertinent to this thesis are discussed. Many of the 11 \CFA syntactic and semantic features used in the thesis should be fairly 12 obvious to the reader. 13 14 \section{\texorpdfstring{Overloading and \lstinline|extern|}{Overloading and extern}} 15 \CFA has extensive overloading, allowing multiple definitions of the same name 16 to be defined.~\cite{Moss18} 17 \begin{cfa} 18 char i; int i; double i; $\C[3.75in]{// variable overload}$ 19 int f(); double f(); $\C{// return overload}$ 20 void g( int ); void g( double ); $\C{// parameter overload}\CRT$ 21 \end{cfa} 22 This feature requires name mangling so the assembly symbols are unique for 23 different overloads. For compatibility with names in C, there is also a syntax 24 to disable name mangling. These unmangled names cannot be overloaded but act as 25 the interface between C and \CFA code. The syntax for disabling/enabling 26 mangling is: 27 \begin{cfa} 28 // name mangling 29 int i; // _X1ii_1 30 @extern "C"@ { // no name mangling 31 int j; // j 32 @extern "Cforall"@ { // name mangling 33 int k; // _X1ki_1 34 } 35 // no name mangling 36 } 37 // name mangling 38 \end{cfa} 39 Both forms of @extern@ affect all the declarations within their nested lexical 40 scope and transition back to the previous mangling state when the lexical scope 41 ends. 42 43 \section{Reference Type} 44 \CFA adds a rebindable reference type to C, but more expressive than the \CC 45 reference. Multi-level references are allowed and act like auto-dereferenced 46 pointers using the ampersand (@&@) instead of the pointer asterisk (@*@). \CFA 47 references may also be mutable or non-mutable. If mutable, a reference variable 48 may be assigned to using the address-of operator (@&@), which converts the 49 reference to a pointer. 50 \begin{cfa} 51 int i, j; 52 int @&@ ri = i, @&&@ rri = ri; 53 rri = 3; // auto-dereference assign to i 54 @&@ri = @&@j; // rebindable 55 ri = 5; // assign to j 56 \end{cfa} 1 \chapter{\CFA Existing Features} 2 3 \CFA (C-for-all)~\cite{Cforall} is an open-source project extending ISO C with modern safety and productivity features, while still ensuring backwards compatibility with C and its programmers. 4 \CFA is designed to have an orthogonal feature-set based closely on the C programming paradigm (non-object-oriented) and these features can be added incrementally to an existing C code-base allowing programmers to learn \CFA on an as-needed basis. 5 6 \section{Overloading and extern} 7 Cforall has overloading, allowing multiple definitions of the same name to 8 be defined.~\cite{Moss18} 9 10 This also adds name mangling so that the assembly symbols are unique for 11 different overloads. For compatability with names in C there is also a 12 syntax to diable the name mangling. These unmangled names cannot be overloaded 13 but act as the interface between C and \CFA code. 14 15 The syntax for disabling mangling is: 16 \begin{cfa} 17 extern "C" { 18 ... 19 } 20 \end{cfa} 21 22 To re-enable mangling once it is disabled the syntax is: 23 \begin{cfa} 24 extern "Cforall" { 25 ... 26 } 27 \end{cfa} 28 29 Both should occur at the declaration level and effect all the declarations 30 in @...@. Neither care about the state of mangling when they begin 31 and will return to that state after the group is finished. So re-enabling 32 is only used to nest areas of mangled and unmangled declarations. 33 34 \section{References} 35 \CFA adds references to C. These are auto-dereferencing pointers and use the 36 same syntax as pointers except they use ampersand (@&@) instead of 37 the asterisk (@*@). They can also be constaint or mutable, if they 38 are mutable they may be assigned to by using the address-of operator 39 (@&@) which converts them into a pointer. 57 40 58 41 \section{Constructors and Destructors} 59 42 60 43 Both constructors and destructors are operators, which means they are just 61 functions with special operator names rather than type names in \CC. The 62 special operator names may be used to call the functions explicitly (not 63 allowed in \CC for constructors). 64 65 In general, operator names in \CFA are constructed by bracketing an operator 66 token with @?@, which indicates where the arguments. For example, infixed 67 multiplication is @?*?@ while prefix dereference is @*?@. This syntax make it 68 easy to tell the difference between prefix operations (such as @++?@) and 69 post-fix operations (@?++@). 70 71 The special name for a constructor is @?{}@, which comes from the 72 initialization syntax in C. The special name for a destructor is @^{}@, where 73 the @^@ has no special meaning. 74 % I don't like the \^{} symbol but $^\wedge$ isn't better. 75 \begin{cfa} 76 struct T { ... }; 77 void ?@{}@(@T &@ this, ...) { ... } // constructor 78 void ?@^{}@(@T &@ this, ...) { ... } // destructor 79 { 80 T s = @{@ ... @}@; // same constructor/initialization braces 81 } // destructor call automatically generated 82 \end{cfa} 83 The first parameter is a reference parameter to the type for the 84 constructor/destructor. Destructors may have multiple parameters. The compiler 85 implicitly matches an overloaded constructor @void ^?{}(T &, ...);@ to an 86 object declaration with associated initialization, and generates a construction 87 call after the object is allocated. When an object goes out of scope, the 88 matching overloaded destructor @void ^?{}(T &);@ is called. Without explicit 89 definition, \CFA creates a default and copy constructor, destructor and 90 assignment (like \CC). It is possible to define constructors/destructors for 91 basic and existing types. 44 functions with special names. The special names are used to define them and 45 may be used to call the functions expicately. The \CFA special names are 46 constructed by taking the tokens in the operators and putting @?@ where 47 the arguments would go. So multiplication is @?*?@ while dereference 48 is @*?@. This also make it easy to tell the difference between 49 pre-fix operations (such as @++?@) and post-fix operations 50 (@?++@). 51 52 The special name for contructors is @?{}@, which comes from the 53 initialization syntax in C. The special name for destructors is 54 @^{}@. % I don't like the \^{} symbol but $^\wedge$ isn't better. 55 56 Any time a type T goes out of scope the destructor matching 57 @void ^?{}(T &);@ is called. In theory this is also true of 58 primitive types such as @int@, but in practice those are no-ops and 59 are usually omitted for optimization. 92 60 93 61 \section{Polymorphism} 94 \CFA uses parametric polymorphism to create functions and types that are 95 defined over multiple types. \CFA polymorphic declarations serve the same role 96 as \CC templates or Java generics. The ``parametric'' means the polymorphism is 97 accomplished by passing argument operations to associate \emph{parameters} at 98 the call site, and these parameters are used in the function to differentiate 99 among the types the function operates on. 100 101 Polymorphic declarations start with a universal @forall@ clause that goes 102 before the standard (monomorphic) declaration. These declarations have the same 103 syntax except they may use the universal type names introduced by the @forall@ 104 clause. For example, the following is a polymorphic identity function that 105 works on any type @T@: 106 \begin{cfa} 107 @forall( T )@ @T@ identity( @T@ val ) { return val; } 108 int forty_two = identity( 42 ); // T bound to int, forty_two == 42 109 \end{cfa} 110 111 To allow a polymorphic function to be separately compiled, the type @T@ must be 112 constrained by the operations used on @T@ in the function body. The @forall@ 113 clauses is augmented with a list of polymorphic variables (local type names) 114 and assertions (constraints), which represent the required operations on those 115 types used in a function, \eg: 116 \begin{cfa} 117 forall( T @| { void do_once(T); }@) // assertion 118 void do_twice(T value) { 119 do_once(value); 120 do_once(value); 121 } 122 void do_once(int i) { ... } // provide assertion 123 int i; 124 do_twice(i); // implicitly pass assertion do_once to do_twice 125 \end{cfa} 126 Any object with a type fulfilling the assertion may be passed as an argument to 127 a @do_twice@ call. 128 129 A polymorphic function can be used in the same way as a normal function. The 130 polymorphic variables are filled in with concrete types and the assertions are 131 checked. An assertion is checked by verifying each assertion operation (with 132 all the variables replaced with the concrete types from the arguments) is 133 defined at a call site. 134 135 Note, a function named @do_once@ is not required in the scope of @do_twice@ to 136 compile it, unlike \CC template expansion. Furthermore, call-site inferencing 137 allows local replacement of the most specific parametric functions needs for a 138 call. 139 \begin{cfa} 140 void do_once(double y) { ... } // global 62 \CFA uses polymorphism to create functions and types that are defined over 63 different types. \CFA polymorphic declarations serve the same role as \CC 64 templates or Java generics. 65 66 Polymorphic declaractions start with a forall clause that goes before the 67 standard (monomorphic) declaration. These declarations have the same syntax 68 except that you may use the names introduced by the forall clause in them. 69 70 Forall clauses are written @forall( ... )@ where @...@ becomes 71 the list of polymorphic variables (local type names) and assertions, which 72 repersent required operations on those types. 73 74 \begin{cfa} 75 forall(dtype T | { void do_once(T &); }) 76 void do_twice(T & value) { 77 do_once(value); 78 do_once(value); 79 } 80 \end{cfa} 81 82 A polymorphic function can be used in the same way normal functions are. 83 The polymorphics variables are filled in with concrete types and the 84 assertions are checked. An assertion checked by seeing if that name of that 85 type (with all the variables replaced with the concrete types) is defined at 86 the the call site. 87 88 As an example, even if no function named @do_once@ is not defined 89 near the definition of @do_twice@ the following code will work. 90 \begin{cfa} 141 91 int quadruple(int x) { 142 void do_once(int y) { y = y * 2; } // local 143 do_twice(x); // using local "do_once" 144 return x; 145 } 146 \end{cfa} 147 Specifically, the complier deduces that @do_twice@'s T is an integer from the 148 argument @x@. It then looks for the most specific definition matching the 149 assertion, which is the nested integral @do_once@ defined within the 150 function. The matched assertion function is then passed as a function pointer 151 to @do_twice@ and called within it. 152 153 To avoid typing long lists of assertions, constraints can be collect into 154 convenient packages called a @trait@, which can then be used in an assertion 155 instead of the individual constraints. 156 \begin{cfa} 157 trait done_once(T) { 158 void do_once(T); 159 } 160 \end{cfa} 161 and the @forall@ list in the previous example is replaced with the trait. 162 \begin{cfa} 163 forall(dtype T | @done_once(T)@) 164 \end{cfa} 165 In general, a trait can contain an arbitrary number of assertions, both 166 functions and variables, and are usually used to create a shorthand for, and 167 give descriptive names to, common groupings of assertions describing a certain 168 functionality, like @sumable@, @listable@, \etc. 169 170 Polymorphic structures and unions are defined by qualifying the aggregate type 171 with @forall@. The type variables work the same except they are used in field 172 declarations instead of parameters, returns, and local variable declarations. 92 void do_once(int & y) { 93 y = y * 2; 94 } 95 do_twice(x); 96 return x; 97 } 98 \end{cfa} 99 This is not the recommended way to implement a quadruple function but it 100 does work. The complier will deduce that @do_twice@'s T is an 101 integer from the argument. It will then look for a definition matching the 102 assertion which is the @do_once@ defined within the function. That 103 function will be passed in as a function pointer to @do_twice@ and 104 called within it. 105 106 To avoid typing out long lists of assertions again and again there are also 107 traits which collect assertions into convenent packages that can then be used 108 in assertion lists instead of all of their components. 109 \begin{cfa} 110 trait done_once(dtype T) { 111 void do_once(T &); 112 } 113 \end{cfa} 114 115 After this the forall list in the previous example could instead be written 116 with the trait instead of the assertion itself. 117 \begin{cfa} 118 forall(dtype T | done_once(T)) 119 \end{cfa} 120 121 Traits can have arbitrary number of assertions in them and are usually used to 122 create short hands for, and give descriptive names to, commond groupings of 123 assertions. 124 125 Polymorphic structures and unions may also be defined by putting a forall 126 clause before the declaration. The type variables work the same way except 127 are now used in field declaractions instead of parameters and local variables. 128 173 129 \begin{cfa} 174 130 forall(dtype T) 175 131 struct node { 176 node(T) * next; // generic linked node 177 T * data; 178 } 179 \end{cfa} 180 The generic type @node(T)@ is an example of a polymorphic-type usage. Like \CC 181 templates usage, a polymorphic-type usage must specify a type parameter. 182 183 There are many other polymorphism features in \CFA but these are the ones used 184 by the exception system. 132 node(T) * next; 133 T * data; 134 } 135 \end{cfa} 136 137 The @node(T)@ is a use of a polymorphic structure. Polymorphic types 138 must be provided their polymorphic parameters. 139 140 There are many other features of polymorphism that have not given here but 141 these are the ones used by the exception system. 185 142 186 143 \section{Concurrency} 187 \CFA has a number of concurrency features: @thread@, @monitor@, @mutex@ 188 parameters, @coroutine@ and @generator@. The two features that interact with 189 the exception system are @thread@ and @coroutine@; they and their supporting 190 constructs are described here. 191 192 \subsection{Coroutine} 193 A coroutine is a type with associated functions, where the functions are not 194 required to finish execution when control is handed back to the caller. Instead 195 they may suspend execution at any time and be resumed later at the point of 196 last suspension. (Generators are stackless and coroutines are stackful.) These 197 types are not concurrent but share some similarities along with common 198 underpinnings, so they are combined with the \CFA threading library. Further 199 discussion in this section only refers to the coroutine because generators are 200 similar. 201 202 In \CFA, a coroutine is created using the @coroutine@ keyword, which is an 203 aggregate type like @struct,@ except the structure is implicitly modified by 204 the compiler to satisfy the @is_coroutine@ trait; hence, a coroutine is 205 restricted by the type system to types that provide this special trait. The 206 coroutine structure acts as the interface between callers and the coroutine, 207 and its fields are used to pass information in and out of coroutine interface 208 functions. 209 210 Here is a simple example where a single field is used to pass (communicate) the 211 next number in a sequence. 144 145 \CFA has a number of concurrency features, @thread@s, 146 @monitor@s and @mutex@ parameters, @coroutine@s and 147 @generator@s. The two features that interact with the exception system 148 are @thread@s and @coroutine@s; they and their supporting 149 constructs will be described here. 150 151 \subsection{Coroutines} 152 Coroutines are routines that do not have to finish execution to hand control 153 back to their caller, instead they may suspend their execution at any time 154 and resume it later. 155 Coroutines are not true concurrency but share some similarities and many of 156 the same underpinnings and so are included as part of the \CFA threading 157 library. 158 159 In \CFA coroutines are created using the @coroutine@ keyword which 160 works just like @struct@ except that the created structure will be 161 modified by the compiler to satify the @is_coroutine@ trait. 162 163 These structures act as the interface between callers and the coroutine, 164 the fields are used to pass information in and out. Here is a simple example 165 where the single field is used to pass the next number in a sequence out. 212 166 \begin{cfa} 213 167 coroutine CountUp { 214 unsigned int next; // communication variable 215 } 216 CountUp countup; 217 \end{cfa} 218 Each coroutine has @main@ function, which takes a reference to a coroutine 219 object and returns @void@. 220 \begin{cfa}[numbers=left] 221 void main(@CountUp & this@) { // argument matches trait is_coroutine 222 unsigned int up = 0; // retained between calls 223 while (true) { 224 next = up; // make "up" available outside function 225 @suspend;@$\label{suspend}$ 226 up += 1; 227 } 228 } 229 \end{cfa} 230 In this function, or functions called by this function (helper functions), the 231 @suspend@ statement is used to return execution to the coroutine's caller 232 without terminating the coroutine. 233 234 A coroutine is resumed by calling the @resume@ function, \eg @resume(countup)@. 235 The first resume calls the @main@ function at the top. Thereafter, resume calls 236 continue a coroutine in the last suspended function after the @suspend@ 237 statement, in this case @main@ line~\ref{suspend}. The @resume@ function takes 238 a reference to the coroutine structure and returns the same reference. The 239 return value allows easy access to communication variables defined in the 240 coroutine object. For example, the @next@ value for coroutine object @countup@ 241 is both generated and collected in the single expression: 242 @resume(countup).next@. 168 unsigned int next; 169 } 170 \end{cfa} 171 172 The routine part of the coroutine is a main function for the coroutine. It 173 takes a reference to a coroutine object and returns nothing. In this function, 174 and any functions called by this function, the suspend statement may be used 175 to return execution to the coroutine's caller. When control returns to the 176 function it continue from that same suspend statement instead of at the top 177 of the function. 178 \begin{cfa} 179 void main(CountUp & this) { 180 unsigned int next = 0; 181 while (true) { 182 this.next = next; 183 suspend; 184 next = next + 1; 185 } 186 } 187 \end{cfa} 188 189 Control is passed to the coroutine with the resume function. This includes the 190 first time when the coroutine is starting up. The resume function takes a 191 reference to the coroutine structure and returns the same reference. The 192 return value is for easy access to communication variables. For example the 193 next value from a count-up can be generated and collected in a single 194 expression: @resume(count).next@. 243 195 244 196 \subsection{Monitors and Mutex} 245 Concurrency does not guarantee ordering; without ordering results are 246 non-deterministic. To claw back ordering, \CFA uses monitors and @mutex@ 247 (mutual exclusion) parameters. A monitor is another kind of aggregate, where 248 the compiler implicitly inserts a lock and instances are compatible with 249 @mutex@ parameters. 250 251 A function that requires deterministic (ordered) execution, acquires mutual 252 exclusion on a monitor object by qualifying an object reference parameter with 253 @mutex@. 254 \begin{cfa} 255 void example(MonitorA & @mutex@ argA, MonitorB & @mutex@ argB); 256 \end{cfa} 257 When the function is called, it implicitly acquires the monitor lock for all of 258 the mutex parameters without deadlock. This semantics means all functions with 259 the same mutex type(s) are part of a critical section for objects of that type 260 and only one runs at a time. 197 198 True concurrency does not garrenty ordering. To get some of that ordering back 199 \CFA uses monitors and mutex (mutual exclution) parameters. A monitor is 200 another special declaration that contains a lock and is compatable with mutex 201 parameters. 202 203 Function parameters can have the @mutex@ qualifiers on reference 204 arguments, for example @void example(a_monitor & mutex arg);@. When the 205 function is called it will acquire the lock on all of the mutex parameters. 206 207 This means that all functions that mutex on a type are part of a critical 208 section and only one will ever run at a time. 261 209 262 210 \subsection{Threads} 263 Functions, generators, and coroutines are sequential so there is only a single 264 (but potentially sophisticated) execution path in a program. Threads introduce 265 multiple execution paths that continue independently. 266 267 For threads to work safely with objects requires mutual exclusion using 268 monitors and mutex parameters. For threads to work safely with other threads, 269 also requires mutual exclusion in the form of a communication rendezvous, which 270 also supports internal synchronization as for mutex objects. For exceptions 271 only the basic two basic operations are important: thread fork and join. 272 273 Threads are created like coroutines with an associated @main@ function: 211 While coroutines allow new things to be done with a single execution path 212 threads actually introduce new paths of execution that continue independently. 213 Now for threads to work together their must be some communication between them 214 and that means the timing of certain operations does have to be known. There 215 or various means of syncronization and mutual exclution provided by \CFA but 216 for exceptions only the basic two -- fork and join -- are needed. 217 218 Threads are created like coroutines except the keyword is changed: 274 219 \begin{cfa} 275 220 thread StringWorker { 276 const char * input;277 int result;221 const char * input; 222 int result; 278 223 }; 224 279 225 void main(StringWorker & this) { 280 const char * localCopy = this.input; 281 // ... do some work, perhaps hashing the string ... 282 this.result = result; 283 } 284 { 285 StringWorker stringworker; // fork thread running in "main" 286 } // implicitly join with thread $\(\Rightarrow\)$ wait for completion 287 \end{cfa} 288 The thread main is where a new thread starts execution after a fork operation 289 and then the thread continues executing until it is finished. If another thread 290 joins with an executing thread, it waits until the executing main completes 291 execution. In other words, everything a thread does is between a fork and join. 292 293 From the outside, this behaviour is accomplished through creation and 294 destruction of a thread object. Implicitly, fork happens after a thread 295 object's constructor is run and join happens before the destructor runs. Join 296 can also be specified explicitly using the @join@ function to wait for a 297 thread's completion independently from its deallocation (\ie destructor 298 call). If @join@ is called explicitly, the destructor does not implicitly join. 226 const char * localCopy = this.input; 227 // ... do some work, perhaps hashing the string ... 228 this.result = result; 229 } 230 \end{cfa} 231 The main function will start executing after the fork operation and continue 232 executing until it is finished. If another thread joins with this one it will 233 wait until main has completed execution. In other words everything the thread 234 does is between fork and join. 235 236 From the outside this is the creation and destruction of the thread object. 237 Fork happens after the constructor is run and join happens before the 238 destructor runs. Join also happens during the @join@ function which 239 can be used to join a thread earlier. If it is used the destructor does not 240 join as that has already been completed. -
doc/theses/andrew_beach_MMath/features.tex
rf99f5ba rcd70477 1 \chapter{Exception Features} 2 3 This chapter covers the design and user interface of the \CFA 4 exception-handling mechanism. 5 6 \section{Virtuals} 7 Virtual types and casts are not required for a basic exception-system but are 8 useful for advanced exception features. However, \CFA is not object-oriented so 9 there is no obvious concept of virtuals. Hence, to create advanced exception 10 features for this work, I needed to designed and implemented a virtual-like 11 system for \CFA. 12 13 Object-oriented languages often organized exceptions into a simple hierarchy, 14 \eg Java. 15 \begin{center} 16 \setlength{\unitlength}{4000sp}% 17 \begin{picture}(1605,612)(2011,-1951) 18 \put(2100,-1411){\vector(1, 0){225}} 19 \put(3450,-1411){\vector(1, 0){225}} 20 \put(3550,-1411){\line(0,-1){225}} 21 \put(3550,-1636){\vector(1, 0){150}} 22 \put(3550,-1636){\line(0,-1){225}} 23 \put(3550,-1861){\vector(1, 0){150}} 24 \put(2025,-1490){\makebox(0,0)[rb]{\LstBasicStyle{exception}}} 25 \put(2400,-1460){\makebox(0,0)[lb]{\LstBasicStyle{arithmetic}}} 26 \put(3750,-1460){\makebox(0,0)[lb]{\LstBasicStyle{underflow}}} 27 \put(3750,-1690){\makebox(0,0)[lb]{\LstBasicStyle{overflow}}} 28 \put(3750,-1920){\makebox(0,0)[lb]{\LstBasicStyle{zerodivide}}} 29 \end{picture}% 30 \end{center} 31 The hierarchy provides the ability to handle an exception at different degrees 32 of specificity (left to right). Hence, it is possible to catch a more general 33 exception-type in higher-level code where the implementation details are 34 unknown, which reduces tight coupling to the lower-level implementation. 35 Otherwise, low-level code changes require higher-level code changes, \eg, 36 changing from raising @underflow@ to @overflow@ at the low level means changing 37 the matching catch at the high level versus catching the general @arithmetic@ 38 exception. In detail, each virtual type may have a parent and can have any 39 number of children. A type's descendants are its children and its children's 40 descendants. A type may not be its own descendant. 41 42 The exception hierarchy allows a handler (@catch@ clause) to match multiple 43 exceptions, \eg a base-type handler catches both base and derived 44 exception-types. 45 \begin{cfa} 46 try { 47 ... 48 } catch(arithmetic &) { 49 ... // handle arithmetic, underflow, overflow, zerodivide 50 } 51 \end{cfa} 52 Most exception mechanisms perform a linear search of the handlers and select 53 the first matching handler, so the order of handers is now important because 54 matching is many to one. 55 56 Each virtual type needs an associated virtual table. A virtual table is a 57 structure with fields for all the virtual members of a type. A virtual type has 58 all the virtual members of its parent and can add more. It may also update the 59 values of the virtual members and often does. 60 61 While much of the virtual infrastructure is created, it is currently only used 62 internally for exception handling. The only user-level feature is the virtual 63 cast, which is the same as the \CC \lstinline[language=C++]|dynamic_cast|. 64 \label{p:VirtualCast} 65 \begin{cfa} 1 \chapter{Features} 2 3 This chapter covers the design and user interface of the \CFA exception 4 handling mechanism. 5 6 \section{Virtual Casts} 7 8 Virtual casts and virtual types are not truly part of the exception system but 9 they did not exist in \CFA and are useful in exceptions. So a minimal version 10 of they virtual system was designed and implemented. 11 12 Virtual types are organized in simple hierarchies. Each virtual type may have 13 a parent and can have any number of children. A type's descendants are its 14 children and its children's descendants. A type may not be its own descendant. 15 16 Each virtual type has an associated virtual table type. A virtual table is a 17 structure that has fields for all the virtual members of a type. A virtual 18 type has all the virtual members of its parent and can add more. It may also 19 update the values of the virtual members and should in many cases. 20 21 Except for virtual casts, this is only used internally in the exception 22 system. There is no general purpose interface for the other features. A 23 a virtual cast has the following syntax: 24 25 \begin{lstlisting} 66 26 (virtual TYPE)EXPRESSION 67 \end{cfa} 68 Note, the syntax and semantics matches a C-cast, rather than the unusual \CC 69 syntax for special casts. Both the type of @EXPRESSION@ and @TYPE@ must be a 70 pointer to a virtual type. The cast dynamically checks if the @EXPRESSION@ type 71 is the same or a subtype of @TYPE@, and if true, returns a pointer to the 72 @EXPRESSION@ object, otherwise it returns @0p@ (null pointer). 73 74 \section{Exception} 27 \end{lstlisting} 28 29 This has the same precedence as a traditional C-cast and can be used in the 30 same places. This will convert the result of EXPRESSION to the type TYPE. Both 31 the type of EXPRESSION and TYPE must be pointers to virtual types. 32 33 The cast is checked and will either return the original value or null, based 34 on the result of the check. The check is does the object pointed at have a 35 type that is a descendant of the target type. If it is the result is the 36 pointer, otherwise the result is null. 37 38 \section{Exceptions} 75 39 % Leaving until later, hopefully it can talk about actual syntax instead 76 40 % of my many strange macros. Syntax aside I will also have to talk about the 77 41 % features all exceptions support. 78 42 79 Exceptions are defined by the trait system; there are a series of traits, and 80 if a type satisfies them, then it can be used as an exception. The following 81 is the base trait all exceptions need to match. 82 \begin{cfa} 83 trait is_exception(exceptT &, virtualT &) { 84 virtualT const & @get_exception_vtable@(exceptT *); 43 \subsection{Exception Traits} 44 Exceptions are defined by the trait system; there are a series of traits and 45 if a type satisfies them then they can be used as exceptions. 46 47 \begin{lstlisting} 48 trait is_exception(dtype exceptT, dtype virtualT) { 49 virtualT const & get_exception_vtable(exceptT *); 85 50 }; 86 \end{cfa} 87 The function takes any pointer, including the null pointer, and returns a 88 reference to the virtual-table object. Defining this function also establishes 89 the virtual type and a virtual-table pair to the \CFA type-resolver and 90 promises @exceptT@ is a virtual type and a child of the base exception-type. 91 92 \PAB{I do not understand this paragraph.} 93 One odd thing about @get_exception_vtable@ is that it should always be a 94 constant function, returning the same value regardless of its argument. A 95 pointer or reference to the virtual table instance could be used instead, 96 however using a function has some ease of implementation advantages and allows 97 for easier disambiguation because the virtual type name (or the address of an 98 instance that is in scope) can be used instead of the mangled virtual table 99 name. Also note the use of the word ``promise'' in the trait 100 description. Currently, \CFA cannot check to see if either @exceptT@ or 101 @virtualT@ match the layout requirements. This is considered part of 102 @get_exception_vtable@'s correct implementation. 103 104 \section{Raise} 105 \CFA provides two kinds of exception raise: termination 106 \see{\VRef{s:Termination}} and resumption \see{\VRef{s:Resumption}}, which are 107 specified with the following traits. 108 \begin{cfa} 51 \end{lstlisting} 52 This is the base trait that all exceptions need to match. 53 The single function takes any pointer (including the null pointer) and 54 returns a reference to the virtual table instance. Defining this function 55 also establishes the virtual type and virtual table pair to the resolver 56 and promises that @exceptT@ is a virtual type and a child of the 57 base exception type. 58 59 One odd thing about @get_exception_vtable@ is that it should always 60 be a constant function, returning the same value regardless of its argument. 61 A pointer or reference to the virtual table instance could be used instead, 62 however using a function has some ease of implementation advantages and 63 allows for easier disambiguation because the virtual type name (or the 64 address of an instance that is in scope) can be used instead of the mangled 65 virtual table name. 66 67 Also note the use of the word ``promise" in the trait description. \CFA 68 cannot currently check to see if either @exceptT@ or 69 @virtualT@ match the layout requirements. Currently this is 70 considered part of @get_exception_vtable@'s correct implementation. 71 72 \begin{lstlisting} 109 73 trait is_termination_exception( 110 exceptT &, virtualT &| is_exception(exceptT, virtualT)) {111 void @defaultTerminationHandler@(exceptT &);74 dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) { 75 void defaultTerminationHandler(exceptT &); 112 76 }; 113 \end{cfa} 114 The function is required to allow a termination raise, but is only called if a 115 termination raise does not find an appropriate handler. 116 117 Allowing a resumption raise is similar. 118 \begin{cfa} 77 \end{lstlisting} 78 The only additional function required to make the exception usable with 79 termination is a default handler. This function is called whenever a 80 termination throw on an exception of this type is preformed and no handler 81 is found. 82 83 \begin{lstlisting} 119 84 trait is_resumption_exception( 120 exceptT &, virtualT &| is_exception(exceptT, virtualT)) {121 void @defaultResumptionHandler@(exceptT &);85 dtype exceptT, dtype virtualT | is_exception(exceptT, virtualT)) { 86 void defaultResumptionHandler(exceptT &); 122 87 }; 123 \end{cfa} 124 The function is required to allow a resumption raise, but is only called if a 125 resumption raise does not find an appropriate handler. 126 127 Finally there are three convenience macros for referring to the these traits: 128 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. Each 129 takes the virtual type's name, and for polymorphic types only, the 130 parenthesized list of polymorphic arguments. These macros do the name mangling 131 to get the virtual-table name and provide the arguments to both sides 132 \PAB{What's a ``side''?} 133 134 \subsection{Termination} 135 \label{s:Termination} 136 137 Termination raise, called ``throw'', is familiar and used in most programming 138 languages with exception handling. The semantics of termination is: search the 139 stack for a matching handler, unwind the stack frames to the matching handler, 140 execute the handler, and continue execution after the handler. Termination is 141 used when execution \emph{cannot} return to the throw. To continue execution, 142 the program must \emph{recover} in the handler from the failed (unwound) 143 execution at the raise to safely proceed after the handler. 144 145 A termination raise is started with the @throw@ statement: 146 \begin{cfa} 88 \end{lstlisting} 89 Creating a resumption exception is exactly the same except for resumption. 90 The name change reflects that and the function is called when a resumption 91 throw on an exception of this type is preformed and no handler is found. 92 93 Finally there are three additional macros that can be used to refer to the 94 these traits. They are @IS_EXCEPTION@, 95 @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. 96 Each takes the virtual type's name and, for polymorphic types only, the 97 parenthesized list of polymorphic arguments. These do the name mangling to 98 get the virtual table name and provide the arguments to both sides. 99 100 \section{Termination} 101 102 Termination exception throws are likely the most familiar kind, as they are 103 used in several popular programming languages. A termination will throw an 104 exception, search the stack for a handler, unwind the stack to where the 105 handler is defined, execute the handler and then continue execution after 106 the handler. They are used when execution cannot continue here. 107 108 Termination has two pieces of syntax it uses. The first is the throw: 109 \begin{lstlisting} 147 110 throw EXPRESSION; 148 \end{ cfa}149 The expression must return a termination-exception reference, where the 150 termination exception has a type with a @void defaultTerminationHandler(T &)@ 151 (default handler) defined. The handler is found at the call site using \CFA's 152 trait system and passed into the exception system along with the exception 153 itself. 154 155 At runtime, a representation of the exception type and an instance of the 156 exception type is copied into managed memory (heap) to ensure it remains in 157 scope during unwinding. It is the user's responsibility to ensure the original 158 exception object at the throw is freed when it goes out of scope. Being 159 allocated on the stack is sufficient for this.160 161 Then the exception system searchesthe stack starting from the throw and162 proceeding towards the base of the stack, from callee to caller. A t each stack163 frame, a check is made for termination handlers defined by the @catch@ clauses 164 of a @try@ statement. 165 \begin{ cfa}111 \end{lstlisting} 112 113 The expression must evaluate to a reference to a termination exception. A 114 termination exception is any exception with a 115 @void defaultTerminationHandler(T &);@ (the default handler) defined 116 on it. The handler is taken from the call sight with \CFA's trait system and 117 passed into the exception system along with the exception itself. 118 119 The exception passed into the system is then copied into managed memory. 120 This is to ensure it remains in scope during unwinding. It is the user's 121 responsibility to make sure the original exception is freed when it goes out 122 of scope. Being allocated on the stack is sufficient for this. 123 124 Then the exception system will search the stack starting from the throw and 125 proceeding towards the base of the stack, from callee to caller. As it goes 126 it will check any termination handlers it finds: 127 128 \begin{lstlisting} 166 129 try { 167 GUARDED_BLOCK 168 } @catch (EXCEPTION_TYPE$\(_1\)$ * NAME)@ { // termination handler 1 169 HANDLER_BLOCK$\(_1\)$ 170 } @catch (EXCEPTION_TYPE$\(_2\)$ * NAME)@ { // termination handler 2 171 HANDLER_BLOCK$\(_2\)$ 130 TRY_BLOCK 131 } catch (EXCEPTION_TYPE * NAME) { 132 HANDLER 172 133 } 173 \end{cfa} 174 The statements in the @GUARDED_BLOCK@ are executed. If those statements, or any 175 functions invoked from those statements, throws an exception, and the exception 176 is not handled by a try statement further up the stack, the termination 177 handlers are searched for a matching exception type from top to bottom. 178 179 Exception matching checks the representation of the thrown exception-type is 180 the same or a descendant type of the exception types in the handler clauses. If 181 there is a match, a pointer to the exception object created at the throw is 182 bound to @NAME@ and the statements in the associated @HANDLER_BLOCK@ are 183 executed. If control reaches the end of the handler, the exception is freed, 184 and control continues after the try statement. 185 186 The default handler visible at the throw statement is used if no matching 187 termination handler is found after the entire stack is searched. At that point, 188 the default handler is called with a reference to the exception object 189 generated at the throw. If the default handler returns, the system default 190 action is executed, which often terminates the program. This feature allows 191 each exception type to define its own action, such as printing an informative 192 error message, when an exception is not handled in the program. 193 194 \subsection{Resumption} 195 \label{s:Resumption} 196 197 Resumption raise, called ``resume'', is as old as termination 198 raise~\cite{Goodenough75} but is less popular. In many ways, resumption is 199 simpler and easier to understand, as it is simply a dynamic call (as in 200 Lisp). The semantics of resumption is: search the stack for a matching handler, 201 execute the handler, and continue execution after the resume. Notice, the stack 202 cannot be unwound because execution returns to the raise point. Resumption is 203 used used when execution \emph{can} return to the resume. To continue 204 execution, the program must \emph{correct} in the handler for the failed 205 execution at the raise so execution can safely continue after the resume. 206 207 A resumption raise is started with the @throwResume@ statement: 208 \begin{cfa} 134 \end{lstlisting} 135 136 This shows a try statement with a single termination handler. The statements 137 in TRY\_BLOCK will be executed when control reaches this statement. While 138 those statements are being executed if a termination exception is thrown and 139 it is not handled by a try statement further up the stack the EHM will check 140 all of the terminations handlers attached to the try block, top to bottom. 141 142 At each handler the EHM will check to see if the thrown exception is a 143 descendant of EXCEPTION\_TYPE. If it is the pointer to the exception is 144 bound to NAME and the statements in HANDLER are executed. If control reaches 145 the end of the handler then it exits the block, the exception is freed and 146 control continues after the try statement. 147 148 The default handler is only used if no handler for the exception is found 149 after the entire stack is searched. When that happens the default handler 150 is called with a reference to the exception as its only argument. If the 151 handler returns control continues from after the throw statement. 152 153 \paragraph{Conditional Catches} 154 155 Catch clauses may also be written as: 156 \begin{lstlisting} 157 catch (EXCEPTION_TYPE * NAME ; CONDITION) 158 \end{lstlisting} 159 This has the same behaviour as a regular catch clause except that if the 160 exception matches the given type the condition is also run. If the result is 161 true only then is this considered a matching handler. If the result is false 162 then the handler does not match and the search continues with the next clause 163 in the try block. 164 165 The condition considers all names in scope at the beginning of the try block 166 to be in scope along with the name introduce in the catch clause itself. 167 168 \paragraph{Re-Throwing} 169 170 You can also re-throw the most recent termination exception with 171 @throw;@. % This is terrible and you should never do it. 172 This can be done in a handler or any function that could be called from a 173 handler. 174 175 This will start another termination throw reusing the exception, meaning it 176 does not copy the exception or allocated any more memory for it. However the 177 default handler is still at the original through and could refer to data that 178 was on the unwound section of the stack. So instead a new default handler that 179 does a program level abort is used. 180 181 \section{Resumption} 182 183 Resumption exceptions are less popular then termination but in many 184 regards are simpler and easier to understand. A resumption throws an exception, 185 searches for a handler on the stack, executes that handler on top of the stack 186 and then continues execution from the throw. These are used when a problem 187 needs to be fixed before execution continues. 188 189 A resumption is thrown with a throw resume statement: 190 \begin{lstlisting} 209 191 throwResume EXPRESSION; 210 \end{cfa} 211 The semantics of the @throwResume@ statement are like the @throw@, but the 212 expression has a type with a @void defaultResumptionHandler(T &)@ (default 213 handler) defined, where the handler is found at the call site by the type 214 system. At runtime, a representation of the exception type and an instance of 215 the exception type is \emph{not} copied because the stack is maintained during 216 the handler search. 217 218 Then the exception system searches the stack starting from the resume and 219 proceeding towards the base of the stack, from callee to caller. At each stack 220 frame, a check is made for resumption handlers defined by the @catchResume@ 221 clauses of a @try@ statement. 222 \begin{cfa} 192 \end{lstlisting} 193 The result of EXPRESSION must be a resumption exception type. A resumption 194 exception type is any type that satisfies the assertion 195 @void defaultResumptionHandler(T &);@ (the default handler). When the 196 statement is executed the expression is evaluated and the result is thrown. 197 198 Handlers are declared using clauses in try statements: 199 \begin{lstlisting} 223 200 try { 224 GUARDED_BLOCK 225 } @catchResume (EXCEPTION_TYPE$\(_1\)$ * NAME)@ { // resumption handler 1 226 HANDLER_BLOCK$\(_1\)$ 227 } @catchResume (EXCEPTION_TYPE$\(_2\)$ * NAME)@ { // resumption handler 2 228 HANDLER_BLOCK$\(_2\)$ 201 TRY_BLOCK 202 } catchResume (EXCEPTION_TYPE * NAME) { 203 HANDLER 229 204 } 230 \end{cfa} 231 The statements in the @GUARDED_BLOCK@ are executed. If those statements, or any 232 functions invoked from those statements, resumes an exception, and the 233 exception is not handled by a try statement further up the stack, the 234 resumption handlers are searched for a matching exception type from top to 235 bottom. (Note, termination and resumption handlers may be intermixed in a @try@ 236 statement but the kind of raise (throw/resume) only matches with the 237 corresponding kind of handler clause.) 238 239 The exception search and matching for resumption is the same as for 240 termination, including exception inheritance. The difference is when control 241 reaches the end of the handler: the resumption handler returns after the resume 242 rather than after the try statement. The resume point assumes the handler has 243 corrected the problem so execution can safely continue. 244 245 Like termination, if no resumption handler is found, the default handler 246 visible at the resume statement is called, and the system default action is 247 executed. 248 249 For resumption, the exception system uses stack marking to partition the 250 resumption search. If another resumption exception is raised in a resumption 251 handler, the second exception search does not start at the point of the 252 original raise. (Remember the stack is not unwound and the current handler is 253 at the top of the stack.) The search for the second resumption starts at the 254 current point on the stack because new try statements may have been pushed by 255 the handler or functions called from the handler. If there is no match back to 256 the point of the current handler, the search skips\label{p:searchskip} the stack frames already 257 searched by the first resume and continues after the try statement. The default 258 handler always continues from default handler associated with the point where 259 the exception is created. 205 \end{lstlisting} 206 This is a simple example with the try block and a single resumption handler. 207 Multiple resumption handlers can be put in a try statement and they can be 208 mixed with termination handlers. 209 210 When a resumption begins it will start searching the stack starting from 211 the throw statement and working its way to the callers. In each try statement 212 handlers will be tried top to bottom. Each handler is checked by seeing if 213 the thrown exception is a descendant of EXCEPTION\_TYPE. If not the search 214 continues. Otherwise NAME is bound to a pointer to the exception and the 215 HANDLER statements are executed. After they are finished executing control 216 continues from the throw statement. 217 218 If no appropriate handler is found then the default handler is called. The 219 throw statement acts as a regular function call passing the exception to 220 the default handler and after the handler finishes executing control continues 221 from the throw statement. 222 223 The exception system also tracks the position of a search on the stack. If 224 another resumption exception is thrown while a resumption handler is running 225 it will first check handlers pushed to the stack by the handler and any 226 functions it called, then it will continue from the try statement that the 227 handler is a part of; except for the default handler where it continues from 228 the throw the default handler was passed to. 229 230 This makes the search pattern for resumption reflect the one for termination, 231 which is what most users expect. 260 232 261 233 % This might need a diagram. But it is an important part of the justification 262 234 % of the design of the traversal order. 263 \begin{verbatim} 264 throwResume2 ----------. 265 | | 266 generated from handler | 267 | | 268 handler | 269 | | 270 throwResume1 -----. : 271 | | : 272 try | : search skip 273 | | : 274 catchResume <----' : 275 | | 276 \end{verbatim} 277 278 This resumption search-pattern reflect the one for termination, which matches 279 with programmer expectations. However, it avoids the \emph{recursive 280 resumption} problem. If parts of the stack are searched multiple times, loops 281 can easily form resulting in infinite recursion. 282 283 Consider the trivial case: 284 \begin{cfa} 235 It also avoids the recursive resumption problem. If the entire stack is 236 searched loops of resumption can form. Consider a handler that handles an 237 exception of type A by resuming an exception of type B and on the same stack, 238 later in the search path, is a second handler that handles B by resuming A. 239 240 Assuming no other handlers on the stack handle A or B then in either traversal 241 system an A resumed from the top of the stack will be handled by the first 242 handler. A B resumed from the top or from the first handler it will be handled 243 by the second handler. The only difference is when A is thrown from the second 244 handler. The entire stack search will call the first handler again, creating a 245 loop. Starting from the position in the stack though will break this loop. 246 247 \paragraph{Conditional Catches} 248 249 Resumption supports conditional catch clauses like termination does. They 250 use the same syntax except the keyword is changed: 251 \begin{lstlisting} 252 catchResume (EXCEPTION_TYPE * NAME ; CONDITION) 253 \end{lstlisting} 254 255 It also has the same behaviour, after the exception type has been matched 256 with the EXCEPTION\_TYPE the CONDITION is evaluated with NAME in scope. If 257 the result is true then the handler is run, otherwise the search continues 258 just as if there had been a type mismatch. 259 260 \paragraph{Re-Throwing} 261 262 You may also re-throw resumptions with a @throwResume;@ statement. 263 This can only be done from inside of a @catchResume@ block. 264 265 Outside of any side effects of any code already run in the handler this will 266 have the same effect as if the exception had not been caught in the first 267 place. 268 269 \section{Finally Clauses} 270 271 A @finally@ clause may be placed at the end of a try statement after 272 all the handler clauses. In the simply case, with no handlers, it looks like 273 this: 274 275 \begin{lstlisting} 285 276 try { 286 throwResume$\(_1\)$ (E &){}; 287 } catch( E * ){288 throwResume; 277 TRY_BLOCK 278 } finally { 279 FINAL_STATEMENTS 289 280 } 290 \end{cfa} 291 Based on termination semantics, programmer expectation is for the re-resume to 292 continue searching the stack frames after the try statement. However, the 293 current try statement is still on the stack below the handler issuing the 294 reresume \see{\VRef{s:Reraise}}. Hence, the try statement catches the re-raise 295 again and does another re-raise \emph{ad infinitum}, which is confusing and 296 difficult to debug. The \CFA resumption search-pattern skips the try statement 297 so the reresume search continues after the try, mathcing programmer 298 expectation. 299 300 \section{Conditional Catch} 301 Both termination and resumption handler-clauses may perform conditional matching: 302 \begin{cfa} 303 catch (EXCEPTION_TYPE * NAME ; @CONDITION@) 304 \end{cfa} 305 First, the same semantics is used to match the exception type. Second, if the 306 exception matches, @CONDITION@ is executed. The condition expression may 307 reference all names in scope at the beginning of the try block and @NAME@ 308 introduced in the handler clause. If the condition is true, then the handler 309 matches. Otherwise, the exception search continues at the next appropriate kind 310 of handler clause in the try block. 311 \begin{cfa} 312 try { 313 f1 = open( ... ); 314 f2 = open( ... ); 315 ... 316 } catch( IOFailure * f ; fd( f ) == f1 ) { 317 // only handle IO failure for f1 318 } 319 \end{cfa} 320 Note, catching @IOFailure@, checking for @f1@ in the handler, and reraising the 321 exception if not @f1@ is different because the reraise does not examine any of 322 remaining handlers in the current try statement. 323 324 \section{Reraise} 325 \label{s:Reraise} 326 Within the handler block or functions called from the handler block, it is 327 possible to reraise the most recently caught exception with @throw@ or 328 @throwResume@, respective. 329 \begin{cfa} 330 catch( ... ) { 331 ... throw; // rethrow 332 } catchResume( ... ) { 333 ... throwResume; // reresume 334 } 335 \end{cfa} 336 The only difference between a raise and a reraise is that reraise does not 337 create a new exception; instead it continues using the current exception, \ie 338 no allocation and copy. However the default handler is still set to the one 339 visible at the raise point, and hence, for termination could refer to data that 340 is part of an unwound stack frame. To prevent this problem, a new default 341 handler is generated that does a program-level abort. 342 343 344 \section{Finally Clauses} 345 A @finally@ clause may be placed at the end of a @try@ statement. 346 \begin{cfa} 347 try { 348 GUARDED_BLOCK 349 } ... // any number or kind of handler clauses 350 } finally { 351 FINALLY_BLOCK 352 } 353 \end{cfa} 354 The @FINALLY_BLOCK@ is executed when the try statement is unwound from the 355 stack, \ie when the @GUARDED_BLOCK@ or any handler clause finishes. Hence, the 356 finally block is always executed. 357 358 Execution of the finally block should always finish, meaning control runs off 359 the end of the block. This requirement ensures always continues as if the 360 finally clause is not present, \ie finally is for cleanup not changing control 361 flow. Because of this requirement, local control flow out of the finally block 362 is forbidden. The compiler precludes any @break@, @continue@, @fallthru@ or 363 @return@ that causes control to leave the finally block. Other ways to leave 364 the finally block, such as a long jump or termination are much harder to check, 365 and at best requiring additional run-time overhead, and so are discouraged. 281 \end{lstlisting} 282 283 Any number of termination handlers and resumption handlers may proceed the 284 finally clause. 285 286 The FINAL\_STATEMENTS, the finally block, are executed whenever the try 287 statement is removed from the stack. This includes: the TRY\_BLOCK finishes 288 executing, a termination exception finishes executing and the stack unwinds. 289 290 Execution of the finally block should finish by letting control run off 291 the end of the block. This is because after the finally block is complete 292 control will continue to where ever it would if the finally clause was not 293 present. 294 295 Because of this local control flow out of the finally block is forbidden. 296 The compiler rejects uses of @break@, @continue@, 297 @fallthru@ and @return@ that would cause control to leave 298 the finally block. Other ways to leave the finally block - such as a long 299 jump or termination - are much harder to check, at best requiring additional 300 run-time overhead, and so are merely discouraged. 366 301 367 302 \section{Cancellation} 368 Cancellation is a stack-level abort, which can be thought of as as an 369 uncatchable termination. It unwinds the entirety of the current stack, and if 370 possible forwards the cancellation exception to a different stack. 371 372 There is no special statement for starting a cancellation; instead the standard 373 library function @cancel_stack@ is called passing an exception. Unlike a 374 raise, this exception is not used in matching only to pass information about 375 the cause of the cancellation. 376 377 Handling of a cancellation depends on which stack is being cancelled. 378 \begin{description} 379 \item[Main Stack:] 380 The main stack is the one used by the program main at the start of execution, 381 and is the only stack in a sequential program. Hence, when cancellation is 382 forwarded to the main stack, there is no other forwarding stack, so after the 383 stack is unwound, there is a program-level abort. 384 385 \item[Thread Stack:] 386 A thread stack is created for a @thread@ object or object that satisfies the 387 @is_thread@ trait. A thread only has two points of communication that must 388 happen: start and join. As the thread must be running to perform a 389 cancellation, it must occur after start and before join, so join is a 390 cancellation point. After the stack is unwound, the thread halts and waits for 391 another thread to join with it. The joining thread, checks for a cancellation, 392 and if present, resumes exception @ThreadCancelled@. 393 394 There is a subtle difference between the explicit join (@join@ function) and 395 implicit join (from a destructor call). The explicit join takes the default 396 handler (@defaultResumptionHandler@) from its calling context, which is used if 397 the exception is not caught. The implicit join does a program abort instead. 398 399 This semantics is for safety. One difficult problem for any exception system is 400 defining semantics when an exception is raised during an exception search: 401 which exception has priority, the original or new exception? No matter which 402 exception is selected, it is possible for the selected one to disrupt or 403 destroy the context required for the other. \PAB{I do not understand the 404 following sentences.} This loss of information can happen with join but as the 405 thread destructor is always run when the stack is being unwound and one 303 304 Cancellation can be thought of as a stack-level abort or as an uncatchable 305 termination. It unwinds the entirety of the current exception and if possible 306 passes an exception to a different stack as a message. 307 308 There is no special statement for starting a cancellation, instead you call 309 the standard library function @cancel\_stack@ which takes an exception. 310 Unlike in a throw this exception is not used in control flow but is just there 311 to pass information about why the cancellation happened. 312 313 The handler is decided entirely by which stack is being canceled. There are 314 three handlers that apply to three different groups of stacks: 315 \begin{itemize} 316 \item Main Stack: 317 The main stack is the one on which the program main is called at the beginning 318 of your program. It is also the only stack you have without the libcfathreads. 319 320 Because of this there is no other stack ``above" (or possibly at all) for main 321 to notify when a cancellation occurs. So after the stack is unwound we do a 322 program level abort. 323 324 \item Thread Stack: 325 Thread stacks are those created @thread@ or otherwise satisfy the 326 @is\_thread@ trait. 327 328 Threads only have two structural points of communication that must happen, 329 start and join. As the thread must be running to preform a cancellation it 330 will be after start and before join, so join is one cancellation uses. 331 332 After the stack is unwound the thread will halt as if had completed normally 333 and wait for another thread to join with it. The other thread, when it joins, 334 checks for a cancellation. If so it will throw the resumption exception 335 @ThreadCancelled@. 336 337 There is a difference here in how explicate joins (with the @join@ 338 function) and implicate joins (from a destructor call). Explicate joins will 339 take the default handler (@defaultResumptionHandler@) from the context 340 and use like a regular through does if the exception is not caught. The 341 implicate join does a program abort instead. 342 343 This is for safety. One of the big problems in exceptions is you cannot handle 344 two terminations or cancellations on the same stack as either can destroy the 345 context required for the other. This can happen with join but as the 346 destructors will always be run when the stack is being unwound and one 406 347 termination/cancellation is already active. Also since they are implicit they 407 348 are easier to forget about. 408 349 409 \item[Coroutine Stack:] A coroutine stack is created for a @coroutine@ object 410 or object that satisfies the @is_coroutine@ trait. A coroutine only knows of 411 two other coroutines, its starter and its last resumer. The last resumer has 412 the tightest coupling to the coroutine it activated. Hence, cancellation of 413 the active coroutine is forwarded to the last resumer after the stack is 414 unwound, as the last resumer has the most precise knowledge about the current 415 execution. When the resumer restarts, it resumes exception 416 @CoroutineCancelled@, which is polymorphic over the coroutine type and has a 417 pointer to the cancelled coroutine. 418 419 The resume function also has an assertion that the @defaultResumptionHandler@ 420 for the exception. So it will use the default handler like a regular throw. 421 \end{description} 350 \item Coroutine Stack: 351 Coroutine stacks are those created with @coroutine@ or otherwise 352 satisfy the @is\_coroutine@ trait. 353 354 A coroutine knows of two other coroutines, its starter and its last resumer. 355 The last resumer is ``closer" so that is the one notified. 356 357 After the stack is unwound control goes to the last resumer. 358 Resume will resume throw a @CoroutineCancelled@ exception, which is 359 polymorphic over the coroutine type and has a pointer to the coroutine being 360 canceled and the canceling exception. The resume function also has an 361 assertion that the @defaultResumptionHandler@ for the exception. So it 362 will use the default handler like a regular throw. 363 364 \end{itemize} -
doc/theses/andrew_beach_MMath/future.tex
rf99f5ba rcd70477 1 1 \chapter{Future Work} 2 2 3 \section{Language Improvements} 4 \CFA is a developing programming language. As such, there are partially or 5 unimplemented features of the language (including several broken components) 6 that I had to workaround while building an exception handling system largely in 7 the \CFA language (some C components). The following are a few of these 8 issues, and once implemented/fixed, how this would affect the exception system. 9 \begin{itemize} 10 \item 11 The implementation of termination is not portable because it includes 12 hand-crafted assembly statements. These sections must be generalized to support 13 more hardware architectures, \eg ARM processor. 14 \item 15 Due to a type-system problem, the catch clause cannot bind the exception to a 16 reference instead of a pointer. Since \CFA has a very general reference 17 capability, programmers will want to use it. Once fixed, this capability should 18 result in little or no change in the exception system. 19 \item 20 Termination handlers cannot use local control-flow transfers, \eg by @break@, 21 @return@, \etc. The reason is that current code generation hoists a handler 22 into a nested function for convenience (versus assemble-code generation at the 23 @try@ statement). Hence, when the handler runs, its code is not in the lexical 24 scope of the @try@ statement, where the local control-flow transfers are 25 meaningful. 26 \end{itemize} 3 \section{Complete Virtual System} 4 The virtual system should be completed. It was never supposed to be part of 5 this project and so minimal work was done on it. A draft of what the complete 6 system might look like was created but it was never finalized or implemented. 7 A future project in \CFA would be to complete that work and to update the 8 parts of the exception system that use the current version. 27 9 28 \section{Complete Virtual System} 29 The virtual system should be completed. It was not supposed to be part of this 30 project, but was thrust upon it to do exception inheritance; hence, only 31 minimal work was done. A draft for a complete virtual system is available but 32 it is not finalized. A future \CFA project is to complete that work and then 33 update the exception system that uses the current version. 34 35 There are several improvements to the virtual system that would improve the 36 exception traits. The most important one is an assertion to check one virtual 37 type is a child of another. This check precisely captures many of the 38 correctness requirements. 10 There are several improvements to the virtual system that would improve 11 the exception traits. The biggest one is an assertion that checks that one 12 virtual type is a child of another virtual type. This would capture many of 13 the requirements much more precisely. 39 14 40 15 The full virtual system might also include other improvement like associated 41 types to allow traits to refer to types not listed in their header. This 42 feature allows exception traits to not refer to the virtual-table type 43 explicitly, removing the need for the current interface macros. 16 types. This is a proposed feature that would allow traits to refer to types 17 not listed in their header. This would allow the exception traits to not 18 refer to the virtual table type explicatly which would remove the need for 19 the interface macros. 44 20 45 \section{Additional Raises} 46 Several other kinds of exception raises were considered beyond termination 47 (@throw@), resumption (@throwResume@), and reraise. 21 \section{Additional Throws} 22 Several other kinds of throws, beyond the termination throw (@throw@), 23 the resumption throw (@throwResume@) and the re-throws, were considered. 24 None were as useful as the core throws but they would likely be worth 25 revising. 48 26 49 The first is a non-local/concurrent raise providing asynchronous exceptions, 50 \ie raising an exception on another stack. This semantics acts like signals 51 allowing for out-of-band communication among coroutines and threads. This kind 52 of raise is often restricted to resumption to allow the target stack to 53 continue execution normally after the exception has been handled. That is, 54 allowing one coroutine/thread to unwind the stack of another via termination is 55 bad software engineering. 27 The first ones are throws for asynchronous exceptions, throwing exceptions 28 from one stack to another. These act like signals allowing for communication 29 between the stacks. This is usually used with resumption as it allows the 30 target stack to continue execution normally after the exception has been 31 handled. 56 32 57 Non-local/concurrent requires more coordination between the concurrency system 58 and the exception system. Many of the interesting design decisions centre 59 a round masking (controlling which exceptions may be thrown at a stack). It60 w ould likely require more of the virtual system and would also effect how61 default handlers are set.33 This would much more coordination between the concurrency system and the 34 exception system to handle. Most of the interesting design decisions around 35 applying asynchronous exceptions appear to be around masking (controlling 36 which exceptions may be thrown at a stack). It would likely require more of 37 the virtual system and would also effect how default handlers are set. 62 38 63 Other raises were considered to mimic bidirectional algebraic effects.64 Algebraic effects are used in some functional languages a llowing onefunction39 The other throws were designed to mimic bidirectional algebraic effects. 40 Algebraic effects are used in some functional languages and allow a function 65 41 to have another function on the stack resolve an effect (which is defined with 66 a functional-like interface). This semantics can be mimicked with resumptions 67 and new raises were discussed to mimic bidirectional algebraic-effects, where 68 control can go back and forth between the function-effect caller and handler 69 while the effect is underway. 42 a function-like interface). 43 These can be mimiced with resumptions and the the new throws were designed 44 to try and mimic bidirectional algebraic effects, where control can go back 45 and forth between the function effect caller and handler while the effect 46 is underway. 70 47 % resume-top & resume-reply 71 These raises would be like the resumption raise except using different search72 patterns to find the handler.73 48 74 \section{Zero-Cost Try} 75 \CFA does not have zero-cost try-statements because the compiler generates C 76 code rather than assembler code \see{\VPageref{p:zero-cost}}. When the compiler 77 does create its own assembly (or LLVM byte-code), then zero-cost try-statements 78 are possible. The downside of zero-cost try-statements is the LSDA complexity, 79 its size (program bloat), and the high cost of raising an exception. 49 These throws would likely be just like the resumption throw except they would 50 use different search patterns to find the handler to reply to. 80 51 81 Alternatively, some research could be done into the simpler alternative method 82 with a non-zero-cost try-statement but much lower cost exception raise. For 83 example, programs are starting to use exception in the normal control path, so 84 more exceptions are thrown. In these cases, the cost balance switches towards 85 low-cost raise. Unfortunately, while exceptions remain exceptional, the 86 libunwind model will probably remain the most effective option. 52 \section{Zero-Cost Exceptions} 53 \CFA does not have zero-cost exceptions because it does not generate assembly 54 but instead generates C code. See the implementation section. When the 55 compiler does start to create its own assembly (or LLVM byte code) then 56 zero-cost exceptions could be implemented. 87 57 88 Zero-cost resumptions is still an open problem. First, because libunwind does 89 not support a successful-exiting stack-search without doing an unwind. 90 Workarounds are possible but awkward. Ideally an extension to libunwind could 91 be made, but that would either require separate maintenance or gain enough 92 support to have it folded into the standard. 58 Now in zero-cost exceptions the only part that is zero-cost are the try 59 blocks. Some research could be done into the alternative methods for systems 60 that expect a lot more exceptions to be thrown, allowing some overhead in 61 entering and leaving try blocks to make throws faster. But while exceptions 62 remain exceptional the libunwind model will probably remain the most effective 63 option. 93 64 94 Also new techniques to skip previously searched parts of the stack need to be 95 developed to handle the recursive resume problem and support advanced algebraic 96 effects. 65 Zero-cost resumptions have more problems to solve. First because libunwind 66 does not support a successful exiting stack search without doing an unwind. 67 There are several ways to hack that functionality in. Ideally an extension to 68 libunwind could be made, but that would either require seperate maintenance 69 or gain enough support to have it folded into the standard. 70 71 Also new techniques to skip previously searched parts of the stack will have 72 to be developed. The recursive resume problem still remains and ideally the 73 same pattern of ignoring sections of the stack. 97 74 98 75 \section{Signal Exceptions} 99 Goodenough~\cite{Goodenough75} suggests three types of exceptions: escape, 100 notify and signal. Escape are termination exceptions, notify are resumption 101 exceptions, leaving signal unimplemented. 76 Exception Handling: Issues and a Proposed Notation suggests there are three 77 types of exceptions: escape, notify and signal. 78 Escape exceptions are our termination exceptions, notify exceptions are 79 resumption exceptions and that leaves signal exception unimplemented. 102 80 103 A signal exception allows either behaviour, \ie after an exception is handled, 104 the handler has the option of returning to the raise or after the @try@ 105 statement. Currently, \CFA fixes the semantics of the handler return 106 syntactically by the @catch@ or @catchResume@ clause. 81 Signal exceptions allow either behaviour, that is after the exception is 82 handled control can either return to the throw or from where the handler is 83 defined. 107 84 108 Signal exception should be reexamined and possibly be supported in \CFA. A very 109 direct translation is to have a new raise and catch pair, and a newstatement110 (or statements) would indicate if the handler returns to the raise or continues111 where it is; but there may be other options.85 The design should be rexamined and be updated for \CFA. A very direct 86 translation would perhaps have a new throw and catch pair and a statement 87 (or statements) could be used to decide if the handler returns to the throw 88 or continues where it is, but there are other options. 112 89 113 For instance , resumption could be extended to cover this use by allowing local114 control flow out of it. This approachwould require an unwind as part of the115 transition as there are stack frames that have to be removed. This approach116 means there is no notify raise, but because \CFA does not have exception 117 signatures, a termination can be thrown from within any resumption handler so 118 there is already a way to do mimicthis in existing \CFA.90 For instance resumption could be extended to cover this use by allowing 91 local control flow out of it. This would require an unwind as part of the 92 transition as there are stack frames that have to be removed. 93 This would mean there is no notify like throw but because \CFA does not have 94 exception signatures a termination can be thrown from any resumption handler 95 already so there are already ways one could try to do this in existing \CFA. 119 96 120 97 % Maybe talk about the escape; and escape CONTROL_STMT; statements or how 121 98 % if we could choose if _Unwind_Resume proceeded to the clean-up stage this 122 99 % would be much easier to implement. 100 101 \section{Language Improvements} 102 There is also a lot of work that are not follow ups to this work in terms of 103 research, some have no interesting research to be done at all, but would 104 improve \CFA as a programming language. The full list of these would 105 naturally be quite extensive but here are a few examples that involve 106 exceptions: 107 108 \begin{itemize} 109 \item The implementation of termination is not portable because it includes 110 some assembly statements. These sections will have to be re-written to so 111 \CFA has full support on more machines. 112 \item Allowing exception handler to bind the exception to a reference instead 113 of a pointer. This should actually result in no change in behaviour so there 114 is no reason not to allow it. It is however a small improvement; giving a bit 115 of flexibility to the user in what style they want to use. 116 \item Enabling local control flow (by @break@, @return@ and 117 similar statements) out of a termination handler. The current set-up makes 118 this very difficult but the catch function that runs the handler after it has 119 been matched could be inlined into the function's body, which would make this 120 much easier. (To do the same for try blocks would probably wait for zero-cost 121 exceptions, which would allow the try block to be inlined as well.) 122 \end{itemize} -
doc/theses/andrew_beach_MMath/implement.tex
rf99f5ba rcd70477 2 2 % Goes over how all the features are implemented. 3 3 4 The implementation work for this thesis covers two components: the virtual5 system and exceptions. Each component is discussed in detail.6 7 4 \section{Virtual System} 8 \label{s:VirtualSystem}9 5 % Virtual table rules. Virtual tables, the pointer to them and the cast. 10 While the \CFA virtual system currently has only one public feature, virtual 11 cast \see{\VPageref{p:VirtualCast}}, substantial structure is required to 12 support it, and provide features for exception handling and the standard 13 library. 14 15 \subsection{Virtual Table} 16 The virtual system is accessed through a private constant field inserted at the 17 beginning of every virtual type, called the virtual-table pointer. This field 18 points at a type's virtual table and is assigned during the object's 19 construction. The address of a virtual table acts as the unique identifier for 20 the virtual type, and the first field of a virtual table is a pointer to the 21 parent virtual-table or @0p@. The remaining fields are duplicated from the 22 parent tables in this type's inheritance chain, followed by any fields this type 23 introduces. Parent fields are duplicated so they can be changed (\CC 24 \lstinline[language=c++]|override|), so that references to the dispatched type 25 are replaced with the current virtual type. 26 \PAB{Can you create a simple diagram of the layout?} 27 % These are always taken by pointer or reference. 28 29 % For each virtual type, a virtual table is constructed. This is both a new type 30 % and an instance of that type. Other instances of the type could be created 31 % but the system doesn't use them. So this section will go over the creation of 32 % the type and the instance. 33 34 A virtual table is created when the virtual type is created. The name of the 35 type is created by mangling the name of the base type. The name of the instance 36 is also generated by name mangling. The fields are initialized automatically. 6 The \CFA virtual system only has one public facing feature: virtual casts. 7 However there is a lot of structure to support that and provide some other 8 features for the standard library. 9 10 All of this is accessed through a field inserted at the beginning of every 11 virtual type. Currently it is called @virtual_table@ but it is not 12 ment to be accessed by the user. This field is a pointer to the type's 13 virtual table instance. It is assigned once during the object's construction 14 and left alone after that. 15 16 \subsection{Virtual Table Construction} 17 For each virtual type a virtual table is constructed. This is both a new type 18 and an instance of that type. Other instances of the type could be created 19 but the system doesn't use them. So this section will go over the creation of 20 the type and the instance. 21 22 Creating the single instance is actually very important. The address of the 23 table acts as the unique identifier for the virtual type. Similarly the first 24 field in every virtual table is the parent's id; a pointer to the parent 25 virtual table instance. 26 27 The remaining fields contain the type's virtual members. First come the ones 28 present on the parent type, in the same order as they were the parent, and 29 then any that this type introduces. The types of the ones inherited from the 30 parent may have a slightly modified type, in that references to the 31 dispatched type are replaced with the current virtual type. These are always 32 taken by pointer or reference. 33 34 The structure itself is created where the virtual type is created. The name 35 of the type is created by mangling the name of the base type. The name of the 36 instance is also generated by name mangling. 37 38 The fields are initialized automatically. 37 39 The parent field is initialized by getting the type of the parent field and 38 40 using that to calculate the mangled name of the parent's virtual table type. 39 41 There are two special fields that are included like normal fields but have 40 42 special initialization rules: the @size@ field is the type's size and is 41 initialized with a @sizeof@ expression, the @align@ field is the type's 42 alignment and uses an @alignof@ expression. The remaining fields are resolved 43 to a name matching the field's name and type using the normal visibility and 44 overload resolution rules of the type system. 45 46 These operations are split up into several groups depending on where they take 47 place which varies for monomorphic and polymorphic types. The first devision is 48 between the declarations and the definitions. Declarations, such as a function 49 signature or a aggregate's name, must always be visible but may be repeated in 50 the form of forward declarations in headers. Definitions, such as function 51 bodies and a aggregate's layout, can be separately compiled but must occur 52 exactly once in a source file. 53 54 \begin{sloppypar} 43 initialized with a sizeof expression, the @align@ field is the type's 44 alignment and uses an alignof expression. The remaining fields are resolved 45 to a name matching the field's name and type using the normal visibility 46 and overload resolution rules of the type system. 47 48 These operations are split up into several groups depending on where they 49 take place which can vary for monomorphic and polymorphic types. The first 50 devision is between the declarations and the definitions. Declarations, such 51 as a function signature or a structure's name, must always be visible but may 52 be repeated so they go in headers. Definitions, such as function bodies and a 53 structure's layout, don't have to be visible on use but must occur exactly 54 once and go into source files. 55 55 56 The declarations include the virtual type definition and forward declarations 56 57 of the virtual table instance, constructor, message function and 57 @get_exception_vtable@. The definition includes the storage and initialization58 of the virtual table instance and the bodies of the three functions. 59 \end{sloppypar} 58 @get_exception_vtable@. The definition includes the storage and 59 initialization of the virtual table instance and the bodies of the three 60 functions. 60 61 61 62 Monomorphic instances put all of these two groups in one place each. 62 Polymorphic instances also split out the core declarations and definitions from 63 the per-instance information. The virtual table type and most of the functions64 are polymorphic so they are all part of the core. The virtual table instance65 and the @get_exception_vtable@ function. 66 67 \begin{sloppypar} 63 64 Polymorphic instances also split out the core declarations and definitions 65 from the per-instance information. The virtual table type and most of the 66 functions are polymorphic so they are all part of the core. The virtual table 67 instance and the @get_exception_vtable@ function. 68 68 69 Coroutines and threads need instances of @CoroutineCancelled@ and 69 @ThreadCancelled@ respectively to use all of their functionality. When a new 70 data type is declared with @coroutine@ or @thread@ the forward declaration for 71 the instance is created as well. The definition of the virtual table is created 72 at the definition of the main function. 73 \end{sloppypar} 70 @ThreadCancelled@ respectively to use all of their functionality. 71 When a new data type is declared with @coroutine@ or @thread@ 72 the forward declaration for the instance is created as well. The definition 73 of the virtual table is created at the definition of the main function. 74 74 75 75 \subsection{Virtual Cast} 76 Virtual casts are implemented as a function call that does the subtype check 77 and a C coercion-cast to do the type conversion. 78 % The C-cast is just to make sure the generated code is correct so the rest of 79 % the section is about that function. 80 The function is 81 \begin{cfa} 82 void * __cfa__virtual_cast( struct __cfa__parent_vtable const * parent, 83 struct __cfa__parent_vtable const * const * child ); 84 } 85 \end{cfa} 86 and it is implemented in the standard library. It takes a pointer to the target 87 type's virtual table and the object pointer being cast. The function performs a 88 linear search starting at the object's virtual-table and walking through the 89 the parent pointers, checking to if it or any of its ancestors are the same as 90 the target-type virtual table-pointer. 91 92 For the generated code, a forward declaration of the virtual works as follows. 93 There is a forward declaration of @__cfa__virtual_cast@ in every \CFA file so 94 it can just be used. The object argument is the expression being cast so that 95 is just placed in the argument list. 96 97 To build the target type parameter, the compiler creates a mapping from 98 concrete type-name -- so for polymorphic types the parameters are filled in -- 99 to virtual table address. Every virtual table declaration is added to the this 100 table; repeats are ignored unless they have conflicting definitions. Note, 101 these declarations do not have to be in scope, but they should usually be 102 introduced as part of the type definition. 103 104 \PAB{I do not understood all of \VRef{s:VirtualSystem}. I think you need to 105 write more to make it clear.} 106 76 Virtual casts are implemented as a function call that does the check and a 77 old C-style cast to do the type conversion. The C-cast is just to make sure 78 the generated code is correct so the rest of the section is about that 79 function. 80 81 The function is @__cfa__virtual_cast@ and it is implemented in the 82 standard library. It takes a pointer to the target type's virtual table and 83 the object pointer being cast. The function is very simple, getting the 84 object's virtual table pointer and then checking to see if it or any of 85 its ancestors, by using the parent pointers, are the same as the target type 86 virtual table pointer. It does this in a simple loop. 87 88 For the generated code a forward decaration of the virtual works as follows. 89 There is a forward declaration of @__cfa__virtual_cast@ in every cfa 90 file so it can just be used. The object argument is the expression being cast 91 so that is just placed in the argument list. 92 93 To build the target type parameter the compiler will create a mapping from 94 concrete type-name -- so for polymorphic types the parameters are filled in 95 -- to virtual table address. Every virtual table declaraction is added to the 96 this table; repeats are ignored unless they have conflicting definitions. 97 This does mean the declaractions have to be in scope, but they should usually 98 be introduced as part of the type definition. 107 99 108 100 \section{Exceptions} … … 114 106 % resumption doesn't as well. 115 107 116 % Many modern languages work with an interal stack that function push and pop 117 % their local data to. Stack unwinding removes large sections of the stack, 118 % often across functions. 119 120 Stack unwinding is the process of removing stack frames (activations) from the 121 stack. On function entry and return, unwinding is handled directly by the code 122 embedded in the function. Usually, the stack-frame size is known statically 123 based on parameter and local variable declarations. For dynamically-sized 124 local variables, a runtime computation is necessary to know the frame 125 size. Finally, a function's frame-size may change during execution as local 126 variables (static or dynamic sized) go in and out of scope. 127 Allocating/deallocating stack space is usually an $O(1)$ operation achieved by 128 bumping the hardware stack-pointer up or down as needed. 129 130 Unwinding across multiple stack frames is more complex because individual stack 131 management code associated with each frame is bypassed. That is, the location 132 of a function's frame-management code is largely unknown and dispersed 133 throughout the function, hence the current frame size managed by that code is 134 also unknown. Hence, code unwinding across frames does not have direct 135 knowledge about what is on the stack, and hence, how much of the stack needs to 136 be removed. 137 138 % At a very basic level this can be done with @setjmp@ \& @longjmp@ which simply 139 % move the top of the stack, discarding everything on the stack above a certain 140 % point. However this ignores all the cleanup code that should be run when 141 % certain sections of the stack are removed (for \CFA these are from destructors 142 % and finally clauses) and also requires that the point to which the stack is 143 % being unwound is known ahead of time. libunwind is used to address both of 144 % these problems. 145 146 The traditional unwinding mechanism for C is implemented by saving a snap-shot 147 of a function's state with @setjmp@ and restoring that snap-shot with 148 @longjmp@. This approach bypasses the need to know stack details by simply 149 reseting to a snap-shot of an arbitrary but existing function frame on the 150 stack. It is up to the programmer to ensure the snap-shot is valid when it is 151 reset, making this unwinding approach fragile with potential errors that are 152 difficult to debug because the stack becomes corrupted. 153 154 However, many languages define cleanup actions that must be taken when objects 155 are deallocated from the stack or blocks end, such as running a variable's 156 destructor or a @try@ statement's @finally@ clause. Handling these mechanisms 157 requires walking the stack and checking each stack frame for these potential 158 actions. 159 160 For exceptions, it must be possible to walk the stack frames in search of @try@ 161 statements to match and execute a handler. For termination exceptions, it must 162 also be possible to unwind all stack frames from the throw to the matching 163 catch, and each of these frames must be checked for cleanup actions. Stack 164 walking is where most of the complexity and expense of exception handling 165 appears. 166 167 One of the most popular tools for stack management is libunwind, a low-level 168 library that provides tools for stack walking, handler execution, and 169 unwinding. What follows is an overview of all the relevant features of 170 libunwind needed for this work, and how \CFA uses them to implement exception 171 handling. 172 173 \subsection{libunwind Usage} 174 Libunwind, accessed through @unwind.h@ on most platforms, is a C library that 175 provides \CC-style stack-unwinding. Its operation is divided into two phases: 176 search and cleanup. The dynamic target search -- phase 1 -- is used to scan the 177 stack and decide where unwinding should stop (but no unwinding occurs). The 178 cleanup -- phase 2 -- does the unwinding and also runs any cleanup code. 179 180 To use libunwind, each function must have a personality function and a Language 181 Specific Data Area (LSDA). The LSDA has the unique information for each 182 function to tell the personality function where a function is executing, its 183 current stack frame, and what handlers should be checked. Theoretically, the 184 LSDA can contain any information but conventionally it is a table with entries 185 representing regions of the function and what has to be done there during 186 unwinding. These regions are bracketed by the instruction pointer. If the 187 instruction pointer is within a region's start/end, then execution is currently 188 executing in that region. Regions are used to mark out the scopes of objects 189 with destructors and try blocks. 190 191 % Libunwind actually does very little, it simply moves down the stack from 192 % function to function. Most of the actions are implemented by the personality 193 % function which libunwind calls on every function. Since this is shared across 194 % many functions or even every function in a language it will need a bit more 195 % information. 196 197 The GCC compilation flag @-fexceptions@ causes the generation of an LSDA and 198 attaches its personality function. \PAB{to what is it attached?} However, this 199 flag only handles the cleanup attribute 200 \begin{cfa} 201 void clean_up( int * var ) { ... } 202 int avar __attribute__(( __cleanup(clean_up) )); 203 \end{cfa} 204 which is used on a variable and specifies a function, \eg @clean_up@, run when 205 the variable goes out of scope. The function is passed a pointer to the object 206 so it can be used to mimic destructors. However, this feature cannot be used to 207 mimic @try@ statements. 208 209 \subsection{Personality Functions} 210 Personality functions have a complex interface specified by libunwind. This 211 section covers some of the important parts of the interface. 212 213 A personality function performs four tasks, although not all have to be 214 present. 215 \begin{lstlisting}[language=C,{moredelim=**[is][\color{red}]{@}{@}}] 216 typedef _Unwind_Reason_Code (*@_Unwind_Personality_Fn@) ( 217 _Unwind_Action @action@, 218 _Unwind_Exception_Class @exception_class@, 219 _Unwind_Exception * @exception@, 220 struct _Unwind_Context * @context@ 221 ); 108 Many modern languages work with an interal stack that function push and pop 109 their local data to. Stack unwinding removes large sections of the stack, 110 often across functions. 111 112 At a very basic level this can be done with @setjmp@ \& @longjmp@ 113 which simply move the top of the stack, discarding everything on the stack 114 above a certain point. However this ignores all the clean-up code that should 115 be run when certain sections of the stack are removed (for \CFA these are from 116 destructors and finally clauses) and also requires that the point to which the 117 stack is being unwound is known ahead of time. libunwind is used to address 118 both of these problems. 119 120 Libunwind, provided in @unwind.h@ on most platorms, is a C library 121 that provides \CPP style stack unwinding. Its operation is divided into two 122 phases. The search phase -- phase 1 -- is used to scan the stack and decide 123 where the unwinding will stop, this allows for a dynamic target. The clean-up 124 phase -- phase 2 -- does the actual unwinding and also runs any clean-up code 125 as it goes. 126 127 To use the libunwind each function must have a personality function and an 128 LSDA (Language Specific Data Area). Libunwind actually does very little, it 129 simply moves down the stack from function to function. Most of the actions are 130 implemented by the personality function which libunwind calls on every 131 function. Since this is shared across many functions or even every function in 132 a language it will need a bit more information. This is provided by the LSDA 133 which has the unique information for each function. 134 135 Theoretically the LSDA can contain anything but conventionally it is a table 136 with entries reperenting areas of the function and what has to be done there 137 during unwinding. These areas are described in terms of where the instruction 138 pointer is. If the current value of the instruction pointer is between two 139 values reperenting the beginning and end of a region then execution is 140 currently being executed. These are used to mark out try blocks and the 141 scopes of objects with destructors to run. 142 143 GCC will generate an LSDA and attach its personality function with the 144 @-fexceptions@ flag. However this only handles the cleanup attribute. 145 This attribute is used on a variable and specifies a function that should be 146 run when the variable goes out of scope. The function is passed a pointer to 147 the object as well so it can be used to mimic destructors. It however cannot 148 be used to mimic try statements. 149 150 \subsection{Implementing Personality Functions} 151 Personality functions have a complex interface specified by libunwind. 152 This section will cover some of the important parts of that interface. 153 154 \begin{lstlisting} 155 typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)( 156 int version, 157 _Unwind_Action action, 158 _Unwind_Exception_Class exception_class, 159 _Unwind_Exception * exception, 160 struct _Unwind_Context * context); 222 161 \end{lstlisting} 223 The @action@ argument is a bitmask of possible actions: 224 \begin{enumerate} 225 \item 226 @_UA_SEARCH_PHASE@ specifies a search phase and tells the personality function 227 to check for handlers. If there is a handler in a stack frame, as defined by 228 the language, the personality function returns @_URC_HANDLER_FOUND@; otherwise 229 it return @_URC_CONTINUE_UNWIND@. 230 231 \item 232 @_UA_CLEANUP_PHASE@ specifies a cleanup phase, where the entire frame is 233 unwound and all cleanup code is run. The personality function does whatever 234 cleanup the language defines (such as running destructors/finalizers) and then 235 generally returns @_URC_CONTINUE_UNWIND@. 236 237 \item 238 \begin{sloppypar} 239 @_UA_HANDLER_FRAME@ specifies a cleanup phase on a function frame that found a 240 handler. The personality function must prepare to return to normal code 241 execution and return @_URC_INSTALL_CONTEXT@. 242 \end{sloppypar} 243 244 \item 245 @_UA_FORCE_UNWIND@ specifies a forced unwind call. Forced unwind only performs 246 the cleanup phase and uses a different means to decide when to stop 247 \see{\VRef{s:ForcedUnwind}}. 248 \end{enumerate} 249 250 The @exception_class@ argument is a copy of the 251 \lstinline[language=C]|exception|'s @exception_class@ field. 252 253 The \lstinline[language=C]|exception| argument is a pointer to the user 254 provided storage object. It has two public fields, the exception class, which 255 is actually just a number, identifying the exception handling mechanism that 256 created it, and the cleanup function. The cleanup function is called if 257 required by the exception. 258 259 The @context@ argument is a pointer to an opaque type passed to helper 260 functions called inside the personality function. 261 262 The return value, @_Unwind_Reason_Code@, is an enumeration of possible messages 162 163 The return value, the reason code, is an enumeration of possible messages 263 164 that can be passed several places in libunwind. It includes a number of 264 165 messages for special cases (some of which should never be used by the … … 266 167 personality function should always return @_URC_CONTINUE_UNWIND@. 267 168 169 The @version@ argument is the verson of the implementation that is 170 calling the personality function. At this point it appears to always be 1 and 171 it will likely stay that way until a new version of the API is updated. 172 173 The @action@ argument is set of flags that tell the personality 174 function when it is being called and what it must do on this invocation. 175 The flags are as follows: 176 \begin{itemize} 177 \item@_UA_SEARCH_PHASE@: This flag is set whenever the personality 178 function is called during the search phase. The personality function should 179 decide if unwinding will stop in this function or not. If it does then the 180 personality function should return @_URC_HANDLER_FOUND@. 181 \item@_UA_CLEANUP_PHASE@: This flag is set whenever the personality 182 function is called during the cleanup phase. If no other flags are set this 183 means the entire frame will be unwound and all cleanup code should be run. 184 \item@_UA_HANDLER_FRAME@: This flag is set during the cleanup phase 185 on the function frame that found the handler. The personality function must 186 prepare to return to normal code execution and return 187 @_URC_INSTALL_CONTEXT@. 188 \item@_UA_FORCE_UNWIND@: This flag is set if the personality function 189 is called through a forced unwind call. Forced unwind only performs the 190 cleanup phase and uses a different means to decide when to stop. See its 191 section below. 192 \end{itemize} 193 194 The @exception_class@ argument is a copy of the @exception@'s 195 @exception_class@ field. 196 197 The @exception@ argument is a pointer to the user provided storage 198 object. It has two public fields, the exception class which is actually just 199 a number that identifies the exception handling mechanism that created it and 200 the other is the clean-up function. The clean-up function is called if the 201 exception needs to 202 203 The @context@ argument is a pointer to an opaque type. This is passed 204 to the many helper functions that can be called inside the personality 205 function. 206 268 207 \subsection{Raise Exception} 269 Raising an exception is the central function of libunwind and it performs a 270 two-staged unwinding. 271 \begin{cfa} 208 This could be considered the central function of libunwind. It preforms the 209 two staged unwinding the library is built around and most of the rest of the 210 interface of libunwind is here to support it. It's signature is as follows: 211 212 \begin{lstlisting} 272 213 _Unwind_Reason_Code _Unwind_RaiseException(_Unwind_Exception *); 273 \end{cfa} 274 First, the function begins the search phase, calling the personality function 275 of the most recent stack frame. It continues to call personality functions 276 traversing the stack from newest to oldest until a function finds a handler or 277 the end of the stack is reached. In the latter case, raise exception returns 278 @_URC_END_OF_STACK@. 279 280 Second, when a handler is matched, raise exception continues onto the cleanup phase. 281 Once again, it calls the personality functions of each stack frame from newest 282 to oldest. This pass stops at the stack frame containing the matching handler. 283 If that personality function has not install a handler, it is an error. 284 285 If an error is encountered, raise exception returns either 286 @_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending on when the 287 error occurred. 214 \end{lstlisting} 215 216 When called the function begins the search phase, calling the personality 217 function of the most recent stack frame. It will continue to call personality 218 functions traversing the stack new-to-old until a function finds a handler or 219 the end of the stack is reached. In the latter case raise exception will 220 return with @_URC_END_OF_STACK@. 221 222 Once a handler has been found raise exception continues onto the the cleanup 223 phase. Once again it will call the personality functins of each stack frame 224 from newest to oldest. This pass will stop at the stack frame that found the 225 handler last time, if that personality function does not install the handler 226 it is an error. 227 228 If an error is encountered raise exception will return either 229 @_URC_FATAL_PHASE1_ERROR@ or @_URC_FATAL_PHASE2_ERROR@ depending 230 on when the error occured. 288 231 289 232 \subsection{Forced Unwind} 290 \label{s:ForcedUnwind} 291 Forced Unwind is the other central function in libunwind. 292 \begin{cfa} 293 _Unwind_Reason_Code _Unwind_ForcedUnwind( _Unwind_Exception *, 294 _Unwind_Stop_Fn, void *); 295 \end{cfa} 296 It also unwinds the stack but it does not use the search phase. Instead another 297 function, the stop function, is used to stop searching. The exception is the 298 same as the one passed to raise exception. The extra arguments are the stop 299 function and the stop parameter. The stop function has a similar interface as a 300 personality function, except it is also passed the stop parameter. 301 \begin{lstlisting}[language=C,{moredelim=**[is][\color{red}]{@}{@}}] 302 typedef _Unwind_Reason_Code (*@_Unwind_Stop_Fn@)( 303 _Unwind_Action @action@, 304 _Unwind_Exception_Class @exception_class@, 305 _Unwind_Exception * @exception@, 306 struct _Unwind_Context * @context@, 307 void * @stop_parameter@); 233 This is the second big function in libunwind. It also unwinds a stack but it 234 does not use the search phase. Instead another function, the stop function, 235 is used to decide when to stop. 236 237 \begin{lstlisting} 238 _Unwind_Reason_Code _Unwind_ForcedUnwind( 239 _Unwind_Exception *, _Unwind_Stop_Fn, void *); 308 240 \end{lstlisting} 309 241 242 The exception is the same as the one passed to raise exception. The extra 243 arguments are the stop function and the stop parameter. The stop function has 244 a similar interface as a personality function, except it is also passed the 245 stop parameter. 246 247 \begin{lstlisting} 248 typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)( 249 int version, 250 _Unwind_Action action, 251 _Unwind_Exception_Class exception_class, 252 _Unwind_Exception * exception, 253 struct _Unwind_Context * context, 254 void * stop_parameter); 255 \end{lstlisting} 256 310 257 The stop function is called at every stack frame before the personality 311 function is called and then once more after all frames of the stack are 312 unwound. 313 314 Each time it is called, the stop function should return @_URC_NO_REASON@ or 315 transfer control directly to other code outside of libunwind. The framework 316 does not provide any assistance here. 317 318 \begin{sloppypar} 319 Its arguments are the same as the paired personality function. The actions 320 @_UA_CLEANUP_PHASE@ and @_UA_FORCE_UNWIND@ are always set when it is 321 called. Beyond the libunwind standard, both GCC and Clang add an extra action 322 on the last call at the end of the stack: @_UA_END_OF_STACK@. 323 \end{sloppypar} 258 function is called and then once more once after all frames of the stack have 259 been unwound. 260 261 Each time it is called the stop function should return @_URC_NO_REASON@ 262 or transfer control directly to other code outside of libunwind. The 263 framework does not provide any assistance here. 264 265 Its arguments are the same as the paired personality function. 266 The actions @_UA_CLEANUP_PHASE@ and @_UA_FORCE_UNWIND@ are always 267 set when it is called. By the official standard that is all but both GCC and 268 Clang add an extra action on the last call at the end of the stack: 269 @_UA_END_OF_STACK@. 324 270 325 271 \section{Exception Context} 326 272 % Should I have another independent section? 327 273 % There are only two things in it, top_resume and current_exception. How it is 328 % stored changes depending on whether or not the thread-library is linked. 329 330 The exception context is global storage used to maintain data across different 331 exception operations and to communicate among different components. 332 333 Each stack must have its own exception context. In a sequential \CFA program, 334 there is only one stack with a single global exception-context. However, when 335 the library @libcfathread@ is linked, there are multiple stacks where each 336 needs its own exception context. 337 338 General access to the exception context is provided by function 339 @this_exception_context@. For sequential execution, this function is defined as 340 a weak symbol in the \CFA system-library, @libcfa@. When a \CFA program is 341 concurrent, it links with @libcfathread@, where this function is defined with a 342 strong symbol replacing the sequential version. 343 344 % The version of the function defined in @libcfa@ is very simple. It returns a 345 % pointer to a global static variable. With only one stack this global instance 346 % is associated with the only stack. 347 348 For coroutines, @this_exception_context@ accesses the exception context stored 349 at the base of the stack. For threads, @this_exception_context@ uses the 350 concurrency library to access the current stack of the thread or coroutine 351 being executed by the thread, and then accesses the exception context stored at 352 the base of this stack. 274 % stored changes depending on wheither or not the thread-library is linked. 275 276 The exception context is a piece of global storage used to maintain data 277 across different exception operations and to communicate between different 278 components. 279 280 Each stack has its own exception context. In a purely sequental program, using 281 only core Cforall, there is only one stack and the context is global. However 282 if the library @libcfathread@ is linked then there can be multiple 283 stacks so they will each need their own. 284 285 To handle this code always gets the exception context from the function 286 @this_exception_context@. The main exception handling code is in 287 @libcfa@ and that library also defines the function as a weak symbol 288 so it acts as a default. Meanwhile in @libcfathread@ the function is 289 defined as a strong symbol that replaces it when the libraries are linked 290 together. 291 292 The version of the function defined in @libcfa@ is very simple. It 293 returns a pointer to a global static variable. With only one stack this 294 global instance is associated with the only stack. 295 296 The version of the function defined in @libcfathread@ has to handle 297 more as there are multiple stacks. The exception context is included as 298 part of the per-stack data stored as part of coroutines. In the cold data 299 section, stored at the base of each stack, is the exception context for that 300 stack. The @this_exception_context@ uses the concurrency library to get 301 the current coroutine and through it the cold data section and the exception 302 context. 353 303 354 304 \section{Termination} … … 356 306 % catches. Talk about GCC nested functions. 357 307 358 Termination exceptions use libunwind heavily because it matches the intended359 use from \CC exceptions closely. The main complication for \CFA is that the 360 compiler generates C code, making it very difficult to generate the assembly to 361 form the LSDA for try blocks or destructors.308 Termination exceptions use libunwind quite heavily because it matches the 309 intended use from \CPP exceptions very closely. The main complication is that 310 since the \CFA compiler works by translating to C code it cannot generate the 311 assembly to form the LSDA for try blocks or destructors. 362 312 363 313 \subsection{Memory Management} 364 The first step of a termination raise is to copy the exception into memory365 managed by the exception system. Currently, the system uses @malloc@, rather 366 than reserved memory or the stack top. The exception handling mechanism manages 367 me mory for the exception as well as memory for libunwind and the system's own368 per-exception storage.369 370 Exceptions are stored in variable -sized blocks. \PAB{Show a memory layout371 figure.} The first component is a fixed sized data structure that containsthe372 information for libunwind and the exception system. The second component is an 373 area of memory big enough to store the exception. Macros with pointer arthritic 374 and type cast areused to move between the components or go from the embedded314 The first step of termination is to copy the exception into memory managed by 315 the exception system. Currently the system just uses malloc, without reserved 316 memory or and ``small allocation" optimizations. The exception handling 317 mechanism manages memory for the exception as well as memory for libunwind 318 and the system's own per-exception storage. 319 320 Exceptions are stored in variable sized block. The first component is a fixed 321 sized data structure that contains the information for libunwind and the 322 exception system. The second component is a blob of memory that is big enough 323 to store the exception. Macros with pointer arthritic and type cast are 324 used to move between the components or go from the embedded 375 325 @_Unwind_Exception@ to the entire node. 376 326 377 All of these nodes are linked together in a list, one list per stack, with the 378 list head stored in the exception context. Within each linked list, the most 379 recently thrown exception is at the head followed by older thrown 380 exceptions. This format allows exceptions to be thrown, while a different 381 exception is being handled. The exception at the head of the list is currently 382 being handled, while other exceptions wait for the exceptions before them to be 383 removed. 384 385 The virtual members in the exception's virtual table provide the size of the 386 exception, the copy function, and the free function, so they are specific to an 387 exception type. The size and copy function are used immediately to copy an 388 exception into managed memory. After the exception is handled the free function 389 is used to clean up the exception and then the entire node is passed to free. 390 391 \subsection{Try Statements and Catch Clauses} 392 The try statement with termination handlers is complex because it must 393 compensate for the lack of assembly-code generated from \CFA. Libunwind 394 requires an LSDA and personality function for control to unwind across a 395 function. The LSDA in particular is hard to mimic in generated C code. 396 397 The workaround is a function called @__cfaehm_try_terminate@ in the standard 398 library. The contents of a try block and the termination handlers are converted 399 into functions. These are then passed to the try terminate function and it 400 calls them. This approach puts a try statement in its own functions so that no 401 function has to deal with both termination handlers and destructors. \PAB{I do 402 not understand the previous sentence.} 403 404 This function has some custom embedded assembly that defines \emph{its} 405 personality function and LSDA. The assembly is created with handcrafted C @asm@ 406 statements, which is why there is only one version of it. The personality 407 function is structured so that it can be expanded, but currently it only 408 handles this one function. Notably, it does not handle any destructors so the 409 function is constructed so that it does need to run it. \PAB{I do not 410 understand the previous sentence.} 327 All of these nodes are strung together in a linked list. One linked list per 328 stack, with the head stored in the exception context. Within each linked list 329 the most recently thrown exception is at the head and the older exceptions 330 are further down the list. This list format allows exceptions to be thrown 331 while a different exception is being handled. Only the exception at the head 332 of the list is currently being handled, the other will wait for the 333 exceptions before them to be removed. 334 335 The virtual members in the exception's virtual table. The size of the 336 exception, the copy function and the free function are all in the virtual 337 table so they are decided per-exception type. The size and copy function are 338 used right away when the exception is copied in to managed memory. After the 339 exception is handled the free function is used to clean up the exception and 340 then the entire node is passed to free. 341 342 \subsection{Try Statements \& Catch Clauses} 343 The try statements with termination handlers have a pretty complex conversion 344 to compensate for the lack of assembly generation. Libunwind requires an LSDA 345 (Language Specific Data Area) and personality function for a function to 346 unwind across it. The LSDA in particular is hard to generate at the level of 347 C which is what the \CFA compiler outputs so a work-around is used. 348 349 This work around is a function called @__cfaehm_try_terminate@ in the 350 standard library. The contents of a try block and the termination handlers 351 are converted into functions. These are then passed to the try terminate 352 function and it calls them. This puts the try statements in their own 353 functions so that no function has to deal with both termination handlers and 354 destructors. 355 356 This function has some custom embedded assembly that defines its personality 357 function and LSDA. This is hand coded in C which is why there is only one 358 version of it, the compiler has no capability to generate it. The personality 359 function is structured so that it may be expanded, but really it only handles 360 this one function. Notably it does not handle any destructors so the function 361 is constructed so that it does need to run it. 411 362 412 363 The three functions passed to try terminate are: 413 \begin{ description}414 \item [try function:] This function is the try block, all the code inside the415 t ry block is placed inside the try function. It takes no parameters and has no416 return value. This function is called during regular execution to run the try 417 block.418 419 \item[match function:] This function is called during the search phase and 420 decides if a catch clause matches the termination exception. It is constructed 421 from the conditional part of each handler and runs each check, top to bottom, 422 in turn, first checking to see if the exception type matches and then if the 423 condition is true. It takes a pointer to the exception and returns 0 if the 424 exception is not handled here. Otherwise the return value is the id of the 425 handler that matches the exception. 426 427 \item[handler function:] This function handles the exception. It takes a 428 pointer to the exception and the handler's id and returns nothing. It is called 429 after the cleanup phase. It is constructed by stitching together the bodies of 430 each handler and dispatches to the selected handler. 431 \end{description} 432 All three functions are created with GCC nested functions. GCC nested functions 433 can be used to create closures, functions that can refer to the state of other 434 functions on the stack. This approach allows the functions to refer to all the 435 variables in scope for the function containing the @try@ statement. These 436 nested functions and all other functions besides @__cfaehm_try_terminate@ in 437 \CFA use the GCC personality function and the @-fexceptions@ flag to generate 438 t he LSDA. This allows destructors to be implemented with the cleanup attribute.364 \begin{itemize} 365 \item The try function: This function is the try block, all the code inside 366 the try block is placed inside the try function. It takes no parameters and 367 has no return value. This function is called during regular execution to run 368 the try block. 369 \item The match function: This function decides if this try statement should 370 handle any given termination exception. It takes a pointer to the exception 371 and returns 0 if the exception is not handled here. Otherwise the return value 372 is the id of the handler that should handle the exception. It is called 373 during the search phase. 374 It is constructed from the conditional part of each handler. It runs each 375 check in turn, first checking to see if the object 376 \item The catch function: This function handles the exception. It takes a 377 pointer to the exception and the handler's id and returns nothing. It is 378 called after the clean-up phase. 379 It is constructed by stitching together the bodies of each handler 380 \end{itemize} 381 All three are created with GCC nested functions. GCC nested functions can be 382 used to create closures, functions that can refer to the state of other 383 functions on the stack. This allows the functions to refer to the main 384 function and all the variables in scope. 385 386 These nested functions and all other functions besides 387 @__cfaehm_try_terminate@ in \CFA use the GCC personality function and 388 the @-fexceptions@ flag to generate the LSDA. This allows destructors 389 to be implemented with the cleanup attribute. 439 390 440 391 \section{Resumption} 441 392 % The stack-local data, the linked list of nodes. 442 393 443 Resumption simple to implement because there is no stack unwinding. The 444 resumption raise uses a list of nodes for its stack traversal. The head of the 445 list is stored in the exception context. The nodes in the list have a pointer 394 Resumption uses a list of nodes for its stack traversal. The head of the list 395 is stored in the exception context. The nodes in the list just have a pointer 446 396 to the next node and a pointer to the handler function. 447 397 448 A resumption raise traverses this list. At each node the handler function is 449 called, passing the exception by pointer. It returns true if the exception is450 handled and false otherwise.451 452 The handler function does both the matching and handling. It computes the453 condition of each @catchResume@ in top-to-bottom order, until it finds a 454 handler that matches. If no handler matches then the function returns455 false. Otherwise the matching handler is run ; if it completes successfully, the456 function returns true. Reresume, through the @throwResume;@ statement, cause 457 the function to return true.398 The on a resumption throw the this list is traversed. At each node the 399 handler function is called and is passed the exception by pointer. It returns 400 true if the exception was handled and false otherwise. 401 402 The handler function does both the matching and catching. It tries each 403 the condition of @catchResume@ in order, top-to-bottom and until it 404 finds a handler that matches. If no handler matches then the function returns 405 false. Otherwise the matching handler is run, if it completes successfully 406 the function returns true. Rethrows, through the @throwResume;@ 407 statement, cause the function to return true. 458 408 459 409 % Recursive Resumption Stuff: 460 Search skipping \see{\VPageref{p:searchskip}}, which ignores parts of the stack 461 already examined, is accomplished by updating the front of the list as the 462 search continues. Before the handler at a node is called the head of the list 463 is updated to the next node of the current node. After the search is complete, 464 successful or not, the head of the list is reset. 465 466 This mechanism means the current handler and every handler that has already 467 been checked are not on the list while a handler is run. If a resumption is 468 thrown during the handling of another resumption the active handlers and all 469 the other handler checked up to this point are not checked again. 410 Blocking out part of the stack is accomplished by updating the front of the 411 list as the search continues. Before the handler at a node is called the head 412 of the list is updated to the next node of the current node. After the search 413 is complete, successful or not, the head of the list is reset. 414 415 This means the current handler and every handler that has already been 416 checked are not on the list while a handler is run. If a resumption is thrown 417 during the handling of another resumption the active handlers and all the 418 other handler checked up to this point will not be checked again. 470 419 471 420 This structure also supports new handler added while the resumption is being 472 421 handled. These are added to the front of the list, pointing back along the 473 stack -- the first one points over all the checked handlers -- and the ordering 474 is maintained. 475 476 \label{p:zero-cost} 477 Note, the resumption implementation has a cost for entering/exiting a @try@ 478 statement with @catchResume@ clauses, whereas a @try@ statement with @catch@ 479 clauses has zero-cost entry/exit. While resumption does not need the stack 480 unwinding and cleanup provided by libunwind, it could use the search phase to 481 providing zero-cost enter/exit using the LSDA. Unfortunately, there is no way 482 to return from a libunwind search without installing a handler or raising an 483 error. Although workarounds might be possible, they are beyond the scope of 484 this thesis. The current resumption implementation has simplicity in its 485 favour. 422 stack -- the first one will point over all the checked handlers -- and the 423 ordering is maintained. 424 425 \subsection{Libunwind Compatibility} 426 Resumption does not use libunwind for two simple reasons. The first is that 427 it does not have to unwind anything so would never need to use the clean-up 428 phase. Still the search phase could be used to make it free to enter or exit 429 a try statement with resumption handlers in the same way termination handlers 430 are for the same trade off in the cost of the throw. This is where the second 431 reason comes in, there is no way to return from a search without installing 432 a handler or raising an error. 433 434 Although work arounds could be created none seemed to be worth it for the 435 prototype. This implementation has no difference in behaviour and is much 436 simpler. 486 437 % Seriously, just compare the size of the two chapters and then consider 487 438 % that unwind is required knowledge for that chapter. … … 489 440 \section{Finally} 490 441 % Uses destructors and GCC nested functions. 491 Finally clauses is placed into a GCC nested-function with a unique name, and no 492 arguments or return values. This nested function is then set as the cleanup 493 function of an empty object that is declared at the beginning of a block placed 494 around the context of the associated @try@ statement. 442 Finally clauses are a simple decomposition to some of the existing features. 443 The code in the block is placed into a GCC nested function with a unique name, 444 no arguments or return values. This nested function is then set as the 445 clean-up function of an empty object that is declared at the beginning of a 446 block placed around the contexts of the try statement. 495 447 496 448 The rest is handled by GCC. The try block and all handlers are inside the 497 block. At completion, control exits the block and the empty object is cleaned498 up, which runs the function that contains the finally code.449 block. When they are complete control exits the block and the empty object 450 is cleaned up, which runs the function that contains the finally code. 499 451 500 452 \section{Cancellation} … … 502 454 503 455 Cancellation also uses libunwind to do its stack traversal and unwinding, 504 however it uses a different primary function @_Unwind_ForcedUnwind@. Details505 of its interface can be found in the \VRef{s:ForcedUnwind}.506 507 The first step of cancellation is to find the cancelled stack and its type:508 coroutine or thread. Fortunately, the threadlibrary stores the main thread509 pointer and the current thread pointer ,and every thread stores a pointer to456 however it uses a different primary function @_Unwind_ForcedUnwind@. 457 Details of its interface can be found in the unwind section. 458 459 The first step of cancellation is to find the stack was cancelled and which 460 type of stack it is. Luckily the threads library stores the main thread 461 pointer and the current thread pointer and every thread stores a pointer to 510 462 its main coroutine and the coroutine it is currently executing. 511 463 512 The first check is if the current thread's main and current coroutine do not 513 match, implying a coroutine cancellation; otherwise, it is a thread 514 cancellation. Otherwise it is a main thread cancellation. \PAB{Previous 515 sentence does not make sense.} 516 517 However, if the threading library is not linked, the sequential execution is on 518 the main stack. Hence, the entire check is skipped because the weak-symbol 519 function is loaded. Therefore, a main thread cancellation is unconditionally 520 performed. 521 522 Regardless of how the stack is chosen, the stop function and parameter are 523 passed to the forced-unwind function. The general pattern of all three stop 524 functions is the same: they continue unwinding until the end of stack when they 525 do there primary work. 526 527 For main stack cancellation, the transfer is just a program abort. 528 529 For coroutine cancellation, the exception is stored on the coroutine's stack, 530 and the coroutine context switches to its last resumer. The rest is handled on 531 the backside of the resume, which check if the resumed coroutine is 532 cancelled. If cancelled, the exception is retrieved from the resumed coroutine, 533 and a @CoroutineCancelled@ exception is constructed and loaded with the 534 cancelled exception. It is then resumed as a regular exception with the default 535 handler coming from the context of the resumption call. 536 537 For thread cancellation, the exception is stored on the thread's main stack and 538 then context switched to the scheduler. The rest is handled by the thread 539 joiner. When the join is complete, the joiner checks if the joined thread is 540 cancelled. If cancelled, the exception is retrieved and the joined thread, and 541 a @ThreadCancelled@ exception is constructed and loaded with the cancelled 542 exception. The default handler is passed in as a function pointer. If it is 543 null (as it is for the auto-generated joins on destructor call), the default is 544 used, which is a program abort. 545 %; which gives the required handling on implicate join. 464 So if the the current thread's main and current coroutine do not match, it is 465 a coroutine cancellation. Otherwise if the main and current thread do not 466 match, it is a thread cancellation. Otherwise it is a main thread 467 cancellation. 468 469 However if the threading library is not linked then execution must be on the 470 main stack as that is the only one that exists. So the entire check is skipped 471 using the linker and weak symbols. Instead the main thread cancellation is 472 unconditionally preformed. 473 474 Regardless of how they are choosen afterwords the stop function and the stop 475 parameter are passed to the forced unwind functon. The general pattern of all 476 three stop functions is the same, they continue unwinding until the end of 477 stack when they do there primary work. 478 479 Main stack cancellation it is very simple. The ``transfer" is just an abort, 480 the program stops executing. 481 482 The coroutine cancellation stores the exception on the coroutine and then 483 does a coroutine context switch. The rest is handled inside resume. Every time 484 control returns from a resumed thread there is a check to see if it is 485 cancelled. If it is the exception is retrieved and the CoroutineCancelled 486 exception is constructed and loaded. It is then thrown as a regular exception 487 with the default handler coming from the context of the resumption call. 488 489 The thread cancellation stores the exception on the thread's main stack and 490 then returns to the scheduler. The rest is handled by the joiner. The wait 491 for the joined thread to finish works the same but after that it checks 492 to see if there was a cancellation. If there was the exception is retrieved 493 and the ThreadCancelled exception is constructed. The default handler is 494 passed in as a function pointer. If it is null (as it is for the 495 auto-generated joins on destructor call) it a default is used that simply 496 calls abort; which gives the required handling on implicate join. -
doc/theses/andrew_beach_MMath/unwinding.tex
rf99f5ba rcd70477 1 \chapter{ \texorpdfstring{Unwinding in \CFA}{Unwinding in Cforall}}1 \chapter{Unwinding in \CFA} 2 2 3 Stack unwinding is the process of removing stack frames (activations) from the 4 stack. On function entry and return, unwinding is handled directly by the code 5 embedded in the function. Usually, the stack-frame size is known statically 6 based on parameters and local variable declarations. For dynamically-sized 7 local variables, a runtime computation is necessary to know the frame 8 size. Finally, a function's frame-size may change during execution as local 9 variables (static or dynamic sized) go in and out of scope. 10 Allocating/deallocating stack space is usually an $O(1)$ operation achieved by 11 bumping the hardware stack-pointer up or down as needed. 3 Stack unwinding is the process of removing things from the stack. Within 4 functions and on function return this is handled directly by the code in the 5 function itself as it knows exactly what is on the stack just from the 6 current location in the function. Unwinding across stack frames means that it 7 is no longer knows exactly what is on the stack or even how much of the stack 8 needs to be removed. 12 9 13 Unwinding across multiple stack frames is more complex because individual stack 14 management code associated with each frame is bypassed. That is, the location 15 of a function's frame code is largely unknown and dispersed throughout the 16 function, hence the current stack-frame size managed by that code is also 17 unknown. Hence, code unwinding across frames does not have direct knowledge 18 about what is on the stack, and hence, how much of the stack needs to be 19 removed.10 Even this is fairly simple if nothing needs to happen when the stack unwinds. 11 Traditional C can unwind the stack by saving and restoring state (with 12 @setjmp@ \& @longjmp@). However many languages define actions that 13 have to be taken when something is removed from the stack, such as running 14 a variable's destructor or a @try@ statement's @finally@ 15 clause. Handling this requires walking the stack going through each stack 16 frame. 20 17 21 The traditional unwinding mechanism for C is implemented by saving a snap-shot 22 of a function's state with @setjmp@ and restoring that snap-shot with 23 @longjmp@. This approach bypasses the need to know stack details by simply 24 reseting to a snap-shot of an arbitrary but existing function frame on the 25 stack. It is up to the programmer to ensure the snap-shot is valid when it is 26 reset, making the code fragile with potential errors that are difficult to 27 debug because the stack becomes corrupted. 18 For exceptions, this means everything from the point the exception is raised 19 to the point it is caught, while checking each frame for handlers during the 20 stack walk to find out where it should be caught. This is where the most of 21 the expense and complexity of exception handling comes from. 28 22 29 However, many languages define cleanup actions that have to be taken when 30 something is deallocated from the stack or blocks end, such as running a 31 variable's destructor or a @try@ statement's @finally@ clause. Handling these 32 mechanisms requires walking the stack and checking each stack frame for these 33 potential actions. 34 35 For exceptions, it must be possible to walk the stack frames in search of try 36 statements with handlers to perform exception matching. For termination 37 exceptions, it must be possible to unwind all stack frames from the throw to 38 the matching catch, and each of these frames must be checked for cleanup 39 actions. Stack walking is where the most of the complexity and expense of 40 exception handling comes from. 41 42 One of the most popular tools for stack management is libunwind, a low level 43 library that provides tools for stack walking and unwinding. What follows is an 44 overview of all the relevant features of libunwind and how \CFA uses them to 45 implement its exception handling. 23 To do all of this we use libunwind, a low level library that provides tools 24 for stack walking and stack unwinding. What follows is an overview of all the 25 relivant features of libunwind and then how \CFA uses them to implement its 26 exception handling. 46 27 47 28 \section{libunwind Usage} 48 \CFA uses two primary functions in libunwind to create most of its exceptional 49 control-flow: @_Unwind_RaiseException@ and @_Unwind_ForcedUnwind@. Their 50 operation is divided into two phases: search and clean-up. The search phase -- 51 phase 1 -- is used to scan the stack but not unwinding it. The clean-up phase 52 -- phase 2 -- is used for unwinding. 29 30 \CFA uses two primary functions in libunwind to create most of its 31 exceptional control-flow: @_Unwind_RaiseException@ and 32 @_Unwind_ForcedUnwind@. 33 Their operation is divided into two phases: search and clean-up. The search 34 phase -- phase 1 -- is used to scan the stack but not unwinding it. The 35 clean-up phase -- phase 2 -- is used for unwinding. 53 36 54 37 The raise-exception function uses both phases. It starts by searching for a … … 61 44 A personality function performs three tasks, although not all have to be 62 45 present. The tasks performed are decided by the actions provided. 63 @_Unwind_Action@ is a bitmask of possible actions and an argument of this type64 is passed into the personality function.46 @_Unwind_Action@ is a bitmask of possible actions and an argument of 47 this type is passed into the personality function. 65 48 \begin{itemize} 66 \item 67 \begin{sloppypar} 68 @_UA_SEARCH_PHASE@ is passed in for the search phase and tells the personality 69 function to check for handlers. If there is a handler in a stack frame, as 70 defined by the language, the personality function returns @_URC_HANDLER_FOUND@; 71 otherwise it return @_URC_CONTINUE_UNWIND@. 72 \end{sloppypar} 73 \item 74 @_UA_CLEANUP_PHASE@ is passed in during the clean-up phase and means part or 75 all of the stack frame is removed. The personality function does whatever 76 clean-up the language defines (such as running destructors/finalizers) and then 77 generally returns @_URC_CONTINUE_UNWIND@. 78 \item 79 @_UA_HANDLER_FRAME@ means the personality function must install a handler. It 80 is also passed in during the clean-up phase and is in addition to the clean-up 81 action. libunwind provides several helpers for the personality function. Once 82 it is done, the personality function returns @_URC_INSTALL_CONTEXT@. 49 \item@_UA_SEARCH_PHASE@ is passed in search phase and tells the 50 personality function to check for handlers. If there is a handler in this 51 stack frame, as defined by the language, the personality function should 52 return @_URC_HANDLER_FOUND@. Otherwise it should return 53 @_URC_CONTINUE_UNWIND@. 54 \item@_UA_CLEANUP_PHASE@ is passed in during the clean-up phase and 55 means part or all of the stack frame is removed. The personality function 56 should do whatever clean-up the language defines 57 (such as running destructors/finalizers) and then generally returns 58 @_URC_CONTINUE_UNWIND@. 59 \item@_UA_HANDLER_FRAME@ means the personality function must install 60 a handler. It is also passed in during the clean-up phase and is in addition 61 to the clean-up action. libunwind provides several helpers for the personality 62 function here. Once it is done, the personality function must return 63 @_URC_INSTALL_CONTEXT@. 83 64 \end{itemize} 84 The personality function is given a number of other arguments. Some ar guments85 are for compatibility,and there is the @struct _Unwind_Context@ pointer which86 ispassed to many helpers to get information about the current stack frame.65 The personality function is given a number of other arguments. Some are for 66 compatability and there is the @struct _Unwind_Context@ pointer which 67 passed to many helpers to get information about the current stack frame. 87 68 88 For cancellation, forced-unwind only performs the clean-up phase. It takes 89 three arguments: a pointer to the exception, a pointer to the stop function and 90 a pointer to the stop parameter. It does most of the same actions as phase two 91 of raise-exception but passes in an extra action to the personality function on 92 each stack frame, @_UA_FORCE_UNWIND@, which means a handler cannot be 69 Forced-unwind only performs the clean-up phase. It takes three arguments: 70 a pointer to the exception, a pointer to the stop function and a pointer to 71 the stop parameter. It does most of the same things as phase two of 72 raise-exception but with some extras. 73 The first it passes in an extra action to the personality function on each 74 stack frame, @_UA_FORCE_UNWIND@, which means a handler cannot be 93 75 installed. 94 76 95 As well, forced-unwind calls the stop function each time it steps into a frame, 96 before calling the personality function. The stop function receives allthe97 s ame arguments as the personality function and the stop parameter supplied to98 forced-unwind.77 The big change is that forced-unwind calls the stop function. Each time it 78 steps into a frame, before calling the personality function, it calls the 79 stop function. The stop function receives all the same arguments as the 80 personality function will and the stop parameter supplied to forced-unwind. 99 81 100 82 The stop function is called one more time at the end of the stack after all 101 stack frames have been removed. The standard API marks this frameby setting83 stack frames have been removed. By the standard API this is marked by setting 102 84 the stack pointer inside the context passed to the stop function. However both 103 85 GCC and Clang add an extra action for this case @_UA_END_OF_STACK@. 104 86 105 Each time the stop function is called, it can do one or two things. When it is 106 not the end of the stack it can return @_URC_NO_REASON@ to continue unwinding. 87 Each time function the stop function is called it can do one or two things. 88 When it is not the end of the stack it can return @_URC_NO_REASON@ to 89 continue unwinding. 107 90 % Is there a reason that NO_REASON is used instead of CONTINUE_UNWIND? 108 The other option is to use some other means to transfer control elsewhere and 109 never return to its caller. libunwind provides no additional tools for 110 a lternate transfers of control.91 Its only other option is to use its own means to transfer control elsewhere 92 and never return to its caller. It may always do this and no additional tools 93 are provided to do it. 111 94 112 \section{\ texorpdfstring{\CFA Implementation}{Cforall Implementation}}95 \section{\CFA Implementation} 113 96 114 To use libunwind, \CFA provides several wrappers, its own storage, personality115 functions, and a stop function.97 To use libunwind, \CFA provides several wrappers, its own storage, 98 personality functions, and a stop function. 116 99 117 100 The wrappers perform three tasks: set-up, clean-up and controlling the … … 125 108 The core control code is called every time a throw -- after set-up -- or 126 109 re-throw is run. It uses raise-exception to search for a handler and to run it 127 if one is found. If no handler is found and raise-exception returns ,then110 if one is found. If no handler is found and raise-exception returns then 128 111 forced-unwind is called to run all destructors on the stack before terminating 129 112 the process. 130 113 131 The stop function is simple. It checks forthe end of stack flag to see if132 unwinding is finished. If so, it calls @exit@ to end the process, otherwise it 133 returns with no-reason to continue unwinding.114 The stop function is very simple. It checks the end of stack flag to see if 115 it is finished unwinding. If so, it calls @exit@ to end the process, 116 otherwise it returns with no-reason to continue unwinding. 134 117 % Yeah, this is going to have to change. 135 118 136 119 The personality routine is more complex because it has to obtain information 137 about the function by scanning the L anguage Specific Data Area (LSDA). This120 about the function by scanning the LSDA (Language Specific Data Area). This 138 121 step allows a single personality function to be used for multiple functions and 139 let s that personality function figure out exactly where in the function140 execution is, what is currently in the stack frame, and what handlers should be141 checked.122 let that personaliity function figure out exactly where in the function 123 execution was, what is currently in the stack frame and what handlers should 124 be checked. 142 125 % Not that we do that yet. 143 126 144 It is also necessary to generate the LSDA, which is difficult. It requires 145 knowledge about the location of the instruction pointer and stack layout, which146 varies with compiler and optimization levels. Fortunately, for frames where 147 there are only destructors, GCC's attribute cleanup with the @-fexception@ flag 148 issufficient to handle unwinding.127 However, generating the LSDA is difficult. It requires knowledge about the 128 location of the instruction pointer and stack layout, which varies with 129 compiler and optimization levels. So for frames where there are only 130 destructors, GCC's attribute cleanup with the @-fexception@ flag is 131 sufficient to handle unwinding. 149 132 150 The only functions that require more information are those containing @try@151 statements. Specifically, only @try@ statements with @catch@ clauses need to be 152 transformed. The @try@ statement is converted into a series of closures that 153 c an access other parts of the function according to scoping rules but can be154 passed around. The @catch@ clauses are converted into two functions: the match 155 function and the handler function.133 The only functions that require more than that are those that contain 134 @try@ statements. A @try@ statement has a @try@ 135 clause, some number of @catch@ clauses and @catchResume@ 136 clauses and may have a @finally@ clause. Of these only @try@ 137 statements with @catch@ clauses need to be transformed and only they 138 and the @try@ clause are involved. 156 139 157 Together the match function and the catch function form the code that runs when 158 an exception passes out of the guarded block for a try statement. The match 159 function is used during the search phase: it is passed an exception and checks 160 each handler to see if the raised exception matches the handler exception. It 161 returns an index that represents which handler matched or there is no 162 match. The catch function is used during the clean-up phase, it is passed an 163 exception and the index of a handler. It casts the exception to the exception 164 type declared in that handler and then runs the handler's body. 140 The @try@ statement is converted into a series of closures which can 141 access other parts of the function according to scoping rules but can be 142 passed around. The @try@ clause is converted into the try functions, 143 almost entirely unchanged. The @catch@ clauses are converted into two 144 functions; the match function and the catch function. 165 145 166 These three functions are passed to @try_terminate@, which is an 146 Together the match function and the catch function form the code that runs 147 when an exception passes out of a try block. The match function is used during 148 the search phase, it is passed an exception and checks each handler to see if 149 it will handle the exception. It returns an index that repersents which 150 handler matched or that none of them did. The catch function is used during 151 the clean-up phase, it is passed an exception and the index of a handler. It 152 casts the exception to the exception type declared in that handler and then 153 runs the handler's body. 154 155 These three functions are passed to @try_terminate@. This is an 167 156 % Maybe I shouldn't quote that, it isn't its actual name. 168 internal hand-written function that has its own personality function and custom169 assembly LSDA for doingthe exception handling in \CFA. During normal170 execution , this function calls the try function and then return. It is only171 when exceptions are thrown that anything interesting happens.157 internal hand-written function that has its own personality function and 158 custom assembly LSD does the exception handling in \CFA. During normal 159 execution all this function does is call the try function and then return. 160 It is only when exceptions are thrown that anything interesting happens. 172 161 173 162 During the search phase the personality function gets the pointer to the match 174 function and calls it. If the match function returns a handler index ,the163 function and calls it. If the match function returns a handler index the 175 164 personality function saves it and reports that the handler has been found, 176 otherwise unwinding continues. During the clean-up phase, the personality177 function only performs an action, when a handler is found in a frame. For each 178 found frame, the personality function installs the handler, which sets the 179 inst ruction pointer in @try_terminate@ to an otherwise unused section that180 calls the catch function, passing it the current exception and handler index. 181 @try_terminate@ returns as soon as the catch function returns. At this point 182 control has returned to normal control flow.165 otherwise unwinding continues. 166 During the clean-up phase the personality function only does anything if the 167 handler was found in this frame. If it was then the personality function 168 installs the handler, which is setting the instruction pointer in 169 @try_terminate@ to an otherwise unused section that calls the catch 170 function, passing it the current exception and handler index. 171 @try_terminate@ returns as soon as the catch function returns. 183 172 184 \PAB{Maybe a diagram would be helpful?} 173 At this point control has returned to normal control flow. -
doc/theses/andrew_beach_MMath/uw-ethesis.tex
rf99f5ba rcd70477 91 91 \hypersetup{ 92 92 plainpages=false, % needed if Roman numbers in frontpages 93 unicode=false, % non-Latin characters in Acrobat 's bookmarks94 pdftoolbar=true, % show Acrobat 's toolbar?95 pdfmenubar=true, % show Acrobat 's menu?93 unicode=false, % non-Latin characters in Acrobat’s bookmarks 94 pdftoolbar=true, % show Acrobat’s toolbar? 95 pdfmenubar=true, % show Acrobat’s menu? 96 96 pdffitwindow=false, % window fit to page when opened 97 97 pdfstartview={FitH}, % fits the width of the page to the window … … 163 163 \input{common} 164 164 \CFAStyle % CFA code-style for all languages 165 \lstset{language=CFA,basicstyle=\linespread{0.9}\tt} % CFA default lnaguage 166 \newcommand{\PAB}[1]{{\color{blue}PAB: #1}} 165 \lstset{basicstyle=\linespread{0.9}\tt} 167 166 168 167 %====================================================================== … … 189 188 \input{existing} 190 189 \input{features} 191 \input{implement} 192 %\input{unwinding} 190 \input{unwinding} 193 191 \input{future} 194 192 -
libcfa/src/Makefile.am
rf99f5ba rcd70477 76 76 stdlib.hfa \ 77 77 time.hfa \ 78 bits/weakso_locks.hfa \79 78 containers/maybe.hfa \ 80 79 containers/pair.hfa \ -
libcfa/src/bits/collection.hfa
rf99f5ba rcd70477 1 // 2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo 3 // 4 // The contents of this file are covered under the licence agreement in the 5 // file "LICENCE" distributed with Cforall. 6 // 7 // bits/collection.hfa -- PUBLIC 8 // Intrusive singly-linked list 9 // 10 // Author : Colby Alexander Parsons & Peter A. Buhr 11 // Created On : Thu Jan 21 19:46:50 2021 12 // Last Modified By : 13 // Last Modified On : 14 // Update Count : 15 // 1 #pragma once 2 #include <stdio.h> // REMOVE THIS AFTER DEBUGGING 16 3 17 #pragma once18 4 19 5 struct Colable { 20 // next node in the list6 struct Colable * next; // next node in the list 21 7 // invariant: (next != 0) <=> listed() 22 struct Colable * next;23 8 }; 24 9 #ifdef __cforall … … 68 53 Collection & ?=?( const Collection & ) = void; // no assignment 69 54 70 void ?{}( Collection & collection ) with( collection ) { 55 void ?{}( Collection & collection ) with( collection ) { 71 56 root = 0p; 72 57 } // post: empty() -
libcfa/src/bits/sequence.hfa
rf99f5ba rcd70477 1 //2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo3 //4 // The contents of this file are covered under the licence agreement in the5 // file "LICENCE" distributed with Cforall.6 //7 // bits/sequence.hfa -- PUBLIC8 // Intrusive doubly-linked list9 //10 // Author : Colby Alexander Parsons & Peter A. Buhr11 // Created On : Thu Jan 21 19:46:50 202112 // Last Modified By :13 // Last Modified On :14 // Update Count :15 //16 17 1 #pragma once 18 2 … … 22 6 struct Seqable { 23 7 __cfa_anonymous_object(Colable); 24 // pointer to previous node in the list 25 struct Seqable * back; 8 struct Seqable * back; // pointer to previous node in the list 26 9 }; 27 10 … … 44 27 return sq->back; 45 28 } 29 30 // // wrappers to make Collection have T 31 // forall( T & ) { 32 // T *& Back( T * n ) { 33 // return (T *)Back( (Seqable *)n ); 34 // } 35 // } // distribution 46 36 } // distribution 47 37 … … 53 43 // and the back field of the last node points at the first node (circular). 54 44 55 forall( T & ) {45 forall( T & | { T *& Back ( T * ); T *& Next ( T * ); } ) { 56 46 struct Sequence { 57 // Plan 9 inheritance 58 inline Collection; 47 inline Collection; // Plan 9 inheritance 59 48 }; 60 49 61 50 static inline { 62 void ?{}( Sequence(T) &, const Sequence(T) & ) = void; // no copy63 Sequence(T) & ?=?( const Sequence(T) & ) = void; // no assignment64 65 void ?{}( Sequence(T) & s ) with( s ) {66 ((Collection &)s){};67 } // post: isEmpty()68 }69 70 static inline forall(| { T *& Back ( T * ); T *& Next ( T * ); }) {71 51 // wrappers to make Collection have T 72 52 T & head( Sequence(T) & s ) with( s ) { 73 53 return *(T *)head( (Collection &)s ); 74 54 } // post: empty() & head() == 0 | !empty() & head() in *s 55 56 void ?{}( Sequence(T) &, const Sequence(T) & ) = void; // no copy 57 Sequence(T) & ?=?( const Sequence(T) & ) = void; // no assignment 58 59 void ?{}( Sequence(T) & s ) with( s ) { 60 ((Collection &)s){}; 61 } // post: isEmpty() 75 62 76 63 // Return a pointer to the last sequence element, without removing it. … … 158 145 return n; 159 146 } // post: n->listed() & *n in *s & succ(n) == bef 160 147 161 148 // pre: n->listed() & *n in *s 162 149 T & remove( Sequence(T) & s, T & n ) with( s ) { // O(1) … … 298 285 299 286 static inline { 300 void ?{}( SeqIterRev(T) & si ) with( si ) { 287 void ?{}( SeqIterRev(T) & si ) with( si ) { 301 288 ((ColIter &)si){}; 302 289 seq = 0p; … … 304 291 305 292 // Create a iterator active in sequence s. 306 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 293 void ?{}( SeqIterRev(T) & si, Sequence(T) & s ) with( si ) { 307 294 ((ColIter &)si){}; 308 295 seq = &s; … … 310 297 } // post: elts = null 311 298 312 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { 299 void ?{}( SeqIterRev(T) & si, Sequence(T) & s, T & start ) with( si ) { 313 300 ((ColIter &)si){}; 314 301 seq = &s; -
libcfa/src/concurrency/locks.cfa
rf99f5ba rcd70477 1 //2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo3 //4 // The contents of this file are covered under the licence agreement in the5 // file "LICENCE" distributed with Cforall.6 //7 // locks.hfa -- LIBCFATHREAD8 // Runtime locks that used with the runtime thread system.9 //10 // Author : Colby Alexander Parsons11 // Created On : Thu Jan 21 19:46:50 202112 // Last Modified By :13 // Last Modified On :14 // Update Count :15 //16 17 #define __cforall_thread__18 19 1 #include "locks.hfa" 20 2 #include "kernel_private.hfa" … … 74 56 75 57 void ^?{}( blocking_lock & this ) {} 76 58 void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };} 59 void ^?{}( single_acquisition_lock & this ) {} 60 void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };} 61 void ^?{}( owner_lock & this ) {} 62 void ?{}( multiple_acquisition_lock & this ) {((blocking_lock &)this){ true, false };} 63 void ^?{}( multiple_acquisition_lock & this ) {} 77 64 78 65 void lock( blocking_lock & this ) with( this ) { … … 181 168 unlock( lock ); 182 169 } 170 171 //----------------------------------------------------------------------------- 172 // Overloaded routines for traits 173 // These routines are temporary until an inheritance bug is fixed 174 void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 175 void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 176 void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); } 177 void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 178 void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 179 size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 180 181 void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); } 182 void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); } 183 void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); } 184 void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); } 185 void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); } 186 size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); } 187 188 void lock ( multiple_acquisition_lock & this ) { lock ( (blocking_lock &)this ); } 189 void unlock ( multiple_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); } 190 void on_wait ( multiple_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); } 191 void on_notify( multiple_acquisition_lock & this, struct $thread * t ){ on_notify( (blocking_lock &)this, t ); } 192 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ){ set_recursion_count( (blocking_lock &)this, recursion ); } 193 size_t get_recursion_count( multiple_acquisition_lock & this ){ return get_recursion_count( (blocking_lock &)this ); } 183 194 184 195 //----------------------------------------------------------------------------- -
libcfa/src/concurrency/locks.hfa
rf99f5ba rcd70477 1 //2 // Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo3 //4 // The contents of this file are covered under the licence agreement in the5 // file "LICENCE" distributed with Cforall.6 //7 // locks.hfa -- PUBLIC8 // Runtime locks that used with the runtime thread system.9 //10 // Author : Colby Alexander Parsons11 // Created On : Thu Jan 21 19:46:50 202112 // Last Modified By :13 // Last Modified On :14 // Update Count :15 //16 17 1 #pragma once 18 2 19 3 #include <stdbool.h> 20 4 21 #include "bits/weakso_locks.hfa" 5 #include "bits/locks.hfa" 6 #include "bits/sequence.hfa" 7 8 #include "invoke.h" 22 9 23 10 #include "time_t.hfa" 24 11 #include "time.hfa" 25 26 //----------27 struct single_acquisition_lock {28 inline blocking_lock;29 };30 31 static inline void ?{}( single_acquisition_lock & this ) {((blocking_lock &)this){ false, false };}32 static inline void ^?{}( single_acquisition_lock & this ) {}33 static inline void lock ( single_acquisition_lock & this ) { lock ( (blocking_lock &)this ); }34 static inline void unlock ( single_acquisition_lock & this ) { unlock ( (blocking_lock &)this ); }35 static inline void on_wait ( single_acquisition_lock & this ) { on_wait( (blocking_lock &)this ); }36 static inline void on_notify ( single_acquisition_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }37 static inline void set_recursion_count( single_acquisition_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }38 static inline size_t get_recursion_count( single_acquisition_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }39 40 //----------41 struct owner_lock {42 inline blocking_lock;43 };44 45 static inline void ?{}( owner_lock & this ) {((blocking_lock &)this){ true, true };}46 static inline void ^?{}( owner_lock & this ) {}47 static inline void lock ( owner_lock & this ) { lock ( (blocking_lock &)this ); }48 static inline void unlock ( owner_lock & this ) { unlock ( (blocking_lock &)this ); }49 static inline void on_wait ( owner_lock & this ) { on_wait( (blocking_lock &)this ); }50 static inline void on_notify( owner_lock & this, struct $thread * t ) { on_notify( (blocking_lock &)this, t ); }51 static inline void set_recursion_count( owner_lock & this, size_t recursion ) { set_recursion_count( (blocking_lock &)this, recursion ); }52 static inline size_t get_recursion_count( owner_lock & this ) { return get_recursion_count( (blocking_lock &)this ); }53 12 54 13 //----------------------------------------------------------------------------- … … 79 38 info_thread(L) *& Next( info_thread(L) * this ); 80 39 } 40 41 //----------------------------------------------------------------------------- 42 // Blocking Locks 43 struct blocking_lock { 44 // Spin lock used for mutual exclusion 45 __spinlock_t lock; 46 47 // List of blocked threads 48 Sequence( $thread ) blocked_threads; 49 50 // Count of current blocked threads 51 size_t wait_count; 52 53 // Flag if the lock allows multiple acquisition 54 bool multi_acquisition; 55 56 // Flag if lock can be released by non owner 57 bool strict_owner; 58 59 // Current thread owning the lock 60 struct $thread * owner; 61 62 // Number of recursion level 63 size_t recursion_count; 64 }; 65 66 struct single_acquisition_lock { 67 inline blocking_lock; 68 }; 69 70 struct owner_lock { 71 inline blocking_lock; 72 }; 73 74 struct multiple_acquisition_lock { 75 inline blocking_lock; 76 }; 77 78 void ?{}( blocking_lock & this, bool multi_acquisition, bool strict_owner ); 79 void ^?{}( blocking_lock & this ); 80 81 void ?{}( single_acquisition_lock & this ); 82 void ^?{}( single_acquisition_lock & this ); 83 84 void ?{}( owner_lock & this ); 85 void ^?{}( owner_lock & this ); 86 87 void ?{}( multiple_acquisition_lock & this ); 88 void ^?{}( multiple_acquisition_lock & this ); 89 90 void lock( blocking_lock & this ); 91 bool try_lock( blocking_lock & this ); 92 void unlock( blocking_lock & this ); 93 void on_notify( blocking_lock & this, struct $thread * t ); 94 void on_wait( blocking_lock & this ); 95 size_t wait_count( blocking_lock & this ); 96 void set_recursion_count( blocking_lock & this, size_t recursion ); 97 size_t get_recursion_count( blocking_lock & this ); 98 99 void lock( single_acquisition_lock & this ); 100 void unlock( single_acquisition_lock & this ); 101 void on_notify( single_acquisition_lock & this, struct $thread * t ); 102 void on_wait( single_acquisition_lock & this ); 103 void set_recursion_count( single_acquisition_lock & this, size_t recursion ); 104 size_t get_recursion_count( single_acquisition_lock & this ); 105 106 void lock( owner_lock & this ); 107 void unlock( owner_lock & this ); 108 void on_notify( owner_lock & this, struct $thread * t ); 109 void on_wait( owner_lock & this ); 110 void set_recursion_count( owner_lock & this, size_t recursion ); 111 size_t get_recursion_count( owner_lock & this ); 112 113 void lock( multiple_acquisition_lock & this ); 114 void unlock( multiple_acquisition_lock & this ); 115 void on_notify( multiple_acquisition_lock & this, struct $thread * t ); 116 void on_wait( multiple_acquisition_lock & this ); 117 void set_recursion_count( multiple_acquisition_lock & this, size_t recursion ); 118 size_t get_recursion_count( multiple_acquisition_lock & this ); 81 119 82 120 //----------------------------------------------------------------------------- -
libcfa/src/stdlib.hfa
rf99f5ba rcd70477 10 10 // Created On : Thu Jan 28 17:12:35 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Jan 21 22:02:13 202113 // Update Count : 5 7412 // Last Modified On : Mon Jan 18 21:51:13 2021 13 // Update Count : 569 14 14 // 15 15 … … 195 195 #pragma GCC diagnostic push 196 196 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 197 assert( size <= sizeof(Fill.t) ); 198 memcpy( (char *)ptr + i, &Fill.t, size ); 197 memcpy( (char *)ptr + i, &Fill.t, sizeof(Fill.t) ); 199 198 #pragma GCC diagnostic pop 200 199 } -
src/Parser/parser.yy
rf99f5ba rcd70477 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Jan 27 08:58:56202113 // Update Count : 46 8012 // Last Modified On : Mon Jan 11 21:32:10 2021 13 // Update Count : 4633 14 14 // 15 15 … … 41 41 42 42 %{ 43 #define YYDEBUG_LEXER_TEXT ( yylval )// lexer loads this up each time43 #define YYDEBUG_LEXER_TEXT (yylval) // lexer loads this up each time 44 44 #define YYDEBUG 1 // get the pretty debugging code to compile 45 45 #define YYERROR_VERBOSE // more information in syntax errors … … 63 63 extern TypedefTable typedefTable; 64 64 65 stack< LinkageSpec::Spec> linkageStack;65 stack< LinkageSpec::Spec > linkageStack; 66 66 67 67 bool appendStr( string & to, string & from ) { … … 187 187 ConstantExpr * constant = dynamic_cast<ConstantExpr *>(type->expr.get()); 188 188 if ( constant && (constant->get_constant()->get_value() == "0" || constant->get_constant()->get_value() == "1") ) { 189 type = new ExpressionNode( new CastExpr( maybeMoveBuild<Expression>(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) );189 type = new ExpressionNode( new CastExpr( maybeMoveBuild< Expression >(type), new BasicType( Type::Qualifiers(), BasicType::SignedInt ) ) ); 190 190 } // if 191 191 return new ForCtrl( … … 440 440 441 441 %type<decl> type_qualifier type_qualifier_name forall type_qualifier_list_opt type_qualifier_list 442 %type<decl> type_specifier type_specifier_nobody enum_specifier_nobody442 %type<decl> type_specifier type_specifier_nobody 443 443 444 444 %type<decl> variable_declarator variable_ptr variable_array variable_function 445 445 %type<decl> variable_abstract_declarator variable_abstract_ptr variable_abstract_array variable_abstract_function 446 446 447 %type<decl> attribute_list_opt attribute_list attribute_ opt attribute attribute_name_listattribute_name447 %type<decl> attribute_list_opt attribute_list attribute_name_list attribute attribute_name 448 448 449 449 // initializers … … 578 578 { $$ = $2; } 579 579 | '(' compound_statement ')' // GCC, lambda expression 580 { $$ = new ExpressionNode( new StmtExpr( dynamic_cast< CompoundStmt *>(maybeMoveBuild<Statement>($2) ) ) ); }580 { $$ = new ExpressionNode( new StmtExpr( dynamic_cast< CompoundStmt * >(maybeMoveBuild< Statement >($2) ) ) ); } 581 581 | type_name '.' identifier // CFA, nested type 582 582 { SemanticError( yylloc, "Qualified name is currently unimplemented." ); $$ = nullptr; } … … 610 610 { 611 611 // create a GenericExpr wrapper with one association pair 612 $$ = new GenericExpr( nullptr, { { maybeMoveBuildType($1), maybeMoveBuild<Expression>( $3) } } );612 $$ = new GenericExpr( nullptr, { { maybeMoveBuildType($1), maybeMoveBuild<Expression>($3) } } ); 613 613 } 614 614 | DEFAULT ':' assignment_expression 615 { $$ = new GenericExpr( nullptr, { { maybeMoveBuild<Expression>( $3) } } ); }615 { $$ = new GenericExpr( nullptr, { { maybeMoveBuild<Expression>($3) } } ); } 616 616 ; 617 617 … … 743 743 switch ( $1 ) { 744 744 case OperKinds::AddressOf: 745 $$ = new ExpressionNode( new AddressExpr( maybeMoveBuild< Expression>( $2 ) ) );745 $$ = new ExpressionNode( new AddressExpr( maybeMoveBuild< Expression >( $2 ) ) ); 746 746 break; 747 747 case OperKinds::PointTo: … … 749 749 break; 750 750 case OperKinds::And: 751 $$ = new ExpressionNode( new AddressExpr( new AddressExpr( maybeMoveBuild< Expression>( $2 ) ) ) );751 $$ = new ExpressionNode( new AddressExpr( new AddressExpr( maybeMoveBuild< Expression >( $2 ) ) ) ); 752 752 break; 753 753 default: … … 762 762 { $$ = new ExpressionNode( build_unary_ptr( OperKinds::Decr, $2 ) ); } 763 763 | SIZEOF unary_expression 764 { $$ = new ExpressionNode( new SizeofExpr( maybeMoveBuild< Expression>( $2 ) ) ); }764 { $$ = new ExpressionNode( new SizeofExpr( maybeMoveBuild< Expression >( $2 ) ) ); } 765 765 | SIZEOF '(' type_no_function ')' 766 766 { $$ = new ExpressionNode( new SizeofExpr( maybeMoveBuildType( $3 ) ) ); } 767 767 | ALIGNOF unary_expression // GCC, variable alignment 768 { $$ = new ExpressionNode( new AlignofExpr( maybeMoveBuild< Expression>( $2 ) ) ); }768 { $$ = new ExpressionNode( new AlignofExpr( maybeMoveBuild< Expression >( $2 ) ) ); } 769 769 | ALIGNOF '(' type_no_function ')' // GCC, type alignment 770 770 { $$ = new ExpressionNode( new AlignofExpr( maybeMoveBuildType( $3 ) ) ); } … … 794 794 { $$ = new ExpressionNode( build_keyword_cast( $2, $5 ) ); } 795 795 | '(' VIRTUAL ')' cast_expression // CFA 796 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression>( $4 ), maybeMoveBuildType( nullptr ) ) ); }796 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression >( $4 ), maybeMoveBuildType( nullptr ) ) ); } 797 797 | '(' VIRTUAL type_no_function ')' cast_expression // CFA 798 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression>( $5 ), maybeMoveBuildType( $3 ) ) ); }798 { $$ = new ExpressionNode( new VirtualCastExpr( maybeMoveBuild< Expression >( $5 ), maybeMoveBuildType( $3 ) ) ); } 799 799 | '(' RETURN type_no_function ')' cast_expression // CFA 800 800 { SemanticError( yylloc, "Return cast is currently unimplemented." ); $$ = nullptr; } … … 977 977 assignment_expression 978 978 | comma_expression ',' assignment_expression 979 { $$ = new ExpressionNode( new CommaExpr( maybeMoveBuild< Expression>( $1 ), maybeMoveBuild<Expression>( $3 ) ) ); }979 { $$ = new ExpressionNode( new CommaExpr( maybeMoveBuild< Expression >( $1 ), maybeMoveBuild< Expression >( $3 ) ) ); } 980 980 ; 981 981 … … 1102 1102 constant_expression { $$ = $1; } 1103 1103 | constant_expression ELLIPSIS constant_expression // GCC, subrange 1104 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression>( $1 ), maybeMoveBuild<Expression>( $3 ) ) ); }1104 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression >( $1 ), maybeMoveBuild< Expression >( $3 ) ) ); } 1105 1105 | subrange // CFA, subrange 1106 1106 ; … … 1247 1247 { $$ = new StatementNode( build_computedgoto( $3 ) ); } 1248 1248 // A semantic check is required to ensure fallthru appears only in the body of a choose statement. 1249 | fall_through_name ';' // CFA1249 | fall_through_name ';' // CFA 1250 1250 { $$ = new StatementNode( build_branch( BranchStmt::FallThrough ) ); } 1251 | fall_through_name identifier_or_type_name ';' // CFA1251 | fall_through_name identifier_or_type_name ';' // CFA 1252 1252 { $$ = new StatementNode( build_branch( $2, BranchStmt::FallThrough ) ); } 1253 1253 | fall_through_name DEFAULT ';' // CFA … … 1448 1448 asm_operand: // GCC 1449 1449 string_literal '(' constant_expression ')' 1450 { $$ = new ExpressionNode( new AsmExpr( nullptr, $1, maybeMoveBuild< Expression>( $3 ) ) ); }1450 { $$ = new ExpressionNode( new AsmExpr( nullptr, $1, maybeMoveBuild< Expression >( $3 ) ) ); } 1451 1451 | '[' IDENTIFIER ']' string_literal '(' constant_expression ')' 1452 { $$ = new ExpressionNode( new AsmExpr( $2, $4, maybeMoveBuild< Expression>( $6 ) ) ); }1452 { $$ = new ExpressionNode( new AsmExpr( $2, $4, maybeMoveBuild< Expression >( $6 ) ) ); } 1453 1453 ; 1454 1454 … … 1736 1736 | sue_type_specifier_nobody 1737 1737 | type_type_specifier 1738 ;1739 1740 enum_specifier_nobody: // type specifier - {...}1741 // Preclude SUE declarations in restricted scopes (see type_specifier_nobody)1742 basic_type_specifier1743 | sue_type_specifier_nobody1744 1738 ; 1745 1739 … … 2010 2004 ; 2011 2005 2006 fred: 2007 // empty 2008 { yyy = false; } 2009 ; 2010 2012 2011 aggregate_type: // struct, union 2013 2012 aggregate_key attribute_list_opt … … 2015 2014 '{' field_declaration_list_opt '}' type_parameters_opt 2016 2015 { $$ = DeclarationNode::newAggregate( $1, nullptr, $7, $5, true )->addQualifiers( $2 ); } 2017 | aggregate_key attribute_list_opt identifier 2016 | aggregate_key attribute_list_opt identifier fred 2018 2017 { 2019 2018 typedefTable.makeTypedef( *$3, forall || typedefTable.getEnclForall() ? TYPEGENname : TYPEDEFname ); // create typedef … … 2021 2020 } 2022 2021 '{' field_declaration_list_opt '}' type_parameters_opt 2023 { $$ = DeclarationNode::newAggregate( $1, $3, $ 8, $6, true )->addQualifiers( $2 ); }2024 | aggregate_key attribute_list_opt type_name 2022 { $$ = DeclarationNode::newAggregate( $1, $3, $9, $7, true )->addQualifiers( $2 ); } 2023 | aggregate_key attribute_list_opt type_name fred 2025 2024 { 2026 2025 // for type_name can be a qualified type name S.T, in which case only the last name in the chain needs a typedef (other names in the chain should already have one) … … 2029 2028 } 2030 2029 '{' field_declaration_list_opt '}' type_parameters_opt 2031 { $$ = DeclarationNode::newAggregate( $1, $3->type->symbolic.name, $ 8, $6, true )->addQualifiers( $2 ); }2030 { $$ = DeclarationNode::newAggregate( $1, $3->type->symbolic.name, $9, $7, true )->addQualifiers( $2 ); } 2032 2031 | aggregate_type_nobody 2033 2032 ; … … 2041 2040 2042 2041 aggregate_type_nobody: // struct, union - {...} 2043 aggregate_key attribute_list_opt identifier 2042 aggregate_key attribute_list_opt identifier fred 2044 2043 { 2045 2044 typedefTable.makeTypedef( *$3, forall || typedefTable.getEnclForall() ? TYPEGENname : TYPEDEFname ); … … 2047 2046 $$ = DeclarationNode::newAggregate( $1, $3, nullptr, nullptr, false )->addQualifiers( $2 ); 2048 2047 } 2049 | aggregate_key attribute_list_opt type_name 2048 | aggregate_key attribute_list_opt type_name fred 2050 2049 { 2051 2050 forall = false; // reset … … 2185 2184 ; 2186 2185 2187 // Cannot use attribute_list_opt because of ambiguity with enum_specifier_nobody, which already parses attribute.2188 // Hence, only a single attribute is allowed after the "ENUM".2189 2186 enum_type: // enum 2190 ENUM attribute_ opt '{' enumerator_list comma_opt '}'2187 ENUM attribute_list_opt '{' enumerator_list comma_opt '}' 2191 2188 { $$ = DeclarationNode::newEnum( nullptr, $4, true )->addQualifiers( $2 ); } 2192 | ENUM attribute_ opt identifier2189 | ENUM attribute_list_opt identifier 2193 2190 { typedefTable.makeTypedef( *$3 ); } 2194 2191 '{' enumerator_list comma_opt '}' 2195 2192 { $$ = DeclarationNode::newEnum( $3, $6, true )->addQualifiers( $2 ); } 2196 | ENUM attribute_ opt typedef // enum cannot be generic2193 | ENUM attribute_list_opt type_name 2197 2194 '{' enumerator_list comma_opt '}' 2198 { $$ = DeclarationNode::newEnum( $3->name, $5, true )->addQualifiers( $2 ); } 2199 | ENUM enum_specifier_nobody '{' enumerator_list comma_opt '}' 2200 // { $$ = DeclarationNode::newEnum( nullptr, $4, true ); } 2201 { SemanticError( yylloc, "Typed enumeration is currently unimplemented." ); $$ = nullptr; } 2202 | ENUM enum_specifier_nobody declarator '{' enumerator_list comma_opt '}' 2203 // { 2204 // typedefTable.makeTypedef( *$3->name ); 2205 // $$ = DeclarationNode::newEnum( nullptr, $5, true ); 2206 // } 2207 { SemanticError( yylloc, "Typed enumeration is currently unimplemented." ); $$ = nullptr; } 2195 { $$ = DeclarationNode::newEnum( $3->type->symbolic.name, $5, true )->addQualifiers( $2 ); } 2208 2196 | enum_type_nobody 2209 2197 ; 2210 2198 2211 2199 enum_type_nobody: // enum - {...} 2212 ENUM attribute_ opt identifier2200 ENUM attribute_list_opt identifier 2213 2201 { 2214 2202 typedefTable.makeTypedef( *$3 ); 2215 2203 $$ = DeclarationNode::newEnum( $3, 0, false )->addQualifiers( $2 ); 2216 2204 } 2217 | ENUM attribute_ opt type_name // enum cannot be generic2205 | ENUM attribute_list_opt type_name 2218 2206 { 2219 2207 typedefTable.makeTypedef( *$3->type->symbolic.name ); … … 2232 2220 // empty 2233 2221 { $$ = nullptr; } 2234 // | '=' constant_expression 2235 // { $$ = $2; } 2236 | '=' initializer 2237 { $$ = $2->get_expression(); } // FIX ME: enum only deals with constant_expression 2222 | '=' constant_expression 2223 { $$ = $2; } 2238 2224 ; 2239 2225 … … 2417 2403 { $$ = $3; } 2418 2404 | '[' push constant_expression ELLIPSIS constant_expression pop ']' // GCC, multiple array elements 2419 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression>( $3 ), maybeMoveBuild<Expression>( $5 ) ) ); }2405 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression >( $3 ), maybeMoveBuild< Expression >( $5 ) ) ); } 2420 2406 | '.' '[' push field_name_list pop ']' // CFA, tuple field selector 2421 2407 { $$ = $4; } … … 2455 2441 type_parameter: // CFA 2456 2442 type_class identifier_or_type_name 2457 { 2458 typedefTable.addToScope( *$2, TYPEDEFname, "9" ); 2459 if ( $1 == TypeDecl::Otype ) { SemanticError( yylloc, "otype keyword is deprecated, use T " ); } 2460 if ( $1 == TypeDecl::Dtype ) { SemanticError( yylloc, "dtype keyword is deprecated, use T &" ); } 2461 if ( $1 == TypeDecl::Ttype ) { SemanticError( yylloc, "ttype keyword is deprecated, use T ..." ); } 2443 { typedefTable.addToScope( *$2, TYPEDEFname, "9" ); 2444 if ( $1 == TypeDecl::Otype ) { SemanticError( yylloc, "otype keyword is deprecated" ); } 2445 if ( $1 == TypeDecl::Dtype ) { SemanticError( yylloc, "dtype keyword is deprecated" ); } 2446 if ( $1 == TypeDecl::Ttype ) { SemanticError( yylloc, "ttype keyword is deprecated" ); } 2462 2447 } 2463 2448 type_initializer_opt assertion_list_opt … … 2753 2738 subrange: 2754 2739 constant_expression '~' constant_expression // CFA, integer subrange 2755 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression>( $1 ), maybeMoveBuild<Expression>( $3 ) ) ); }2740 { $$ = new ExpressionNode( new RangeExpr( maybeMoveBuild< Expression >( $1 ), maybeMoveBuild< Expression >( $3 ) ) ); } 2756 2741 ; 2757 2742 … … 2777 2762 | attribute_list attribute 2778 2763 { $$ = $2->addQualifiers( $1 ); } 2779 ;2780 2781 attribute_opt:2782 // empty2783 { $$ = nullptr; }2784 | attribute2785 2764 ; 2786 2765 -
tests/.expect/attributes.nast.x64.txt
rf99f5ba rcd70477 6 6 7 7 } 8 struct __a nonymous0 {8 struct __attribute__ ((unused)) __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1;29 28 struct __attribute__ ((unused)) Agn1; 30 29 struct __attribute__ ((unused)) Agn2 { -
tests/.expect/attributes.nast.x86.txt
rf99f5ba rcd70477 6 6 7 7 } 8 struct __a nonymous0 {8 struct __attribute__ ((unused)) __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1;29 28 struct __attribute__ ((unused)) Agn1; 30 29 struct __attribute__ ((unused)) Agn2 { -
tests/.expect/attributes.oast.x64.txt
rf99f5ba rcd70477 6 6 7 7 } 8 struct __a nonymous0 {8 struct __attribute__ ((unused)) __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1;29 28 struct __attribute__ ((unused)) Agn1; 30 29 struct __attribute__ ((unused)) Agn2 { -
tests/.expect/attributes.oast.x86.txt
rf99f5ba rcd70477 6 6 7 7 } 8 struct __a nonymous0 {8 struct __attribute__ ((unused)) __anonymous0 { 9 9 }; 10 10 static inline void _X12_constructorFv_S12__anonymous0_autogen___1(struct __anonymous0 *_X4_dstS12__anonymous0_1); … … 26 26 return _X4_retS12__anonymous0_1; 27 27 } 28 __attribute__ ((unused)) struct __anonymous0 _X5DummyS12__anonymous0_1;29 28 struct __attribute__ ((unused)) Agn1; 30 29 struct __attribute__ ((unused)) Agn2 { -
tests/alloc2.cfa
rf99f5ba rcd70477 16 16 bool passed = (malloc_size(ip) == size) && (malloc_usable_size(ip) >= size) && (malloc_alignment(ip) == align) && ((uintptr_t)ip % align == 0); 17 17 if (!passed) { 18 printf("failed test %3d: %4 zu %4zu but got %4zu ( %3zu ) %4zu\n", tests_total, size, align, malloc_size(ip), malloc_usable_size(ip), malloc_alignment(ip));18 printf("failed test %3d: %4lu %4lu but got %4lu ( %3lu ) %4lu\n", tests_total, size, align, malloc_size(ip), malloc_usable_size(ip), malloc_alignment(ip)); 19 19 tests_failed += 1; 20 20 } -
tests/attributes.cfa
rf99f5ba rcd70477 10 10 // Created On : Mon Feb 6 16:07:02 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Jan 25 21:26:41 202113 // Update Count : 2012 // Last Modified On : Tue Nov 6 17:51:12 2018 13 // Update Count : 17 14 14 // 15 15 … … 22 22 23 23 // aggregate_name 24 struct __attribute__(( unused )) {} Dummy;24 struct __attribute__(( unused )) {}; 25 25 struct __attribute__(( unused )) Agn1; 26 26 struct __attribute__(( unused )) Agn2 {}; -
tools/prettyprinter/Makefile.am
rf99f5ba rcd70477 11 11 ## Created On : Wed Jun 28 12:07:10 2017 12 12 ## Last Modified By : Peter A. Buhr 13 ## Last Modified On : Thu Jan 28 08:48:22 202114 ## Update Count : 2 313 ## Last Modified On : Mon Apr 16 09:43:23 2018 14 ## Update Count : 20 15 15 ############################################################################### 16 16 … … 20 20 BUILT_SOURCES = parser.hh 21 21 22 AM_YFLAGS = -d -t -v -Wno-yacc22 AM_YFLAGS = -d -t -v 23 23 24 24 SRC = lex.ll \ … … 34 34 pretty_CXXFLAGS = -Wno-deprecated -Wall -DYY_NO_INPUT -O2 -g -std=c++14 35 35 36 M OSTLYCLEANFILES = parser.output36 MAINTAINERCLEANFILES = parser.output -
tools/prettyprinter/ParserTypes.h
rf99f5ba rcd70477 13 13 // Created On : Sun Dec 16 15:00:49 2001 14 14 // Last Modified By : Peter A. Buhr 15 // Last Modified On : Tue Jan 26 23:05:34 202116 // Update Count : 17 615 // Last Modified On : Sat Jul 22 10:13:09 2017 16 // Update Count : 175 17 17 // 18 18 19 19 #pragma once 20 20 21 extern "C"int yylex();21 int yylex(); 22 22 23 23 #include <string> -
tools/prettyprinter/parser.yy
rf99f5ba rcd70477 10 10 // Created On : Sat Dec 15 13:44:21 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Jan 26 22:50:03 202113 // Update Count : 105 312 // Last Modified On : Sun Apr 15 21:40:30 2018 13 // Update Count : 1052 14 14 // 15 15 … … 17 17 #define YYDEBUG_LEXER_TEXT( yylval ) // lexer loads this up each time 18 18 #define YYDEBUG 1 // get the pretty debugging code to compile 19 #define YYERROR_VERBOSE // more information in syntax errors20 19 21 20 #include <iostream>
Note:
See TracChangeset
for help on using the changeset viewer.