Changes in / [882ad37:3ca540f]


Ignore:
Files:
3 added
9 deleted
64 edited

Legend:

Unmodified
Added
Removed
  • doc/proposals/concurrency/text/basics.tex

    r882ad37 r3ca540f  
    44% ======================================================================
    55% ======================================================================
    6 Before any detailed discussion of the concurrency and parallelism in \CFA, it is important to describe the basics of concurrency and how they are expressed in \CFA user-code.
     6Before any detailed discussion of the concurrency and parallelism in \CFA, it is important to describe the basics of concurrency and how they are expressed in \CFA user code.
    77
    88\section{Basics of concurrency}
     
    1111Execution with a single thread and multiple stacks where the thread is self-scheduling deterministically across the stacks is called coroutining. Execution with a single and multiple stacks but where the thread is scheduled by an oracle (non-deterministic from the thread perspective) across the stacks is called concurrency.
    1212
    13 Therefore, a minimal concurrency system can be achieved by creating coroutines, which instead of context switching among each other, always ask an oracle where to context switch next. While coroutines can execute on the caller's stack-frame, stack-full coroutines allow full generality and are sufficient as the basis for concurrency. The aforementioned oracle is a scheduler and the whole system now follows a cooperative threading-model (a.k.a non-preemptive scheduling). The oracle/scheduler can either be a stack-less or stack-full entity and correspondingly require one or two context switches to run a different coroutine. In any case, a subset of concurrency related challenges start to appear. For the complete set of concurrency challenges to occur, the only feature missing is preemption.
    14 
    15 A scheduler introduces order of execution uncertainty, while preemption introduces uncertainty about where context-switches occur. Mutual-exclusion and synchronization are ways of limiting non-determinism in a concurrent system. Now it is important to understand that uncertainty is desirable; uncertainty can be used by runtime systems to significantly increase performance and is often the basis of giving a user the illusion that tasks are running in parallel. Optimal performance in concurrent applications is often obtained by having as much non-determinism as correctness allows.
    16 
    17 \section{\protect\CFA 's Thread Building Blocks}
    18 One of the important features that is missing in C is threading. On modern architectures, a lack of threading is unacceptable~\cite{Sutter05, Sutter05b}, and therefore modern programming languages must have the proper tools to allow users to write performant concurrent programs to take advantage of parallelism. As an extension of C, \CFA needs to express these concepts in a way that is as natural as possible to programmers familiar with imperative languages. And being a system-level language means programmers expect to choose precisely which features they need and which cost they are willing to pay.
    19 
    20 \section{Coroutines: A stepping stone}\label{coroutine}
    21 While the main focus of this proposal is concurrency and parallelism, it is important to address coroutines, which are actually a significant building block of a concurrency system. Coroutines need to deal with context-switches and other context-management operations. Therefore, this proposal includes coroutines both as an intermediate step for the implementation of threads, and a first class feature of \CFA. Furthermore, many design challenges of threads are at least partially present in designing coroutines, which makes the design effort that much more relevant. The core \acrshort{api} of coroutines revolve around two features: independent call stacks and \code{suspend}/\code{resume}.
     13Therefore, a minimal concurrency system can be achieved by creating coroutines, which instead of context-switching among each other, always ask an oracle where to context-switch next. While coroutines can execute on the caller?s stack-frame, stack-full coroutines allow full generality and are sufficient as the basis for concurrency. The aforementioned oracle is a scheduler and the whole system now follows a cooperative threading-model (aka non-preemptive scheduling). The oracle/scheduler can either be a stack-less or stack-full entity and correspondingly require one or two context-switches to run a different coroutine. In any case, a subset of concurrency related challenges start to appear. For the complete set of concurrency challenges to occur, the only feature missing is preemption.
     14
     15A scheduler introduces order of execution uncertainty, while preemption introduces uncertainty about where context switches occur. Mutual exclusion and synchronization are ways of limiting non-determinism in a concurrent system. Now it is important to understand that uncertainty is desirable; uncertainty can be used by runtime systems to significantly increase performance and is often the basis of giving a user the illusion that tasks are running in parallel. Optimal performance in concurrent applications is often obtained by having as much non-determinism as correctness allows.
     16
     17\section{\protect\CFA's Thread Building Blocks}
     18One of the important features that are missing in C is threading. On modern architectures, a lack of threading is unacceptable~\cite{Sutter05, Sutter05b}, and therefore modern programming languages must have the proper tools to allow users to write efficient concurrent programs to take advantage of parallelism. As an extension of C, \CFA needs to express these concepts in a way that is as natural as possible to programmers familiar with imperative languages. And being a system-level language means programmers expect to choose precisely which features they need and which cost they are willing to pay.
     19
     20\section{Coroutines: A Stepping Stone}\label{coroutine}
     21While the main focus of this proposal is concurrency and parallelism, it is important to address coroutines, which are actually a significant building block of a concurrency system. Coroutines need to deal with context switches and other context-management operations. Therefore, this proposal includes coroutines both as an intermediate step for the implementation of threads, and a first-class feature of \CFA. Furthermore, many design challenges of threads are at least partially present in designing coroutines, which makes the design effort that much more relevant. The core \acrshort{api} of coroutines revolves around two features: independent call-stacks and \code{suspend}/\code{resume}.
    2222
    2323\begin{table}
     
    133133\end{table}
    134134
    135 A good example of a problem made easier with coroutines is generators, like the Fibonacci sequence. This problem comes with the challenge of decoupling how a sequence is generated and how it is used. Table \ref{lst:fibonacci-c} shows conventional approaches to writing generators in C. All three of these approach suffer from strong coupling. The left and center approaches require that the generator have knowledge of how the sequence is used, while the rightmost approach requires holding internal state between calls on behalf of the generator and makes it much harder to handle corner cases like the Fibonacci seed.
     135A good example of a problem made easier with coroutines is generators, like the Fibonacci sequence. This problem comes with the challenge of decoupling how a sequence is generated and how it is used. Table \ref{lst:fibonacci-c} shows conventional approaches to writing generators in C. All three of these approach suffer from strong coupling. The left and centre approaches require that the generator have knowledge of how the sequence is used, while the rightmost approach requires holding internal state between calls on behalf of the generator and makes it much harder to handle corner cases like the Fibonacci seed.
    136136
    137137Listing \ref{lst:fibonacci-cfa} is an example of a solution to the Fibonacci problem using \CFA coroutines, where the coroutine stack holds sufficient state for the next generation. This solution has the advantage of having very strong decoupling between how the sequence is generated and how it is used. Indeed, this version is as easy to use as the \code{fibonacci_state} solution, while the implementation is very similar to the \code{fibonacci_func} example.
     
    233233One important design challenge for implementing coroutines and threads (shown in section \ref{threads}) is that the runtime system needs to run code after the user-constructor runs to connect the fully constructed object into the system. In the case of coroutines, this challenge is simpler since there is no non-determinism from preemption or scheduling. However, the underlying challenge remains the same for coroutines and threads.
    234234
    235 The runtime system needs to create the coroutine's stack and more importantly prepare it for the first resumption. The timing of the creation is non-trivial since users both expect to have fully constructed objects once execution enters the coroutine main and to be able to resume the coroutine from the constructor. There are several solutions to this problem but the chosen options effectively forces the design of the coroutine.
    236 
    237 Furthermore, \CFA faces an extra challenge as polymorphic routines create invisible thunks when casted to non-polymorphic routines and these thunks have function scope. For example, the following code, while looking benign, can run into undefined behaviour because of thunks:
     235The runtime system needs to create the coroutine?s stack and more importantly prepare it for the first resumption. The timing of the creation is non-trivial since users both expect to have fully constructed objects once execution enters the coroutine main and to be able to resume the coroutine from the constructor. There are several solutions to this problem but the chosen option effectively forces the design of the coroutine.
     236
     237Furthermore, \CFA faces an extra challenge as polymorphic routines create invisible thunks when cast to non-polymorphic routines and these thunks have function scope. For example, the following code, while looking benign, can run into undefined behaviour because of thunks:
    238238
    239239\begin{cfacode}
     
    268268}
    269269\end{ccode}
    270 The problem in this example is a storage management issue, the function pointer \code{_thunk0} is only valid until the end of the block, which limits the viable solutions because storing the function pointer for too long causes Undefined Behavior; i.e., the stack-based thunk being destroyed before it can be used. This challenge is an extension of challenges that come with second-class routines. Indeed, GCC nested routines also have the limitation that nested routine cannot be passed outside of the declaration scope. The case of coroutines and threads is simply an extension of this problem to multiple call-stacks.
     270The problem in this example is a storage management issue, the function pointer \code{_thunk0} is only valid until the end of the block, which limits the viable solutions because storing the function pointer for too long causes Undefined Behaviour; i.e., the stack-based thunk being destroyed before it can be used. This challenge is an extension of challenges that come with second-class routines. Indeed, GCC nested routines also have the limitation that nested routine cannot be passed outside of the declaration scope. The case of coroutines and threads is simply an extension of this problem to multiple call stacks.
    271271
    272272\subsection{Alternative: Composition}
     
    310310symmetric_coroutine<>::yield_type
    311311\end{cfacode}
    312 Often, the canonical threading paradigm in languages is based on function pointers, pthread being one of the most well known examples. The main problem of this approach is that the thread usage is limited to a generic handle that must otherwise be wrapped in a custom type. Since the custom type is simple to write in \CFA and solves several issues, added support for routine/lambda based coroutines adds very little.
     312Often, the canonical threading paradigm in languages is based on function pointers, pthread being one of the most well-known examples. The main problem of this approach is that the thread usage is limited to a generic handle that must otherwise be wrapped in a custom type. Since the custom type is simple to write in \CFA and solves several issues, added support for routine/lambda based coroutines adds very little.
    313313
    314314A variation of this would be to use a simple function pointer in the same way pthread does for threads :
     
    327327This semantics is more common for thread interfaces but coroutines work equally well. As discussed in section \ref{threads}, this approach is superseded by static approaches in terms of expressivity.
    328328
    329 \subsection{Alternative: Trait-based coroutines}
    330 
    331 Finally the underlying approach, which is the one closest to \CFA idioms, is to use trait-based lazy coroutines. This approach defines a coroutine as anything that satisfies the trait \code{is_coroutine} and is used as a coroutine.
     329\subsection{Alternative: Trait-Based Coroutines}
     330
     331Finally, the underlying approach, which is the one closest to \CFA idioms, is to use trait-based lazy coroutines. This approach defines a coroutine as anything that satisfies the trait \code{is_coroutine} and is used as a coroutine.
    332332
    333333\begin{cfacode}
     
    369369
    370370\section{Thread Interface}\label{threads}
    371 The basic building blocks of multi-threading in \CFA are \glspl{cfathread}. Both user and kernel threads are supported, where user threads are the concurrency mechanism and kernel threads are the parallel mechanism. User threads offer a flexible and lightweight interface. A thread can be declared using a struct declaration \code{thread} as follows:
     371The basic building blocks of multithreading in \CFA are \glspl{cfathread}. Both user and kernel threads are supported, where user threads are the concurrency mechanism and kernel threads are the parallel mechanism. User threads offer a flexible and lightweight interface. A thread can be declared using a struct declaration \code{thread} as follows:
    372372
    373373\begin{cfacode}
     
    394394\end{cfacode}
    395395
    396 In this example, threads of type \code{foo} start execution in the \code{void main(foo &)} routine, which prints \code{"Hello World!"}. While this thesis encourages this approach to enforce strongly-typed programming, users may prefer to use the routine-based thread semantics for the sake of simplicity. With the static semantics it is trivial to write a thread type that takes a function pointer as a parameter and executes it on its stack asynchronously.
     396In this example, threads of type \code{foo} start execution in the \code{void main(foo &)} routine, which prints \code{"Hello World!".} While this thesis encourages this approach to enforce strongly typed programming, users may prefer to use the routine-based thread semantics for the sake of simplicity. With the static semantics it is trivial to write a thread type that takes a function pointer as a parameter and executes it on its stack asynchronously.
    397397\begin{cfacode}
    398398typedef void (*voidFunc)(int);
     
    419419int main() {
    420420        FuncRunner f = {hello, 42};
    421         return 0'
    422 }
    423 \end{cfacode}
    424 
    425 A consequence of the strongly-typed approach to main is that memory layout of parameters and return values to/from a thread are now explicitly specified in the \acrshort{api}.
     421        return 0?
     422}
     423\end{cfacode}
     424
     425A consequence of the strongly typed approach to main is that memory layout of parameters and return values to/from a thread are now explicitly specified in the \acrshort{api}.
    426426
    427427Of course for threads to be useful, it must be possible to start and stop threads and wait for them to complete execution. While using an \acrshort{api} such as \code{fork} and \code{join} is relatively common in the literature, such an interface is unnecessary. Indeed, the simplest approach is to use \acrshort{raii} principles and have threads \code{fork} after the constructor has completed and \code{join} before the destructor runs.
  • doc/proposals/concurrency/text/cforall.tex

    r882ad37 r3ca540f  
    77The following is a quick introduction to the \CFA language, specifically tailored to the features needed to support concurrency.
    88
    9 \CFA is an extension of ISO-C and therefore supports all of the same paradigms as C. It is a non-object-oriented system-language, meaning most of the major abstractions have either no runtime overhead or can be opt-out easily. Like C, the basics of \CFA revolve around structures and routines, which are thin abstractions over machine code. The vast majority of the code produced by the \CFA translator respects memory-layouts and calling-conventions laid out by C. Interestingly, while \CFA is not an object-oriented language, lacking the concept of a receiver (e.g., {\tt this}), it does have some notion of objects\footnote{C defines the term objects as : ``region of data storage in the execution environment, the contents of which can represent
    10 values''~\cite[3.15]{C11}}, most importantly construction and destruction of objects. Most of the following code examples can be found on the \CFA website~\cite{www-cfa}
     9\CFA is an extension of ISO-C and therefore supports all of the same paradigms as C. It is a non-object-oriented system-language, meaning most of the major abstractions have either no runtime overhead or can be opted out easily. Like C, the basics of \CFA revolve around structures and routines, which are thin abstractions over machine code. The vast majority of the code produced by the \CFA translator respects memory layouts and calling conventions laid out by C. Interestingly, while \CFA is not an object-oriented language, lacking the concept of a receiver (e.g., {\tt this}), it does have some notion of objects\footnote{C defines the term objects as : ``region of data storage in the execution environment, the contents of which can represent
     10values''~\cite[3.15]{C11}}, most importantly construction and destruction of objects. Most of the following code examples can be found on the \CFA website~\cite{www-cfa}.
    1111
    1212% ======================================================================
     
    7272% ======================================================================
    7373\section{Constructors/Destructors}
    74 Object life-time is often a challenge in concurrency. \CFA uses the approach of giving concurrent meaning to object life-time as a mean of synchronization and/or mutual exclusion. Since \CFA relies heavily on the life time of objects, constructors and destructors are a core feature required for concurrency and parallelism. \CFA uses the following syntax for constructors and destructors :
     74Object lifetime is often a challenge in concurrency. \CFA uses the approach of giving concurrent meaning to object lifetime as a means of synchronization and/or mutual exclusion. Since \CFA relies heavily on the lifetime of objects, constructors and destructors is a core feature required for concurrency and parallelism. \CFA uses the following syntax for constructors and destructors :
    7575\begin{cfacode}
    7676struct S {
     
    135135\end{cfacode}
    136136
    137 Note that the type use for assertions can be either an \code{otype} or a \code{dtype}. Types declares as \code{otype} refer to ``complete'' objects, i.e., objects with a size, a default constructor, a copy constructor, a destructor and an assignment operator. Using \code{dtype} on the other hand has none of these assumptions but is extremely restrictive, it only guarantees the object is addressable.
     137Note that the type use for assertions can be either an \code{otype} or a \code{dtype}. Types declared as \code{otype} refer to ``complete'' objects, i.e., objects with a size, a default constructor, a copy constructor, a destructor and an assignment operator. Using \code{dtype,} on the other hand, has none of these assumptions but is extremely restrictive, it only guarantees the object is addressable.
    138138
    139139% ======================================================================
  • doc/proposals/concurrency/text/concurrency.tex

    r882ad37 r3ca540f  
    44% ======================================================================
    55% ======================================================================
    6 Several tool can be used to solve concurrency challenges. Since many of these challenges appear with the use of mutable shared-state, some languages and libraries simply disallow mutable shared-state (Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, Akka (Scala)~\cite{Akka}). In these paradigms, interaction among concurrent objects relies on message passing~\cite{Thoth,Harmony,V-Kernel} or other paradigms closely relate to networking concepts (channels~\cite{CSP,Go} for example). However, in languages that use routine calls as their core abstraction-mechanism, these approaches force a clear distinction between concurrent and non-concurrent paradigms (i.e., message passing versus routine call). This distinction in turn means that, in order to be effective, programmers need to learn two sets of designs patterns. While this distinction can be hidden away in library code, effective use of the library still has to take both paradigms into account.
     6Several tools can be used to solve concurrency challenges. Since many of these challenges appear with the use of mutable shared-state, some languages and libraries simply disallow mutable shared-state (Erlang~\cite{Erlang}, Haskell~\cite{Haskell}, Akka (Scala)~\cite{Akka}). In these paradigms, interaction among concurrent objects relies on message passing~\cite{Thoth,Harmony,V-Kernel} or other paradigms closely relate to networking concepts (channels~\cite{CSP,Go} for example). However, in languages that use routine calls as their core abstraction mechanism, these approaches force a clear distinction between concurrent and non-concurrent paradigms (i.e., message passing versus routine calls). This distinction in turn means that, in order to be effective, programmers need to learn two sets of design patterns. While this distinction can be hidden away in library code, effective use of the library still has to take both paradigms into account.
    77
    88Approaches based on shared memory are more closely related to non-concurrent paradigms since they often rely on basic constructs like routine calls and shared objects. At the lowest level, concurrent paradigms are implemented as atomic operations and locks. Many such mechanisms have been proposed, including semaphores~\cite{Dijkstra68b} and path expressions~\cite{Campbell74}. However, for productivity reasons it is desirable to have a higher-level construct be the core concurrency paradigm~\cite{HPP:Study}.
    99
    10 An approach that is worth mentioning because it is gaining in popularity is transactional memory~\cite{Herlihy93}. While this approach is even pursued by system languages like \CC~\cite{Cpp-Transactions}, the performance and feature set is currently too restrictive to be the main concurrency paradigm for systems language, which is why it was rejected as the core paradigm for concurrency in \CFA.
    11 
    12 One of the most natural, elegant, and efficient mechanisms for synchronization and communication, especially for shared-memory systems, is the \emph{monitor}. Monitors were first proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}. Many programming languages---e.g., Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}---provide monitors as explicit language constructs. In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as semaphores or locks to simulate monitors. For these reasons, this project proposes monitors as the core concurrency-construct.
     10An approach that is worth mentioning because it is gaining in popularity is transactional memory~\cite{Herlihy93}. While this approach is even pursued by system languages like \CC~\cite{Cpp-Transactions}, the performance and feature set is currently too restrictive to be the main concurrency paradigm for system languages, which is why it was rejected as the core paradigm for concurrency in \CFA.
     11
     12One of the most natural, elegant, and efficient mechanisms for synchronization and communication, especially for shared-memory systems, is the \emph{monitor}. Monitors were first proposed by Brinch Hansen~\cite{Hansen73} and later described and extended by C.A.R.~Hoare~\cite{Hoare74}. Many programming languages---e.g., Concurrent Pascal~\cite{ConcurrentPascal}, Mesa~\cite{Mesa}, Modula~\cite{Modula-2}, Turing~\cite{Turing:old}, Modula-3~\cite{Modula-3}, NeWS~\cite{NeWS}, Emerald~\cite{Emerald}, \uC~\cite{Buhr92a} and Java~\cite{Java}---provide monitors as explicit language constructs. In addition, operating-system kernels and device drivers have a monitor-like structure, although they often use lower-level primitives such as semaphores or locks to simulate monitors. For these reasons, this project proposes monitors as the core concurrency construct.
    1313
    1414\section{Basics}
     
    1919
    2020\subsection{Synchronization}
    21 As for mutual-exclusion, low-level synchronization primitives often offer good performance and good flexibility at the cost of ease of use. Again, higher-level mechanism often simplify usage by adding better coupling between synchronization and data, e.g.: message passing, or offering a simpler solution to otherwise involved challenges. As mentioned above, synchronization can be expressed as guaranteeing that event \textit{X} always happens before \textit{Y}. Most of the time, synchronization happens within a critical section, where threads must acquire mutual-exclusion in a certain order. However, it may also be desirable to guarantee that event \textit{Z} does not occur between \textit{X} and \textit{Y}. Not satisfying this property is called barging. For example, where event \textit{X} tries to effect event \textit{Y} but another thread acquires the critical section and emits \textit{Z} before \textit{Y}. The classic example is the thread that finishes using a resource and unblocks a thread waiting to use the resource, but the unblocked thread must compete again to acquire the resource. Preventing or detecting barging is an involved challenge with low-level locks, which can be made much easier by higher-level constructs. This challenge is often split into two different methods, barging avoidance and barging prevention. Algorithms that use flag variables to detect barging threads are said to be using barging avoidance, while algorithms that baton-pass locks~\cite{Andrews89} between threads instead of releasing the locks are said to be using barging prevention.
     21As for mutual-exclusion, low-level synchronization primitives often offer good performance and good flexibility at the cost of ease of use. Again, higher-level mechanisms often simplify usage by adding better coupling between synchronization and data, e.g.: message passing, or offering a simpler solution to otherwise involved challenges. As mentioned above, synchronization can be expressed as guaranteeing that event \textit{X} always happens before \textit{Y}. Most of the time, synchronization happens within a critical section, where threads must acquire mutual-exclusion in a certain order. However, it may also be desirable to guarantee that event \textit{Z} does not occur between \textit{X} and \textit{Y}. Not satisfying this property is called barging. For example, where event \textit{X} tries to effect event \textit{Y} but another thread acquires the critical section and emits \textit{Z} before \textit{Y}. The classic example is the thread that finishes using a resource and unblocks a thread waiting to use the resource, but the unblocked thread must compete again to acquire the resource. Preventing or detecting barging is an involved challenge with low-level locks, which can be made much easier by higher-level constructs. This challenge is often split into two different methods, barging avoidance and barging prevention. Algorithms that use flag variables to detect barging threads are said to be using barging avoidance, while algorithms that baton-pass locks~\cite{Andrews89} between threads instead of releasing the locks are said to be using barging prevention.
    2222
    2323% ======================================================================
     
    2626% ======================================================================
    2727% ======================================================================
    28 A monitor is a set of routines that ensure mutual exclusion when accessing shared state. This concept is generally associated with Object-Oriented Languages like Java~\cite{Java} or \uC~\cite{uC++book} but does not strictly require OO semantics. The only requirements is the ability to declare a handle to a shared object and a set of routines that act on it :
     28A monitor is a set of routines that ensure mutual exclusion when accessing shared state. This concept is generally associated with Object-Oriented Languages like Java~\cite{Java} or \uC~\cite{uC++book} but does not strictly require OO semantics. The only requirement is the ability to declare a handle to a shared object and a set of routines that act on it :
    2929\begin{cfacode}
    3030typedef /*some monitor type*/ monitor;
     
    3939% ======================================================================
    4040% ======================================================================
    41 \subsection{Call semantics} \label{call}
     41\subsection{Call Semantics} \label{call}
    4242% ======================================================================
    4343% ======================================================================
     
    103103int f5(graph(monitor*) & mutex m);
    104104\end{cfacode}
    105 The problem is to identify which object(s) should be acquired. Furthermore, each object needs to be acquired only once. In the case of simple routines like \code{f1} and \code{f2} it is easy to identify an exhaustive list of objects to acquire on entry. Adding indirections (\code{f3}) still allows the compiler and programmer to identify which object is acquired. However, adding in arrays (\code{f4}) makes it much harder. Array lengths are not necessarily known in C, and even then, making sure objects are only acquired once becomes none-trivial. This problem can be extended to absurd limits like \code{f5}, which uses a graph of monitors. To make the issue tractable, this project imposes the requirement that a routine may only acquire one monitor per parameter and it must be the type of the parameter with at most one level of indirection (ignoring potential qualifiers). Also note that while routine \code{f3} can be supported, meaning that monitor \code{**m} is be acquired, passing an array to this routine would be type safe and yet result in undefined behaviour because only the first element of the array is acquired. However, this ambiguity is part of the C type-system with respects to arrays. For this reason, \code{mutex} is disallowed in the context where arrays may be passed:
     105The problem is to identify which object(s) should be acquired. Furthermore, each object needs to be acquired only once. In the case of simple routines like \code{f1} and \code{f2} it is easy to identify an exhaustive list of objects to acquire on entry. Adding indirections (\code{f3}) still allows the compiler and programmer to identify which object is acquired. However, adding in arrays (\code{f4}) makes it much harder. Array lengths are not necessarily known in C, and even then, making sure objects are only acquired once becomes none-trivial. This problem can be extended to absurd limits like \code{f5}, which uses a graph of monitors. To make the issue tractable, this project imposes the requirement that a routine may only acquire one monitor per parameter and it must be the type of the parameter with at most one level of indirection (ignoring potential qualifiers). Also note that while routine \code{f3} can be supported, meaning that monitor \code{**m} is acquired, passing an array to this routine would be type-safe and yet result in undefined behaviour because only the first element of the array is acquired. However, this ambiguity is part of the C type-system with respects to arrays. For this reason, \code{mutex} is disallowed in the context where arrays may be passed:
    106106\begin{cfacode}
    107107int f1(monitor& mutex m);   //Okay : recommended case
     
    137137The \gls{multi-acq} monitor lock allows a monitor lock to be acquired by both \code{bar} or \code{baz} and acquired again in \code{foo}. In the calls to \code{bar} and \code{baz} the monitors are acquired in opposite order.
    138138
    139 However, such use leads to the lock acquiring order problem. In the example above, the user uses implicit ordering in the case of function \code{foo} but explicit ordering in the case of \code{bar} and \code{baz}. This subtle difference means that calling these routines concurrently may lead to deadlock and is therefore Undefined Behavior. As shown~\cite{Lister77}, solving this problem requires:
     139However, such use leads to the lock acquiring order problems. In the example above, the user uses implicit ordering in the case of function \code{foo} but explicit ordering in the case of \code{bar} and \code{baz}. This subtle difference means that calling these routines concurrently may lead to deadlock and is therefore Undefined Behaviour. As shown~\cite{Lister77}, solving this problem requires:
    140140\begin{enumerate}
    141141        \item Dynamically tracking of the monitor-call order.
     
    155155}
    156156\end{cfacode}
    157 This example shows a trivial solution to the bank-account transfer-problem~\cite{BankTransfer}. Without \gls{multi-acq} and \gls{bulk-acq}, the solution to this problem is much more involved and requires careful engineering.
     157This example shows a trivial solution to the bank-account transfer problem~\cite{BankTransfer}. Without \gls{multi-acq} and \gls{bulk-acq}, the solution to this problem is much more involved and requires careful engineering.
    158158
    159159\subsection{\code{mutex} statement} \label{mutex-stmt}
    160160
    161 The call semantics discussed above have one software engineering issue, only a named routine can acquire the mutual-exclusion of a set of monitor. \CFA offers the \code{mutex} statement to workaround the need for unnecessary names, avoiding a major software engineering problem~\cite{2FTwoHardThings}. Table \ref{lst:mutex-stmt} shows an example of the \code{mutex} statement, which introduces a new scope in which the mutual-exclusion of a set of monitor is acquired. Beyond naming, the \code{mutex} statement has no semantic difference from a routine call with \code{mutex} parameters.
     161The call semantics discussed above have one software engineering issue, only a named routine can acquire the mutual-exclusion of a set of monitor. \CFA offers the \code{mutex} statement to work around the need for unnecessary names, avoiding a major software engineering problem~\cite{2FTwoHardThings}. Table \ref{lst:mutex-stmt} shows an example of the \code{mutex} statement, which introduces a new scope in which the mutual-exclusion of a set of monitor is acquired. Beyond naming, the \code{mutex} statement has no semantic difference from a routine call with \code{mutex} parameters.
    162162
    163163\begin{table}
     
    196196% ======================================================================
    197197% ======================================================================
    198 Once the call semantics are established, the next step is to establish data semantics. Indeed, until now a monitor is used simply as a generic handle but in most cases monitors contain shared data. This data should be intrinsic to the monitor declaration to prevent any accidental use of data without its appropriate protection. For example, here is a complete version of the counter showed in section \ref{call}:
     198Once the call semantics are established, the next step is to establish data semantics. Indeed, until now a monitor is used simply as a generic handle but in most cases monitors contain shared data. This data should be intrinsic to the monitor declaration to prevent any accidental use of data without its appropriate protection. For example, here is a complete version of the counter shown in section \ref{call}:
    199199\begin{cfacode}
    200200monitor counter_t {
     
    227227% ======================================================================
    228228% ======================================================================
    229 \section{Internal scheduling} \label{intsched}
     229\section{Internal Scheduling} \label{intsched}
    230230% ======================================================================
    231231% ======================================================================
    232232In addition to mutual exclusion, the monitors at the core of \CFA's concurrency can also be used to achieve synchronization. With monitors, this capability is generally achieved with internal or external scheduling as in~\cite{Hoare74}. Since internal scheduling within a single monitor is mostly a solved problem, this thesis concentrates on extending internal scheduling to multiple monitors. Indeed, like the \gls{bulk-acq} semantics, internal scheduling extends to multiple monitors in a way that is natural to the user but requires additional complexity on the implementation side.
    233233
    234 First, here is a simple example of internal-scheduling :
     234First, here is a simple example of internal scheduling :
    235235
    236236\begin{cfacode}
     
    253253}
    254254\end{cfacode}
    255 There are two details to note here. First, the \code{signal} is a delayed operation, it only unblocks the waiting thread when it reaches the end of the critical section. This semantic is needed to respect mutual-exclusion, i.e., the signaller and signalled thread cannot be in the monitor simultaneously. The alternative is to return immediately after the call to \code{signal}, which is significantly more restrictive. Second, in \CFA, while it is common to store a \code{condition} as a field of the monitor, a \code{condition} variable can be stored/created independently of a monitor. Here routine \code{foo} waits for the \code{signal} from \code{bar} before making further progress, effectively ensuring a basic ordering.
    256 
    257 An important aspect of the implementation is that \CFA does not allow barging, which means that once function \code{bar} releases the monitor, \code{foo} is guaranteed to resume immediately after (unless some other thread waited on the same condition). This guarantee offers the benefit of not having to loop around waits to recheck that a condition is met. The main reason \CFA offers this guarantee is that users can easily introduce barging if it becomes a necessity but adding barging prevention or barging avoidance is more involved without language support. Supporting barging prevention as well as extending internal scheduling to multiple monitors is the main source of complexity in the design of \CFA concurrency.
    258 
    259 % ======================================================================
    260 % ======================================================================
    261 \subsection{Internal Scheduling - multi monitor}
    262 % ======================================================================
    263 % ======================================================================
    264 It is easier to understand the problem of multi-monitor scheduling using a series of pseudo-code examples. Note that for simplicity in the following snippets of pseudo-code, waiting and signalling is done using an implicit condition variable, like Java built-in monitors. Indeed, \code{wait} statements always use the implicit condition variable as parameter and explicitly names the monitors (A and B) associated with the condition. Note that in \CFA, condition variables are tied to a \emph{group} of monitors on first use (called branding), which means that using internal scheduling with distinct sets of monitors requires one condition variable per set of monitors. The example below shows the simple case of having two threads (one for each column) and a single monitor A.
     255There are two details to note here. First, the \code{signal} is a delayed operation, it only unblocks the waiting thread when it reaches the end of the critical section. This semantic is needed to respect mutual-exclusion, i.e., the signaller and signalled thread cannot be in the monitor simultaneously. The alternative is to return immediately after the call to \code{signal}, which is significantly more restrictive. Second, in \CFA, while it is common to store a \code{condition} as a field of the monitor, a \code{condition} variable can be stored/created independently of a monitor. Here routine \code{foo} waits for the \code{signal} from \code{bar} before making further progress, ensuring a basic ordering.
     256
     257An important aspect of the implementation is that \CFA does not allow barging, which means that once function \code{bar} releases the monitor, \code{foo} is guaranteed to resume immediately after (unless some other thread waited on the same condition). This guarantee offers the benefit of not having to loop around waits to recheck that a condition is met. The main reason \CFA offers this guarantee is that users can easily introduce barging if it becomes a necessity but adding barging prevention or barging avoidance is more involved without language support. Supporting barging prevention as well as extending internal scheduling to multiple monitors is the main source of complexity in the design and implementation of \CFA concurrency.
     258
     259% ======================================================================
     260% ======================================================================
     261\subsection{Internal Scheduling - Multi-Monitor}
     262% ======================================================================
     263% ======================================================================
     264It is easier to understand the problem of multi-monitor scheduling using a series of pseudo-code examples. Note that for simplicity in the following snippets of pseudo-code, waiting and signalling is done using an implicit condition variable, like Java built-in monitors. Indeed, \code{wait} statements always use the implicit condition variable as parameters and explicitly names the monitors (A and B) associated with the condition. Note that in \CFA, condition variables are tied to a \emph{group} of monitors on first use (called branding), which means that using internal scheduling with distinct sets of monitors requires one condition variable per set of monitors. The example below shows the simple case of having two threads (one for each column) and a single monitor A.
    265265
    266266\begin{multicols}{2}
     
    297297\end{pseudo}
    298298\end{multicols}
    299 This version uses \gls{bulk-acq} (denoted using the {\sf\&} symbol), but the presence of multiple monitors does not add a particularly new meaning. Synchronization happens between the two threads in exactly the same way and order. The only difference is that mutual exclusion covers more monitors. On the implementation side, handling multiple monitors does add a degree of complexity as the next few examples demonstrate.
    300 
    301 While deadlock issues can occur when nesting monitors, these issues are only a symptom of the fact that locks, and by extension monitors, are not perfectly composable. For monitors, a well known deadlock problem is the Nested Monitor Problem~\cite{Lister77}, which occurs when a \code{wait} is made by a thread that holds more than one monitor. For example, the following pseudo-code runs into the nested-monitor problem :
     299\noindent This version uses \gls{bulk-acq} (denoted using the {\sf\&} symbol), but the presence of multiple monitors does not add a particularly new meaning. Synchronization happens between the two threads in exactly the same way and order. The only difference is that mutual exclusion covers a group of monitors. On the implementation side, handling multiple monitors does add a degree of complexity as the next few examples demonstrate.
     300
     301While deadlock issues can occur when nesting monitors, these issues are only a symptom of the fact that locks, and by extension monitors, are not perfectly composable. For monitors, a well-known deadlock problem is the Nested Monitor Problem~\cite{Lister77}, which occurs when a \code{wait} is made by a thread that holds more than one monitor. For example, the following pseudo-code runs into the nested-monitor problem :
    302302\begin{multicols}{2}
    303303\begin{pseudo}
     
    319319\end{pseudo}
    320320\end{multicols}
    321 The \code{wait} only releases monitor \code{B} so the signalling thread cannot acquire monitor \code{A} to get to the \code{signal}. Attempting release of all acquired monitors at the \code{wait} introduces a different set of problems, such as releasing monitor \code{C}, which has nothing to do with the \code{signal}.
     321\noindent The \code{wait} only releases monitor \code{B} so the signalling thread cannot acquire monitor \code{A} to get to the \code{signal}. Attempting release of all acquired monitors at the \code{wait} introduces a different set of problems, such as releasing monitor \code{C}, which has nothing to do with the \code{signal}.
    322322
    323323However, for monitors as for locks, it is possible to write a program using nesting without encountering any problems if nesting is done correctly. For example, the next pseudo-code snippet acquires monitors {\sf A} then {\sf B} before waiting, while only acquiring {\sf B} when signalling, effectively avoiding the Nested Monitor Problem~\cite{Lister77}.
     
    343343\end{multicols}
    344344
    345 This simple refactoring may not be possible, forcing more complex restructuring.
    346 
    347 % ======================================================================
    348 % ======================================================================
    349 \subsection{Internal Scheduling - in depth}
    350 % ======================================================================
    351 % ======================================================================
    352 
    353 A larger example is presented to show complex issues for \gls{bulk-acq} and all the implementation options are analyzed. Listing \ref{lst:int-bulk-pseudo} shows an example where \gls{bulk-acq} adds a significant layer of complexity to the internal signalling semantics, and listing \ref{lst:int-bulk-cfa} shows the corresponding \CFA code to implement the pseudo-code in listing \ref{lst:int-bulk-pseudo}. For the purpose of translating the given pseudo-code into \CFA-code, any method of introducing a monitor is acceptable, e.g., \code{mutex} parameter, global variables, pointer parameters or using locals with the \code{mutex}-statement.
     345\noindent However, this simple refactoring may not be possible, forcing more complex restructuring.
     346
     347% ======================================================================
     348% ======================================================================
     349\subsection{Internal Scheduling - In Depth}
     350% ======================================================================
     351% ======================================================================
     352
     353A larger example is presented to show complex issues for \gls{bulk-acq} and all the implementation options are analyzed. Listing \ref{lst:int-bulk-pseudo} shows an example where \gls{bulk-acq} adds a significant layer of complexity to the internal signalling semantics, and listing \ref{lst:int-bulk-cfa} shows the corresponding \CFA code to implement the pseudo-code in listing \ref{lst:int-bulk-pseudo}. For the purpose of translating the given pseudo-code into \CFA-code, any method of introducing a monitor is acceptable, e.g., \code{mutex} parameters, global variables, pointer parameters, or using locals with the \code{mutex}-statement.
    354354
    355355\begin{figure}[!t]
     
    376376                |\label{line:signal1}|signal A & B
    377377                //Code Section 7
    378         release A & B
     378        |\label{line:releaseFirst}|release A & B
    379379        //Code Section 8
    380380|\label{line:lastRelease}|release A
     
    446446\end{figure}
    447447
    448 The complexity begins at code sections 4 and 8, which are where the existing semantics of internal scheduling need to be extended for multiple monitors. The root of the problem is that \gls{bulk-acq} is used in a context where one of the monitors is already acquired and is why it is important to define the behaviour of the previous pseudo-code. When the signaller thread reaches the location where it should ``release \code{A & B}'' (listing \ref{lst:int-bulk-pseudo} line \ref{line:signal1}), it must actually transfer ownership of monitor \code{B} to the waiting thread. This ownership transfer is required in order to prevent barging into \code{B} by another thread, since both the signalling and signalled threads still need monitor \code{A}. There are three options.
    449 
    450 \subsubsection{Delaying signals}
    451 The obvious solution to solve the problem of multi-monitor scheduling is to keep ownership of all locks until the last lock is ready to be transferred. It can be argued that that moment is when the last lock is no longer needed because this semantics fits most closely to the behaviour of single-monitor scheduling. This solution has the main benefit of transferring ownership of groups of monitors, which simplifies the semantics from multiple objects to a single group of objects, effectively making the existing single-monitor semantic viable by simply changing monitors to monitor groups. The naive approach to this solution is to only release monitors once every monitor in a group can be released. However, since some monitors are never released (i.e., the monitor of a thread), this interpretation means groups can grow but may never shrink. A more interesting interpretation is to only transfer groups as one but to recreate the groups on every operation, i.e., limit ownership transfer to one per \code{signal}/\code{release}.
    452 
    453 However, this solution can become much more complicated depending on what is executed while secretly holding B (listing \ref{lst:int-secret} line \ref{line:secret}).
    454 The goal in this solution is to avoid the need to transfer ownership of a subset of the condition monitors. However, listing \ref{lst:dependency} shows a slightly different example where a third thread is waiting on monitor \code{A}, using a different condition variable. Because the third thread is signalled when secretly holding \code{B}, the goal  becomes unreachable. Depending on the order of signals (listing \ref{lst:dependency} line \ref{line:signal-ab} and \ref{line:signal-a}) two cases can happen :
     448The complexity begins at code sections 4 and 8 in listing \ref{lst:int-bulk-pseudo}, which are where the existing semantics of internal scheduling needs to be extended for multiple monitors. The root of the problem is that \gls{bulk-acq} is used in a context where one of the monitors is already acquired and is why it is important to define the behaviour of the previous pseudo-code. When the signaller thread reaches the location where it should ``release \code{A & B}'' (listing \ref{lst:int-bulk-pseudo} line \ref{line:releaseFirst}), it must actually transfer ownership of monitor \code{B} to the waiting thread. This ownership transfer is required in order to prevent barging into \code{B} by another thread, since both the signalling and signalled threads still need monitor \code{A}. There are three options.
     449
     450\subsubsection{Delaying Signals}
     451The obvious solution to solve the problem of multi-monitor scheduling is to keep ownership of all locks until the last lock is ready to be transferred. It can be argued that that moment is when the last lock is no longer needed because this semantics fits most closely to the behaviour of single-monitor scheduling. This solution has the main benefit of transferring ownership of groups of monitors, which simplifies the semantics from multiple objects to a single group of objects, effectively making the existing single-monitor semantic viable by simply changing monitors to monitor groups. This solution releases the monitors once every monitor in a group can be released. However, since some monitors are never released (i.e., the monitor of a thread), this interpretation means a group might never be released. A more interesting interpretation is to transfer the group until it can be disbanded, which means the group is not passed further and a thread can retain its locks.
     452
     453However, listing \ref{lst:int-secret} shows this solution can become much more complicated depending on what is executed while secretly holding B at line \ref{line:secret}, while avoiding the need to transfer ownership of a subset of the condition monitors. Listing \ref{lst:dependency} shows a slightly different example where a third thread is waiting on monitor \code{A}, using a different condition variable. Because the third thread is signalled when secretly holding \code{B}, the goal  becomes unreachable. Depending on the order of signals (listing \ref{lst:dependency} line \ref{line:signal-ab} and \ref{line:signal-a}) two cases can happen :
    455454
    456455\paragraph{Case 1: thread $\alpha$ goes first.} In this case, the problem is that monitor \code{A} needs to be passed to thread $\beta$ when thread $\alpha$ is done with it.
     
    460459Note that ordering is not determined by a race condition but by whether signalled threads are enqueued in FIFO or FILO order. However, regardless of the answer, users can move line \ref{line:signal-a} before line \ref{line:signal-ab} and get the reverse effect for listing \ref{lst:dependency}.
    461460
    462 In both cases, the threads need to be able to distinguish, on a per monitor basis, which ones need to be released and which ones need to be transferred, which means monitors cannot be handled as a single homogeneous group and therefore effectively precludes this approach.
     461In both cases, the threads need to be able to distinguish, on a per monitor basis, which ones need to be released and which ones need to be transferred, which means knowing when to dispand a group becomes complex and inefficient (see next section) and therefore effectively precludes this approach.
    463462
    464463\subsubsection{Dependency graphs}
     
    502501\end{figure}
    503502
    504 In the listing \ref{lst:int-bulk-pseudo} pseudo-code, there is a solution that satisfies both barging prevention and mutual exclusion. If ownership of both monitors is transferred to the waiter when the signaller releases \code{A & B} and then the waiter transfers back ownership of \code{A} back to the signaller when it releases it, then the problem is solved (\code{B} is no longer in use at this point). Dynamically finding the correct order is therefore the second possible solution. The problem is effectively resolving a dependency graph of ownership requirements. Here even the simplest of code snippets requires two transfers and it seems to increase in a manner close to polynomial. This complexity explosion can be seen in listing \ref{lst:explosion}, which is just a direct extension to three monitors, requires at least three ownership transfer and has multiple solutions. Furthermore, the presence of multiple solutions for ownership transfer can cause deadlock problems if a specific solution is not consistently picked; In the same way that multiple lock acquiring order can cause deadlocks.
     503In listing \ref{lst:int-bulk-pseudo}, there is a solution that satisfies both barging prevention and mutual exclusion. If ownership of both monitors is transferred to the waiter when the signaller releases \code{A & B} and then the waiter transfers back ownership of \code{A} back to the signaller when it releases it, then the problem is solved (\code{B} is no longer in use at this point). Dynamically finding the correct order is therefore the second possible solution. The problem is effectively resolving a dependency graph of ownership requirements. Here even the simplest of code snippets requires two transfers and it seems to increase in a manner close to polynomial. This complexity explosion can be seen in listing \ref{lst:explosion}, which is just a direct extension to three monitors, requires at least three ownership transfer and has multiple solutions. Furthermore, the presence of multiple solutions for ownership transfer can cause deadlock problems if a specific solution is not consistently picked; In the same way that multiple lock acquiring order can cause deadlocks.
    505504\begin{figure}
    506505\begin{multicols}{2}
     
    531530\end{figure}
    532531
    533 Listing \ref{lst:dependency} is the three threads example used in the delayed signals solution. Figure \ref{fig:dependency} shows the corresponding dependency graph that results, where every node is a statement of one of the three threads, and the arrows the dependency of that statement (e.g., $\alpha1$ must happen before $\alpha2$). The extra challenge is that this dependency graph is effectively post-mortem, but the runtime system needs to be able to build and solve these graphs as the dependency unfolds. Resolving dependency graphs being a complex and expensive endeavour, this solution is not the preferred one.
    534 
    535 \subsubsection{Partial signalling} \label{partial-sig}
    536 Finally, the solution that is chosen for \CFA is to use partial signalling. Again using listing \ref{lst:int-bulk-pseudo}, the partial signalling solution transfers ownership of monitor \code{B} at lines \ref{line:signal1} to the waiter but does not wake the waiting thread since it is still using monitor \code{A}. Only when it reaches line \ref{line:lastRelease} does it actually wakeup the waiting thread. This solution has the benefit that complexity is encapsulated into only two actions, passing monitors to the next owner when they should be released and conditionally waking threads if all conditions are met. This solution has a much simpler implementation than a dependency graph solving algorithm, which is why it was chosen. Furthermore, after being fully implemented, this solution does not appear to have any significant downsides.
    537 
    538 While listing \ref{lst:dependency} is a complicated problem for previous solutions, it can be solved easily with partial signalling :
     532Given the three threads example in listing \ref{lst:dependency}, figure \ref{fig:dependency} shows the corresponding dependency graph that results, where every node is a statement of one of the three threads, and the arrows the dependency of that statement (e.g., $\alpha1$ must happen before $\alpha2$). The extra challenge is that this dependency graph is effectively post-mortem, but the runtime system needs to be able to build and solve these graphs as the dependency unfolds. Resolving dependency graphs being a complex and expensive endeavour, this solution is not the preferred one.
     533
     534\subsubsection{Partial Signalling} \label{partial-sig}
     535Finally, the solution that is chosen for \CFA is to use partial signalling. Again using listing \ref{lst:int-bulk-pseudo}, the partial signalling solution transfers ownership of monitor \code{B} at lines \ref{line:signal1} to the waiter but does not wake the waiting thread since it is still using monitor \code{A}. Only when it reaches line \ref{line:lastRelease} does it actually wake up the waiting thread. This solution has the benefit that complexity is encapsulated into only two actions, passing monitors to the next owner when they should be released and conditionally waking threads if all conditions are met. This solution has a much simpler implementation than a dependency graph solving algorithms, which is why it was chosen. Furthermore, after being fully implemented, this solution does not appear to have any significant downsides.
     536
     537Using partial signalling, listing \ref{lst:dependency} can be solved easily :
    539538\begin{itemize}
    540539        \item When thread $\gamma$ reaches line \ref{line:release-ab} it transfers monitor \code{B} to thread $\alpha$ and continues to hold monitor \code{A}.
    541540        \item When thread $\gamma$ reaches line \ref{line:release-a}  it transfers monitor \code{A} to thread $\beta$  and wakes it up.
    542541        \item When thread $\beta$  reaches line \ref{line:release-aa} it transfers monitor \code{A} to thread $\alpha$ and wakes it up.
    543         \item Problem solved!
    544542\end{itemize}
    545543
     
    654652An important note is that, until now, signalling a monitor was a delayed operation. The ownership of the monitor is transferred only when the monitor would have otherwise been released, not at the point of the \code{signal} statement. However, in some cases, it may be more convenient for users to immediately transfer ownership to the thread that is waiting for cooperation, which is achieved using the \code{signal_block} routine.
    655653
    656 The example in table \ref{tbl:datingservice} highlights the difference in behaviour. As mentioned, \code{signal} only transfers ownership once the current critical section exits, this behaviour requires additional synchronization when a two-way handshake is needed. To avoid this explicit synchronization, the \code{condition} type offers the \code{signal_block} routine, which handles the two-way handshake as shown in the example. This feature removes the need for a second condition variables and simplifies programming. Like every other monitor semantic, \code{signal_block} uses barging prevention, which means mutual-exclusion is baton-passed both on the frond-end and the back-end of the call to \code{signal_block}, meaning no other thread can acquire the monitor either before or after the call.
     654The example in table \ref{tbl:datingservice} highlights the difference in behaviour. As mentioned, \code{signal} only transfers ownership once the current critical section exits, this behaviour requires additional synchronization when a two-way handshake is needed. To avoid this explicit synchronization, the \code{condition} type offers the \code{signal_block} routine, which handles the two-way handshake as shown in the example. This feature removes the need for a second condition variables and simplifies programming. Like every other monitor semantic, \code{signal_block} uses barging prevention, which means mutual-exclusion is baton-passed both on the frond end and the back end of the call to \code{signal_block}, meaning no other thread can acquire the monitor either before or after the call.
    657655
    658656% ======================================================================
     
    723721\end{tabular}
    724722\end{center}
    725 This method is more constrained and explicit, which helps users reduce the non-deterministic nature of concurrency. Indeed, as the following examples demonstrates, external scheduling allows users to wait for events from other threads without the concern of unrelated events occurring. External scheduling can generally be done either in terms of control flow (e.g., Ada with \code{accept}, \uC with \code{_Accept}) or in terms of data (e.g., Go with channels). Of course, both of these paradigms have their own strengths and weaknesses, but for this project control-flow semantics were chosen to stay consistent with the rest of the languages semantics. Two challenges specific to \CFA arise when trying to add external scheduling with loose object definitions and multiple-monitor routines. The previous example shows a simple use \code{_Accept} versus \code{wait}/\code{signal} and its advantages. Note that while other languages often use \code{accept}/\code{select} as the core external scheduling keyword, \CFA uses \code{waitfor} to prevent name collisions with existing socket \acrshort{api}s.
    726 
    727 For the \code{P} member above using internal scheduling, the call to \code{wait} only guarantees that \code{V} is the last routine to access the monitor, allowing a third routine, say \code{isInUse()}, acquire mutual exclusion several times while routine \code{P} is waiting. On the other hand, external scheduling guarantees that while routine \code{P} is waiting, no routine other than \code{V} can acquire the monitor.
    728 
    729 % ======================================================================
    730 % ======================================================================
    731 \subsection{Loose object definitions}
    732 % ======================================================================
    733 % ======================================================================
    734 In \uC, a monitor class declaration includee an exhaustive list of monitor operations. Since \CFA is not object oriented, monitors become both more difficult to implement and less clear for a user:
     723This method is more constrained and explicit, which helps users reduce the non-deterministic nature of concurrency. Indeed, as the following examples demonstrates, external scheduling allows users to wait for events from other threads without the concern of unrelated events occurring. External scheduling can generally be done either in terms of control flow (e.g., Ada with \code{accept}, \uC with \code{_Accept}) or in terms of data (e.g., Go with channels). Of course, both of these paradigms have their own strengths and weaknesses, but for this project control-flow semantics was chosen to stay consistent with the rest of the languages semantics. Two challenges specific to \CFA arise when trying to add external scheduling with loose object definitions and multiple-monitor routines. The previous example shows a simple use \code{_Accept} versus \code{wait}/\code{signal} and its advantages. Note that while other languages often use \code{accept}/\code{select} as the core external scheduling keyword, \CFA uses \code{waitfor} to prevent name collisions with existing socket \acrshort{api}s.
     724
     725For the \code{P} member above using internal scheduling, the call to \code{wait} only guarantees that \code{V} is the last routine to access the monitor, allowing a third routine, say \code{isInUse()}, acquire mutual exclusion several times while routine \code{P} is waiting. On the other hand, external scheduling guarantees that while routine \code{P} is waiting, no other routine than \code{V} can acquire the monitor.
     726
     727% ======================================================================
     728% ======================================================================
     729\subsection{Loose Object Definitions}
     730% ======================================================================
     731% ======================================================================
     732In \uC, a monitor class declaration includes an exhaustive list of monitor operations. Since \CFA is not object oriented, monitors become both more difficult to implement and less clear for a user:
    735733
    736734\begin{cfacode}
     
    748746\end{cfacode}
    749747
    750 Furthermore, external scheduling is an example where implementation constraints become visible from the interface. Here is the pseudo code for the entering phase of a monitor:
     748Furthermore, external scheduling is an example where implementation constraints become visible from the interface. Here is the pseudo-code for the entering phase of a monitor:
    751749\begin{center}
    752750\begin{tabular}{l}
     
    763761\end{tabular}
    764762\end{center}
    765 For the first two conditions, it is easy to implement a check that can evaluate the condition in a few instruction. However, a fast check for \pscode{monitor accepts me} is much harder to implement depending on the constraints put on the monitors. Indeed, monitors are often expressed as an entry queue and some acceptor queue as in the following figure:
     763For the first two conditions, it is easy to implement a check that can evaluate the condition in a few instructions. However, a fast check for \pscode{monitor accepts me} is much harder to implement depending on the constraints put on the monitors. Indeed, monitors are often expressed as an entry queue and some acceptor queue as in the following figure:
    766764
    767765\begin{figure}[H]
     
    772770\end{figure}
    773771
    774 There are other alternatives to these pictures, but in the case of this picture, implementing a fast accept check is relatively easy. Restricted to a fixed number of mutex members, N, the accept check reduces to updating a bitmask when the acceptor queue changes, a check that executes in a single instruction even with a fairly large number (e.g., 128) of mutex members. This approach requires a dense unique ordering of routines with an upper-bound and that ordering must be consistent across translation units. For OO languages these constraints are common, since objects only offer adding member routines consistently across translation units via inheritence. However, in \CFA users can extend objects with mutex routines that are only visible in certain translation unit. This means that establishing a program-wide dense-ordering among mutex routines can only be done in the program linking phase, and still could have issues when using dynamically shared objects.
     772There are other alternatives to these pictures, but in the case of this picture, implementing a fast accept check is relatively easy. Restricted to a fixed number of mutex members, N, the accept check reduces to updating a bitmask when the acceptor queue changes, a check that executes in a single instruction even with a fairly large number (e.g., 128) of mutex members. This approach requires a unique dense ordering of routines with an upper-bound and that ordering must be consistent across translation units. For OO languages these constraints are common, since objects only offer adding member routines consistently across translation units via inheritance. However, in \CFA users can extend objects with mutex routines that are only visible in certain translation unit. This means that establishing a program-wide dense-ordering among mutex routines can only be done in the program linking phase, and still could have issues when using dynamically shared objects.
    775773
    776774The alternative is to alter the implementation like this:
    777 
    778775\begin{center}
    779776{\resizebox{0.4\textwidth}{!}{\input{ext_monitor}}}
    780777\end{center}
    781 
    782 Here, the mutex routine called is associated with a thread on the entry queue while a list of acceptable routines is kept seperately. Generating a mask dynamically means that the storage for the mask information can vary between calls to \code{waitfor}, allowing for more flexibility and extensions. Storing an array of accepted function-pointers replaces the single instruction bitmask compare with dereferencing a pointer followed by a linear search. Furthermore, supporting nested external scheduling (e.g., listing \ref{lst:nest-ext}) may now require additional searches for the \code{waitfor} statement to check if a routine is already queued.
     778Here, the mutex routine called is associated with a thread on the entry queue while a list of acceptable routines is kept separate. Generating a mask dynamically means that the storage for the mask information can vary between calls to \code{waitfor}, allowing for more flexibility and extensions. Storing an array of accepted function pointers replaces the single instruction bitmask comparison with dereferencing a pointer followed by a linear search. Furthermore, supporting nested external scheduling (e.g., listing \ref{lst:nest-ext}) may now require additional searches for the \code{waitfor} statement to check if a routine is already queued.
    783779
    784780\begin{figure}
     
    797793\end{figure}
    798794
    799 Note that in the second picture, tasks need to always keep track of the monitors associated with mutex routines, and the routine mask needs to have both a function pointer and a set of monitors, as is be discussed in the next section. These details are omitted from the picture for the sake of simplicity.
    800 
    801 At this point, a decision must be made between flexibility and performance. Many design decisions in \CFA achieve both flexibility and performance, for example polymorphic routines add significant flexibility but inlining them means the optimizer can easily remove any runtime cost. Here however, the cost of flexibility cannot be trivially removed. In the end, the most flexible approach has been chosen since it allows users to write programs that would otherwise be  hard to write. This decision is based on the assumption that writing fast but inflexible locks is closer to a solved problems than writing locks that are as flexible as external scheduling in \CFA.
    802 
    803 % ======================================================================
    804 % ======================================================================
    805 \subsection{Multi-monitor scheduling}
    806 % ======================================================================
    807 % ======================================================================
    808 
    809 External scheduling, like internal scheduling, becomes significantly more complex when introducing multi-monitor syntax. Even in the simplest possible case, some new semantics need to be established:
     795Note that in the second picture, tasks need to always keep track of the monitors associated with mutex routines, and the routine mask needs to have both a function pointer and a set of monitors, as is discussed in the next section. These details are omitted from the picture for the sake of simplicity.
     796
     797At this point, a decision must be made between flexibility and performance. Many design decisions in \CFA achieve both flexibility and performance, for example polymorphic routines add significant flexibility but inlining them means the optimizer can easily remove any runtime cost. Here, however, the cost of flexibility cannot be trivially removed. In the end, the most flexible approach has been chosen since it allows users to write programs that would otherwise be  hard to write. This decision is based on the assumption that writing fast but inflexible locks is closer to a solved problem than writing locks that are as flexible as external scheduling in \CFA.
     798
     799% ======================================================================
     800% ======================================================================
     801\subsection{Multi-Monitor Scheduling}
     802% ======================================================================
     803% ======================================================================
     804
     805External scheduling, like internal scheduling, becomes significantly more complex when introducing multi-monitor syntax. Even in the simplest possible case, some new semantics needs to be established:
    810806\begin{cfacode}
    811807monitor M {};
     
    837833
    838834void g(M & mutex a, M & mutex b) {
    839         //wait for call to f with argument a and b
     835        //wait for call to f with arguments a and b
    840836        waitfor(f, a, b);
    841837}
     
    870866% ======================================================================
    871867% ======================================================================
    872 \subsection{\code{waitfor} semantics}
    873 % ======================================================================
    874 % ======================================================================
    875 
    876 Syntactically, the \code{waitfor} statement takes a function identifier and a set of monitors. While the set of monitors can be any list of expression, the function name is more restricted because the compiler validates at compile time the validity of the function type and the parameters used with the \code{waitfor} statement. It checks that the set of monitors passed in matches the requirements for a function call. Listing \ref{lst:waitfor} shows various usage of the waitfor statement and which are acceptable. The choice of the function type is made ignoring any non-\code{mutex} parameter. One limitation of the current implementation is that it does not handle overloading but overloading is possible.
     868\subsection{\code{waitfor} Semantics}
     869% ======================================================================
     870% ======================================================================
     871
     872Syntactically, the \code{waitfor} statement takes a function identifier and a set of monitors. While the set of monitors can be any list of expressions, the function name is more restricted because the compiler validates at compile time the validity of the function type and the parameters used with the \code{waitfor} statement. It checks that the set of monitors passed in matches the requirements for a function call. Listing \ref{lst:waitfor} shows various usages of the waitfor statement and which are acceptable. The choice of the function type is made ignoring any non-\code{mutex} parameter. One limitation of the current implementation is that it does not handle overloading but overloading is possible.
    877873\begin{figure}
    878874\begin{cfacode}[caption={Various correct and incorrect uses of the waitfor statement},label={lst:waitfor}]
     
    908904\end{figure}
    909905
    910 Finally, for added flexibility, \CFA supports constructing a complex \code{waitfor} statement using the \code{or}, \code{timeout} and \code{else}. Indeed, multiple \code{waitfor} clauses can be chained together using \code{or}; this chain forms a single statement that uses baton-pass to any one function that fits one of the function+monitor set passed in. To enable users to tell which accepted function executed, \code{waitfor}s are followed by a statement (including the null statement \code{;}) or a compound statement, which is executed after the clause is triggered. A \code{waitfor} chain can also be followed by a \code{timeout}, to signify an upper bound on the wait, or an \code{else}, to signify that the call should be non-blocking, which checks for a matching function call already arrived and otherwise continues. Any and all of these clauses can be preceded by a \code{when} condition to dynamically toggle the accept clauses on or off based on some current state. Listing \ref{lst:waitfor2}, demonstrates several complex masks and some incorrect ones.
     906Finally, for added flexibility, \CFA supports constructing a complex \code{waitfor} statement using the \code{or}, \code{timeout} and \code{else}. Indeed, multiple \code{waitfor} clauses can be chained together using \code{or}; this chain forms a single statement that uses baton pass to any function that fits one of the function+monitor set passed in. To enable users to tell which accepted function executed, \code{waitfor}s are followed by a statement (including the null statement \code{;}) or a compound statement, which is executed after the clause is triggered. A \code{waitfor} chain can also be followed by a \code{timeout}, to signify an upper bound on the wait, or an \code{else}, to signify that the call should be non-blocking, which checks for a matching function call already arrived and otherwise continues. Any and all of these clauses can be preceded by a \code{when} condition to dynamically toggle the accept clauses on or off based on some current state. Listing \ref{lst:waitfor2}, demonstrates several complex masks and some incorrect ones.
    911907
    912908\begin{figure}
     
    973969% ======================================================================
    974970% ======================================================================
    975 \subsection{Waiting for the destructor}
    976 % ======================================================================
    977 % ======================================================================
    978 An interesting use for the \code{waitfor} statement is destructor semantics. Indeed, the \code{waitfor} statement can accept any \code{mutex} routine, which includes the destructor (see section \ref{data}). However, with the semantics discussed until now, waiting for the destructor does not make any sense since using an object after its destructor is called is undefined behaviour. The simplest approach is to disallow \code{waitfor} on a destructor. However, a more expressive approach is to flip execution ordering when waiting for the destructor, meaning that waiting for the destructor allows the destructor to run after the current \code{mutex} routine, similarly to how a condition is signalled.
     971\subsection{Waiting For The Destructor}
     972% ======================================================================
     973% ======================================================================
     974An interesting use for the \code{waitfor} statement is destructor semantics. Indeed, the \code{waitfor} statement can accept any \code{mutex} routine, which includes the destructor (see section \ref{data}). However, with the semantics discussed until now, waiting for the destructor does not make any sense since using an object after its destructor is called is undefined behaviour. The simplest approach is to disallow \code{waitfor} on a destructor. However, a more expressive approach is to flip ordering of execution when waiting for the destructor, meaning that waiting for the destructor allows the destructor to run after the current \code{mutex} routine, similarly to how a condition is signalled.
    979975\begin{figure}
    980976\begin{cfacode}[caption={Example of an executor which executes action in series until the destructor is called.},label={lst:dtor-order}]
  • doc/proposals/concurrency/text/frontpgs.tex

    r882ad37 r3ca540f  
    8080\begin{center}\textbf{Abstract}\end{center}
    8181
    82 \CFA is a modern, non-object-oriented extension of the C programming language. This thesis serves as a definition and an implementation for the concurrency and parallelism \CFA offers. These features are created from scratch due to the lack of concurrency in ISO C. Monitors are introduced as a high-level tool for control-flow based concurrency. In addition, lightweight threads are also introduced into the language. Specifically, the contribution of this thesis is two-fold: it extends the existing semantics of monitors introduce by~\cite{Hoare74} to handle monitors in groups and also details the engineering effort needed to introduce these features as core language features. Indeed, these features are added in respect with expectations of C programmers and are backwards compatible as much as possible.
     82\CFA is a modern, non-object-oriented extension of the C programming language. This thesis serves as a definition and an implementation for the concurrency and parallelism \CFA offers. These features are created from scratch due to the lack of concurrency in ISO C. Lightweight threads are introduced into the language. In addition, monitors are introduced as a high-level tool for control-flow based synchronization and mutual-exclusion. The main contributions of this thesis are two-fold: it extends the existing semantics of monitors introduce by~\cite{Hoare74} to handle monitors in groups and also details the engineering effort needed to introduce these features as core language features. Indeed, these features are added with respect to expectations of C programmers, and integrate with the \CFA type-system and other language features.
    8383
    8484
     
    9595I would like to thank Professors Martin Karsten and Gregor Richards, for reading my thesis and providing helpful feedback.
    9696
    97 Thanks to Aaron Moss, Rob Schluntz and Andrew Beach for their work on the \CFA project as well as all the discussions which have help me concretize the ideas in this thesis.
     97Thanks to Aaron Moss, Rob Schluntz and Andrew Beach for their work on the \CFA project as well as all the discussions which have helped me concretize the ideas in this thesis.
    9898
    99 Finally, I acknowledge that this as been possible thanks to the financial help offered by the David R. Cheriton School of Computer Science and the corperate partnership with Huawei Ltd.
     99Finally, I acknowledge that this has been possible thanks to the financial help offered by the David R. Cheriton School of Computer Science and the corporate partnership with Huawei Ltd.
    100100
    101101\cleardoublepage
  • doc/proposals/concurrency/text/future.tex

    r882ad37 r3ca540f  
    11
    22\chapter{Conclusion}
    3 As mentionned in the introduction, this thesis provides a minimal concurrency \acrshort{api} that is simple, efficient and usable as the basis for higher-level features. The approach presented is based on a lighweight thread system for parallelism which sits on top of clusters of processors. This M:N model is jugded to be both more efficient and allow more flexibility for users. Furthermore, this document introduces monitors as the main concurrency tool for users. This thesis also offers a novel approach which allows using multiple monitors at once without running into the Nested Monitor Problem~\cite{Lister77}. It also offers a full implmentation of the concurrency runtime wirtten enterily in \CFA, effectively the largest \CFA code base to date.
     3This thesis has achieved a minimal concurrency \acrshort{api} that is simple, efficient and usable as the basis for higher-level features. The approach presented is based on a lightweight thread-system for parallelism, which sits on top of clusters of processors. This M:N model is judged to be both more efficient and allow more flexibility for users. Furthermore, this document introduces monitors as the main concurrency tool for users. This thesis also offers a novel approach allowing multiple monitors to be accessed simultaneously without running into the Nested Monitor Problem~\cite{Lister77}. It also offers a full implementation of the concurrency runtime written entirely in \CFA, effectively the largest \CFA code base to date.
    44
    55
     
    1111
    1212\subsection{Performance} \label{futur:perf}
    13 This thesis presents a first implementation of the \CFA runtime. Therefore, there is still significant work to do to improve performance. Many of the data structures and algorithms will change in the future to more efficient versions. For example, \CFA the number of monitors in a single \gls{bulk-acq} is only bound by the stack size, this is probably unnecessarily generous. It may be possible that limiting the number help increase performance. However, it is not obvious that the benefit would be significant.
     13This thesis presents a first implementation of the \CFA runtime. Therefore, there is still significant work to improve performance. Many of the data structures and algorithms may change in the future to more efficient versions. For example, the number of monitors in a single \gls{bulk-acq} is only bound by the stack size, this is probably unnecessarily generous. It may be possible that limiting the number helps increase performance. However, it is not obvious that the benefit would be significant.
    1414
    1515\subsection{Flexible Scheduling} \label{futur:sched}
    16 An important part of concurrency is scheduling. Different scheduling algorithm can affect performance (both in terms of average and variation). However, no single scheduler is optimal for all workloads and therefore there is value in being able to change the scheduler for given programs. One solution is to offer various tweaking options to users, allowing the scheduler to be adjusted to the requirements of the workload. However, in order to be truly flexible, it would be interesting to allow users to add arbitrary data and arbitrary scheduling algorithms to the scheduler. For example, a web server could attach Type-of-Service information to threads and have a ``ToS aware'' scheduling algorithm tailored to this specific web server. This path of flexible schedulers will be explored for \CFA.
     16An important part of concurrency is scheduling. Different scheduling algorithms can affect performance (both in terms of average and variation). However, no single scheduler is optimal for all workloads and therefore there is value in being able to change the scheduler for given programs. One solution is to offer various tweaking options to users, allowing the scheduler to be adjusted to the requirements of the workload. However, in order to be truly flexible, it would be interesting to allow users to add arbitrary data and arbitrary scheduling algorithms. For example, a web server could attach Type-of-Service information to threads and have a ``ToS aware'' scheduling algorithm tailored to this specific web server. This path of flexible schedulers will be explored for \CFA.
    1717
    1818\subsection{Non-Blocking IO} \label{futur:nbio}
    19 While most of the parallelism tools are aimed at data parallelism and control-flow parallelism, many modern workloads are not bound on computation but on IO operations, a common case being web-servers and XaaS (anything as a service). These type of workloads often require significant engineering around amortizing costs of blocking IO operations. At its core, Non-Blocking IO is a operating system level feature that allows queuing IO operations (e.g., network operations) and registering for notifications instead of waiting for requests to complete. In this context, the role of the language make Non-Blocking IO easily available and with low overhead. The current trend is to use asynchronous programming using tools like callbacks and/or futures and promises, which can be seen in frameworks like Node.js~\cite{NodeJs} for JavaScript, Spring MVC~\cite{SpringMVC} for Java and Django~\cite{Django} for Python. However, while these are valid solutions, they lead to code that is harder to read and maintain because it is much less linear.
     19While most of the parallelism tools are aimed at data parallelism and control-flow parallelism, many modern workloads are not bound on computation but on IO operations, a common case being web servers and XaaS (anything as a service). These types of workloads often require significant engineering around amortizing costs of blocking IO operations. At its core, Non-Blocking IO is an operating system level feature that allows queuing IO operations (e.g., network operations) and registering for notifications instead of waiting for requests to complete. In this context, the role of the language makes Non-Blocking IO easily available and with low overhead. The current trend is to use asynchronous programming using tools like callbacks and/or futures and promises, which can be seen in frameworks like Node.js~\cite{NodeJs} for JavaScript, Spring MVC~\cite{SpringMVC} for Java and Django~\cite{Django} for Python. However, while these are valid solutions, they lead to code that is harder to read and maintain because it is much less linear.
    2020
    21 \subsection{Other concurrency tools} \label{futur:tools}
    22 While monitors offer a flexible and powerful concurrent core for \CFA, other concurrency tools are also necessary for a complete multi-paradigm concurrency package. Example of such tools can include simple locks and condition variables, futures and promises~\cite{promises}, executors and actors. These additional features are useful when monitors offer a level of abstraction that is inadequate for certain tasks.
     21\subsection{Other Concurrency Tools} \label{futur:tools}
     22While monitors offer a flexible and powerful concurrent core for \CFA, other concurrency tools are also necessary for a complete multi-paradigm concurrency package. Examples of such tools can include simple locks and condition variables, futures and promises~\cite{promises}, executors and actors. These additional features are useful when monitors offer a level of abstraction that is inadequate for certain tasks.
    2323
    24 \subsection{Implicit threading} \label{futur:implcit}
    25 Simpler applications can benefit greatly from having implicit parallelism. That is, parallelism that does not rely on the user to write concurrency. This type of parallelism can be achieved both at the language level and at the library level. The canonical example of implicit parallelism is parallel for loops, which are the simplest example of a divide and conquer algorithm~\cite{uC++book}. Table \ref{lst:parfor} shows three different code examples that accomplish point-wise sums of large arrays. Note that none of these examples explicitly declare any concurrency or parallelism objects.
     24\subsection{Implicit Threading} \label{futur:implcit}
     25Simpler applications can benefit greatly from having implicit parallelism. That is, parallelism that does not rely on the user to write concurrency. This type of parallelism can be achieved both at the language level and at the library level. The canonical example of implicit parallelism is parallel for loops, which are the simplest example of a divide and conquer algorithms~\cite{uC++book}. Table \ref{lst:parfor} shows three different code examples that accomplish point-wise sums of large arrays. Note that none of these examples explicitly declare any concurrency or parallelism objects.
    2626
    2727\begin{table}
     
    108108\end{table}
    109109
    110 Implicit parallelism is a restrictive solution and therefore has its limitations. However, it is a quick and simple approach to parallelism, which may very well be sufficient for smaller applications and reduces the amount of boiler-plate needed to start benefiting from parallelism in modern CPUs.
     110Implicit parallelism is a restrictive solution and therefore has its limitations. However, it is a quick and simple approach to parallelism, which may very well be sufficient for smaller applications and reduces the amount of boilerplate needed to start benefiting from parallelism in modern CPUs.
    111111
    112112
  • doc/proposals/concurrency/text/internals.tex

    r882ad37 r3ca540f  
    11
    2 \chapter{Behind the scene}
    3 There are several challenges specific to \CFA when implementing concurrency. These challenges are a direct result of \gls{bulk-acq} and loose object-definitions. These two constraints are the root cause of most design decisions in the implementation. Furthermore, to avoid contention from dynamically allocating memory in a concurrent environment, the internal-scheduling design is (almost) entirely free of mallocs. This approach avoids the chicken and egg problem~\cite{Chicken} of having a memory allocator that relies on the threading system and a threading system that relies on the runtime. This extra goal means that memory management is a constant concern in the design of the system.
    4 
    5 The main memory concern for concurrency is queues. All blocking operations are made by parking threads onto queues and all queues are designed with intrusive nodes, where each not has pre-allocated link fields for chaining, to avoid the need for memory allocation. Since several concurrency operations can use an unbound amount of memory (depending on \gls{bulk-acq}), statically defining information in the intrusive fields of threads is insufficient.The only way to use a variable amount of memory without requiring memory allocation is to pre-allocate large buffers of memory eagerly and store the information in these buffers. Conveniently, the callstack fits that description and is easy to use, which is why it is used heavily in the implementation of internal scheduling, particularly variable-length arrays. Since stack allocation is based around scope, the first step of the implementation is to identify the scopes that are available to store the information, and which of these can have a variable-length array. The threads and the condition both have a fixed amount of memory, while mutex-routines and the actual blocking call allow for an unbound amount, within the stack size.
     2\chapter{Behind the Scenes}
     3There are several challenges specific to \CFA when implementing concurrency. These challenges are a direct result of \gls{bulk-acq} and loose object definitions. These two constraints are the root cause of most design decisions in the implementation. Furthermore, to avoid contention from dynamically allocating memory in a concurrent environment, the internal-scheduling design is (almost) entirely free of mallocs. This approach avoids the chicken and egg problem~\cite{Chicken} of having a memory allocator that relies on the threading system and a threading system that relies on the runtime. This extra goal means that memory management is a constant concern in the design of the system.
     4
     5The main memory concern for concurrency is queues. All blocking operations are made by parking threads onto queues and all queues are designed with intrusive nodes, where each node has pre-allocated link fields for chaining, to avoid the need for memory allocation. Since several concurrency operations can use an unbound amount of memory (depending on \gls{bulk-acq}), statically defining information in the intrusive fields of threads is insufficient.The only way to use a variable amount of memory without requiring memory allocation is to pre-allocate large buffers of memory eagerly and store the information in these buffers. Conveniently, the call stack fits that description and is easy to use, which is why it is used heavily in the implementation of internal scheduling, particularly variable-length arrays. Since stack allocation is based on scopes, the first step of the implementation is to identify the scopes that are available to store the information, and which of these can have a variable-length array. The threads and the condition both have a fixed amount of memory, while mute routines and blocking calls allow for an unbound amount, within the stack size.
    66
    77Note that since the major contributions of this thesis are extending monitor semantics to \gls{bulk-acq} and loose object definitions, any challenges that are not resulting of these characteristics of \CFA are considered as solved problems and therefore not discussed.
     
    99% ======================================================================
    1010% ======================================================================
    11 \section{Mutex routines}
    12 % ======================================================================
    13 % ======================================================================
    14 
    15 The first step towards the monitor implementation is simple mutex-routines. In the single monitor case, mutual-exclusion is done using the entry/exit procedure in listing \ref{lst:entry1}. The entry/exit procedures do not have to be extended to support multiple monitors. Indeed it is sufficient to enter/leave monitors one-by-one as long as the order is correct to prevent deadlock~\cite{Havender68}. In \CFA, ordering of monitor acquisition relies on memory ordering. This approach is sufficient because all objects are guaranteed to have distinct non-overlapping memory layouts and mutual-exclusion for a monitor is only defined for its lifetime, meaning that destroying a monitor while it is acquired is Undefined Behavior. When a mutex call is made, the concerned monitors are aggregated into a variable-length pointer-array and sorted based on pointer values. This array persists for the entire duration of the mutual-exclusion and its ordering reused extensively.
     11\section{Mutex Routines}
     12% ======================================================================
     13% ======================================================================
     14
     15The first step towards the monitor implementation is simple mute routines. In the single monitor case, mutual-exclusion is done using the entry/exit procedure in listing \ref{lst:entry1}. The entry/exit procedures do not have to be extended to support multiple monitors. Indeed it is sufficient to enter/leave monitors one-by-one as long as the order is correct to prevent deadlock~\cite{Havender68}. In \CFA, ordering of monitor acquisition relies on memory ordering. This approach is sufficient because all objects are guaranteed to have distinct non-overlapping memory layouts and mutual-exclusion for a monitor is only defined for its lifetime, meaning that destroying a monitor while it is acquired is Undefined Behaviour. When a mutex call is made, the concerned monitors are aggregated into a variable-length pointer array and sorted based on pointer values. This array persists for the entire duration of the mutual-exclusion and its ordering reused extensively.
    1616\begin{figure}
    1717\begin{multicols}{2}
     
    109109\end{cfacode}
    110110
    111 Both entry-point and \gls{callsite-locking} are feasible implementations. The current \CFA implementations uses entry-point locking because it requires less work when using \gls{raii}, effectively transferring the burden of implementation to object construction/destruction. It is harder to use \gls{raii} for call-site locking, as it does not necessarily have an existing scope that matches exactly the scope of the mutual exclusion, i.e.: the function body. For example, the monitor call can appear in the middle of an expression. Furthermore, entry-point locking requires less code generation since any useful routine multiple times, but there is only one entry-point for many call-sites.
     111Both entry point and \gls{callsite-locking} are feasible implementations. The current \CFA implementation uses entry-point locking because it requires less work when using \gls{raii}, effectively transferring the burden of implementation to object construction/destruction. It is harder to use \gls{raii} for call-site locking, as it does not necessarily have an existing scope that matches exactly the scope of the mutual exclusion, i.e.: the function body. For example, the monitor call can appear in the middle of an expression. Furthermore, entry-point locking requires less code generation since any useful routine multiple times, but there is only one entry point for many call sites.
    112112
    113113% ======================================================================
     
    117117% ======================================================================
    118118
    119 Figure \ref{fig:system1} shows a high-level picture if the \CFA runtime system in regards to concurrency. Each component of the picture is explained in details in the flowing sections.
     119Figure \ref{fig:system1} shows a high-level picture if the \CFA runtime system in regards to concurrency. Each component of the picture is explained in detail in the flowing sections.
    120120
    121121\begin{figure}
     
    128128
    129129\subsection{Context Switching}
    130 As mentioned in section \ref{coroutine}, coroutines are a stepping stone for implementing threading, because they share the same mechanism for context-switching between different stacks. To improve performance and simplicity, context-switching is implemented using the following assumption: all context-switches happen inside a specific function call. This assumption means that the context-switch only has to copy the callee-saved registers onto the stack and then switch the stack registers with the ones of the target coroutine/thread. Note that the instruction pointer can be left untouched since the context-switch is always inside the same function. Threads however do not context-switch between each other directly. They context-switch to the scheduler. This method is called a 2-step context-switch and has the advantage of having a clear distinction between user code and the kernel where scheduling and other system operation happen. Obviously, this doubles the context-switch cost because threads must context-switch to an intermediate stack. The alternative 1-step context-switch uses the stack of the ``from'' thread to schedule and then context-switches directly to the ``to'' thread. However, the performance of the 2-step context-switch is still superior to a \code{pthread_yield} (see section \ref{results}). Additionally, for users in need for optimal performance, it is important to note that having a 2-step context-switch as the default does not prevent \CFA from offering a 1-step context-switch (akin to the Microsoft \code{SwitchToFiber}~\cite{switchToWindows} routine). This option is not currently present in \CFA but the changes required to add it are strictly additive.
     130As mentioned in section \ref{coroutine}, coroutines are a stepping stone for implementing threading, because they share the same mechanism for context-switching between different stacks. To improve performance and simplicity, context-switching is implemented using the following assumption: all context-switches happen inside a specific function call. This assumption means that the context-switch only has to copy the callee-saved registers onto the stack and then switch the stack registers with the ones of the target coroutine/thread. Note that the instruction pointer can be left untouched since the context-switch is always inside the same function. Threads, however, do not context-switch between each other directly. They context-switch to the scheduler. This method is called a 2-step context-switch and has the advantage of having a clear distinction between user code and the kernel where scheduling and other system operations happen. Obviously, this doubles the context-switch cost because threads must context-switch to an intermediate stack. The alternative 1-step context-switch uses the stack of the ``from'' thread to schedule and then context-switches directly to the ``to'' thread. However, the performance of the 2-step context-switch is still superior to a \code{pthread_yield} (see section \ref{results}). Additionally, for users in need for optimal performance, it is important to note that having a 2-step context-switch as the default does not prevent \CFA from offering a 1-step context-switch (akin to the Microsoft \code{SwitchToFiber}~\cite{switchToWindows} routine). This option is not currently present in \CFA but the changes required to add it are strictly additive.
    131131
    132132\subsection{Processors}
    133 Parallelism in \CFA is built around using processors to specify how much parallelism is desired. \CFA processors are object wrappers around kernel threads, specifically pthreads in the current implementation of \CFA. Indeed, any parallelism must go through operating-system libraries. However, \glspl{uthread} are still the main source of concurrency, processors are simply the underlying source of parallelism. Indeed, processor \glspl{kthread} simply fetch a \gls{uthread} from the scheduler and run it; they are effectively executers for user-threads. The main benefit of this approach is that it offers a well defined boundary between kernel code and user code, for example, kernel thread quiescing, scheduling and interrupt handling. Processors internally use coroutines to take advantage of the existing context-switching semantics.
    134 
    135 \subsection{Stack management}
    136 One of the challenges of this system is to reduce the footprint as much as possible. Specifically, all pthreads created also have a stack created with them, which should be used as much as possible. Normally, coroutines also create there own stack to run on, however, in the case of the coroutines used for processors, these coroutines run directly on the \gls{kthread} stack, effectively stealing the processor stack. The exception to this rule is the Main Processor, i.e. the initial \gls{kthread} that is given to any program. In order to respect C user-expectations, the stack of the initial kernel thread, the main stack of the program, is used by the main user thread rather than the main processor, which can grow very large
     133Parallelism in \CFA is built around using processors to specify how much parallelism is desired. \CFA processors are object wrappers around kernel threads, specifically pthreads in the current implementation of \CFA. Indeed, any parallelism must go through operating-system libraries. However, \glspl{uthread} are still the main source of concurrency, processors are simply the underlying source of parallelism. Indeed, processor \glspl{kthread} simply fetch a \gls{uthread} from the scheduler and run it; they are effectively executers for user-threads. The main benefit of this approach is that it offers a well-defined boundary between kernel code and user code, for example, kernel thread quiescing, scheduling and interrupt handling. Processors internally use coroutines to take advantage of the existing context-switching semantics.
     134
     135\subsection{Stack Management}
     136One of the challenges of this system is to reduce the footprint as much as possible. Specifically, all pthreads created also have a stack created with them, which should be used as much as possible. Normally, coroutines also create there own stack to run on, however, in the case of the coroutines used for processors, these coroutines run directly on the \gls{kthread} stack, effectively stealing the processor stack. The exception to this rule is the Main Processor, i.e. the initial \gls{kthread} that is given to any program. In order to respect C user expectations, the stack of the initial kernel thread, the main stack of the program, is used by the main user thread rather than the main processor, which can grow very large.
    137137
    138138\subsection{Preemption} \label{preemption}
    139 Finally, an important aspect for any complete threading system is preemption. As mentioned in chapter \ref{basics}, preemption introduces an extra degree of uncertainty, which enables users to have multiple threads interleave transparently, rather than having to cooperate among threads for proper scheduling and CPU distribution. Indeed, preemption is desirable because it adds a degree of isolation among threads. In a fully cooperative system, any thread that runs a long loop can starve other threads, while in a preemptive system, starvation can still occur but it does not rely on every thread having to yield or block on a regular basis, which reduces significantly a programmer burden. Obviously, preemption is not optimal for every workload, however any preemptive system can become a cooperative system by making the time-slices extremely large. Therefore, \CFA uses a preemptive threading system.
     139Finally, an important aspect for any complete threading system is preemption. As mentioned in chapter \ref{basics}, preemption introduces an extra degree of uncertainty, which enables users to have multiple threads interleave transparently, rather than having to cooperate among threads for proper scheduling and CPU distribution. Indeed, preemption is desirable because it adds a degree of isolation among threads. In a fully cooperative system, any thread that runs a long loop can starve other threads, while in a preemptive system, starvation can still occur but it does not rely on every thread having to yield or block on a regular basis, which reduces significantly a programmer burden. Obviously, preemption is not optimal for every workload. However any preemptive system can become a cooperative system by making the time slices extremely large. Therefore, \CFA uses a preemptive threading system.
    140140
    141141Preemption in \CFA is based on kernel timers, which are used to run a discrete-event simulation. Every processor keeps track of the current time and registers an expiration time with the preemption system. When the preemption system receives a change in preemption, it inserts the time in a sorted order and sets a kernel timer for the closest one, effectively stepping through preemption events on each signal sent by the timer. These timers use the Linux signal {\tt SIGALRM}, which is delivered to the process rather than the kernel-thread. This results in an implementation problem, because when delivering signals to a process, the kernel can deliver the signal to any kernel thread for which the signal is not blocked, i.e. :
     
    146146For the sake of simplicity and in order to prevent the case of having two threads receiving alarms simultaneously, \CFA programs block the {\tt SIGALRM} signal on every kernel thread except one. Now because of how involuntary context-switches are handled, the kernel thread handling {\tt SIGALRM} cannot also be a processor thread.
    147147
    148 Involuntary context-switching is done by sending signal {\tt SIGUSER1} to the corresponding proces\-sor and having the thread yield from inside the signal handler. This approach effectively context-switches away from the signal-handler back to the kernel and the signal-handler frame is eventually unwound when the thread is scheduled again. As a result, a signal-handler can start on one kernel thread and terminate on a second kernel thread (but the same user thread). It is important to note that signal-handlers save and restore signal masks because user-thread migration can cause a signal mask to migrate from one kernel thread to another. This behaviour is only a problem if all kernel threads, among which a user thread can migrate, differ in terms of signal masks\footnote{Sadly, official POSIX documentation is silent on what distinguishes ``async-signal-safe'' functions from other functions.}. However, since the kernel thread handling preemption requires a different signal mask, executing user threads on the kernel-alarm thread can cause deadlocks. For this reason, the alarm thread is in a tight loop around a system call to \code{sigwaitinfo}, requiring very little CPU time for preemption. One final detail about the alarm thread is how to wake it when additional communication is required (e.g., on thread termination). This unblocking is also done using {\tt SIGALRM}, but sent through the \code{pthread_sigqueue}. Indeed, \code{sigwait} can differentiate signals sent from \code{pthread_sigqueue} from signals sent from alarms or the kernel.
     148Involuntary context-switching is done by sending signal {\tt SIGUSER1} to the corresponding proces\-sor and having the thread yield from inside the signal handler. This approach effectively context-switches away from the signal handler back to the kernel and the signal-handler frame is eventually unwound when the thread is scheduled again. As a result, a signal-handler can start on one kernel thread and terminate on a second kernel thread (but the same user thread). It is important to note that signal-handlers save and restore signal masks because user-thread migration can cause a signal mask to migrate from one kernel thread to another. This behaviour is only a problem if all kernel threads, among which a user thread can migrate, differ in terms of signal masks\footnote{Sadly, official POSIX documentation is silent on what distinguishes ``async-signal-safe'' functions from other functions.}. However, since the kernel thread handling preemption requires a different signal mask, executing user threads on the kernel-alarm thread can cause deadlocks. For this reason, the alarm thread is in a tight loop around a system call to \code{sigwaitinfo}, requiring very little CPU time for preemption. One final detail about the alarm thread is how to wake it when additional communication is required (e.g., on thread termination). This unblocking is also done using {\tt SIGALRM}, but sent through the \code{pthread_sigqueue}. Indeed, \code{sigwait} can differentiate signals sent from \code{pthread_sigqueue} from signals sent from alarms or the kernel.
    149149
    150150\subsection{Scheduler}
     
    153153% ======================================================================
    154154% ======================================================================
    155 \section{Internal scheduling} \label{impl:intsched}
     155\section{Internal Scheduling} \label{impl:intsched}
    156156% ======================================================================
    157157% ======================================================================
     
    165165\end{figure}
    166166
    167 This picture has several components, the two most important being the entry-queue and the AS-stack. The entry-queue is an (almost) FIFO list where threads waiting to enter are parked, while the acceptor-signaler (AS) stack is a FILO list used for threads that have been signalled or otherwise marked as running next.
     167This picture has several components, the two most important being the entry queue and the AS-stack. The entry queue is an (almost) FIFO list where threads waiting to enter are parked, while the acceptor/signaller (AS) stack is a FILO list used for threads that have been signalled or otherwise marked as running next.
    168168
    169169For \CFA, this picture does not have support for blocking multiple monitors on a single condition. To support \gls{bulk-acq} two changes to this picture are required. First, it is no longer helpful to attach the condition to \emph{a single} monitor. Secondly, the thread waiting on the condition has to be separated across multiple monitors, seen in figure \ref{fig:monitor_cfa}.
     
    173173{\resizebox{0.8\textwidth}{!}{\input{int_monitor}}}
    174174\end{center}
    175 \caption{Illustration of \CFA monitor}
     175\caption{Illustration of \CFA Monitor}
    176176\label{fig:monitor_cfa}
    177177\end{figure}
    178178
    179 This picture and the proper entry and leave algorithms (see listing \ref{lst:entry2}) is the fundamental implementation of internal scheduling. Note that when a thread is moved from the condition to the AS-stack, it is conceptually split the thread into N pieces, where N is the number of monitors specified in the parameter list. The thread is woken up when all the pieces have popped from the AS-stacks and made active. In this picture, the threads are split into halves but this is only because there are two monitors. For a specific signaling operation every monitor needs a piece of thread on its AS-stack.
     179This picture and the proper entry and leave algorithms (see listing \ref{lst:entry2}) is the fundamental implementation of internal scheduling. Note that when a thread is moved from the condition to the AS-stack, it is conceptually split the thread into N pieces, where N is the number of monitors specified in the parameter list. The thread is woken up when all the pieces have popped from the AS-stacks and made active. In this picture, the threads are split into halves but this is only because there are two monitors. For a specific signalling operation every monitor needs a piece of thread on its AS-stack.
    180180
    181181\begin{figure}[b]
     
    210210\end{figure}
    211211
    212 Some important things to notice about the exit routine. The solution discussed in \ref{intsched} can be seen in the exit routine of listing \ref{lst:entry2}. Basically, the solution boils down to having a separate data structure for the condition queue and the AS-stack, and unconditionally transferring ownership of the monitors but only unblocking the thread when the last monitor has transferred ownership. This solution is deadlock safe as well as preventing any potential barging. The data structure used for the AS-stack are reused extensively for external scheduling, but in the case of internal scheduling, the data is allocated using variable-length arrays on the call-stack of the \code{wait} and \code{signal_block} routines.
     212Some important things to notice about the exit routine. The solution discussed in \ref{intsched} can be seen in the exit routine of listing \ref{lst:entry2}. Basically, the solution boils down to having a separate data structure for the condition queue and the AS-stack, and unconditionally transferring ownership of the monitors but only unblocking the thread when the last monitor has transferred ownership. This solution is deadlock safe as well as preventing any potential barging. The data structures used for the AS-stack are reused extensively for external scheduling, but in the case of internal scheduling, the data is allocated using variable-length arrays on the call stack of the \code{wait} and \code{signal_block} routines.
    213213
    214214\begin{figure}[H]
     
    220220\end{figure}
    221221
    222 Figure \ref{fig:structs} shows a high-level representation of these data-structures. The main idea behind them is that, a thread cannot contain an arbitrary number of intrusive stacks for linking onto monitor. The \code{condition node} is the data structure that is queued onto a condition variable and, when signaled, the condition queue is popped and each \code{condition criterion} are moved to the AS-stack. Once all the criterion have be popped from their respective AS-stacks, the thread is woken-up, which is what is shown in listing \ref{lst:entry2}.
    223 
    224 % ======================================================================
    225 % ======================================================================
    226 \section{External scheduling}
     222Figure \ref{fig:structs} shows a high-level representation of these data structures. The main idea behind them is that, a thread cannot contain an arbitrary number of intrusive stacks for linking onto monitor. The \code{condition node} is the data structure that is queued onto a condition variable and, when signalled, the condition queue is popped and each \code{condition criterion} is moved to the AS-stack. Once all the criteria have been popped from their respective AS-stacks, the thread is woken up, which is what is shown in listing \ref{lst:entry2}.
     223
     224% ======================================================================
     225% ======================================================================
     226\section{External Scheduling}
    227227% ======================================================================
    228228% ======================================================================
     
    232232\begin{itemize}
    233233        \item The queue of the monitor with the lowest address is no longer a true FIFO queue because threads can be moved to the front of the queue. These queues need to contain a set of monitors for each of the waiting threads. Therefore, another thread whose set contains the same lowest address monitor but different lower priority monitors may arrive first but enter the critical section after a thread with the correct pairing.
    234         \item The queue of the lowest priority monitor is both required and potentially unused. Indeed, since it is not known at compile time which monitor is the monitor with have the lowest address, every monitor needs to have the correct queues even though it is possible that some queues go unused for the entire duration of the program, for example if a monitor is only used in a specific pair.
     234        \item The queue of the lowest priority monitor is both required and potentially unused. Indeed, since it is not known at compile time which monitor is the monitor which has the lowest address, every monitor needs to have the correct queues even though it is possible that some queues go unused for the entire duration of the program, for example if a monitor is only used in a specific pair.
    235235\end{itemize}
    236236Therefore, the following modifications need to be made to support external scheduling :
     
    241241\end{itemize}
    242242
    243 \subsection{External scheduling - destructors}
    244 Finally, to support the ordering inversion of destructors, the code generation needs to be modified to use a special entry routine. This routine is needed because of the storage requirements of the call order inversion. Indeed, when waiting for the destructors, storage is need for the waiting context and the lifetime of said storage needs to outlive the waiting operation it is needed for. For regular \code{waitfor} statements, the call-stack of the routine itself matches this requirement but it is no longer the case when waiting for the destructor since it is pushed on to the AS-stack for later. The waitfor semantics can then be adjusted correspondingly, as seen in listing \ref{lst:entry-dtor}
     243\subsection{External Scheduling - Destructors}
     244Finally, to support the ordering inversion of destructors, the code generation needs to be modified to use a special entry routine. This routine is needed because of the storage requirements of the call order inversion. Indeed, when waiting for the destructors, storage is needed for the waiting context and the lifetime of said storage needs to outlive the waiting operation it is needed for. For regular \code{waitfor} statements, the call stack of the routine itself matches this requirement but it is no longer the case when waiting for the destructor since it is pushed on to the AS-stack for later. The waitfor semantics can then be adjusted correspondingly, as seen in listing \ref{lst:entry-dtor}
    245245
    246246\begin{figure}
     
    253253        continue
    254254elif matches waitfor mask
    255         push criterions to AS-stack
     255        push criteria to AS-stack
    256256        continue
    257257else
  • doc/proposals/concurrency/text/parallelism.tex

    r882ad37 r3ca540f  
    1010
    1111\section{Paradigms}
    12 \subsection{User-level threads}
    13 A direct improvement on the \gls{kthread} approach is to use \glspl{uthread}. These threads offer most of the same features that the operating system already provide but can be used on a much larger scale. This approach is the most powerful solution as it allows all the features of multi-threading, while removing several of the more expensive costs of kernel threads. The down side is that almost none of the low-level threading problems are hidden; users still have to think about data races, deadlocks and synchronization issues. These issues can be somewhat alleviated by a concurrency toolkit with strong guarantees but the parallelism toolkit offers very little to reduce complexity in itself.
     12\subsection{User-Level Threads}
     13A direct improvement on the \gls{kthread} approach is to use \glspl{uthread}. These threads offer most of the same features that the operating system already provides but can be used on a much larger scale. This approach is the most powerful solution as it allows all the features of multithreading, while removing several of the more expensive costs of kernel threads. The downside is that almost none of the low-level threading problems are hidden; users still have to think about data races, deadlocks and synchronization issues. These issues can be somewhat alleviated by a concurrency toolkit with strong guarantees but the parallelism toolkit offers very little to reduce complexity in itself.
    1414
    1515Examples of languages that support \glspl{uthread} are Erlang~\cite{Erlang} and \uC~\cite{uC++book}.
    1616
    17 \subsection{Fibers : user-level threads without preemption} \label{fibers}
    18 A popular variant of \glspl{uthread} is what is often referred to as \glspl{fiber}. However, \glspl{fiber} do not present meaningful semantical differences with \glspl{uthread}. The significant difference between \glspl{uthread} and \glspl{fiber} is the lack of \gls{preemption} in the latter. Advocates of \glspl{fiber} list their high performance and ease of implementation as majors strengths but the performance difference between \glspl{uthread} and \glspl{fiber} is controversial, and the ease of implementation, while true, is a weak argument in the context of language design. Therefore this proposal largely ignores fibers.
     17\subsection{Fibers : User-Level Threads Without Preemption} \label{fibers}
     18A popular variant of \glspl{uthread} is what is often referred to as \glspl{fiber}. However, \glspl{fiber} do not present meaningful semantic differences with \glspl{uthread}. The significant difference between \glspl{uthread} and \glspl{fiber} is the lack of \gls{preemption} in the latter. Advocates of \glspl{fiber} list their high performance and ease of implementation as major strengths but the performance difference between \glspl{uthread} and \glspl{fiber} is controversial, and the ease of implementation, while true, is a weak argument in the context of language design. Therefore this proposal largely ignores fibers.
    1919
    2020An example of a language that uses fibers is Go~\cite{Go}
    2121
    22 \subsection{Jobs and thread pools}
    23 An approach on the opposite end of the spectrum is to base parallelism on \glspl{pool}. Indeed, \glspl{pool} offer limited flexibility but at the benefit of a simpler user interface. In \gls{pool} based systems, users express parallelism as units of work, called jobs, and a dependency graph (either explicit or implicit) that tie them together. This approach means users need not worry about concurrency but significantly limit the interaction that can occur among jobs. Indeed, any \gls{job} that blocks also blocks the underlying worker, which effectively means the CPU utilization, and therefore throughput, suffers noticeably. It can be argued that a solution to this problem is to use more workers than available cores. However, unless the number of jobs and the number of workers are comparable, having a significant amount of blocked jobs always results in idles cores.
     22\subsection{Jobs and Thread Pools}
     23An approach on the opposite end of the spectrum is to base parallelism on \glspl{pool}. Indeed, \glspl{pool} offer limited flexibility but at the benefit of a simpler user interface. In \gls{pool} based systems, users express parallelism as units of work, called jobs, and a dependency graph (either explicit or implicit) that ties them together. This approach means users need not worry about concurrency but significantly limit the interaction that can occur among jobs. Indeed, any \gls{job} that blocks also block the underlying worker, which effectively means the CPU utilization, and therefore throughput, suffers noticeably. It can be argued that a solution to this problem is to use more workers than available cores. However, unless the number of jobs and the number of workers are comparable, having a significant number of blocked jobs always results in idles cores.
    2424
    2525The gold standard of this implementation is Intel's TBB library~\cite{TBB}.
    2626
    27 \subsection{Paradigm performance}
    28 While the choice between the three paradigms listed above may have significant performance implication, it is difficult to pin-down the performance implications of choosing a model at the language level. Indeed, in many situations one of these paradigms may show better performance but it all strongly depends on the workload. Having a large amount of mostly independent units of work to execute almost guarantees that the \gls{pool} based system has the best performance thanks to the lower memory overhead (i.e., no thread stack per job). However, interactions among jobs can easily exacerbate contention. User-level threads allow fine-grain context switching, which results in better resource utilization, but a context switch is more expensive and the extra control means users need to tweak more variables to get the desired performance. Finally, if the units of uninterrupted work are large enough the paradigm choice is largely amortized by the actual work done.
     27\subsection{Paradigm Performance}
     28While the choice between the three paradigms listed above may have significant performance implication, it is difficult to pin down the performance implications of choosing a model at the language level. Indeed, in many situations one of these paradigms may show better performance but it all strongly depends on the workload. Having a large amount of mostly independent units of work to execute almost guarantees that the \gls{pool}-based system has the best performance thanks to the lower memory overhead (i.e., no thread stack per job). However, interactions among jobs can easily exacerbate contention. User-level threads allow fine-grain context switching, which results in better resource utilization, but a context switch is more expensive and the extra control means users need to tweak more variables to get the desired performance. Finally, if the units of uninterrupted work are large enough the paradigm choice is largely amortized by the actual work done.
    2929
    3030\section{The \protect\CFA\ Kernel : Processors, Clusters and Threads}\label{kernel}
     
    3333\Glspl{cfacluster} have not been fully implemented in the context of this thesis, currently \CFA only supports one \gls{cfacluster}, the initial one.
    3434
    35 \subsection{Future Work: Machine setup}\label{machine}
     35\subsection{Future Work: Machine Setup}\label{machine}
    3636While this was not done in the context of this thesis, another important aspect of clusters is affinity. While many common desktop and laptop PCs have homogeneous CPUs, other devices often have more heterogeneous setups. For example, a system using \acrshort{numa} configurations may benefit from users being able to tie clusters and\/or kernel threads to certain CPU cores. OS support for CPU affinity is now common~\cite{affinityLinux, affinityWindows, affinityFreebsd, affinityNetbsd, affinityMacosx} which means it is both possible and desirable for \CFA to offer an abstraction mechanism for portable CPU affinity.
    3737
    3838\subsection{Paradigms}\label{cfaparadigms}
    39 Given these building blocks, it is possible to reproduce all three of the popular paradigms. Indeed, \glspl{uthread} is the default paradigm in \CFA. However, disabling \gls{preemption} on the \gls{cfacluster} means \glspl{cfathread} effectively become \glspl{fiber}. Since several \glspl{cfacluster} with different scheduling policy can coexist in the same application, this allows \glspl{fiber} and \glspl{uthread} to coexist in the runtime of an application. Finally, it is possible to build executors for thread pools from \glspl{uthread} or \glspl{fiber}, which includes specialize jobs like actors~\cite{Actors}.
     39Given these building blocks, it is possible to reproduce all three of the popular paradigms. Indeed, \glspl{uthread} is the default paradigm in \CFA. However, disabling \gls{preemption} on the \gls{cfacluster} means \glspl{cfathread} effectively become \glspl{fiber}. Since several \glspl{cfacluster} with different scheduling policy can coexist in the same application, this allows \glspl{fiber} and \glspl{uthread} to coexist in the runtime of an application. Finally, it is possible to build executors for thread pools from \glspl{uthread} or \glspl{fiber}, which includes specialized jobs like actors~\cite{Actors}.
  • doc/proposals/concurrency/text/results.tex

    r882ad37 r3ca540f  
    11% ======================================================================
    22% ======================================================================
    3 \chapter{Performance results} \label{results}
     3\chapter{Performance Results} \label{results}
    44% ======================================================================
    55% ======================================================================
    6 \section{Machine setup}
    7 Table \ref{tab:machine} shows the characteristics of the machine used to run the benchmarks. All tests where made on this machine.
     6\section{Machine Setup}
     7Table \ref{tab:machine} shows the characteristics of the machine used to run the benchmarks. All tests were made on this machine.
    88\begin{table}[H]
    99\begin{center}
     
    3737\end{table}
    3838
    39 \section{Micro benchmarks}
     39\section{Micro Benchmarks}
    4040All benchmarks are run using the same harness to produce the results, seen as the \code{BENCH()} macro in the following examples. This macro uses the following logic to benchmark the code :
    4141\begin{pseudo}
     
    4646        result = (after - before) / N;
    4747\end{pseudo}
    48 The method used to get time is \code{clock_gettime(CLOCK_THREAD_CPUTIME_ID);}. Each benchmark is using many iterations of a simple call to measure the cost of the call. The specific number of iteration depends on the specific benchmark.
    49 
    50 \subsection{Context-switching}
    51 The first interesting benchmark is to measure how long context-switches take. The simplest approach to do this is to yield on a thread, which executes a 2-step context switch. In order to make the comparison fair, coroutines also execute a 2-step context-switch (\gls{uthread} to \gls{kthread} then \gls{kthread} to \gls{uthread}), which is a resume/suspend cycle instead of a yield. Listing \ref{lst:ctx-switch} shows the code for coroutines and threads whith the results in table \ref{tab:ctx-switch}. All omitted tests are functionally identical to one of these tests.
     48The method used to get time is \code{clock_gettime(CLOCK_THREAD_CPUTIME_ID);}. Each benchmark is using many iterations of a simple call to measure the cost of the call. The specific number of iterations depends on the specific benchmark.
     49
     50\subsection{Context-Switching}
     51The first interesting benchmark is to measure how long context-switches take. The simplest approach to do this is to yield on a thread, which executes a 2-step context switch. In order to make the comparison fair, coroutines also execute a 2-step context-switch (\gls{uthread} to \gls{kthread} then \gls{kthread} to \gls{uthread}), which is a resume/suspend cycle instead of a yield. Listing \ref{lst:ctx-switch} shows the code for coroutines and threads with the results in table \ref{tab:ctx-switch}. All omitted tests are functionally identical to one of these tests.
    5252\begin{figure}
    5353\begin{multicols}{2}
     
    114114\end{table}
    115115
    116 \subsection{Mutual-exclusion}
    117 The next interesting benchmark is to measure the overhead to enter/leave a critical-section. For monitors, the simplest approach is to measure how long it takes to enter and leave a monitor routine. Listing \ref{lst:mutex} shows the code for \CFA. To put the results in context, the cost of entering a non-inline function and the cost of acquiring and releasing a pthread mutex lock are also measured. The results can be shown in table \ref{tab:mutex}.
     116\subsection{Mutual-Exclusion}
     117The next interesting benchmark is to measure the overhead to enter/leave a critical-section. For monitors, the simplest approach is to measure how long it takes to enter and leave a monitor routine. Listing \ref{lst:mutex} shows the code for \CFA. To put the results in context, the cost of entering a non-inline function and the cost of acquiring and releasing a pthread mutex lock is also measured. The results can be shown in table \ref{tab:mutex}.
    118118
    119119\begin{figure}
     
    156156\end{table}
    157157
    158 \subsection{Internal scheduling}
     158\subsection{Internal Scheduling}
    159159The internal-scheduling benchmark measures the cost of waiting on and signalling a condition variable. Listing \ref{lst:int-sched} shows the code for \CFA, with results table \ref{tab:int-sched}. As with all other benchmarks, all omitted tests are functionally identical to one of these tests.
    160160
     
    211211\end{table}
    212212
    213 \subsection{External scheduling}
     213\subsection{External Scheduling}
    214214The Internal scheduling benchmark measures the cost of the \code{waitfor} statement (\code{_Accept} in \uC). Listing \ref{lst:ext-sched} shows the code for \CFA, with results in table \ref{tab:ext-sched}. As with all other benchmarks, all omitted tests are functionally identical to one of these tests.
    215215
     
    264264\end{table}
    265265
    266 \subsection{Object creation}
    267 Finally, the last benchmark measurs the cost of creation for concurrent objects. Listing \ref{lst:creation} shows the code for pthreads and \CFA threads, with results shown in table \ref{tab:creation}. As with all other benchmarks, all omitted tests are functionally identical to one of these tests. The only note here is that the call-stacks of \CFA coroutines are lazily created, therefore without priming the coroutine, the creation cost is very low.
     266\subsection{Object Creation}
     267Finally, the last benchmark measures the cost of creation for concurrent objects. Listing \ref{lst:creation} shows the code for pthreads and \CFA threads, with results shown in table \ref{tab:creation}. As with all other benchmarks, all omitted tests are functionally identical to one of these tests. The only note here is that the call stacks of \CFA coroutines are lazily created, therefore without priming the coroutine, the creation cost is very low.
    268268
    269269\begin{figure}
     
    327327\end{tabular}
    328328\end{center}
    329 \caption{Creation comparison. All numbers are in nanoseconds(\si{\nano\second})}
     329\caption{Creation comparison. All numbers are in nanoseconds(\si{\nano\second}).}
    330330\label{tab:creation}
    331331\end{table}
  • doc/proposals/concurrency/text/together.tex

    r882ad37 r3ca540f  
    11% ======================================================================
    22% ======================================================================
    3 \chapter{Putting it all together}
     3\chapter{Putting It All Together}
    44% ======================================================================
    55% ======================================================================
    66
    77
    8 \section{Threads as monitors}
     8\section{Threads As Monitors}
    99As it was subtly alluded in section \ref{threads}, \code{thread}s in \CFA are in fact monitors, which means that all monitor features are available when using threads. For example, here is a very simple two thread pipeline that could be used for a simulator of a game engine :
    1010\begin{figure}[H]
  • doc/proposals/concurrency/version

    r882ad37 r3ca540f  
    1 0.11.280
     10.11.299
  • src/CodeGen/FixNames.cc

    r882ad37 r3ca540f  
    1919#include <string>                  // for string, operator!=, operator==
    2020
     21#include "Common/PassVisitor.h"
    2122#include "Common/SemanticError.h"  // for SemanticError
    2223#include "FixMain.h"               // for FixMain
     
    3233
    3334namespace CodeGen {
    34         class FixNames : public Visitor {
     35        class FixNames : public WithGuards {
    3536          public:
    36                 virtual void visit( ObjectDecl *objectDecl );
    37                 virtual void visit( FunctionDecl *functionDecl );
     37                void postvisit( ObjectDecl *objectDecl );
     38                void postvisit( FunctionDecl *functionDecl );
    3839
    39                 virtual void visit( CompoundStmt *compoundStmt );
     40                void previsit( CompoundStmt *compoundStmt );
    4041          private:
    4142                int scopeLevel = 1;
     
    9394        }
    9495
    95         void fixNames( std::list< Declaration* > translationUnit ) {
    96                 FixNames fixer;
     96        void fixNames( std::list< Declaration* > & translationUnit ) {
     97                PassVisitor<FixNames> fixer;
    9798                acceptAll( translationUnit, fixer );
    9899        }
    99100
    100         void FixNames::fixDWT( DeclarationWithType *dwt ) {
     101        void FixNames::fixDWT( DeclarationWithType * dwt ) {
    101102                if ( dwt->get_name() != "" ) {
    102103                        if ( LinkageSpec::isMangled( dwt->get_linkage() ) ) {
     
    107108        }
    108109
    109         void FixNames::visit( ObjectDecl *objectDecl ) {
    110                 Visitor::visit( objectDecl );
     110        void FixNames::postvisit( ObjectDecl * objectDecl ) {
    111111                fixDWT( objectDecl );
    112112        }
    113113
    114         void FixNames::visit( FunctionDecl *functionDecl ) {
    115                 Visitor::visit( functionDecl );
     114        void FixNames::postvisit( FunctionDecl * functionDecl ) {
    116115                fixDWT( functionDecl );
    117116
     
    121120                                throw SemanticError("Main expected to have 0, 2 or 3 arguments\n", functionDecl);
    122121                        }
    123                         functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new ConstantExpr( Constant::from_int( 0 ) ) ) );
     122                        functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( new ConstantExpr( Constant::from_int( 0 ) ) ) );
    124123                        CodeGen::FixMain::registerMain( functionDecl );
    125124                }
    126125        }
    127126
    128         void FixNames::visit( CompoundStmt *compoundStmt ) {
     127        void FixNames::previsit( CompoundStmt * ) {
    129128                scopeLevel++;
    130                 Visitor::visit( compoundStmt );
    131                 scopeLevel--;
     129                GuardAction( [this](){ scopeLevel--; } );
    132130        }
    133131} // namespace CodeGen
  • src/CodeGen/FixNames.h

    r882ad37 r3ca540f  
    55// file "LICENCE" distributed with Cforall.
    66//
    7 // FixNames.h -- 
     7// FixNames.h --
    88//
    99// Author           : Richard C. Bilson
     
    2222namespace CodeGen {
    2323        /// mangles object and function names
    24         void fixNames( std::list< Declaration* > translationUnit );
     24        void fixNames( std::list< Declaration* > & translationUnit );
    2525} // namespace CodeGen
    2626
  • src/Common/PassVisitor.impl.h

    r882ad37 r3ca540f  
    5555                it,
    5656                [](Declaration * decl) -> auto {
    57                         return new DeclStmt( noLabels, decl );
     57                        return new DeclStmt( decl );
    5858                }
    5959        );
     
    251251            || ( empty( beforeDecls ) && empty( afterDecls )) );
    252252
    253         CompoundStmt *compound = new CompoundStmt( noLabels );
     253        CompoundStmt *compound = new CompoundStmt();
    254254        if( !empty(beforeDecls) ) { splice( std::back_inserter( compound->get_kids() ), beforeDecls ); }
    255255        if( !empty(beforeStmts) ) { compound->get_kids().splice( compound->get_kids().end(), *beforeStmts ); }
  • src/Concurrency/Keywords.cc

    r882ad37 r3ca540f  
    3838
    3939namespace Concurrency {
    40 
    41         namespace {
    42                 const std::list<Label> noLabels;
    43                 const std::list< Attribute * > noAttributes;
    44                 Type::StorageClasses noStorage;
    45                 Type::Qualifiers noQualifiers;
    46         }
    47 
    4840        //=============================================================================================
    4941        // Pass declarations
     
    296288                ObjectDecl * this_decl = new ObjectDecl(
    297289                        "this",
    298                         noStorage,
     290                        noStorageClasses,
    299291                        LinkageSpec::Cforall,
    300292                        nullptr,
     
    313305                        new ObjectDecl(
    314306                                "ret",
    315                                 noStorage,
     307                                noStorageClasses,
    316308                                LinkageSpec::Cforall,
    317309                                nullptr,
     
    346338                        main_decl = new FunctionDecl(
    347339                                "main",
    348                                 noStorage,
     340                                noStorageClasses,
    349341                                LinkageSpec::Cforall,
    350342                                main_type,
     
    363355                ObjectDecl * field = new ObjectDecl(
    364356                        field_name,
    365                         noStorage,
     357                        noStorageClasses,
    366358                        LinkageSpec::Cforall,
    367359                        nullptr,
     
    379371
    380372        void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) {
    381                 CompoundStmt * statement = new CompoundStmt( noLabels );
     373                CompoundStmt * statement = new CompoundStmt();
    382374                statement->push_back(
    383375                        new ReturnStmt(
    384                                 noLabels,
    385376                                new AddressExpr(
    386377                                        new MemberExpr(
     
    488479                ObjectDecl * monitors = new ObjectDecl(
    489480                        "__monitor",
    490                         noStorage,
     481                        noStorageClasses,
    491482                        LinkageSpec::Cforall,
    492483                        nullptr,
     
    509500                // monitor_guard_t __guard = { __monitors, #, func };
    510501                body->push_front(
    511                         new DeclStmt( noLabels, new ObjectDecl(
     502                        new DeclStmt( new ObjectDecl(
    512503                                "__guard",
    513                                 noStorage,
     504                                noStorageClasses,
    514505                                LinkageSpec::Cforall,
    515506                                nullptr,
     
    530521
    531522                //monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) };
    532                 body->push_front( new DeclStmt( noLabels, monitors) );
     523                body->push_front( new DeclStmt( monitors) );
    533524        }
    534525
     
    536527                ObjectDecl * monitors = new ObjectDecl(
    537528                        "__monitors",
    538                         noStorage,
     529                        noStorageClasses,
    539530                        LinkageSpec::Cforall,
    540531                        nullptr,
     
    569560                // monitor_guard_t __guard = { __monitors, #, func };
    570561                body->push_front(
    571                         new DeclStmt( noLabels, new ObjectDecl(
     562                        new DeclStmt( new ObjectDecl(
    572563                                "__guard",
    573                                 noStorage,
     564                                noStorageClasses,
    574565                                LinkageSpec::Cforall,
    575566                                nullptr,
     
    591582
    592583                //monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) };
    593                 body->push_front( new DeclStmt( noLabels, monitors) );
     584                body->push_front( new DeclStmt( monitors) );
    594585        }
    595586
     
    631622                stmt->push_back(
    632623                        new ExprStmt(
    633                                 noLabels,
    634624                                new UntypedExpr(
    635625                                        new NameExpr( "__thrd_start" ),
  • src/Concurrency/Waitfor.cc

    r882ad37 r3ca540f  
    100100
    101101namespace Concurrency {
    102 
    103         namespace {
    104                 const std::list<Label> noLabels;
    105                 const std::list< Attribute * > noAttributes;
    106                 Type::StorageClasses noStorage;
    107                 Type::Qualifiers noQualifiers;
    108         }
    109 
    110102        //=============================================================================================
    111103        // Pass declarations
     
    203195                        ResolvExpr::findVoidExpression( expr, indexer );
    204196
    205                         return new ExprStmt( noLabels, expr );
     197                        return new ExprStmt( expr );
    206198                }
    207199
     
    259251                if( !decl_monitor || !decl_acceptable || !decl_mask ) throw SemanticError( "waitfor keyword requires monitors to be in scope, add #include <monitor>", waitfor );
    260252
    261                 CompoundStmt * stmt = new CompoundStmt( noLabels );
     253                CompoundStmt * stmt = new CompoundStmt();
    262254
    263255                ObjectDecl * acceptables = declare( waitfor->clauses.size(), stmt );
     
    281273                );
    282274
    283                 CompoundStmt * compound = new CompoundStmt( noLabels );
     275                CompoundStmt * compound = new CompoundStmt();
    284276                stmt->push_back( new IfStmt(
    285                         noLabels,
    286277                        safeCond( new VariableExpr( flag ) ),
    287278                        compound,
     
    313304                );
    314305
    315                 stmt->push_back( new DeclStmt( noLabels, acceptables) );
     306                stmt->push_back( new DeclStmt( acceptables) );
    316307
    317308                Expression * set = new UntypedExpr(
     
    326317                ResolvExpr::findVoidExpression( set, indexer );
    327318
    328                 stmt->push_back( new ExprStmt( noLabels, set ) );
     319                stmt->push_back( new ExprStmt( set ) );
    329320
    330321                return acceptables;
     
    341332                );
    342333
    343                 stmt->push_back( new DeclStmt( noLabels, flag) );
     334                stmt->push_back( new DeclStmt( flag) );
    344335
    345336                return flag;
     
    357348                ResolvExpr::findVoidExpression( expr, indexer );
    358349
    359                 return new ExprStmt( noLabels, expr );
     350                return new ExprStmt( expr );
    360351        }
    361352
     
    399390                );
    400391
    401                 stmt->push_back( new DeclStmt( noLabels, mon) );
     392                stmt->push_back( new DeclStmt( mon) );
    402393
    403394                return mon;
     
    411402
    412403                stmt->push_back( new IfStmt(
    413                         noLabels,
    414404                        safeCond( clause.condition ),
    415405                        new CompoundStmt({
     
    447437                );
    448438
    449                 stmt->push_back( new DeclStmt( noLabels, timeout ) );
     439                stmt->push_back( new DeclStmt( timeout ) );
    450440
    451441                if( time ) {
    452442                        stmt->push_back( new IfStmt(
    453                                 noLabels,
    454443                                safeCond( time_cond ),
    455444                                new CompoundStmt({
    456445                                        new ExprStmt(
    457                                                 noLabels,
    458446                                                makeOpAssign(
    459447                                                        new VariableExpr( timeout ),
     
    471459                if( has_else ) {
    472460                        stmt->push_back( new IfStmt(
    473                                 noLabels,
    474461                                safeCond( else_cond ),
    475462                                new CompoundStmt({
    476463                                        new ExprStmt(
    477                                                 noLabels,
    478464                                                makeOpAssign(
    479465                                                        new VariableExpr( timeout ),
     
    511497                );
    512498
    513                 stmt->push_back( new DeclStmt( noLabels, index ) );
     499                stmt->push_back( new DeclStmt( index ) );
    514500
    515501                ObjectDecl * mask = ObjectDecl::newObject(
     
    526512                );
    527513
    528                 stmt->push_back( new DeclStmt( noLabels, mask ) );
     514                stmt->push_back( new DeclStmt( mask ) );
    529515
    530516                stmt->push_back( new ExprStmt(
    531                         noLabels,
    532517                        new ApplicationExpr(
    533518                                VariableExpr::functionPointer( decl_waitfor ),
     
    557542        ) {
    558543                SwitchStmt * swtch = new SwitchStmt(
    559                         noLabels,
    560544                        result,
    561545                        std::list<Statement *>()
     
    566550                        swtch->statements.push_back(
    567551                                new CaseStmt(
    568                                         noLabels,
    569552                                        new ConstantExpr( Constant::from_ulong( i++ ) ),
    570553                                        {
    571554                                                clause.statement,
    572555                                                new BranchStmt(
    573                                                         noLabels,
    574556                                                        "",
    575557                                                        BranchStmt::Break
     
    583565                        swtch->statements.push_back(
    584566                                new CaseStmt(
    585                                         noLabels,
    586567                                        new ConstantExpr( Constant::from_int( -2 ) ),
    587568                                        {
    588569                                                waitfor->timeout.statement,
    589570                                                new BranchStmt(
    590                                                         noLabels,
    591571                                                        "",
    592572                                                        BranchStmt::Break
     
    600580                        swtch->statements.push_back(
    601581                                new CaseStmt(
    602                                         noLabels,
    603582                                        new ConstantExpr( Constant::from_int( -1 ) ),
    604583                                        {
    605584                                                waitfor->orelse.statement,
    606585                                                new BranchStmt(
    607                                                         noLabels,
    608586                                                        "",
    609587                                                        BranchStmt::Break
  • src/ControlStruct/ExceptTranslate.cc

    r882ad37 r3ca540f  
    3030#include "SynTree/Expression.h"       // for UntypedExpr, ConstantExpr, Name...
    3131#include "SynTree/Initializer.h"      // for SingleInit, ListInit
    32 #include "SynTree/Label.h"            // for Label, noLabels
     32#include "SynTree/Label.h"            // for Label
    3333#include "SynTree/Mutator.h"          // for mutateAll
    3434#include "SynTree/Statement.h"        // for CompoundStmt, CatchStmt, ThrowStmt
     
    5757
    5858        void appendDeclStmt( CompoundStmt * block, Declaration * item ) {
    59                 block->push_back(new DeclStmt(noLabels, item));
     59                block->push_back(new DeclStmt(item));
    6060        }
    6161
     
    205205                throwStmt->set_expr( nullptr );
    206206                delete throwStmt;
    207                 return new ExprStmt( noLabels, call );
     207                return new ExprStmt( call );
    208208        }
    209209
     
    211211                        ThrowStmt *throwStmt ) {
    212212                // __throw_terminate( `throwStmt->get_name()` ); }
    213                 return create_given_throw( "__cfaehm__throw_terminate", throwStmt );
     213                return create_given_throw( "__cfaabi_ehm__throw_terminate", throwStmt );
    214214        }
    215215
     
    220220                assert( handler_except_decl );
    221221
    222                 CompoundStmt * result = new CompoundStmt( throwStmt->get_labels() );
    223                 result->push_back( new ExprStmt( noLabels, UntypedExpr::createAssign(
     222                CompoundStmt * result = new CompoundStmt();
     223                result->labels =  throwStmt->labels;
     224                result->push_back( new ExprStmt( UntypedExpr::createAssign(
    224225                        nameOf( handler_except_decl ),
    225226                        new ConstantExpr( Constant::null(
     
    231232                        ) ) );
    232233                result->push_back( new ExprStmt(
    233                         noLabels,
    234                         new UntypedExpr( new NameExpr( "__cfaehm__rethrow_terminate" ) )
     234                        new UntypedExpr( new NameExpr( "__cfaabi_ehm__rethrow_terminate" ) )
    235235                        ) );
    236236                delete throwStmt;
     
    241241                        ThrowStmt *throwStmt ) {
    242242                // __throw_resume( `throwStmt->get_name` );
    243                 return create_given_throw( "__cfaehm__throw_resume", throwStmt );
     243                return create_given_throw( "__cfaabi_ehm__throw_resume", throwStmt );
    244244        }
    245245
     
    248248                // return false;
    249249                Statement * result = new ReturnStmt(
    250                         throwStmt->get_labels(),
    251250                        new ConstantExpr( Constant::from_bool( false ) )
    252251                        );
     252                result->labels = throwStmt->labels;
    253253                delete throwStmt;
    254254                return result;
     
    291291                        // }
    292292                        // return;
    293                         CompoundStmt * block = new CompoundStmt( noLabels );
     293                        CompoundStmt * block = new CompoundStmt();
    294294
    295295                        // Just copy the exception value. (Post Validation)
     
    304304                                        ) })
    305305                                );
    306                         block->push_back( new DeclStmt( noLabels, local_except ) );
     306                        block->push_back( new DeclStmt( local_except ) );
    307307
    308308                        // Add the cleanup attribute.
    309309                        local_except->get_attributes().push_back( new Attribute(
    310310                                "cleanup",
    311                                 { new NameExpr( "__cfaehm__cleanup_terminate" ) }
     311                                { new NameExpr( "__cfaabi_ehm__cleanup_terminate" ) }
    312312                                ) );
    313313
     
    324324
    325325                        std::list<Statement *> caseBody
    326                                         { block, new ReturnStmt( noLabels, nullptr ) };
     326                                        { block, new ReturnStmt( nullptr ) };
    327327                        handler_wrappers.push_back( new CaseStmt(
    328                                 noLabels,
    329328                                new ConstantExpr( Constant::from_int( index ) ),
    330329                                caseBody
     
    340339
    341340                SwitchStmt * handler_lookup = new SwitchStmt(
    342                         noLabels,
    343341                        nameOf( index_obj ),
    344342                        stmt_handlers
    345343                        );
    346                 CompoundStmt * body = new CompoundStmt( noLabels );
     344                CompoundStmt * body = new CompoundStmt();
    347345                body->push_back( handler_lookup );
    348346
     
    363361                // }
    364362
    365                 CompoundStmt * block = new CompoundStmt( noLabels );
     363                CompoundStmt * block = new CompoundStmt();
    366364
    367365                // Local Declaration
     
    369367                        dynamic_cast<ObjectDecl *>( modded_handler->get_decl() );
    370368                assert( local_except );
    371                 block->push_back( new DeclStmt( noLabels, local_except ) );
     369                block->push_back( new DeclStmt( local_except ) );
    372370
    373371                // Check for type match.
     
    381379                }
    382380                // Construct the match condition.
    383                 block->push_back( new IfStmt( noLabels,
     381                block->push_back( new IfStmt(
    384382                        cond, modded_handler->get_body(), nullptr ) );
    385383
     
    397395                // }
    398396
    399                 CompoundStmt * body = new CompoundStmt( noLabels );
     397                CompoundStmt * body = new CompoundStmt();
    400398
    401399                FunctionType * func_type = match_func_t.clone();
     
    413411
    414412                        // Create new body.
    415                         handler->set_body( new ReturnStmt( noLabels,
     413                        handler->set_body( new ReturnStmt(
    416414                                new ConstantExpr( Constant::from_int( index ) ) ) );
    417415
     
    421419                }
    422420
    423                 body->push_back( new ReturnStmt( noLabels,
     421                body->push_back( new ReturnStmt(
    424422                        new ConstantExpr( Constant::from_int( 0 ) ) ) );
    425423
     
    432430                        FunctionDecl * terminate_catch,
    433431                        FunctionDecl * terminate_match ) {
    434                 // { __cfaehm__try_terminate(`try`, `catch`, `match`); }
     432                // { __cfaabi_ehm__try_terminate(`try`, `catch`, `match`); }
    435433
    436434                UntypedExpr * caller = new UntypedExpr( new NameExpr(
    437                         "__cfaehm__try_terminate" ) );
     435                        "__cfaabi_ehm__try_terminate" ) );
    438436                std::list<Expression *>& args = caller->get_args();
    439437                args.push_back( nameOf( try_wrapper ) );
     
    441439                args.push_back( nameOf( terminate_match ) );
    442440
    443                 CompoundStmt * callStmt = new CompoundStmt( noLabels );
    444                 callStmt->push_back( new ExprStmt( noLabels, caller ) );
     441                CompoundStmt * callStmt = new CompoundStmt();
     442                callStmt->push_back( new ExprStmt( caller ) );
    445443                return callStmt;
    446444        }
     
    451449                //     HANDLER WRAPPERS { `hander->body`; return true; }
    452450                // }
    453                 CompoundStmt * body = new CompoundStmt( noLabels );
     451                CompoundStmt * body = new CompoundStmt();
    454452
    455453                FunctionType * func_type = handle_func_t.clone();
     
    464462                                dynamic_cast<CompoundStmt*>( handler->get_body() );
    465463                        if ( ! handling_code ) {
    466                                 handling_code = new CompoundStmt( noLabels );
     464                                handling_code = new CompoundStmt();
    467465                                handling_code->push_back( handler->get_body() );
    468466                        }
    469                         handling_code->push_back( new ReturnStmt( noLabels,
     467                        handling_code->push_back( new ReturnStmt(
    470468                                new ConstantExpr( Constant::from_bool( true ) ) ) );
    471469                        handler->set_body( handling_code );
     
    476474                }
    477475
    478                 body->push_back( new ReturnStmt( noLabels,
     476                body->push_back( new ReturnStmt(
    479477                        new ConstantExpr( Constant::from_bool( false ) ) ) );
    480478
     
    486484                        Statement * wraps,
    487485                        FunctionDecl * resume_handler ) {
    488                 CompoundStmt * body = new CompoundStmt( noLabels );
     486                CompoundStmt * body = new CompoundStmt();
    489487
    490488                // struct __try_resume_node __resume_node
    491                 //      __attribute__((cleanup( __cfaehm__try_resume_cleanup )));
     489                //      __attribute__((cleanup( __cfaabi_ehm__try_resume_cleanup )));
    492490                // ** unwinding of the stack here could cause problems **
    493491                // ** however I don't think that can happen currently **
    494                 // __cfaehm__try_resume_setup( &__resume_node, resume_handler );
     492                // __cfaabi_ehm__try_resume_setup( &__resume_node, resume_handler );
    495493
    496494                std::list< Attribute * > attributes;
     
    498496                        std::list< Expression * > attr_params;
    499497                        attr_params.push_back( new NameExpr(
    500                                 "__cfaehm__try_resume_cleanup" ) );
     498                                "__cfaabi_ehm__try_resume_cleanup" ) );
    501499                        attributes.push_back( new Attribute( "cleanup", attr_params ) );
    502500                }
     
    517515
    518516                UntypedExpr *setup = new UntypedExpr( new NameExpr(
    519                         "__cfaehm__try_resume_setup" ) );
     517                        "__cfaabi_ehm__try_resume_setup" ) );
    520518                setup->get_args().push_back( new AddressExpr( nameOf( obj ) ) );
    521519                setup->get_args().push_back( nameOf( resume_handler ) );
    522520
    523                 body->push_back( new ExprStmt( noLabels, setup ) );
     521                body->push_back( new ExprStmt( setup ) );
    524522
    525523                body->push_back( wraps );
     
    542540        ObjectDecl * ExceptionMutatorCore::create_finally_hook(
    543541                        FunctionDecl * finally_wrapper ) {
    544                 // struct __cfaehm__cleanup_hook __finally_hook
     542                // struct __cfaabi_ehm__cleanup_hook __finally_hook
    545543                //      __attribute__((cleanup( finally_wrapper )));
    546544
     
    596594                        // Skip children?
    597595                        return;
    598                 } else if ( structDecl->get_name() == "__cfaehm__base_exception_t" ) {
     596                } else if ( structDecl->get_name() == "__cfaabi_ehm__base_exception_t" ) {
    599597                        assert( nullptr == except_decl );
    600598                        except_decl = structDecl;
    601599                        init_func_types();
    602                 } else if ( structDecl->get_name() == "__cfaehm__try_resume_node" ) {
     600                } else if ( structDecl->get_name() == "__cfaabi_ehm__try_resume_node" ) {
    603601                        assert( nullptr == node_decl );
    604602                        node_decl = structDecl;
    605                 } else if ( structDecl->get_name() == "__cfaehm__cleanup_hook" ) {
     603                } else if ( structDecl->get_name() == "__cfaabi_ehm__cleanup_hook" ) {
    606604                        assert( nullptr == hook_decl );
    607605                        hook_decl = structDecl;
     
    646644                // Generate a prefix for the function names?
    647645
    648                 CompoundStmt * block = new CompoundStmt( noLabels );
     646                CompoundStmt * block = new CompoundStmt();
    649647                CompoundStmt * inner = take_try_block( tryStmt );
    650648
  • src/ControlStruct/ForExprMutator.cc

    r882ad37 r3ca540f  
    2929                // Create compound statement, move initializers outside,
    3030                // the resut of the original stays as is.
    31                 CompoundStmt *block = new CompoundStmt( std::list< Label >() );
     31                CompoundStmt *block = new CompoundStmt();
    3232                std::list<Statement *> &stmts = block->get_kids();
    3333                stmts.splice( stmts.end(), init );
  • src/ControlStruct/LabelFixer.cc

    r882ad37 r3ca540f  
    3737        }
    3838
    39         void LabelFixer::visit( FunctionDecl *functionDecl ) {
     39        void LabelFixer::previsit( FunctionDecl * ) {
    4040                // need to go into a nested function in a fresh state
    41                 std::map < Label, Entry *> oldLabelTable = labelTable;
     41                GuardValue( labelTable );
    4242                labelTable.clear();
     43        }
    4344
    44                 maybeAccept( functionDecl->get_statements(), *this );
    45 
     45        void LabelFixer::postvisit( FunctionDecl * functionDecl ) {
    4646                MLEMutator mlemut( resolveJumps(), generator );
    4747                functionDecl->acceptMutator( mlemut );
    48 
    49                 // and remember the outer function's labels when
    50                 // returning to it
    51                 labelTable = oldLabelTable;
    5248        }
    5349
    5450        // prune to at most one label definition for each statement
    55         void LabelFixer::visit( Statement *stmt ) {
     51        void LabelFixer::previsit( Statement *stmt ) {
    5652                std::list< Label > &labels = stmt->get_labels();
    5753
     
    6258        }
    6359
    64         void LabelFixer::visit( BranchStmt *branchStmt ) {
    65                 visit ( ( Statement * )branchStmt );
     60        void LabelFixer::previsit( BranchStmt *branchStmt ) {
     61                previsit( ( Statement *)branchStmt );
    6662
    6763                // for labeled branches, add an entry to the label table
     
    7268        }
    7369
    74         void LabelFixer::visit( UntypedExpr *untyped ) {
    75                 if ( NameExpr * func = dynamic_cast< NameExpr * >( untyped->get_function() ) ) {
    76                         if ( func->get_name() == "&&" ) {
    77                                 NameExpr * arg = dynamic_cast< NameExpr * >( untyped->get_args().front() );
    78                                 Label target = arg->get_name();
    79                                 assert( target != "" );
    80                                 setLabelsUsg( target, untyped );
    81                         } else {
    82                                 Visitor::visit( untyped );
    83                         }
    84                 }
     70        void LabelFixer::previsit( LabelAddressExpr * addrExpr ) {
     71                Label & target = addrExpr->arg;
     72                assert( target != "" );
     73                setLabelsUsg( target, addrExpr );
    8574        }
    8675
  • src/ControlStruct/LabelFixer.h

    r882ad37 r3ca540f  
    1919#include <map>                     // for map
    2020
     21#include "Common/PassVisitor.h"
    2122#include "Common/SemanticError.h"  // for SemanticError
    2223#include "SynTree/Label.h"         // for Label
     
    2627namespace ControlStruct {
    2728        /// normalizes label definitions and generates multi-level exit labels
    28 class LabelGenerator;
     29        class LabelGenerator;
    2930
    30         class LabelFixer final : public Visitor {
    31                 typedef Visitor Parent;
     31        class LabelFixer final : public WithGuards {
    3232          public:
    3333                LabelFixer( LabelGenerator *gen = 0 );
     
    3535                std::map < Label, Statement * > *resolveJumps() throw ( SemanticError );
    3636
    37                 using Visitor::visit;
    38 
    3937                // Declarations
    40                 virtual void visit( FunctionDecl *functionDecl ) override;
     38                void previsit( FunctionDecl *functionDecl );
     39                void postvisit( FunctionDecl *functionDecl );
    4140
    4241                // Statements
    43                 void visit( Statement *stmt );
     42                void previsit( Statement *stmt );
     43                void previsit( BranchStmt *branchStmt );
    4444
    45                 virtual void visit( CompoundStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    46                 virtual void visit( NullStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    47                 virtual void visit( ExprStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    48                 virtual void visit( IfStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    49                 virtual void visit( WhileStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    50                 virtual void visit( ForStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    51                 virtual void visit( SwitchStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    52                 virtual void visit( CaseStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    53                 virtual void visit( ReturnStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    54                 virtual void visit( TryStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    55                 virtual void visit( CatchStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    56                 virtual void visit( DeclStmt *stmt ) override { visit( (Statement *)stmt ); return Parent::visit( stmt ); }
    57                 virtual void visit( BranchStmt *branchStmt ) override;
    58                 virtual void visit( UntypedExpr *untyped ) override;
     45                // Expressions
     46                void previsit( LabelAddressExpr *addrExpr );
    5947
    6048                Label setLabelsDef( std::list< Label > &, Statement *definition );
  • src/ControlStruct/MLEMutator.cc

    r882ad37 r3ca540f  
    149149
    150150                        if ( CaseStmt * c = dynamic_cast< CaseStmt * >( statements.back() ) ) {
    151                                 std::list<Label> temp; temp.push_back( brkLabel );
    152                                 c->get_statements().push_back( new BranchStmt( temp, Label("brkLabel"), BranchStmt::Break ) );
     151                                Statement * stmt = new BranchStmt( Label("brkLabel"), BranchStmt::Break );
     152                                stmt->labels.push_back( brkLabel );
     153                                c->get_statements().push_back( stmt );
    153154                        } else assert(0); // as of this point, all statements of a switch are still CaseStmts
    154155                } // if
     
    232233                // transform break/continue statements into goto to simplify later handling of branches
    233234                delete branchStmt;
    234                 return new BranchStmt( std::list<Label>(), exitLabel, BranchStmt::Goto );
     235                return new BranchStmt( exitLabel, BranchStmt::Goto );
    235236        }
    236237
     
    239240                CompoundStmt *newBody;
    240241                if ( ! (newBody = dynamic_cast<CompoundStmt *>( bodyLoop )) ) {
    241                         newBody = new CompoundStmt( std::list< Label >() );
     242                        newBody = new CompoundStmt();
    242243                        newBody->get_kids().push_back( bodyLoop );
    243244                } // if
  • src/ControlStruct/Mutate.cc

    r882ad37 r3ca540f  
    2424#include "SynTree/Declaration.h"   // for Declaration
    2525#include "SynTree/Mutator.h"       // for mutateAll
    26 //#include "ExceptMutator.h"
    2726
    2827#include "Common/PassVisitor.h"    // for PassVisitor
     
    3736
    3837                // normalizes label definitions and generates multi-level exit labels
    39                 LabelFixer lfix;
    40 
    41                 //ExceptMutator exc;
     38                PassVisitor<LabelFixer> lfix;
    4239
    4340                mutateAll( translationUnit, formut );
    4441                acceptAll( translationUnit, lfix );
    45                 //mutateAll( translationUnit, exc );
    4642        }
    4743} // namespace CodeGen
  • src/GenPoly/Box.cc

    r882ad37 r3ca540f  
    4949#include "SynTree/Expression.h"          // for ApplicationExpr, UntypedExpr
    5050#include "SynTree/Initializer.h"         // for SingleInit, Initializer, Lis...
    51 #include "SynTree/Label.h"               // for Label, noLabels
     51#include "SynTree/Label.h"               // for Label
    5252#include "SynTree/Mutator.h"             // for maybeMutate, Mutator, mutateAll
    5353#include "SynTree/Statement.h"           // for ExprStmt, DeclStmt, ReturnStmt
     
    293293                FunctionDecl *layoutDecl = new FunctionDecl( layoutofName( typeDecl ),
    294294                                                                                                         functionNesting > 0 ? Type::StorageClasses() : Type::StorageClasses( Type::Static ),
    295                                                                                                          LinkageSpec::AutoGen, layoutFnType, new CompoundStmt( noLabels ),
     295                                                                                                         LinkageSpec::AutoGen, layoutFnType, new CompoundStmt(),
    296296                                                                                                         std::list< Attribute * >(), Type::FuncSpecifiers( Type::Inline ) );
    297297                layoutDecl->fixUniqueId();
     
    321321        /// makes an if-statement with a single-expression if-block and no then block
    322322        Statement *makeCond( Expression *cond, Expression *ifPart ) {
    323                 return new IfStmt( noLabels, cond, new ExprStmt( noLabels, ifPart ), 0 );
     323                return new IfStmt( cond, new ExprStmt( ifPart ), 0 );
    324324        }
    325325
     
    340340        /// adds an expression to a compound statement
    341341        void addExpr( CompoundStmt *stmts, Expression *expr ) {
    342                 stmts->get_kids().push_back( new ExprStmt( noLabels, expr ) );
     342                stmts->get_kids().push_back( new ExprStmt( expr ) );
    343343        }
    344344
     
    629629                ObjectDecl *Pass1::makeTemporary( Type *type ) {
    630630                        ObjectDecl *newObj = new ObjectDecl( tempNamer.newName(), Type::StorageClasses(), LinkageSpec::C, 0, type, 0 );
    631                         stmtsToAddBefore.push_back( new DeclStmt( noLabels, newObj ) );
     631                        stmtsToAddBefore.push_back( new DeclStmt( newObj ) );
    632632                        return newObj;
    633633                }
     
    740740                                ObjectDecl *newObj = ObjectDecl::newObject( tempNamer.newName(), newType, nullptr );
    741741                                newObj->get_type()->get_qualifiers() = Type::Qualifiers(); // TODO: is this right???
    742                                 stmtsToAddBefore.push_back( new DeclStmt( noLabels, newObj ) );
     742                                stmtsToAddBefore.push_back( new DeclStmt( newObj ) );
    743743                                UntypedExpr *assign = new UntypedExpr( new NameExpr( "?=?" ) ); // TODO: why doesn't this just use initialization syntax?
    744744                                assign->get_args().push_back( new VariableExpr( newObj ) );
    745745                                assign->get_args().push_back( arg );
    746                                 stmtsToAddBefore.push_back( new ExprStmt( noLabels, assign ) );
     746                                stmtsToAddBefore.push_back( new ExprStmt( assign ) );
    747747                                arg = new AddressExpr( new VariableExpr( newObj ) );
    748748                        } // if
     
    888888                                // void return
    889889                                addAdapterParams( adapteeApp, arg, param, adapterType->get_parameters().end(), realParam, tyVars );
    890                                 bodyStmt = new ExprStmt( noLabels, adapteeApp );
     890                                bodyStmt = new ExprStmt( adapteeApp );
    891891                        } else if ( isDynType( adaptee->get_returnVals().front()->get_type(), tyVars ) ) {
    892892                                // return type T
     
    900900                                addAdapterParams( adapteeApp, arg, param, adapterType->get_parameters().end(), realParam, tyVars );
    901901                                assign->get_args().push_back( adapteeApp );
    902                                 bodyStmt = new ExprStmt( noLabels, assign );
     902                                bodyStmt = new ExprStmt( assign );
    903903                        } else {
    904904                                // adapter for a function that returns a monomorphic value
    905905                                addAdapterParams( adapteeApp, arg, param, adapterType->get_parameters().end(), realParam, tyVars );
    906                                 bodyStmt = new ReturnStmt( noLabels, adapteeApp );
     906                                bodyStmt = new ReturnStmt( adapteeApp );
    907907                        } // if
    908                         CompoundStmt *adapterBody = new CompoundStmt( noLabels );
     908                        CompoundStmt *adapterBody = new CompoundStmt();
    909909                        adapterBody->get_kids().push_back( bodyStmt );
    910910                        std::string adapterName = makeAdapterName( mangleName );
     
    952952                                                std::pair< AdapterIter, bool > answer = adapters.insert( std::pair< std::string, DeclarationWithType *>( mangleName, newAdapter ) );
    953953                                                adapter = answer.first;
    954                                                 stmtsToAddBefore.push_back( new DeclStmt( noLabels, newAdapter ) );
     954                                                stmtsToAddBefore.push_back( new DeclStmt( newAdapter ) );
    955955                                        } // if
    956956                                        assert( adapter != adapters.end() );
     
    12791279                                                retval->set_name( "_retval" );
    12801280                                        }
    1281                                         functionDecl->get_statements()->get_kids().push_front( new DeclStmt( noLabels, retval ) );
     1281                                        functionDecl->get_statements()->get_kids().push_front( new DeclStmt( retval ) );
    12821282                                        DeclarationWithType * newRet = retval->clone(); // for ownership purposes
    12831283                                        ftype->get_returnVals().front() = newRet;
     
    15191519                                        // (alloca was previously used, but can't be safely used in loops)
    15201520                                        ObjectDecl *newBuf = ObjectDecl::newObject( bufNamer.newName(), polyToMonoType( objectDecl->type ), nullptr );
    1521                                         stmtsToAddBefore.push_back( new DeclStmt( noLabels, newBuf ) );
     1521                                        stmtsToAddBefore.push_back( new DeclStmt( newBuf ) );
    15221522
    15231523                                        delete objectDecl->get_init();
     
    15981598                ObjectDecl *PolyGenericCalculator::makeVar( const std::string &name, Type *type, Initializer *init ) {
    15991599                        ObjectDecl *newObj = new ObjectDecl( name, Type::StorageClasses(), LinkageSpec::C, 0, type, init );
    1600                         stmtsToAddBefore.push_back( new DeclStmt( noLabels, newObj ) );
     1600                        stmtsToAddBefore.push_back( new DeclStmt( newObj ) );
    16011601                        return newObj;
    16021602                }
     
    16771677                                        addOtypeParamsToLayoutCall( layoutCall, otypeParams );
    16781678
    1679                                         stmtsToAddBefore.push_back( new ExprStmt( noLabels, layoutCall ) );
     1679                                        stmtsToAddBefore.push_back( new ExprStmt( layoutCall ) );
    16801680                                }
    16811681
     
    17031703                                addOtypeParamsToLayoutCall( layoutCall, otypeParams );
    17041704
    1705                                 stmtsToAddBefore.push_back( new ExprStmt( noLabels, layoutCall ) );
     1705                                stmtsToAddBefore.push_back( new ExprStmt( layoutCall ) );
    17061706
    17071707                                return true;
  • src/GenPoly/InstantiateGeneric.cc

    r882ad37 r3ca540f  
    517517                                        Expression * init = new CastExpr( new AddressExpr( memberExpr ), new PointerType( Type::Qualifiers(), concType->clone() ) );
    518518                                        ObjectDecl * tmp = ObjectDecl::newObject( tmpNamer.newName(), new ReferenceType( Type::Qualifiers(), concType ), new SingleInit( init ) );
    519                                         stmtsToAddBefore.push_back( new DeclStmt( noLabels, tmp ) );
     519                                        stmtsToAddBefore.push_back( new DeclStmt( tmp ) );
    520520                                        return new VariableExpr( tmp );
    521521                                } else {
  • src/GenPoly/Specialize.cc

    r882ad37 r3ca540f  
    3535#include "SynTree/Declaration.h"         // for FunctionDecl, DeclarationWit...
    3636#include "SynTree/Expression.h"          // for ApplicationExpr, Expression
    37 #include "SynTree/Label.h"               // for Label, noLabels
     37#include "SynTree/Label.h"               // for Label
    3838#include "SynTree/Mutator.h"             // for mutateAll
    3939#include "SynTree/Statement.h"           // for CompoundStmt, DeclStmt, Expr...
     
    234234                } // if
    235235                // create new thunk with same signature as formal type (C linkage, empty body)
    236                 FunctionDecl *thunkFunc = new FunctionDecl( thunkNamer.newName(), Type::StorageClasses(), LinkageSpec::C, newType, new CompoundStmt( noLabels ) );
     236                FunctionDecl *thunkFunc = new FunctionDecl( thunkNamer.newName(), Type::StorageClasses(), LinkageSpec::C, newType, new CompoundStmt() );
    237237                thunkFunc->fixUniqueId();
    238238
     
    287287                Statement *appStmt;
    288288                if ( funType->returnVals.empty() ) {
    289                         appStmt = new ExprStmt( noLabels, appExpr );
    290                 } else {
    291                         appStmt = new ReturnStmt( noLabels, appExpr );
     289                        appStmt = new ExprStmt( appExpr );
     290                } else {
     291                        appStmt = new ReturnStmt( appExpr );
    292292                } // if
    293293                thunkFunc->statements->kids.push_back( appStmt );
    294294
    295295                // add thunk definition to queue of statements to add
    296                 stmtsToAddBefore.push_back( new DeclStmt( noLabels, thunkFunc ) );
     296                stmtsToAddBefore.push_back( new DeclStmt( thunkFunc ) );
    297297                // return address of thunk function as replacement expression
    298298                return new AddressExpr( new VariableExpr( thunkFunc ) );
  • src/InitTweak/FixGlobalInit.cc

    r882ad37 r3ca540f  
    2020#include <algorithm>               // for replace_if
    2121
     22#include "Common/PassVisitor.h"
    2223#include "Common/SemanticError.h"  // for SemanticError
    2324#include "Common/UniqueName.h"     // for UniqueName
     
    2930#include "SynTree/Expression.h"    // for ConstantExpr, Expression (ptr only)
    3031#include "SynTree/Initializer.h"   // for ConstructorInit, Initializer
    31 #include "SynTree/Label.h"         // for Label, noLabels
     32#include "SynTree/Label.h"         // for Label
    3233#include "SynTree/Statement.h"     // for CompoundStmt, Statement (ptr only)
    3334#include "SynTree/Type.h"          // for Type, Type::StorageClasses, Functi...
     
    3536
    3637namespace InitTweak {
    37         class GlobalFixer : public Visitor {
     38        class GlobalFixer : public WithShortCircuiting {
    3839          public:
    3940                GlobalFixer( const std::string & name, bool inLibrary );
    4041
    41                 virtual void visit( ObjectDecl *objDecl );
    42                 virtual void visit( FunctionDecl *functionDecl );
    43                 virtual void visit( StructDecl *aggregateDecl );
    44                 virtual void visit( UnionDecl *aggregateDecl );
    45                 virtual void visit( EnumDecl *aggregateDecl );
    46                 virtual void visit( TraitDecl *aggregateDecl );
    47                 virtual void visit( TypeDecl *typeDecl );
     42                void previsit( ObjectDecl *objDecl );
     43                void previsit( FunctionDecl *functionDecl );
     44                void previsit( StructDecl *aggregateDecl );
     45                void previsit( UnionDecl *aggregateDecl );
     46                void previsit( EnumDecl *aggregateDecl );
     47                void previsit( TraitDecl *aggregateDecl );
     48                void previsit( TypeDecl *typeDecl );
    4849
    4950                UniqueName tempNamer;
     
    5354
    5455        void fixGlobalInit( std::list< Declaration * > & translationUnit, const std::string & name, bool inLibrary ) {
    55                 GlobalFixer fixer( name, inLibrary );
    56                 acceptAll( translationUnit, fixer );
     56                PassVisitor<GlobalFixer> visitor( name, inLibrary );
     57                acceptAll( translationUnit, visitor );
     58                GlobalFixer & fixer = visitor.pass;
    5759                // don't need to include function if it's empty
    5860                if ( fixer.initFunction->get_statements()->get_kids().empty() ) {
     
    9294                        dtorParameters.push_back( new ConstantExpr( Constant::from_int( 102 ) ) );
    9395                }
    94                 initFunction = new FunctionDecl( "_init_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt( noLabels ) );
     96                initFunction = new FunctionDecl( "_init_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt() );
    9597                initFunction->get_attributes().push_back( new Attribute( "constructor", ctorParameters ) );
    96                 destroyFunction = new FunctionDecl( "_destroy_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt( noLabels ) );
     98                destroyFunction = new FunctionDecl( "_destroy_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt() );
    9799                destroyFunction->get_attributes().push_back( new Attribute( "destructor", dtorParameters ) );
    98100        }
    99101
    100         void GlobalFixer::visit( ObjectDecl *objDecl ) {
     102        void GlobalFixer::previsit( ObjectDecl *objDecl ) {
    101103                std::list< Statement * > & initStatements = initFunction->get_statements()->get_kids();
    102104                std::list< Statement * > & destroyStatements = destroyFunction->get_statements()->get_kids();
     
    134136
    135137        // only modify global variables
    136         void GlobalFixer::visit( __attribute__((unused)) FunctionDecl *functionDecl ) {}
    137         void GlobalFixer::visit( __attribute__((unused)) StructDecl *aggregateDecl ) {}
    138         void GlobalFixer::visit( __attribute__((unused)) UnionDecl *aggregateDecl ) {}
    139         void GlobalFixer::visit( __attribute__((unused)) EnumDecl *aggregateDecl ) {}
    140         void GlobalFixer::visit( __attribute__((unused)) TraitDecl *aggregateDecl ) {}
    141         void GlobalFixer::visit( __attribute__((unused)) TypeDecl *typeDecl ) {}
     138        void GlobalFixer::previsit( FunctionDecl * ) { visit_children = false; }
     139        void GlobalFixer::previsit( StructDecl * ) { visit_children = false; }
     140        void GlobalFixer::previsit( UnionDecl * ) { visit_children = false; }
     141        void GlobalFixer::previsit( EnumDecl * ) { visit_children = false; }
     142        void GlobalFixer::previsit( TraitDecl * ) { visit_children = false; }
     143        void GlobalFixer::previsit( TypeDecl * ) { visit_children = false; }
    142144
    143145} // namespace InitTweak
  • src/InitTweak/FixInit.cc

    r882ad37 r3ca540f  
    4949#include "SynTree/Expression.h"        // for UniqueExpr, VariableExpr, Unty...
    5050#include "SynTree/Initializer.h"       // for ConstructorInit, SingleInit
    51 #include "SynTree/Label.h"             // for Label, noLabels, operator<
     51#include "SynTree/Label.h"             // for Label, operator<
    5252#include "SynTree/Mutator.h"           // for mutateAll, Mutator, maybeMutate
    5353#include "SynTree/Statement.h"         // for ExprStmt, CompoundStmt, Branch...
     
    544544                        // add all temporary declarations and their constructors
    545545                        for ( ObjectDecl * obj : tempDecls ) {
    546                                 stmtsToAddBefore.push_back( new DeclStmt( noLabels, obj ) );
     546                                stmtsToAddBefore.push_back( new DeclStmt( obj ) );
    547547                        } // for
    548548                        for ( ObjectDecl * obj : returnDecls ) {
    549                                 stmtsToAddBefore.push_back( new DeclStmt( noLabels, obj ) );
     549                                stmtsToAddBefore.push_back( new DeclStmt( obj ) );
    550550                        } // for
    551551
    552552                        // add destructors after current statement
    553553                        for ( Expression * dtor : dtors ) {
    554                                 stmtsToAddAfter.push_back( new ExprStmt( noLabels, dtor ) );
     554                                stmtsToAddAfter.push_back( new ExprStmt( dtor ) );
    555555                        } // for
    556556
     
    598598                        if ( ! result->isVoid() ) {
    599599                                for ( ObjectDecl * obj : stmtExpr->get_returnDecls() ) {
    600                                         stmtsToAddBefore.push_back( new DeclStmt( noLabels, obj ) );
     600                                        stmtsToAddBefore.push_back( new DeclStmt( obj ) );
    601601                                } // for
    602602                                // add destructors after current statement
    603603                                for ( Expression * dtor : stmtExpr->get_dtors() ) {
    604                                         stmtsToAddAfter.push_back( new ExprStmt( noLabels, dtor ) );
     604                                        stmtsToAddAfter.push_back( new ExprStmt( dtor ) );
    605605                                } // for
    606606                                // must have a non-empty body, otherwise it wouldn't have a result
    607607                                assert( ! stmts.empty() );
    608608                                assert( ! stmtExpr->get_returnDecls().empty() );
    609                                 stmts.push_back( new ExprStmt( noLabels, new VariableExpr( stmtExpr->get_returnDecls().front() ) ) );
     609                                stmts.push_back( new ExprStmt( new VariableExpr( stmtExpr->get_returnDecls().front() ) ) );
    610610                                stmtExpr->get_returnDecls().clear();
    611611                                stmtExpr->get_dtors().clear();
     
    685685
    686686                                                // generate body of if
    687                                                 CompoundStmt * initStmts = new CompoundStmt( noLabels );
     687                                                CompoundStmt * initStmts = new CompoundStmt();
    688688                                                std::list< Statement * > & body = initStmts->get_kids();
    689689                                                body.push_back( ctor );
    690                                                 body.push_back( new ExprStmt( noLabels, setTrue ) );
     690                                                body.push_back( new ExprStmt( setTrue ) );
    691691
    692692                                                // put it all together
    693                                                 IfStmt * ifStmt = new IfStmt( noLabels, new VariableExpr( isUninitializedVar ), initStmts, 0 );
    694                                                 stmtsToAddAfter.push_back( new DeclStmt( noLabels, isUninitializedVar ) );
     693                                                IfStmt * ifStmt = new IfStmt( new VariableExpr( isUninitializedVar ), initStmts, 0 );
     694                                                stmtsToAddAfter.push_back( new DeclStmt( isUninitializedVar ) );
    695695                                                stmtsToAddAfter.push_back( ifStmt );
    696696
     
    707707
    708708                                                        // void __objName_dtor_atexitN(...) {...}
    709                                                         FunctionDecl * dtorCaller = new FunctionDecl( objDecl->get_mangleName() + dtorCallerNamer.newName(), Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt( noLabels ) );
     709                                                        FunctionDecl * dtorCaller = new FunctionDecl( objDecl->get_mangleName() + dtorCallerNamer.newName(), Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt() );
    710710                                                        dtorCaller->fixUniqueId();
    711711                                                        dtorCaller->get_statements()->push_back( dtorStmt );
     
    715715                                                        callAtexit->get_args().push_back( new VariableExpr( dtorCaller ) );
    716716
    717                                                         body.push_back( new ExprStmt( noLabels, callAtexit ) );
     717                                                        body.push_back( new ExprStmt( callAtexit ) );
    718718
    719719                                                        // hoist variable and dtor caller decls to list of decls that will be added into global scope
  • src/InitTweak/InitTweak.cc

    r882ad37 r3ca540f  
    55#include <memory>                  // for __shared_ptr
    66
     7#include "Common/PassVisitor.h"
    78#include "Common/SemanticError.h"  // for SemanticError
    89#include "Common/UniqueName.h"     // for UniqueName
     
    1920#include "SynTree/Expression.h"    // for Expression, UntypedExpr, Applicati...
    2021#include "SynTree/Initializer.h"   // for Initializer, ListInit, Designation
    21 #include "SynTree/Label.h"         // for Label, noLabels
     22#include "SynTree/Label.h"         // for Label
    2223#include "SynTree/Statement.h"     // for CompoundStmt, ExprStmt, BranchStmt
    2324#include "SynTree/Type.h"          // for FunctionType, ArrayType, PointerType
     
    2930namespace InitTweak {
    3031        namespace {
    31                 class HasDesignations : public Visitor {
    32                 public:
     32                struct HasDesignations : public WithShortCircuiting {
    3333                        bool hasDesignations = false;
    34                         virtual void visit( Designation * des ) {
    35                                 if ( ! des->get_designators().empty() ) hasDesignations = true;
    36                                 else Visitor::visit( des );
     34
     35                        void previsit( BaseSyntaxNode * ) {
     36                                // short circuit if we already know there are designations
     37                                if ( hasDesignations ) visit_children = false;
     38                        }
     39
     40                        void previsit( Designation * des ) {
     41                                // short circuit if we already know there are designations
     42                                if ( hasDesignations ) visit_children = false;
     43                                else if ( ! des->get_designators().empty() ) {
     44                                        hasDesignations = true;
     45                                        visit_children = false;
     46                                }
    3747                        }
    3848                };
    3949
    40                 class InitDepthChecker : public Visitor {
    41                 public:
     50                struct InitDepthChecker : public WithGuards {
    4251                        bool depthOkay = true;
    4352                        Type * type;
     
    5160                                maxDepth++;
    5261                        }
    53                         virtual void visit( ListInit * listInit ) {
     62                        void previsit( ListInit * ) {
    5463                                curDepth++;
     64                                GuardAction( [this]() { curDepth--; } );
    5565                                if ( curDepth > maxDepth ) depthOkay = false;
    56                                 Visitor::visit( listInit );
    57                                 curDepth--;
    5866                        }
    5967                };
    6068
    61                 class InitFlattener : public Visitor {
    62                         public:
    63                         virtual void visit( SingleInit * singleInit );
    64                         virtual void visit( ListInit * listInit );
     69                struct InitFlattener : public WithShortCircuiting {
     70                        void previsit( SingleInit * singleInit ) {
     71                                visit_children = false;
     72                                argList.push_back( singleInit->value->clone() );
     73                        }
    6574                        std::list< Expression * > argList;
    6675                };
    6776
    68                 void InitFlattener::visit( SingleInit * singleInit ) {
    69                         argList.push_back( singleInit->get_value()->clone() );
    70                 }
    71 
    72                 void InitFlattener::visit( ListInit * listInit ) {
    73                         // flatten nested list inits
    74                         std::list<Initializer*>::iterator it = listInit->begin();
    75                         for ( ; it != listInit->end(); ++it ) {
    76                                 (*it)->accept( *this );
    77                         }
    78                 }
    7977        }
    8078
    8179        std::list< Expression * > makeInitList( Initializer * init ) {
    82                 InitFlattener flattener;
     80                PassVisitor<InitFlattener> flattener;
    8381                maybeAccept( init, flattener );
    84                 return flattener.argList;
     82                return flattener.pass.argList;
    8583        }
    8684
    8785        bool isDesignated( Initializer * init ) {
    88                 HasDesignations finder;
     86                PassVisitor<HasDesignations> finder;
    8987                maybeAccept( init, finder );
    90                 return finder.hasDesignations;
     88                return finder.pass.hasDesignations;
    9189        }
    9290
    9391        bool checkInitDepth( ObjectDecl * objDecl ) {
    94                 InitDepthChecker checker( objDecl->get_type() );
    95                 maybeAccept( objDecl->get_init(), checker );
    96                 return checker.depthOkay;
     92                PassVisitor<InitDepthChecker> checker( objDecl->type );
     93                maybeAccept( objDecl->init, checker );
     94                return checker.pass.depthOkay;
    9795        }
    9896
     
    195193                        callExpr->get_args().splice( callExpr->get_args().end(), args );
    196194
    197                         *out++ = new IfStmt( noLabels, cond, new ExprStmt( noLabels, callExpr ), nullptr );
     195                        *out++ = new IfStmt( cond, new ExprStmt( callExpr ), nullptr );
    198196
    199197                        UntypedExpr * increment = new UntypedExpr( new NameExpr( "++?" ) );
    200198                        increment->get_args().push_back( index->clone() );
    201                         *out++ = new ExprStmt( noLabels, increment );
     199                        *out++ = new ExprStmt( increment );
    202200                }
    203201
     
    244242                                        std::list< Statement * > stmts;
    245243                                        build( callExpr, idx, idxEnd, init, back_inserter( stmts ) );
    246                                         stmts.push_back( new BranchStmt( noLabels, switchLabel, BranchStmt::Break ) );
    247                                         CaseStmt * caseStmt = new CaseStmt( noLabels, condition, stmts );
     244                                        stmts.push_back( new BranchStmt( switchLabel, BranchStmt::Break ) );
     245                                        CaseStmt * caseStmt = new CaseStmt( condition, stmts );
    248246                                        branches.push_back( caseStmt );
    249247                                }
    250                                 *out++ = new SwitchStmt( noLabels, index->clone(), branches );
    251                                 *out++ = new NullStmt( std::list<Label>{ switchLabel } );
     248                                *out++ = new SwitchStmt( index->clone(), branches );
     249                                *out++ = new NullStmt( { switchLabel } );
    252250                        }
    253251                }
     
    262260        Statement * InitImpl::buildListInit( UntypedExpr * dst, std::list< Expression * > & indices ) {
    263261                if ( ! init ) return nullptr;
    264                 CompoundStmt * block = new CompoundStmt( noLabels );
     262                CompoundStmt * block = new CompoundStmt();
    265263                build( dst, indices.begin(), indices.end(), init, back_inserter( block->get_kids() ) );
    266264                if ( block->get_kids().empty() ) {
     
    309307        }
    310308
    311         class CallFinder : public Visitor {
    312         public:
    313                 typedef Visitor Parent;
     309        struct CallFinder {
    314310                CallFinder( const std::list< std::string > & names ) : names( names ) {}
    315311
    316                 virtual void visit( ApplicationExpr * appExpr ) {
     312                void postvisit( ApplicationExpr * appExpr ) {
    317313                        handleCallExpr( appExpr );
    318314                }
    319315
    320                 virtual void visit( UntypedExpr * untypedExpr ) {
     316                void postvisit( UntypedExpr * untypedExpr ) {
    321317                        handleCallExpr( untypedExpr );
    322318                }
     
    328324                template< typename CallExpr >
    329325                void handleCallExpr( CallExpr * expr ) {
    330                         Parent::visit( expr );
    331326                        std::string fname = getFunctionName( expr );
    332327                        if ( std::find( names.begin(), names.end(), fname ) != names.end() ) {
     
    337332
    338333        void collectCtorDtorCalls( Statement * stmt, std::list< Expression * > & matches ) {
    339                 static CallFinder finder( std::list< std::string >{ "?{}", "^?{}" } );
    340                 finder.matches = &matches;
     334                static PassVisitor<CallFinder> finder( std::list< std::string >{ "?{}", "^?{}" } );
     335                finder.pass.matches = &matches;
    341336                maybeAccept( stmt, finder );
    342337        }
     
    544539        }
    545540
    546         class ConstExprChecker : public Visitor {
    547         public:
    548                 ConstExprChecker() : isConstExpr( true ) {}
    549 
    550                 using Visitor::visit;
    551 
    552                 virtual void visit( ApplicationExpr * ) { isConstExpr = false; }
    553                 virtual void visit( UntypedExpr * ) { isConstExpr = false; }
    554                 virtual void visit( NameExpr * ) { isConstExpr = false; }
    555                 // virtual void visit( CastExpr *castExpr ) { isConstExpr = false; }
    556                 virtual void visit( AddressExpr *addressExpr ) {
     541        struct ConstExprChecker : public WithShortCircuiting {
     542                // most expressions are not const expr
     543                void previsit( Expression * ) { isConstExpr = false; visit_children = false; }
     544
     545                void previsit( AddressExpr *addressExpr ) {
     546                        visit_children = false;
     547
    557548                        // address of a variable or member expression is constexpr
    558549                        Expression * arg = addressExpr->get_arg();
    559550                        if ( ! dynamic_cast< NameExpr * >( arg) && ! dynamic_cast< VariableExpr * >( arg ) && ! dynamic_cast< MemberExpr * >( arg ) && ! dynamic_cast< UntypedMemberExpr * >( arg ) ) isConstExpr = false;
    560551                }
    561                 virtual void visit( UntypedMemberExpr * ) { isConstExpr = false; }
    562                 virtual void visit( MemberExpr * ) { isConstExpr = false; }
    563                 virtual void visit( VariableExpr * ) { isConstExpr = false; }
    564                 // these might be okay?
    565                 // virtual void visit( SizeofExpr *sizeofExpr );
    566                 // virtual void visit( AlignofExpr *alignofExpr );
    567                 // virtual void visit( UntypedOffsetofExpr *offsetofExpr );
    568                 // virtual void visit( OffsetofExpr *offsetofExpr );
    569                 // virtual void visit( OffsetPackExpr *offsetPackExpr );
    570                 // virtual void visit( AttrExpr *attrExpr );
    571                 // virtual void visit( CommaExpr *commaExpr );
    572                 // virtual void visit( LogicalExpr *logicalExpr );
    573                 // virtual void visit( ConditionalExpr *conditionalExpr );
    574                 virtual void visit( TypeExpr * ) { isConstExpr = false; }
    575                 virtual void visit( AsmExpr * ) { isConstExpr = false; }
    576                 virtual void visit( UntypedValofExpr * ) { isConstExpr = false; }
    577                 virtual void visit( CompoundLiteralExpr * ) { isConstExpr = false; }
    578                 virtual void visit( UntypedTupleExpr * ) { isConstExpr = false; }
    579                 virtual void visit( TupleExpr * ) { isConstExpr = false; }
    580                 virtual void visit( TupleAssignExpr * ) { isConstExpr = false; }
    581 
    582                 bool isConstExpr;
     552
     553                // these expressions may be const expr, depending on their children
     554                void previsit( SizeofExpr * ) {}
     555                void previsit( AlignofExpr * ) {}
     556                void previsit( UntypedOffsetofExpr * ) {}
     557                void previsit( OffsetofExpr * ) {}
     558                void previsit( OffsetPackExpr * ) {}
     559                void previsit( AttrExpr * ) {}
     560                void previsit( CommaExpr * ) {}
     561                void previsit( LogicalExpr * ) {}
     562                void previsit( ConditionalExpr * ) {}
     563                void previsit( CastExpr * ) {}
     564                void previsit( ConstantExpr * ) {}
     565
     566                bool isConstExpr = true;
    583567        };
    584568
    585569        bool isConstExpr( Expression * expr ) {
    586570                if ( expr ) {
    587                         ConstExprChecker checker;
     571                        PassVisitor<ConstExprChecker> checker;
    588572                        expr->accept( checker );
    589                         return checker.isConstExpr;
     573                        return checker.pass.isConstExpr;
    590574                }
    591575                return true;
     
    594578        bool isConstExpr( Initializer * init ) {
    595579                if ( init ) {
    596                         ConstExprChecker checker;
     580                        PassVisitor<ConstExprChecker> checker;
    597581                        init->accept( checker );
    598                         return checker.isConstExpr;
     582                        return checker.pass.isConstExpr;
    599583                } // if
    600584                // for all intents and purposes, no initializer means const expr
  • src/MakeLibCfa.cc

    r882ad37 r3ca540f  
    116116                        } // for
    117117
    118                         funcDecl->set_statements( new CompoundStmt( std::list< Label >() ) );
     118                        funcDecl->set_statements( new CompoundStmt() );
    119119                        newDecls.push_back( funcDecl );
    120120
     
    130130                          case CodeGen::OT_INFIXASSIGN:
    131131                                        // return the recursive call
    132                                         stmt = new ReturnStmt( noLabels, newExpr );
     132                                        stmt = new ReturnStmt( newExpr );
    133133                                        break;
    134134                          case CodeGen::OT_CTOR:
    135135                          case CodeGen::OT_DTOR:
    136136                                        // execute the recursive call
    137                                         stmt = new ExprStmt( noLabels, newExpr );
     137                                        stmt = new ExprStmt( newExpr );
    138138                                        break;
    139139                          case CodeGen::OT_CONSTANT:
  • src/Makefile.in

    r882ad37 r3ca540f  
    215215        SymTab/driver_cfa_cpp-Validate.$(OBJEXT) \
    216216        SymTab/driver_cfa_cpp-FixFunction.$(OBJEXT) \
    217         SymTab/driver_cfa_cpp-ImplementationType.$(OBJEXT) \
    218         SymTab/driver_cfa_cpp-TypeEquality.$(OBJEXT) \
    219217        SymTab/driver_cfa_cpp-Autogen.$(OBJEXT) \
    220218        SynTree/driver_cfa_cpp-Type.$(OBJEXT) \
     
    514512        ResolvExpr/CurrentObject.cc ResolvExpr/ExplodedActual.cc \
    515513        SymTab/Indexer.cc SymTab/Mangler.cc SymTab/Validate.cc \
    516         SymTab/FixFunction.cc SymTab/ImplementationType.cc \
    517         SymTab/TypeEquality.cc SymTab/Autogen.cc SynTree/Type.cc \
     514        SymTab/FixFunction.cc SymTab/Autogen.cc SynTree/Type.cc \
    518515        SynTree/VoidType.cc SynTree/BasicType.cc \
    519516        SynTree/PointerType.cc SynTree/ArrayType.cc \
     
    844841SymTab/driver_cfa_cpp-FixFunction.$(OBJEXT): SymTab/$(am__dirstamp) \
    845842        SymTab/$(DEPDIR)/$(am__dirstamp)
    846 SymTab/driver_cfa_cpp-ImplementationType.$(OBJEXT):  \
    847         SymTab/$(am__dirstamp) SymTab/$(DEPDIR)/$(am__dirstamp)
    848 SymTab/driver_cfa_cpp-TypeEquality.$(OBJEXT): SymTab/$(am__dirstamp) \
    849         SymTab/$(DEPDIR)/$(am__dirstamp)
    850843SymTab/driver_cfa_cpp-Autogen.$(OBJEXT): SymTab/$(am__dirstamp) \
    851844        SymTab/$(DEPDIR)/$(am__dirstamp)
     
    10401033@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-Autogen.Po@am__quote@
    10411034@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-FixFunction.Po@am__quote@
    1042 @AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-ImplementationType.Po@am__quote@
    10431035@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-Indexer.Po@am__quote@
    10441036@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-Mangler.Po@am__quote@
    1045 @AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-TypeEquality.Po@am__quote@
    10461037@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-Validate.Po@am__quote@
    10471038@AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-AddressExpr.Po@am__quote@
     
    20392030@AMDEP_TRUE@@am__fastdepCXX_FALSE@      DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    20402031@am__fastdepCXX_FALSE@  $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SymTab/driver_cfa_cpp-FixFunction.obj `if test -f 'SymTab/FixFunction.cc'; then $(CYGPATH_W) 'SymTab/FixFunction.cc'; else $(CYGPATH_W) '$(srcdir)/SymTab/FixFunction.cc'; fi`
    2041 
    2042 SymTab/driver_cfa_cpp-ImplementationType.o: SymTab/ImplementationType.cc
    2043 @am__fastdepCXX_TRUE@   $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT SymTab/driver_cfa_cpp-ImplementationType.o -MD -MP -MF SymTab/$(DEPDIR)/driver_cfa_cpp-ImplementationType.Tpo -c -o SymTab/driver_cfa_cpp-ImplementationType.o `test -f 'SymTab/ImplementationType.cc' || echo '$(srcdir)/'`SymTab/ImplementationType.cc
    2044 @am__fastdepCXX_TRUE@   $(AM_V_at)$(am__mv) SymTab/$(DEPDIR)/driver_cfa_cpp-ImplementationType.Tpo SymTab/$(DEPDIR)/driver_cfa_cpp-ImplementationType.Po
    2045 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      $(AM_V_CXX)source='SymTab/ImplementationType.cc' object='SymTab/driver_cfa_cpp-ImplementationType.o' libtool=no @AMDEPBACKSLASH@
    2046 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    2047 @am__fastdepCXX_FALSE@  $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SymTab/driver_cfa_cpp-ImplementationType.o `test -f 'SymTab/ImplementationType.cc' || echo '$(srcdir)/'`SymTab/ImplementationType.cc
    2048 
    2049 SymTab/driver_cfa_cpp-ImplementationType.obj: SymTab/ImplementationType.cc
    2050 @am__fastdepCXX_TRUE@   $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT SymTab/driver_cfa_cpp-ImplementationType.obj -MD -MP -MF SymTab/$(DEPDIR)/driver_cfa_cpp-ImplementationType.Tpo -c -o SymTab/driver_cfa_cpp-ImplementationType.obj `if test -f 'SymTab/ImplementationType.cc'; then $(CYGPATH_W) 'SymTab/ImplementationType.cc'; else $(CYGPATH_W) '$(srcdir)/SymTab/ImplementationType.cc'; fi`
    2051 @am__fastdepCXX_TRUE@   $(AM_V_at)$(am__mv) SymTab/$(DEPDIR)/driver_cfa_cpp-ImplementationType.Tpo SymTab/$(DEPDIR)/driver_cfa_cpp-ImplementationType.Po
    2052 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      $(AM_V_CXX)source='SymTab/ImplementationType.cc' object='SymTab/driver_cfa_cpp-ImplementationType.obj' libtool=no @AMDEPBACKSLASH@
    2053 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    2054 @am__fastdepCXX_FALSE@  $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SymTab/driver_cfa_cpp-ImplementationType.obj `if test -f 'SymTab/ImplementationType.cc'; then $(CYGPATH_W) 'SymTab/ImplementationType.cc'; else $(CYGPATH_W) '$(srcdir)/SymTab/ImplementationType.cc'; fi`
    2055 
    2056 SymTab/driver_cfa_cpp-TypeEquality.o: SymTab/TypeEquality.cc
    2057 @am__fastdepCXX_TRUE@   $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT SymTab/driver_cfa_cpp-TypeEquality.o -MD -MP -MF SymTab/$(DEPDIR)/driver_cfa_cpp-TypeEquality.Tpo -c -o SymTab/driver_cfa_cpp-TypeEquality.o `test -f 'SymTab/TypeEquality.cc' || echo '$(srcdir)/'`SymTab/TypeEquality.cc
    2058 @am__fastdepCXX_TRUE@   $(AM_V_at)$(am__mv) SymTab/$(DEPDIR)/driver_cfa_cpp-TypeEquality.Tpo SymTab/$(DEPDIR)/driver_cfa_cpp-TypeEquality.Po
    2059 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      $(AM_V_CXX)source='SymTab/TypeEquality.cc' object='SymTab/driver_cfa_cpp-TypeEquality.o' libtool=no @AMDEPBACKSLASH@
    2060 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    2061 @am__fastdepCXX_FALSE@  $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SymTab/driver_cfa_cpp-TypeEquality.o `test -f 'SymTab/TypeEquality.cc' || echo '$(srcdir)/'`SymTab/TypeEquality.cc
    2062 
    2063 SymTab/driver_cfa_cpp-TypeEquality.obj: SymTab/TypeEquality.cc
    2064 @am__fastdepCXX_TRUE@   $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -MT SymTab/driver_cfa_cpp-TypeEquality.obj -MD -MP -MF SymTab/$(DEPDIR)/driver_cfa_cpp-TypeEquality.Tpo -c -o SymTab/driver_cfa_cpp-TypeEquality.obj `if test -f 'SymTab/TypeEquality.cc'; then $(CYGPATH_W) 'SymTab/TypeEquality.cc'; else $(CYGPATH_W) '$(srcdir)/SymTab/TypeEquality.cc'; fi`
    2065 @am__fastdepCXX_TRUE@   $(AM_V_at)$(am__mv) SymTab/$(DEPDIR)/driver_cfa_cpp-TypeEquality.Tpo SymTab/$(DEPDIR)/driver_cfa_cpp-TypeEquality.Po
    2066 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      $(AM_V_CXX)source='SymTab/TypeEquality.cc' object='SymTab/driver_cfa_cpp-TypeEquality.obj' libtool=no @AMDEPBACKSLASH@
    2067 @AMDEP_TRUE@@am__fastdepCXX_FALSE@      DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    2068 @am__fastdepCXX_FALSE@  $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(driver_cfa_cpp_CXXFLAGS) $(CXXFLAGS) -c -o SymTab/driver_cfa_cpp-TypeEquality.obj `if test -f 'SymTab/TypeEquality.cc'; then $(CYGPATH_W) 'SymTab/TypeEquality.cc'; else $(CYGPATH_W) '$(srcdir)/SymTab/TypeEquality.cc'; fi`
    20692032
    20702033SymTab/driver_cfa_cpp-Autogen.o: SymTab/Autogen.cc
  • src/Parser/StatementNode.cc

    r882ad37 r3ca540f  
    3737        DeclarationNode *agg = decl->extractAggregate();
    3838        if ( agg ) {
    39                 StatementNode *nextStmt = new StatementNode( new DeclStmt( noLabels, maybeBuild< Declaration >( decl ) ) );
     39                StatementNode *nextStmt = new StatementNode( new DeclStmt( maybeBuild< Declaration >( decl ) ) );
    4040                set_next( nextStmt );
    4141                if ( decl->get_next() ) {
     
    5050                agg = decl;
    5151        } // if
    52         stmt.reset( new DeclStmt( noLabels, maybeMoveBuild< Declaration >(agg) ) );
     52        stmt.reset( new DeclStmt( maybeMoveBuild< Declaration >(agg) ) );
    5353} // StatementNode::StatementNode
    5454
     
    7575
    7676        if ( e )
    77                 return new ExprStmt( noLabels, e );
     77                return new ExprStmt( e );
    7878        else
    79                 return new NullStmt( noLabels );
     79                return new NullStmt();
    8080}
    8181
     
    113113        }
    114114        delete ctl;
    115         return new IfStmt( noLabels, cond, thenb, elseb, init );
     115        return new IfStmt( cond, thenb, elseb, init );
    116116}
    117117
     
    120120        buildMoveList< Statement, StatementNode >( stmt, branches );
    121121        // branches.size() == 0 for switch (...) {}, i.e., no declaration or statements
    122         return new SwitchStmt( noLabels, maybeMoveBuild< Expression >(ctl), branches );
     122        return new SwitchStmt( maybeMoveBuild< Expression >(ctl), branches );
    123123}
    124124Statement *build_case( ExpressionNode *ctl ) {
    125125        std::list< Statement * > branches;
    126         return new CaseStmt( noLabels, maybeMoveBuild< Expression >(ctl), branches );
     126        return new CaseStmt( maybeMoveBuild< Expression >(ctl), branches );
    127127}
    128128Statement *build_default() {
    129129        std::list< Statement * > branches;
    130         return new CaseStmt( noLabels, nullptr, branches, true );
     130        return new CaseStmt( nullptr, branches, true );
    131131}
    132132
     
    135135        buildMoveList< Statement, StatementNode >( stmt, branches );
    136136        assert( branches.size() == 1 );
    137         return new WhileStmt( noLabels, notZeroExpr( maybeMoveBuild< Expression >(ctl) ), branches.front(), kind );
     137        return new WhileStmt( notZeroExpr( maybeMoveBuild< Expression >(ctl) ), branches.front(), kind );
    138138}
    139139
     
    157157
    158158        delete forctl;
    159         return new ForStmt( noLabels, init, cond, incr, branches.front() );
     159        return new ForStmt( init, cond, incr, branches.front() );
    160160}
    161161
    162162Statement *build_branch( BranchStmt::Type kind ) {
    163         Statement * ret = new BranchStmt( noLabels, "", kind );
     163        Statement * ret = new BranchStmt( "", kind );
    164164        return ret;
    165165}
    166166Statement *build_branch( std::string *identifier, BranchStmt::Type kind ) {
    167         Statement * ret = new BranchStmt( noLabels, *identifier, kind );
     167        Statement * ret = new BranchStmt( *identifier, kind );
    168168        delete identifier;                                                                      // allocated by lexer
    169169        return ret;
    170170}
    171171Statement *build_computedgoto( ExpressionNode *ctl ) {
    172         return new BranchStmt( noLabels, maybeMoveBuild< Expression >(ctl), BranchStmt::Goto );
     172        return new BranchStmt( maybeMoveBuild< Expression >(ctl), BranchStmt::Goto );
    173173}
    174174
     
    176176        std::list< Expression * > exps;
    177177        buildMoveList( ctl, exps );
    178         return new ReturnStmt( noLabels, exps.size() > 0 ? exps.back() : nullptr );
     178        return new ReturnStmt( exps.size() > 0 ? exps.back() : nullptr );
    179179}
    180180
     
    183183        buildMoveList( ctl, exps );
    184184        assertf( exps.size() < 2, "This means we are leaking memory");
    185         return new ThrowStmt( noLabels, ThrowStmt::Terminate, !exps.empty() ? exps.back() : nullptr );
     185        return new ThrowStmt( ThrowStmt::Terminate, !exps.empty() ? exps.back() : nullptr );
    186186}
    187187
     
    190190        buildMoveList( ctl, exps );
    191191        assertf( exps.size() < 2, "This means we are leaking memory");
    192         return new ThrowStmt( noLabels, ThrowStmt::Resume, !exps.empty() ? exps.back() : nullptr );
     192        return new ThrowStmt( ThrowStmt::Resume, !exps.empty() ? exps.back() : nullptr );
    193193}
    194194
     
    204204        CompoundStmt *tryBlock = strict_dynamic_cast< CompoundStmt * >(maybeMoveBuild< Statement >(try_stmt));
    205205        FinallyStmt *finallyBlock = dynamic_cast< FinallyStmt * >(maybeMoveBuild< Statement >(finally_stmt) );
    206         return new TryStmt( noLabels, tryBlock, branches, finallyBlock );
     206        return new TryStmt( tryBlock, branches, finallyBlock );
    207207}
    208208Statement *build_catch( CatchStmt::Kind kind, DeclarationNode *decl, ExpressionNode *cond, StatementNode *body ) {
     
    210210        buildMoveList< Statement, StatementNode >( body, branches );
    211211        assert( branches.size() == 1 );
    212         return new CatchStmt( noLabels, kind, maybeMoveBuild< Declaration >(decl), maybeMoveBuild< Expression >(cond), branches.front() );
     212        return new CatchStmt( kind, maybeMoveBuild< Declaration >(decl), maybeMoveBuild< Expression >(cond), branches.front() );
    213213}
    214214Statement *build_finally( StatementNode *stmt ) {
     
    216216        buildMoveList< Statement, StatementNode >( stmt, branches );
    217217        assert( branches.size() == 1 );
    218         return new FinallyStmt( noLabels, dynamic_cast< CompoundStmt * >( branches.front() ) );
     218        return new FinallyStmt( dynamic_cast< CompoundStmt * >( branches.front() ) );
    219219}
    220220
     
    296296
    297297Statement *build_compound( StatementNode *first ) {
    298         CompoundStmt *cs = new CompoundStmt( noLabels );
     298        CompoundStmt *cs = new CompoundStmt();
    299299        buildMoveList( first, cs->get_kids() );
    300300        return cs;
     
    308308        buildMoveList( input, in );
    309309        buildMoveList( clobber, clob );
    310         return new AsmStmt( noLabels, voltile, instruction, out, in, clob, gotolabels ? gotolabels->labels : noLabels );
     310        return new AsmStmt( voltile, instruction, out, in, clob, gotolabels ? gotolabels->labels : noLabels );
    311311}
    312312
  • src/ResolvExpr/Resolver.cc

    r882ad37 r3ca540f  
    370370                if ( throwStmt->get_expr() ) {
    371371                        StructDecl * exception_decl =
    372                                 indexer.lookupStruct( "__cfaehm__base_exception_t" );
     372                                indexer.lookupStruct( "__cfaabi_ehm__base_exception_t" );
    373373                        assert( exception_decl );
    374374                        Type * exceptType = new PointerType( noQualifiers, new StructInstType( noQualifiers, exception_decl ) );
  • src/SymTab/AddVisit.h

    r882ad37 r3ca540f  
    2424                        // add any new declarations after the previous statement
    2525                        for ( std::list< Declaration* >::iterator decl = visitor.declsToAddAfter.begin(); decl != visitor.declsToAddAfter.end(); ++decl ) {
    26                                 DeclStmt *declStmt = new DeclStmt( noLabels, *decl );
     26                                DeclStmt *declStmt = new DeclStmt( *decl );
    2727                                stmts.insert( stmt, declStmt );
    2828                        }
     
    3636                        // add any new declarations before the statement
    3737                        for ( std::list< Declaration* >::iterator decl = visitor.declsToAdd.begin(); decl != visitor.declsToAdd.end(); ++decl ) {
    38                                 DeclStmt *declStmt = new DeclStmt( noLabels, *decl );
     38                                DeclStmt *declStmt = new DeclStmt( *decl );
    3939                                stmts.insert( stmt, declStmt );
    4040                        }
  • src/SymTab/Autogen.cc

    r882ad37 r3ca540f  
    264264                Type::StorageClasses scs = functionNesting > 0 ? Type::StorageClasses() : Type::StorageClasses( Type::Static );
    265265                LinkageSpec::Spec spec = isIntrinsic ? LinkageSpec::Intrinsic : LinkageSpec::AutoGen;
    266                 FunctionDecl * decl = new FunctionDecl( fname, scs, spec, ftype, new CompoundStmt( noLabels ),
     266                FunctionDecl * decl = new FunctionDecl( fname, scs, spec, ftype, new CompoundStmt(),
    267267                                                                                                std::list< Attribute * >(), Type::FuncSpecifiers( Type::Inline ) );
    268268                decl->fixUniqueId();
     
    299299                                assert( assignType->returnVals.size() == 1 );
    300300                                ObjectDecl * dstParam = strict_dynamic_cast< ObjectDecl * >( assignType->parameters.front() );
    301                                 dcl->statements->push_back( new ReturnStmt( noLabels, new VariableExpr( dstParam ) ) );
     301                                dcl->statements->push_back( new ReturnStmt( new VariableExpr( dstParam ) ) );
    302302                        }
    303303                        resolve( dcl );
     
    468468                copy->args.push_back( new AddressExpr( new VariableExpr( srcParam ) ) );
    469469                copy->args.push_back( new SizeofExpr( srcParam->get_type()->clone() ) );
    470                 *out++ = new ExprStmt( noLabels, copy );
     470                *out++ = new ExprStmt( copy );
    471471        }
    472472
     
    544544                        callExpr->get_args().push_back( new VariableExpr( dstParam ) );
    545545                        callExpr->get_args().push_back( new VariableExpr( srcParam ) );
    546                         funcDecl->statements->push_back( new ExprStmt( noLabels, callExpr ) );
     546                        funcDecl->statements->push_back( new ExprStmt( callExpr ) );
    547547                } else {
    548548                        // default ctor/dtor body is empty - add unused attribute to parameter to silence warnings
     
    569569                expr->args.push_back( new CastExpr( new VariableExpr( dst ), new ReferenceType( Type::Qualifiers(), typeDecl->base->clone() ) ) );
    570570                if ( src ) expr->args.push_back( new CastExpr( new VariableExpr( src ), typeDecl->base->clone() ) );
    571                 dcl->statements->kids.push_back( new ExprStmt( noLabels, expr ) );
     571                dcl->statements->kids.push_back( new ExprStmt( expr ) );
    572572        };
    573573
     
    664664                        untyped->get_args().push_back( new VariableExpr( ftype->get_parameters().back() ) );
    665665                }
    666                 function->get_statements()->get_kids().push_back( new ExprStmt( noLabels, untyped ) );
    667                 function->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) );
     666                function->get_statements()->get_kids().push_back( new ExprStmt( untyped ) );
     667                function->get_statements()->get_kids().push_back( new ReturnStmt( UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) );
    668668        }
    669669
  • src/SymTab/Autogen.h

    r882ad37 r3ca540f  
    104104                fExpr->args.splice( fExpr->args.end(), args );
    105105
    106                 *out++ = new ExprStmt( noLabels, fExpr );
     106                *out++ = new ExprStmt( fExpr );
    107107
    108108                srcParam.clearArrayIndices();
     
    162162
    163163                // for stmt's body, eventually containing call
    164                 CompoundStmt * body = new CompoundStmt( noLabels );
     164                CompoundStmt * body = new CompoundStmt();
    165165                Statement * listInit = genCall( srcParam, dstParam, fname, back_inserter( body->kids ), array->base, addCast, forward );
    166166
    167167                // block containing for stmt and index variable
    168168                std::list<Statement *> initList;
    169                 CompoundStmt * block = new CompoundStmt( noLabels );
    170                 block->push_back( new DeclStmt( noLabels, index ) );
     169                CompoundStmt * block = new CompoundStmt();
     170                block->push_back( new DeclStmt( index ) );
    171171                if ( listInit ) block->get_kids().push_back( listInit );
    172                 block->push_back( new ForStmt( noLabels, initList, cond, inc, body ) );
     172                block->push_back( new ForStmt( initList, cond, inc, body ) );
    173173
    174174                *out++ = block;
  • src/SymTab/Validate.cc

    r882ad37 r3ca540f  
    8181
    8282namespace SymTab {
    83         class HoistStruct final : public Visitor {
    84                 template< typename Visitor >
    85                 friend void acceptAndAdd( std::list< Declaration * > &translationUnit, Visitor &visitor );
    86             template< typename Visitor >
    87             friend void addVisitStatementList( std::list< Statement* > &stmts, Visitor &visitor );
    88           public:
     83        struct HoistStruct final : public WithDeclsToAdd, public WithGuards {
    8984                /// Flattens nested struct types
    9085                static void hoistStruct( std::list< Declaration * > &translationUnit );
    9186
    92                 std::list< Declaration * > &get_declsToAdd() { return declsToAdd; }
    93 
    94                 virtual void visit( EnumInstType *enumInstType );
    95                 virtual void visit( StructInstType *structInstType );
    96                 virtual void visit( UnionInstType *unionInstType );
    97                 virtual void visit( StructDecl *aggregateDecl );
    98                 virtual void visit( UnionDecl *aggregateDecl );
    99 
    100                 virtual void visit( CompoundStmt *compoundStmt );
    101                 virtual void visit( SwitchStmt *switchStmt );
     87                void previsit( EnumInstType * enumInstType );
     88                void previsit( StructInstType * structInstType );
     89                void previsit( UnionInstType * unionInstType );
     90                void previsit( StructDecl * aggregateDecl );
     91                void previsit( UnionDecl * aggregateDecl );
     92
    10293          private:
    103                 HoistStruct();
    104 
    10594                template< typename AggDecl > void handleAggregate( AggDecl *aggregateDecl );
    10695
    107                 std::list< Declaration * > declsToAdd, declsToAddAfter;
    108                 bool inStruct;
     96                bool inStruct = false;
    10997        };
    11098
     
    305293
    306294        void HoistStruct::hoistStruct( std::list< Declaration * > &translationUnit ) {
    307                 HoistStruct hoister;
    308                 acceptAndAdd( translationUnit, hoister );
    309         }
    310 
    311         HoistStruct::HoistStruct() : inStruct( false ) {
     295                PassVisitor<HoistStruct> hoister;
     296                acceptAll( translationUnit, hoister );
    312297        }
    313298
     
    320305                if ( inStruct ) {
    321306                        // Add elements in stack order corresponding to nesting structure.
    322                         declsToAdd.push_front( aggregateDecl );
    323                         Visitor::visit( aggregateDecl );
     307                        declsToAddBefore.push_front( aggregateDecl );
    324308                } else {
     309                        GuardValue( inStruct );
    325310                        inStruct = true;
    326                         Visitor::visit( aggregateDecl );
    327                         inStruct = false;
    328311                } // if
    329312                // Always remove the hoisted aggregate from the inner structure.
    330                 filter( aggregateDecl->get_members(), isStructOrUnion, false );
    331         }
    332 
    333         void HoistStruct::visit( EnumInstType *structInstType ) {
    334                 if ( structInstType->get_baseEnum() ) {
    335                         declsToAdd.push_front( structInstType->get_baseEnum() );
    336                 }
    337         }
    338 
    339         void HoistStruct::visit( StructInstType *structInstType ) {
    340                 if ( structInstType->get_baseStruct() ) {
    341                         declsToAdd.push_front( structInstType->get_baseStruct() );
    342                 }
    343         }
    344 
    345         void HoistStruct::visit( UnionInstType *structInstType ) {
    346                 if ( structInstType->get_baseUnion() ) {
    347                         declsToAdd.push_front( structInstType->get_baseUnion() );
    348                 }
    349         }
    350 
    351         void HoistStruct::visit( StructDecl *aggregateDecl ) {
     313                GuardAction( [this, aggregateDecl]() { filter( aggregateDecl->members, isStructOrUnion, false ); } );
     314        }
     315
     316        void HoistStruct::previsit( EnumInstType * inst ) {
     317                if ( inst->baseEnum ) {
     318                        declsToAddBefore.push_front( inst->baseEnum );
     319                }
     320        }
     321
     322        void HoistStruct::previsit( StructInstType * inst ) {
     323                if ( inst->baseStruct ) {
     324                        declsToAddBefore.push_front( inst->baseStruct );
     325                }
     326        }
     327
     328        void HoistStruct::previsit( UnionInstType * inst ) {
     329                if ( inst->baseUnion ) {
     330                        declsToAddBefore.push_front( inst->baseUnion );
     331                }
     332        }
     333
     334        void HoistStruct::previsit( StructDecl * aggregateDecl ) {
    352335                handleAggregate( aggregateDecl );
    353336        }
    354337
    355         void HoistStruct::visit( UnionDecl *aggregateDecl ) {
     338        void HoistStruct::previsit( UnionDecl * aggregateDecl ) {
    356339                handleAggregate( aggregateDecl );
    357         }
    358 
    359         void HoistStruct::visit( CompoundStmt *compoundStmt ) {
    360                 addVisit( compoundStmt, *this );
    361         }
    362 
    363         void HoistStruct::visit( SwitchStmt *switchStmt ) {
    364                 addVisit( switchStmt, *this );
    365340        }
    366341
  • src/SymTab/module.mk

    r882ad37 r3ca540f  
    1919       SymTab/Validate.cc \
    2020       SymTab/FixFunction.cc \
    21        SymTab/ImplementationType.cc \
    22        SymTab/TypeEquality.cc \
    2321       SymTab/Autogen.cc
  • src/SynTree/CompoundStmt.cc

    r882ad37 r3ca540f  
    2828using std::endl;
    2929
    30 CompoundStmt::CompoundStmt( std::list<Label> labels ) : Statement( labels ) {
     30CompoundStmt::CompoundStmt() : Statement() {
    3131}
    3232
    33 CompoundStmt::CompoundStmt( std::list<Statement *> stmts ) : Statement( noLabels ), kids( stmts ) {
     33CompoundStmt::CompoundStmt( std::list<Statement *> stmts ) : Statement(), kids( stmts ) {
    3434}
    3535
  • src/SynTree/DeclStmt.cc

    r882ad37 r3ca540f  
    2323#include "SynTree/Label.h"   // for Label
    2424
    25 DeclStmt::DeclStmt( std::list<Label> labels, Declaration *decl ) : Statement( labels ), decl( decl ) {
     25DeclStmt::DeclStmt( Declaration *decl ) : Statement(), decl( decl ) {
    2626}
    2727
  • src/SynTree/Statement.cc

    r882ad37 r3ca540f  
    3232using std::endl;
    3333
    34 Statement::Statement( std::list<Label> labels ) : labels( labels ) {}
     34Statement::Statement( const std::list<Label> & labels ) : labels( labels ) {}
    3535
    3636void Statement::print( std::ostream & os, Indenter ) const {
     
    4646Statement::~Statement() {}
    4747
    48 ExprStmt::ExprStmt( std::list<Label> labels, Expression *expr ) : Statement( labels ), expr( expr ) {}
     48ExprStmt::ExprStmt( Expression *expr ) : Statement(), expr( expr ) {}
    4949
    5050ExprStmt::ExprStmt( const ExprStmt &other ) : Statement( other ), expr( maybeClone( other.expr ) ) {}
     
    6060
    6161
    62 AsmStmt::AsmStmt( std::list<Label> labels, bool voltile, Expression *instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels ) : Statement( labels ), voltile( voltile ), instruction( instruction ), output( output ), input( input ), clobber( clobber ), gotolabels( gotolabels ) {}
     62AsmStmt::AsmStmt( bool voltile, Expression *instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels ) : Statement(), voltile( voltile ), instruction( instruction ), output( output ), input( input ), clobber( clobber ), gotolabels( gotolabels ) {}
    6363
    6464AsmStmt::AsmStmt( const AsmStmt & other ) : Statement( other ), voltile( other.voltile ), instruction( maybeClone( other.instruction ) ), gotolabels( other.gotolabels ) {
     
    9696const char *BranchStmt::brType[] = { "Goto", "Break", "Continue" };
    9797
    98 BranchStmt::BranchStmt( std::list<Label> labels, Label target, Type type ) throw ( SemanticError ) :
    99         Statement( labels ), originalTarget( target ), target( target ), computedTarget( nullptr ), type( type ) {
     98BranchStmt::BranchStmt( Label target, Type type ) throw ( SemanticError ) :
     99        Statement(), originalTarget( target ), target( target ), computedTarget( nullptr ), type( type ) {
    100100        //actually this is a syntactic error signaled by the parser
    101101        if ( type == BranchStmt::Goto && target.empty() ) {
     
    104104}
    105105
    106 BranchStmt::BranchStmt( std::list<Label> labels, Expression *computedTarget, Type type ) throw ( SemanticError ) :
    107         Statement( labels ), computedTarget( computedTarget ), type( type ) {
     106BranchStmt::BranchStmt( Expression *computedTarget, Type type ) throw ( SemanticError ) :
     107        Statement(), computedTarget( computedTarget ), type( type ) {
    108108        if ( type != BranchStmt::Goto || computedTarget == nullptr ) {
    109109                throw SemanticError("Computed target not valid in branch statement");
     
    118118}
    119119
    120 ReturnStmt::ReturnStmt( std::list<Label> labels, Expression *expr ) : Statement( labels ), expr( expr ) {}
     120ReturnStmt::ReturnStmt( Expression *expr ) : Statement(), expr( expr ) {}
    121121
    122122ReturnStmt::ReturnStmt( const ReturnStmt & other ) : Statement( other ), expr( maybeClone( other.expr ) ) {}
     
    135135}
    136136
    137 IfStmt::IfStmt( std::list<Label> labels, Expression *condition, Statement *thenPart, Statement *elsePart, std::list<Statement *> initialization ):
    138         Statement( labels ), condition( condition ), thenPart( thenPart ), elsePart( elsePart ), initialization( initialization ) {}
     137IfStmt::IfStmt( Expression *condition, Statement *thenPart, Statement *elsePart, std::list<Statement *> initialization ):
     138        Statement(), condition( condition ), thenPart( thenPart ), elsePart( elsePart ), initialization( initialization ) {}
    139139
    140140IfStmt::IfStmt( const IfStmt & other ) :
     
    176176}
    177177
    178 SwitchStmt::SwitchStmt( std::list<Label> labels, Expression * condition, const std::list<Statement *> &statements ):
    179         Statement( labels ), condition( condition ), statements( statements ) {
     178SwitchStmt::SwitchStmt( Expression * condition, const std::list<Statement *> &statements ):
     179        Statement(), condition( condition ), statements( statements ) {
    180180}
    181181
     
    201201}
    202202
    203 CaseStmt::CaseStmt( std::list<Label> labels, Expression *condition, const std::list<Statement *> &statements, bool deflt ) throw ( SemanticError ) :
    204         Statement( labels ), condition( condition ), stmts( statements ), _isDefault( deflt ) {
     203CaseStmt::CaseStmt( Expression *condition, const std::list<Statement *> &statements, bool deflt ) throw ( SemanticError ) :
     204        Statement(), condition( condition ), stmts( statements ), _isDefault( deflt ) {
    205205        if ( isDefault() && condition != 0 ) throw SemanticError("default case with condition: ", condition);
    206206}
     
    216216}
    217217
    218 CaseStmt * CaseStmt::makeDefault( std::list<Label> labels, std::list<Statement *> stmts ) {
    219         return new CaseStmt( labels, 0, stmts, true );
     218CaseStmt * CaseStmt::makeDefault( const std::list<Label> & labels, std::list<Statement *> stmts ) {
     219        CaseStmt * stmt = new CaseStmt( nullptr, stmts, true );
     220        stmt->labels = labels;
     221        return stmt;
    220222}
    221223
     
    233235}
    234236
    235 WhileStmt::WhileStmt( std::list<Label> labels, Expression *condition, Statement *body, bool isDoWhile ):
    236         Statement( labels ), condition( condition), body( body), isDoWhile( isDoWhile) {
     237WhileStmt::WhileStmt( Expression *condition, Statement *body, bool isDoWhile ):
     238        Statement(), condition( condition), body( body), isDoWhile( isDoWhile) {
    237239}
    238240
     
    255257}
    256258
    257 ForStmt::ForStmt( std::list<Label> labels, std::list<Statement *> initialization, Expression *condition, Expression *increment, Statement *body ):
    258         Statement( labels ), initialization( initialization ), condition( condition ), increment( increment ), body( body ) {
     259ForStmt::ForStmt( std::list<Statement *> initialization, Expression *condition, Expression *increment, Statement *body ):
     260        Statement(), initialization( initialization ), condition( condition ), increment( increment ), body( body ) {
    259261}
    260262
     
    302304}
    303305
    304 ThrowStmt::ThrowStmt( std::list<Label> labels, Kind kind, Expression * expr, Expression * target ) :
    305                 Statement( labels ), kind(kind), expr(expr), target(target)     {
     306ThrowStmt::ThrowStmt( Kind kind, Expression * expr, Expression * target ) :
     307                Statement(), kind(kind), expr(expr), target(target)     {
    306308        assertf(Resume == kind || nullptr == target, "Non-local termination throw is not accepted." );
    307309}
     
    326328}
    327329
    328 TryStmt::TryStmt( std::list<Label> labels, CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock ) :
    329         Statement( labels ), block( tryBlock ),  handlers( handlers ), finallyBlock( finallyBlock ) {
     330TryStmt::TryStmt( CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock ) :
     331        Statement(), block( tryBlock ),  handlers( handlers ), finallyBlock( finallyBlock ) {
    330332}
    331333
     
    359361}
    360362
    361 CatchStmt::CatchStmt( std::list<Label> labels, Kind kind, Declaration *decl, Expression *cond, Statement *body ) :
    362         Statement( labels ), kind ( kind ), decl ( decl ), cond ( cond ), body( body ) {
     363CatchStmt::CatchStmt( Kind kind, Declaration *decl, Expression *cond, Statement *body ) :
     364        Statement(), kind ( kind ), decl ( decl ), cond ( cond ), body( body ) {
    363365                assertf( decl, "Catch clause must have a declaration." );
    364366}
     
    391393
    392394
    393 FinallyStmt::FinallyStmt( std::list<Label> labels, CompoundStmt *block ) : Statement( labels ), block( block ) {
    394         assert( labels.empty() ); // finally statement cannot be labeled
     395FinallyStmt::FinallyStmt( CompoundStmt *block ) : Statement(), block( block ) {
    395396}
    396397
     
    408409}
    409410
    410 WaitForStmt::WaitForStmt( std::list<Label> labels ) : Statement( labels ) {
     411WaitForStmt::WaitForStmt() : Statement() {
    411412        timeout.time      = nullptr;
    412413        timeout.statement = nullptr;
     
    456457
    457458
    458 WithStmt::WithStmt( const std::list< Expression * > & exprs, Statement * stmt ) : Statement( std::list<Label>() ), exprs( exprs ), stmt( stmt ) {}
     459WithStmt::WithStmt( const std::list< Expression * > & exprs, Statement * stmt ) : Statement(), exprs( exprs ), stmt( stmt ) {}
    459460WithStmt::WithStmt( const WithStmt & other ) : Statement( other ), stmt( maybeClone( other.stmt ) ) {
    460461        cloneAll( other.exprs, exprs );
     
    472473
    473474
    474 NullStmt::NullStmt( std::list<Label> labels ) : Statement( labels ) {}
    475 NullStmt::NullStmt() : Statement( std::list<Label>() ) {}
     475NullStmt::NullStmt( const std::list<Label> & labels ) : Statement( labels ) {
     476}
    476477
    477478void NullStmt::print( std::ostream &os, Indenter ) const {
     
    479480}
    480481
    481 ImplicitCtorDtorStmt::ImplicitCtorDtorStmt( Statement * callStmt ) : Statement( std::list<Label>() ), callStmt( callStmt ) {
     482ImplicitCtorDtorStmt::ImplicitCtorDtorStmt( Statement * callStmt ) : Statement(), callStmt( callStmt ) {
    482483        assert( callStmt );
    483484}
  • src/SynTree/Statement.h

    r882ad37 r3ca540f  
    3737        std::list<Label> labels;
    3838
    39         Statement( std::list<Label> labels );
     39        Statement( const std::list<Label> & labels = {} );
    4040        virtual ~Statement();
    4141
     
    5353        std::list<Statement*> kids;
    5454
    55         CompoundStmt( std::list<Label> labels );
     55        CompoundStmt();
    5656        CompoundStmt( std::list<Statement *> stmts );
    5757        CompoundStmt( const CompoundStmt &other );
     
    7070class NullStmt : public Statement {
    7171  public:
    72         NullStmt();
    73         NullStmt( std::list<Label> labels );
     72        NullStmt( const std::list<Label> & labels = {} );
    7473
    7574        virtual NullStmt *clone() const override { return new NullStmt( *this ); }
     
    8382        Expression *expr;
    8483
    85         ExprStmt( std::list<Label> labels, Expression *expr );
     84        ExprStmt( Expression *expr );
    8685        ExprStmt( const ExprStmt &other );
    8786        virtual ~ExprStmt();
     
    104103        std::list<Label> gotolabels;
    105104
    106         AsmStmt( std::list<Label> labels, bool voltile, Expression *instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels );
     105        AsmStmt( bool voltile, Expression *instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels );
    107106        AsmStmt( const AsmStmt &other );
    108107        virtual ~AsmStmt();
     
    134133        std::list<Statement *> initialization;
    135134
    136         IfStmt( std::list<Label> labels, Expression *condition, Statement *thenPart, Statement *elsePart,
     135        IfStmt( Expression *condition, Statement *thenPart, Statement *elsePart,
    137136                        std::list<Statement *> initialization = std::list<Statement *>() );
    138137        IfStmt( const IfStmt &other );
     
    158157        std::list<Statement *> statements;
    159158
    160         SwitchStmt( std::list<Label> labels, Expression *condition, const std::list<Statement *> &statements );
     159        SwitchStmt( Expression *condition, const std::list<Statement *> &statements );
    161160        SwitchStmt( const SwitchStmt &other );
    162161        virtual ~SwitchStmt();
     
    180179        std::list<Statement *> stmts;
    181180
    182         CaseStmt( std::list<Label> labels, Expression *conditions, const std::list<Statement *> &stmts, bool isdef = false ) throw(SemanticError);
     181        CaseStmt( Expression *conditions, const std::list<Statement *> &stmts, bool isdef = false ) throw(SemanticError);
    183182        CaseStmt( const CaseStmt &other );
    184183        virtual ~CaseStmt();
    185184
    186         static CaseStmt * makeDefault( std::list<Label> labels = std::list<Label>(), std::list<Statement *> stmts = std::list<Statement *>() );
     185        static CaseStmt * makeDefault( const std::list<Label> & labels = {}, std::list<Statement *> stmts = std::list<Statement *>() );
    187186
    188187        bool isDefault() const { return _isDefault; }
     
    210209        bool isDoWhile;
    211210
    212         WhileStmt( std::list<Label> labels, Expression *condition,
     211        WhileStmt( Expression *condition,
    213212               Statement *body, bool isDoWhile = false );
    214213        WhileStmt( const WhileStmt &other );
     
    235234        Statement *body;
    236235
    237         ForStmt( std::list<Label> labels, std::list<Statement *> initialization,
     236        ForStmt( std::list<Statement *> initialization,
    238237             Expression *condition = 0, Expression *increment = 0, Statement *body = 0 );
    239238        ForStmt( const ForStmt &other );
     
    264263        Type type;
    265264
    266         BranchStmt( std::list<Label> labels, Label target, Type ) throw (SemanticError);
    267         BranchStmt( std::list<Label> labels, Expression *computedTarget, Type ) throw (SemanticError);
     265        BranchStmt( Label target, Type ) throw (SemanticError);
     266        BranchStmt( Expression *computedTarget, Type ) throw (SemanticError);
    268267
    269268        Label get_originalTarget() { return originalTarget; }
     
    289288        Expression *expr;
    290289
    291         ReturnStmt( std::list<Label> labels, Expression *expr );
     290        ReturnStmt( Expression *expr );
    292291        ReturnStmt( const ReturnStmt &other );
    293292        virtual ~ReturnStmt();
     
    310309        Expression * target;
    311310
    312         ThrowStmt( std::list<Label> labels, Kind kind, Expression * expr, Expression * target = nullptr );
     311        ThrowStmt( Kind kind, Expression * expr, Expression * target = nullptr );
    313312        ThrowStmt( const ThrowStmt &other );
    314313        virtual ~ThrowStmt();
     
    332331        FinallyStmt * finallyBlock;
    333332
    334         TryStmt( std::list<Label> labels, CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock = 0 );
     333        TryStmt( CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock = 0 );
    335334        TryStmt( const TryStmt &other );
    336335        virtual ~TryStmt();
     
    358357        Statement *body;
    359358
    360         CatchStmt( std::list<Label> labels, Kind kind, Declaration *decl,
     359        CatchStmt( Kind kind, Declaration *decl,
    361360                   Expression *cond, Statement *body );
    362361        CatchStmt( const CatchStmt &other );
     
    381380        CompoundStmt *block;
    382381
    383         FinallyStmt( std::list<Label> labels, CompoundStmt *block );
     382        FinallyStmt( CompoundStmt *block );
    384383        FinallyStmt( const FinallyStmt &other );
    385384        virtual ~FinallyStmt();
     
    408407        };
    409408
    410         WaitForStmt( std::list<Label> labels = noLabels );
     409        WaitForStmt();
    411410        WaitForStmt( const WaitForStmt & );
    412411        virtual ~WaitForStmt();
     
    453452        Declaration *decl;
    454453
    455         DeclStmt( std::list<Label> labels, Declaration *decl );
     454        DeclStmt( Declaration *decl );
    456455        DeclStmt( const DeclStmt &other );
    457456        virtual ~DeclStmt();
  • src/SynTree/TupleExpr.cc

    r882ad37 r3ca540f  
    2323#include "Declaration.h"        // for ObjectDecl
    2424#include "Expression.h"         // for Expression, TupleExpr, TupleIndexExpr
    25 #include "SynTree/Label.h"      // for Label, noLabels
     25#include "SynTree/Label.h"      // for Label
    2626#include "SynTree/Statement.h"  // for CompoundStmt, DeclStmt, ExprStmt, Sta...
    2727#include "Tuples/Tuples.h"      // for makeTupleType
     
    8989        // convert internally into a StmtExpr which contains the declarations and produces the tuple of the assignments
    9090        set_result( Tuples::makeTupleType( assigns ) );
    91         CompoundStmt * compoundStmt = new CompoundStmt( noLabels );
     91        CompoundStmt * compoundStmt = new CompoundStmt();
    9292        std::list< Statement * > & stmts = compoundStmt->get_kids();
    9393        for ( ObjectDecl * obj : tempDecls ) {
    94                 stmts.push_back( new DeclStmt( noLabels, obj ) );
     94                stmts.push_back( new DeclStmt( obj ) );
    9595        }
    9696        TupleExpr * tupleExpr = new TupleExpr( assigns );
    9797        assert( tupleExpr->get_result() );
    98         stmts.push_back( new ExprStmt( noLabels, tupleExpr ) );
     98        stmts.push_back( new ExprStmt( tupleExpr ) );
    9999        stmtExpr = new StmtExpr( compoundStmt );
    100100}
  • src/Tuples/TupleAssignment.cc

    r882ad37 r3ca540f  
    2323
    2424#include "CodeGen/OperatorTable.h"
     25#include "Common/PassVisitor.h"
    2526#include "Common/UniqueName.h"             // for UniqueName
    2627#include "Common/utility.h"                // for zipWith
     
    6162                struct Matcher {
    6263                  public:
    63                         Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, const 
     64                        Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, const
    6465                                ResolvExpr::AltList& rhs );
    6566                        virtual ~Matcher() {}
     
    7576                struct MassAssignMatcher : public Matcher {
    7677                  public:
    77                         MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 
     78                        MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs,
    7879                                const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
    7980                        virtual void match( std::list< Expression * > &out );
     
    8283                struct MultipleAssignMatcher : public Matcher {
    8384                  public:
    84                         MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 
     85                        MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs,
    8586                                const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
    8687                        virtual void match( std::list< Expression * > &out );
     
    119120        }
    120121
    121         void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr, 
     122        void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr,
    122123                                std::vector<ResolvExpr::AlternativeFinder> &args ) {
    123124                TupleAssignSpotter spotter( currentFinder );
     
    128129                : currentFinder(f) {}
    129130
    130         void TupleAssignSpotter::spot( UntypedExpr * expr, 
     131        void TupleAssignSpotter::spot( UntypedExpr * expr,
    131132                        std::vector<ResolvExpr::AlternativeFinder> &args ) {
    132133                if (  NameExpr *op = dynamic_cast< NameExpr * >(expr->get_function()) ) {
     
    137138                                if ( args.size() == 0 ) return;
    138139
    139                                 // if an assignment only takes 1 argument, that's odd, but maybe someone wrote 
     140                                // if an assignment only takes 1 argument, that's odd, but maybe someone wrote
    140141                                // the function, in which case AlternativeFinder will handle it normally
    141142                                if ( args.size() == 1 && CodeGen::isAssignment( fname ) ) return;
     
    146147                                        if ( ! refToTuple(lhsAlt.expr) ) continue;
    147148
    148                                         // explode is aware of casts - ensure every LHS expression is sent into explode 
     149                                        // explode is aware of casts - ensure every LHS expression is sent into explode
    149150                                        // with a reference cast
    150                                         // xxx - this seems to change the alternatives before the normal 
     151                                        // xxx - this seems to change the alternatives before the normal
    151152                                        //  AlternativeFinder flow; maybe this is desired?
    152153                                        if ( ! dynamic_cast<CastExpr*>( lhsAlt.expr ) ) {
    153                                                 lhsAlt.expr = new CastExpr( lhsAlt.expr, 
    154                                                                 new ReferenceType( Type::Qualifiers(), 
     154                                                lhsAlt.expr = new CastExpr( lhsAlt.expr,
     155                                                                new ReferenceType( Type::Qualifiers(),
    155156                                                                        lhsAlt.expr->get_result()->clone() ) );
    156157                                        }
     
    160161                                        explode( lhsAlt, currentFinder.get_indexer(), back_inserter(lhs), true );
    161162                                        for ( ResolvExpr::Alternative& alt : lhs ) {
    162                                                 // each LHS value must be a reference - some come in with a cast expression, 
     163                                                // each LHS value must be a reference - some come in with a cast expression,
    163164                                                // if not just cast to reference here
    164165                                                if ( ! dynamic_cast<ReferenceType*>( alt.expr->get_result() ) ) {
    165                                                         alt.expr = new CastExpr( alt.expr, 
    166                                                                 new ReferenceType( Type::Qualifiers(), 
     166                                                        alt.expr = new CastExpr( alt.expr,
     167                                                                new ReferenceType( Type::Qualifiers(),
    167168                                                                        alt.expr->get_result()->clone() ) );
    168169                                                }
     
    178179                                                // TODO build iterative version of this instead of using combos
    179180                                                std::vector< ResolvExpr::AltList > rhsAlts;
    180                                                 combos( std::next(args.begin(), 1), args.end(), 
     181                                                combos( std::next(args.begin(), 1), args.end(),
    181182                                                        std::back_inserter( rhsAlts ) );
    182183                                                for ( const ResolvExpr::AltList& rhsAlt : rhsAlts ) {
    183184                                                        // multiple assignment
    184185                                                        ResolvExpr::AltList rhs;
    185                                                         explode( rhsAlt, currentFinder.get_indexer(), 
     186                                                        explode( rhsAlt, currentFinder.get_indexer(),
    186187                                                                std::back_inserter(rhs), true );
    187188                                                        matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );
     
    193194                                                        if ( isTuple(rhsAlt.expr) ) {
    194195                                                                // multiple assignment
    195                                                                 explode( rhsAlt, currentFinder.get_indexer(), 
     196                                                                explode( rhsAlt, currentFinder.get_indexer(),
    196197                                                                        std::back_inserter(rhs), true );
    197198                                                                matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );
     
    222223                ResolvExpr::AltList current;
    223224                // now resolve new assignments
    224                 for ( std::list< Expression * >::iterator i = new_assigns.begin(); 
     225                for ( std::list< Expression * >::iterator i = new_assigns.begin();
    225226                                i != new_assigns.end(); ++i ) {
    226227                        PRINT(
     
    229230                        )
    230231
    231                         ResolvExpr::AlternativeFinder finder{ currentFinder.get_indexer(), 
     232                        ResolvExpr::AlternativeFinder finder{ currentFinder.get_indexer(),
    232233                                currentFinder.get_environ() };
    233234                        try {
     
    253254                // xxx -- was push_front
    254255                currentFinder.get_alternatives().push_back( ResolvExpr::Alternative(
    255                         new TupleAssignExpr(solved_assigns, matcher->tmpDecls), matcher->compositeEnv, 
     256                        new TupleAssignExpr(solved_assigns, matcher->tmpDecls), matcher->compositeEnv,
    256257                        ResolvExpr::sumCost( current ) + matcher->baseCost ) );
    257258        }
    258259
    259         TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter, 
    260                 const ResolvExpr::AltList &lhs, const ResolvExpr::AltList &rhs ) 
    261         : lhs(lhs), rhs(rhs), spotter(spotter), 
     260        TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter,
     261                const ResolvExpr::AltList &lhs, const ResolvExpr::AltList &rhs )
     262        : lhs(lhs), rhs(rhs), spotter(spotter),
    262263          baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ) {
    263264                simpleCombineEnvironments( lhs.begin(), lhs.end(), compositeEnv );
     
    277278        // xxx - maybe this should happen in alternative finder for every StmtExpr?
    278279        // xxx - it's possible that these environments could contain some useful information. Maybe the right thing to do is aggregate the environments and pass the aggregate back to be added into the compositeEnv
    279         struct EnvRemover : public Visitor {
    280                 virtual void visit( ExprStmt * stmt ) {
    281                         delete stmt->get_expr()->get_env();
    282                         stmt->get_expr()->set_env( nullptr );
    283                         Visitor::visit( stmt );
     280        struct EnvRemover {
     281                void previsit( ExprStmt * stmt ) {
     282                        delete stmt->expr->env;
     283                        stmt->expr->env = nullptr;
    284284                }
    285285        };
     
    293293                        ret->set_init( ctorInit );
    294294                        ResolvExpr::resolveCtorInit( ctorInit, spotter.currentFinder.get_indexer() ); // resolve ctor/dtors for the new object
    295                         EnvRemover rm; // remove environments from subexpressions of StmtExprs
     295                        PassVisitor<EnvRemover> rm; // remove environments from subexpressions of StmtExprs
    296296                        ctorInit->accept( rm );
    297297                }
  • src/Tuples/TupleExpansion.cc

    r882ad37 r3ca540f  
    315315        namespace {
    316316                /// determines if impurity (read: side-effects) may exist in a piece of code. Currently gives a very crude approximation, wherein any function call expression means the code may be impure
    317                 class ImpurityDetector : public Visitor {
    318                 public:
     317                struct ImpurityDetector : public WithShortCircuiting {
    319318                        ImpurityDetector( bool ignoreUnique ) : ignoreUnique( ignoreUnique ) {}
    320319
    321                         typedef Visitor Parent;
    322                         virtual void visit( ApplicationExpr * appExpr ) {
     320                        void previsit( ApplicationExpr * appExpr ) {
     321                                visit_children = false;
    323322                                if ( DeclarationWithType * function = InitTweak::getFunction( appExpr ) ) {
    324323                                        if ( function->get_linkage() == LinkageSpec::Intrinsic ) {
    325324                                                if ( function->get_name() == "*?" || function->get_name() == "?[?]" ) {
    326325                                                        // intrinsic dereference, subscript are pure, but need to recursively look for impurity
    327                                                         Parent::visit( appExpr );
     326                                                        visit_children = true;
    328327                                                        return;
    329328                                                }
     
    332331                                maybeImpure = true;
    333332                        }
    334                         virtual void visit( UntypedExpr * ) { maybeImpure = true; }
    335                         virtual void visit( UniqueExpr * unq ) {
     333                        void previsit( UntypedExpr * ) { maybeImpure = true; visit_children = false; }
     334                        void previsit( UniqueExpr * ) {
    336335                                if ( ignoreUnique ) {
    337336                                        // bottom out at unique expression.
    338337                                        // The existence of a unique expression doesn't change the purity of an expression.
    339338                                        // That is, even if the wrapped expression is impure, the wrapper protects the rest of the expression.
     339                                        visit_children = false;
    340340                                        return;
    341341                                }
    342                                 maybeAccept( unq->expr, *this );
    343342                        }
    344343
     
    349348
    350349        bool maybeImpure( Expression * expr ) {
    351                 ImpurityDetector detector( false );
     350                PassVisitor<ImpurityDetector> detector( false );
    352351                expr->accept( detector );
    353                 return detector.maybeImpure;
     352                return detector.pass.maybeImpure;
    354353        }
    355354
    356355        bool maybeImpureIgnoreUnique( Expression * expr ) {
    357                 ImpurityDetector detector( true );
     356                PassVisitor<ImpurityDetector> detector( true );
    358357                expr->accept( detector );
    359                 return detector.maybeImpure;
     358                return detector.pass.maybeImpure;
    360359        }
    361360} // namespace Tuples
  • src/driver/cfa.cc

    r882ad37 r3ca540f  
    275275                args[nargs] = "-Xlinker";
    276276                nargs += 1;
    277                 args[nargs] = "--undefined=__lib_debug_write";
     277                args[nargs] = "--undefined=__cfaabi_dbg_bits_write";
    278278                nargs += 1;
    279279
  • src/libcfa/Makefile.am

    r882ad37 r3ca540f  
    5555
    5656libobjs = ${headers:=.o}
    57 libsrc = libcfa-prelude.c interpose.c libhdr/libdebug.c ${headers:=.c} \
     57libsrc = libcfa-prelude.c interpose.c bits/debug.c ${headers:=.c} \
    5858         assert.c exception.c virtual.c
    5959
     
    100100        math                            \
    101101        gmp                             \
     102        bits/align.h            \
    102103        bits/containers.h               \
    103104        bits/defs.h             \
     105        bits/debug.h            \
    104106        bits/locks.h            \
    105         concurrency/invoke.h    \
    106         libhdr.h                        \
    107         libhdr/libalign.h       \
    108         libhdr/libdebug.h       \
    109         libhdr/libtools.h
     107        concurrency/invoke.h
    110108
    111109CLEANFILES = libcfa-prelude.c
  • src/libcfa/Makefile.in

    r882ad37 r3ca540f  
    149149libcfa_d_a_LIBADD =
    150150am__libcfa_d_a_SOURCES_DIST = libcfa-prelude.c interpose.c \
    151         libhdr/libdebug.c fstream.c iostream.c iterator.c limits.c \
     151        bits/debug.c fstream.c iostream.c iterator.c limits.c \
    152152        rational.c stdlib.c containers/maybe.c containers/pair.c \
    153153        containers/result.c containers/vector.c \
     
    175175@BUILD_CONCURRENCY_TRUE@        concurrency/libcfa_d_a-preemption.$(OBJEXT)
    176176am__objects_4 = libcfa_d_a-libcfa-prelude.$(OBJEXT) \
    177         libcfa_d_a-interpose.$(OBJEXT) \
    178         libhdr/libcfa_d_a-libdebug.$(OBJEXT) $(am__objects_2) \
    179         libcfa_d_a-assert.$(OBJEXT) libcfa_d_a-exception.$(OBJEXT) \
    180         libcfa_d_a-virtual.$(OBJEXT) $(am__objects_3)
     177        libcfa_d_a-interpose.$(OBJEXT) bits/libcfa_d_a-debug.$(OBJEXT) \
     178        $(am__objects_2) libcfa_d_a-assert.$(OBJEXT) \
     179        libcfa_d_a-exception.$(OBJEXT) libcfa_d_a-virtual.$(OBJEXT) \
     180        $(am__objects_3)
    181181am_libcfa_d_a_OBJECTS = $(am__objects_4)
    182182libcfa_d_a_OBJECTS = $(am_libcfa_d_a_OBJECTS)
    183183libcfa_a_AR = $(AR) $(ARFLAGS)
    184184libcfa_a_LIBADD =
    185 am__libcfa_a_SOURCES_DIST = libcfa-prelude.c interpose.c \
    186         libhdr/libdebug.c fstream.c iostream.c iterator.c limits.c \
    187         rational.c stdlib.c containers/maybe.c containers/pair.c \
    188         containers/result.c containers/vector.c \
    189         concurrency/coroutine.c concurrency/thread.c \
    190         concurrency/kernel.c concurrency/monitor.c assert.c \
    191         exception.c virtual.c concurrency/CtxSwitch-@MACHINE_TYPE@.S \
    192         concurrency/alarm.c concurrency/invoke.c \
    193         concurrency/preemption.c
     185am__libcfa_a_SOURCES_DIST = libcfa-prelude.c interpose.c bits/debug.c \
     186        fstream.c iostream.c iterator.c limits.c rational.c stdlib.c \
     187        containers/maybe.c containers/pair.c containers/result.c \
     188        containers/vector.c concurrency/coroutine.c \
     189        concurrency/thread.c concurrency/kernel.c \
     190        concurrency/monitor.c assert.c exception.c virtual.c \
     191        concurrency/CtxSwitch-@MACHINE_TYPE@.S concurrency/alarm.c \
     192        concurrency/invoke.c concurrency/preemption.c
    194193@BUILD_CONCURRENCY_TRUE@am__objects_5 = concurrency/libcfa_a-coroutine.$(OBJEXT) \
    195194@BUILD_CONCURRENCY_TRUE@        concurrency/libcfa_a-thread.$(OBJEXT) \
     
    208207@BUILD_CONCURRENCY_TRUE@        concurrency/libcfa_a-preemption.$(OBJEXT)
    209208am__objects_8 = libcfa_a-libcfa-prelude.$(OBJEXT) \
    210         libcfa_a-interpose.$(OBJEXT) \
    211         libhdr/libcfa_a-libdebug.$(OBJEXT) $(am__objects_6) \
    212         libcfa_a-assert.$(OBJEXT) libcfa_a-exception.$(OBJEXT) \
    213         libcfa_a-virtual.$(OBJEXT) $(am__objects_7)
     209        libcfa_a-interpose.$(OBJEXT) bits/libcfa_a-debug.$(OBJEXT) \
     210        $(am__objects_6) libcfa_a-assert.$(OBJEXT) \
     211        libcfa_a-exception.$(OBJEXT) libcfa_a-virtual.$(OBJEXT) \
     212        $(am__objects_7)
    214213am_libcfa_a_OBJECTS = $(am__objects_8)
    215214libcfa_a_OBJECTS = $(am_libcfa_a_OBJECTS)
     
    264263        containers/result containers/vector concurrency/coroutine \
    265264        concurrency/thread concurrency/kernel concurrency/monitor \
    266         ${shell echo stdhdr/*} math gmp bits/containers.h bits/defs.h \
    267         bits/locks.h concurrency/invoke.h libhdr.h libhdr/libalign.h \
    268         libhdr/libdebug.h libhdr/libtools.h
     265        ${shell echo stdhdr/*} math gmp bits/align.h bits/containers.h \
     266        bits/defs.h bits/debug.h bits/locks.h concurrency/invoke.h
    269267HEADERS = $(nobase_cfa_include_HEADERS)
    270268am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
     
    424422        containers/vector $(am__append_3)
    425423libobjs = ${headers:=.o}
    426 libsrc = libcfa-prelude.c interpose.c libhdr/libdebug.c ${headers:=.c} \
     424libsrc = libcfa-prelude.c interpose.c bits/debug.c ${headers:=.c} \
    427425        assert.c exception.c virtual.c $(am__append_4)
    428426libcfa_a_SOURCES = ${libsrc}
     
    437435        math                            \
    438436        gmp                             \
     437        bits/align.h            \
    439438        bits/containers.h               \
    440439        bits/defs.h             \
     440        bits/debug.h            \
    441441        bits/locks.h            \
    442         concurrency/invoke.h    \
    443         libhdr.h                        \
    444         libhdr/libalign.h       \
    445         libhdr/libdebug.h       \
    446         libhdr/libtools.h
     442        concurrency/invoke.h
    447443
    448444CLEANFILES = libcfa-prelude.c
     
    511507clean-libLIBRARIES:
    512508        -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
    513 libhdr/$(am__dirstamp):
    514         @$(MKDIR_P) libhdr
    515         @: > libhdr/$(am__dirstamp)
    516 libhdr/$(DEPDIR)/$(am__dirstamp):
    517         @$(MKDIR_P) libhdr/$(DEPDIR)
    518         @: > libhdr/$(DEPDIR)/$(am__dirstamp)
    519 libhdr/libcfa_d_a-libdebug.$(OBJEXT): libhdr/$(am__dirstamp) \
    520         libhdr/$(DEPDIR)/$(am__dirstamp)
     509bits/$(am__dirstamp):
     510        @$(MKDIR_P) bits
     511        @: > bits/$(am__dirstamp)
     512bits/$(DEPDIR)/$(am__dirstamp):
     513        @$(MKDIR_P) bits/$(DEPDIR)
     514        @: > bits/$(DEPDIR)/$(am__dirstamp)
     515bits/libcfa_d_a-debug.$(OBJEXT): bits/$(am__dirstamp) \
     516        bits/$(DEPDIR)/$(am__dirstamp)
    521517containers/$(am__dirstamp):
    522518        @$(MKDIR_P) containers
     
    563559        $(AM_V_AR)$(libcfa_d_a_AR) libcfa-d.a $(libcfa_d_a_OBJECTS) $(libcfa_d_a_LIBADD)
    564560        $(AM_V_at)$(RANLIB) libcfa-d.a
    565 libhdr/libcfa_a-libdebug.$(OBJEXT): libhdr/$(am__dirstamp) \
    566         libhdr/$(DEPDIR)/$(am__dirstamp)
     561bits/libcfa_a-debug.$(OBJEXT): bits/$(am__dirstamp) \
     562        bits/$(DEPDIR)/$(am__dirstamp)
    567563containers/libcfa_a-maybe.$(OBJEXT): containers/$(am__dirstamp) \
    568564        containers/$(DEPDIR)/$(am__dirstamp)
     
    596592mostlyclean-compile:
    597593        -rm -f *.$(OBJEXT)
     594        -rm -f bits/*.$(OBJEXT)
    598595        -rm -f concurrency/*.$(OBJEXT)
    599596        -rm -f containers/*.$(OBJEXT)
    600         -rm -f libhdr/*.$(OBJEXT)
    601597
    602598distclean-compile:
     
    625621@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcfa_d_a-stdlib.Po@am__quote@
    626622@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcfa_d_a-virtual.Po@am__quote@
     623@AMDEP_TRUE@@am__include@ @am__quote@bits/$(DEPDIR)/libcfa_a-debug.Po@am__quote@
     624@AMDEP_TRUE@@am__include@ @am__quote@bits/$(DEPDIR)/libcfa_d_a-debug.Po@am__quote@
    627625@AMDEP_TRUE@@am__include@ @am__quote@concurrency/$(DEPDIR)/CtxSwitch-@MACHINE_TYPE@.Po@am__quote@
    628626@AMDEP_TRUE@@am__include@ @am__quote@concurrency/$(DEPDIR)/libcfa_a-alarm.Po@am__quote@
     
    648646@AMDEP_TRUE@@am__include@ @am__quote@containers/$(DEPDIR)/libcfa_d_a-result.Po@am__quote@
    649647@AMDEP_TRUE@@am__include@ @am__quote@containers/$(DEPDIR)/libcfa_d_a-vector.Po@am__quote@
    650 @AMDEP_TRUE@@am__include@ @am__quote@libhdr/$(DEPDIR)/libcfa_a-libdebug.Po@am__quote@
    651 @AMDEP_TRUE@@am__include@ @am__quote@libhdr/$(DEPDIR)/libcfa_d_a-libdebug.Po@am__quote@
    652648
    653649.S.o:
     
    704700@am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -c -o libcfa_d_a-interpose.obj `if test -f 'interpose.c'; then $(CYGPATH_W) 'interpose.c'; else $(CYGPATH_W) '$(srcdir)/interpose.c'; fi`
    705701
    706 libhdr/libcfa_d_a-libdebug.o: libhdr/libdebug.c
    707 @am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -MT libhdr/libcfa_d_a-libdebug.o -MD -MP -MF libhdr/$(DEPDIR)/libcfa_d_a-libdebug.Tpo -c -o libhdr/libcfa_d_a-libdebug.o `test -f 'libhdr/libdebug.c' || echo '$(srcdir)/'`libhdr/libdebug.c
    708 @am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) libhdr/$(DEPDIR)/libcfa_d_a-libdebug.Tpo libhdr/$(DEPDIR)/libcfa_d_a-libdebug.Po
    709 @AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='libhdr/libdebug.c' object='libhdr/libcfa_d_a-libdebug.o' libtool=no @AMDEPBACKSLASH@
    710 @AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    711 @am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -c -o libhdr/libcfa_d_a-libdebug.o `test -f 'libhdr/libdebug.c' || echo '$(srcdir)/'`libhdr/libdebug.c
    712 
    713 libhdr/libcfa_d_a-libdebug.obj: libhdr/libdebug.c
    714 @am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -MT libhdr/libcfa_d_a-libdebug.obj -MD -MP -MF libhdr/$(DEPDIR)/libcfa_d_a-libdebug.Tpo -c -o libhdr/libcfa_d_a-libdebug.obj `if test -f 'libhdr/libdebug.c'; then $(CYGPATH_W) 'libhdr/libdebug.c'; else $(CYGPATH_W) '$(srcdir)/libhdr/libdebug.c'; fi`
    715 @am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) libhdr/$(DEPDIR)/libcfa_d_a-libdebug.Tpo libhdr/$(DEPDIR)/libcfa_d_a-libdebug.Po
    716 @AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='libhdr/libdebug.c' object='libhdr/libcfa_d_a-libdebug.obj' libtool=no @AMDEPBACKSLASH@
    717 @AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    718 @am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -c -o libhdr/libcfa_d_a-libdebug.obj `if test -f 'libhdr/libdebug.c'; then $(CYGPATH_W) 'libhdr/libdebug.c'; else $(CYGPATH_W) '$(srcdir)/libhdr/libdebug.c'; fi`
     702bits/libcfa_d_a-debug.o: bits/debug.c
     703@am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -MT bits/libcfa_d_a-debug.o -MD -MP -MF bits/$(DEPDIR)/libcfa_d_a-debug.Tpo -c -o bits/libcfa_d_a-debug.o `test -f 'bits/debug.c' || echo '$(srcdir)/'`bits/debug.c
     704@am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) bits/$(DEPDIR)/libcfa_d_a-debug.Tpo bits/$(DEPDIR)/libcfa_d_a-debug.Po
     705@AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='bits/debug.c' object='bits/libcfa_d_a-debug.o' libtool=no @AMDEPBACKSLASH@
     706@AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
     707@am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -c -o bits/libcfa_d_a-debug.o `test -f 'bits/debug.c' || echo '$(srcdir)/'`bits/debug.c
     708
     709bits/libcfa_d_a-debug.obj: bits/debug.c
     710@am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -MT bits/libcfa_d_a-debug.obj -MD -MP -MF bits/$(DEPDIR)/libcfa_d_a-debug.Tpo -c -o bits/libcfa_d_a-debug.obj `if test -f 'bits/debug.c'; then $(CYGPATH_W) 'bits/debug.c'; else $(CYGPATH_W) '$(srcdir)/bits/debug.c'; fi`
     711@am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) bits/$(DEPDIR)/libcfa_d_a-debug.Tpo bits/$(DEPDIR)/libcfa_d_a-debug.Po
     712@AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='bits/debug.c' object='bits/libcfa_d_a-debug.obj' libtool=no @AMDEPBACKSLASH@
     713@AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
     714@am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_d_a_CFLAGS) $(CFLAGS) -c -o bits/libcfa_d_a-debug.obj `if test -f 'bits/debug.c'; then $(CYGPATH_W) 'bits/debug.c'; else $(CYGPATH_W) '$(srcdir)/bits/debug.c'; fi`
    719715
    720716libcfa_d_a-fstream.o: fstream.c
     
    998994@am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -c -o libcfa_a-interpose.obj `if test -f 'interpose.c'; then $(CYGPATH_W) 'interpose.c'; else $(CYGPATH_W) '$(srcdir)/interpose.c'; fi`
    999995
    1000 libhdr/libcfa_a-libdebug.o: libhdr/libdebug.c
    1001 @am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -MT libhdr/libcfa_a-libdebug.o -MD -MP -MF libhdr/$(DEPDIR)/libcfa_a-libdebug.Tpo -c -o libhdr/libcfa_a-libdebug.o `test -f 'libhdr/libdebug.c' || echo '$(srcdir)/'`libhdr/libdebug.c
    1002 @am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) libhdr/$(DEPDIR)/libcfa_a-libdebug.Tpo libhdr/$(DEPDIR)/libcfa_a-libdebug.Po
    1003 @AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='libhdr/libdebug.c' object='libhdr/libcfa_a-libdebug.o' libtool=no @AMDEPBACKSLASH@
    1004 @AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    1005 @am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -c -o libhdr/libcfa_a-libdebug.o `test -f 'libhdr/libdebug.c' || echo '$(srcdir)/'`libhdr/libdebug.c
    1006 
    1007 libhdr/libcfa_a-libdebug.obj: libhdr/libdebug.c
    1008 @am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -MT libhdr/libcfa_a-libdebug.obj -MD -MP -MF libhdr/$(DEPDIR)/libcfa_a-libdebug.Tpo -c -o libhdr/libcfa_a-libdebug.obj `if test -f 'libhdr/libdebug.c'; then $(CYGPATH_W) 'libhdr/libdebug.c'; else $(CYGPATH_W) '$(srcdir)/libhdr/libdebug.c'; fi`
    1009 @am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) libhdr/$(DEPDIR)/libcfa_a-libdebug.Tpo libhdr/$(DEPDIR)/libcfa_a-libdebug.Po
    1010 @AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='libhdr/libdebug.c' object='libhdr/libcfa_a-libdebug.obj' libtool=no @AMDEPBACKSLASH@
    1011 @AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    1012 @am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -c -o libhdr/libcfa_a-libdebug.obj `if test -f 'libhdr/libdebug.c'; then $(CYGPATH_W) 'libhdr/libdebug.c'; else $(CYGPATH_W) '$(srcdir)/libhdr/libdebug.c'; fi`
     996bits/libcfa_a-debug.o: bits/debug.c
     997@am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -MT bits/libcfa_a-debug.o -MD -MP -MF bits/$(DEPDIR)/libcfa_a-debug.Tpo -c -o bits/libcfa_a-debug.o `test -f 'bits/debug.c' || echo '$(srcdir)/'`bits/debug.c
     998@am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) bits/$(DEPDIR)/libcfa_a-debug.Tpo bits/$(DEPDIR)/libcfa_a-debug.Po
     999@AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='bits/debug.c' object='bits/libcfa_a-debug.o' libtool=no @AMDEPBACKSLASH@
     1000@AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
     1001@am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -c -o bits/libcfa_a-debug.o `test -f 'bits/debug.c' || echo '$(srcdir)/'`bits/debug.c
     1002
     1003bits/libcfa_a-debug.obj: bits/debug.c
     1004@am__fastdepCC_TRUE@    $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -MT bits/libcfa_a-debug.obj -MD -MP -MF bits/$(DEPDIR)/libcfa_a-debug.Tpo -c -o bits/libcfa_a-debug.obj `if test -f 'bits/debug.c'; then $(CYGPATH_W) 'bits/debug.c'; else $(CYGPATH_W) '$(srcdir)/bits/debug.c'; fi`
     1005@am__fastdepCC_TRUE@    $(AM_V_at)$(am__mv) bits/$(DEPDIR)/libcfa_a-debug.Tpo bits/$(DEPDIR)/libcfa_a-debug.Po
     1006@AMDEP_TRUE@@am__fastdepCC_FALSE@       $(AM_V_CC)source='bits/debug.c' object='bits/libcfa_a-debug.obj' libtool=no @AMDEPBACKSLASH@
     1007@AMDEP_TRUE@@am__fastdepCC_FALSE@       DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
     1008@am__fastdepCC_FALSE@   $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libcfa_a_CFLAGS) $(CFLAGS) -c -o bits/libcfa_a-debug.obj `if test -f 'bits/debug.c'; then $(CYGPATH_W) 'bits/debug.c'; else $(CYGPATH_W) '$(srcdir)/bits/debug.c'; fi`
    10131009
    10141010libcfa_a-fstream.o: fstream.c
     
    14111407        -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
    14121408        -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
     1409        -rm -f bits/$(DEPDIR)/$(am__dirstamp)
     1410        -rm -f bits/$(am__dirstamp)
    14131411        -rm -f concurrency/$(DEPDIR)/$(am__dirstamp)
    14141412        -rm -f concurrency/$(am__dirstamp)
    14151413        -rm -f containers/$(DEPDIR)/$(am__dirstamp)
    14161414        -rm -f containers/$(am__dirstamp)
    1417         -rm -f libhdr/$(DEPDIR)/$(am__dirstamp)
    1418         -rm -f libhdr/$(am__dirstamp)
    14191415
    14201416maintainer-clean-generic:
     
    14261422
    14271423distclean: distclean-am
    1428         -rm -rf ./$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR) libhdr/$(DEPDIR)
     1424        -rm -rf ./$(DEPDIR) bits/$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR)
    14291425        -rm -f Makefile
    14301426distclean-am: clean-am distclean-compile distclean-generic \
     
    14721468
    14731469maintainer-clean: maintainer-clean-am
    1474         -rm -rf ./$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR) libhdr/$(DEPDIR)
     1470        -rm -rf ./$(DEPDIR) bits/$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR)
    14751471        -rm -f Makefile
    14761472maintainer-clean-am: distclean-am maintainer-clean-generic \
  • src/libcfa/assert.c

    r882ad37 r3ca540f  
    1717#include <stdarg.h>                                                             // varargs
    1818#include <stdio.h>                                                              // fprintf
    19 #include "libhdr/libdebug.h"
     19#include "bits/debug.h"
    2020
    2121extern "C" {
     
    2626        // called by macro assert in assert.h
    2727        void __assert_fail( const char *assertion, const char *file, unsigned int line, const char *function ) {
    28                 __lib_debug_print_safe( CFA_ASSERT_FMT ".\n", __progname, function, line, file );
     28                __cfaabi_dbg_bits_print_safe( CFA_ASSERT_FMT ".\n", __progname, function, line, file );
    2929                abort();
    3030        }
     
    3232        // called by macro assertf
    3333        void __assert_fail_f( const char *assertion, const char *file, unsigned int line, const char *function, const char *fmt, ... ) {
    34                 __lib_debug_acquire();
    35                 __lib_debug_print_nolock( CFA_ASSERT_FMT ": ", __progname, function, line, file );
     34                __cfaabi_dbg_bits_acquire();
     35                __cfaabi_dbg_bits_print_nolock( CFA_ASSERT_FMT ": ", __progname, function, line, file );
    3636
    3737                va_list args;
    3838                va_start( args, fmt );
    39                 __lib_debug_print_vararg( fmt, args );
     39                __cfaabi_dbg_bits_print_vararg( fmt, args );
    4040                va_end( args );
    4141
    42                 __lib_debug_print_nolock( "\n" );
    43                 __lib_debug_release();
     42                __cfaabi_dbg_bits_print_nolock( "\n" );
     43                __cfaabi_dbg_bits_release();
    4444                abort();
    4545        }
  • src/libcfa/bits/containers.h

    r882ad37 r3ca540f  
    1515#pragma once
    1616
     17#include "bits/align.h"
    1718#include "bits/defs.h"
    18 #include "libhdr.h"
    1919
    2020//-----------------------------------------------------------------------------
  • src/libcfa/bits/defs.h

    r882ad37 r3ca540f  
    3232#define __cfa_anonymous_object __cfa_anonymous_object
    3333#endif
     34
     35#ifdef __cforall
     36extern "C" {
     37#endif
     38void abortf( const char fmt[], ... ) __attribute__ ((__nothrow__, __leaf__, __noreturn__));
     39#ifdef __cforall
     40}
     41#endif
  • src/libcfa/bits/locks.h

    r882ad37 r3ca540f  
    1616#pragma once
    1717
     18#include "bits/debug.h"
    1819#include "bits/defs.h"
    19 
    20 #include "libhdr.h"
    2120
    2221// pause to prevent excess processor bus usage
     
    6564
    6665        // Lock the spinlock, return false if already acquired
    67         static inline _Bool try_lock  ( __spinlock_t & this DEBUG_CTX_PARAM2 ) {
     66        static inline _Bool try_lock  ( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
    6867                _Bool result = __lock_test_and_test_and_set( this.lock );
    69                 LIB_DEBUG_DO(
     68                __cfaabi_dbg_debug_do(
    7069                        if( result ) {
    7170                                this.prev_name = caller;
     
    7776
    7877        // Lock the spinlock, spin if already acquired
    79         static inline void lock( __spinlock_t & this DEBUG_CTX_PARAM2 ) {
     78        static inline void lock( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
    8079                #ifndef NOEXPBACK
    8180                        enum { SPIN_START = 4, SPIN_END = 64 * 1024, };
     
    9897                        #endif
    9998                }
    100                 LIB_DEBUG_DO(
     99                __cfaabi_dbg_debug_do(
    101100                        this.prev_name = caller;
    102101                        this.prev_thrd = this_thread;
     
    105104
    106105        // Lock the spinlock, spin if already acquired
    107         static inline void lock_yield( __spinlock_t & this DEBUG_CTX_PARAM2 ) {
     106        static inline void lock_yield( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
    108107                for ( unsigned int i = 1;; i += 1 ) {
    109108                        if ( __lock_test_and_test_and_set( this.lock ) ) break;
    110109                        yield( i );
    111110                }
    112                 LIB_DEBUG_DO(
     111                __cfaabi_dbg_debug_do(
    113112                        this.prev_name = caller;
    114113                        this.prev_thrd = this_thread;
  • src/libcfa/concurrency/alarm.c

    r882ad37 r3ca540f  
    2323}
    2424
    25 #include "libhdr.h"
    26 
    2725#include "alarm.h"
    2826#include "kernel_private.h"
     
    110108}
    111109
    112 LIB_DEBUG_DO( bool validate( alarm_list_t * this ) {
     110__cfaabi_dbg_debug_do( bool validate( alarm_list_t * this ) {
    113111        alarm_node_t ** it = &this->head;
    114112        while( (*it) ) {
     
    186184
    187185        disable_interrupts();
    188         lock( event_kernel->lock DEBUG_CTX2 );
     186        lock( event_kernel->lock __cfaabi_dbg_ctx2 );
    189187        {
    190188                verify( validate( alarms ) );</