Changes in / [c13e8dc8:9c35431]


Ignore:
Files:
75 added
73 deleted
88 edited

Legend:

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

    rc13e8dc8 r9c35431  
    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 (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 
    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 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}
    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 revolves 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 (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
     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 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}
     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 revolve 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 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.
     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 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.
    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 option effectively forces the design of the coroutine.
    236 
    237 Furthermore, \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:
     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 options effectively forces the design of the coroutine.
     236
     237Furthermore, \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:
    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 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.
     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 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.
    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 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:
     371The 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:
    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

    rc13e8dc8 r9c35431  
    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 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
    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 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
     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 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 :
     74Object 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 :
    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 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.
     137Note 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.
    138138
    139139% ======================================================================
  • doc/proposals/concurrency/text/concurrency.tex

    rc13e8dc8 r9c35431  
    44% ======================================================================
    55% ======================================================================
    6 Several 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.
     6Several 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.
    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 system languages, 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 systems language, 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 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.
     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 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.
    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 requirement 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 requirements 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 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 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:
    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 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:
     139However, 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:
    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 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.
     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 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.
    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 shown 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 showed 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, 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 and implementation 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 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.
     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, effectively 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 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 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.
    265265
    266266\begin{multicols}{2}
     
    297297\end{pseudo}
    298298\end{multicols}
    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 
    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 :
     299This 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
     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 \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}.
     321The \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 \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 
    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} parameters, global variables, pointer parameters, or using locals with the \code{mutex}-statement.
     345This 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} parameter, 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         |\label{line:releaseFirst}|release A & B
     378        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 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}
    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. 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 
    453 However, 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 :
     448The 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}
     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. 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
     453However, 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}).
     454The 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 :
    454455
    455456\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.
     
    459460Note 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}.
    460461
    461 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 knowing when to dispand a group becomes complex and inefficient (see next section) and therefore effectively precludes this approach.
     462In 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.
    462463
    463464\subsubsection{Dependency graphs}
     
    501502\end{figure}
    502503
    503 In 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.
     504In 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.
    504505\begin{figure}
    505506\begin{multicols}{2}
     
    530531\end{figure}
    531532
    532 Given 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}
    535 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 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 
    537 Using partial signalling, listing \ref{lst:dependency} can be solved easily :
     533Listing \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}
     536Finally, 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
     538While listing \ref{lst:dependency} is a complicated problem for previous solutions, it can be solved easily with partial signalling :
    538539\begin{itemize}
    539540        \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}.
    540541        \item When thread $\gamma$ reaches line \ref{line:release-a}  it transfers monitor \code{A} to thread $\beta$  and wakes it up.
    541542        \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!
    542544\end{itemize}
    543545
     
    652654An 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.
    653655
    654 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.
     656The 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.
    655657
    656658% ======================================================================
     
    721723\end{tabular}
    722724\end{center}
    723 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 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 
    725 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 other routine than \code{V} can acquire the monitor.
    726 
    727 % ======================================================================
    728 % ======================================================================
    729 \subsection{Loose Object Definitions}
    730 % ======================================================================
    731 % ======================================================================
    732 In \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:
     725This 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
     727For 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% ======================================================================
     734In \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:
    733735
    734736\begin{cfacode}
     
    746748\end{cfacode}
    747749
    748 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:
     750Furthermore, 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:
    749751\begin{center}
    750752\begin{tabular}{l}
     
    761763\end{tabular}
    762764\end{center}
    763 For 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:
     765For 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:
    764766
    765767\begin{figure}[H]
     
    770772\end{figure}
    771773
    772 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 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.
     774There 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.
    773775
    774776The alternative is to alter the implementation like this:
     777
    775778\begin{center}
    776779{\resizebox{0.4\textwidth}{!}{\input{ext_monitor}}}
    777780\end{center}
    778 Here, 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.
     781
     782Here, 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.
    779783
    780784\begin{figure}
     
    793797\end{figure}
    794798
    795 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 discussed in the next section. These details are omitted from the picture for the sake of simplicity.
    796 
    797 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 problem than writing locks that are as flexible as external scheduling in \CFA.
    798 
    799 % ======================================================================
    800 % ======================================================================
    801 \subsection{Multi-Monitor Scheduling}
    802 % ======================================================================
    803 % ======================================================================
    804 
    805 External 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:
     799Note 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
     801At 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
     809External 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:
    806810\begin{cfacode}
    807811monitor M {};
     
    833837
    834838void g(M & mutex a, M & mutex b) {
    835         //wait for call to f with arguments a and b
     839        //wait for call to f with argument a and b
    836840        waitfor(f, a, b);
    837841}
     
    866870% ======================================================================
    867871% ======================================================================
    868 \subsection{\code{waitfor} Semantics}
    869 % ======================================================================
    870 % ======================================================================
    871 
    872 Syntactically, 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.
     872\subsection{\code{waitfor} semantics}
     873% ======================================================================
     874% ======================================================================
     875
     876Syntactically, 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.
    873877\begin{figure}
    874878\begin{cfacode}[caption={Various correct and incorrect uses of the waitfor statement},label={lst:waitfor}]
     
    904908\end{figure}
    905909
    906 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 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.
     910Finally, 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.
    907911
    908912\begin{figure}
     
    969973% ======================================================================
    970974% ======================================================================
    971 \subsection{Waiting For The Destructor}
    972 % ======================================================================
    973 % ======================================================================
    974 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 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.
     975\subsection{Waiting for the destructor}
     976% ======================================================================
     977% ======================================================================
     978An 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.
    975979\begin{figure}
    976980\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

    rc13e8dc8 r9c35431  
    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. 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.
     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.
    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 helped 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 help me concretize the ideas in this thesis.
    9898
    99 Finally, 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.
     99Finally, 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.
    100100
    101101\cleardoublepage
  • doc/proposals/concurrency/text/future.tex

    rc13e8dc8 r9c35431  
    11
    22\chapter{Conclusion}
    3 This 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.
     3As 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.
    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 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.
     13This 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.
    1414
    1515\subsection{Flexible Scheduling} \label{futur:sched}
    16 An 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.
     16An 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.
    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 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.
     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 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.
    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. 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.
     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. 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.
    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 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.
     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 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.
    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 boilerplate 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 boiler-plate needed to start benefiting from parallelism in modern CPUs.
    111111
    112112
  • doc/proposals/concurrency/text/internals.tex

    rc13e8dc8 r9c35431  
    11
    2 \chapter{Behind the Scenes}
    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 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.
     2\chapter{Behind the scene}
     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 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.
    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 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.
     11\section{Mutex routines}
     12% ======================================================================
     13% ======================================================================
     14
     15The 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.
    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 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.
     111Both 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.
    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 detail 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 details 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 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.
     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 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.
    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/signaller (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-signaler (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 signalling 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 signaling 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 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.
     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 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.
    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 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}
     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 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}
    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 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.
     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.
    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 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}
     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 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}
    245245
    246246\begin{figure}
     
    253253        continue
    254254elif matches waitfor mask
    255         push criteria to AS-stack
     255        push criterions to AS-stack
    256256        continue
    257257else
  • doc/proposals/concurrency/text/parallelism.tex

    rc13e8dc8 r9c35431  
    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 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.
     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 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.
    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 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.
     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 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.
    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 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.
     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 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.
    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 specialized 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 specialize jobs like actors~\cite{Actors}.
  • doc/proposals/concurrency/text/results.tex

    rc13e8dc8 r9c35431  
    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 were 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 where 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 iterations 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 with 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 iteration 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 whith 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 is 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 are 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 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.
     266\subsection{Object creation}
     267Finally, 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.
    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

    rc13e8dc8 r9c35431  
    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

    rc13e8dc8 r9c35431  
    1 0.11.299
     10.11.280
  • src/CodeGen/CodeGenerator.cc

    rc13e8dc8 r9c35431  
    10121012        }
    10131013
    1014         void CodeGenerator::postvisit( WithStmt * with ) {
    1015                 if ( ! genC ) {
    1016                         output << "with ( ";
    1017                         genCommaList( with->exprs.begin(), with->exprs.end() );
    1018                         output << " ) ";
    1019                 }
    1020                 with->stmt->accept( *visitor );
    1021         }
    10221014
    10231015        void CodeGenerator::postvisit( WhileStmt * whileStmt ) {
  • src/CodeGen/CodeGenerator.h

    rc13e8dc8 r9c35431  
    102102                void postvisit( CatchStmt * );
    103103                void postvisit( WaitForStmt * );
    104                 void postvisit( WithStmt * );
    105104                void postvisit( WhileStmt * );
    106105                void postvisit( ForStmt * );
  • src/CodeGen/FixNames.cc

    rc13e8dc8 r9c35431  
    1919#include <string>                  // for string, operator!=, operator==
    2020
    21 #include "Common/PassVisitor.h"
    2221#include "Common/SemanticError.h"  // for SemanticError
    2322#include "FixMain.h"               // for FixMain
     
    3332
    3433namespace CodeGen {
    35         class FixNames : public WithGuards {
     34        class FixNames : public Visitor {
    3635          public:
    37                 void postvisit( ObjectDecl *objectDecl );
    38                 void postvisit( FunctionDecl *functionDecl );
     36                virtual void visit( ObjectDecl *objectDecl );
     37                virtual void visit( FunctionDecl *functionDecl );
    3938
    40                 void previsit( CompoundStmt *compoundStmt );
     39                virtual void visit( CompoundStmt *compoundStmt );
    4140          private:
    4241                int scopeLevel = 1;
     
    9493        }
    9594
    96         void fixNames( std::list< Declaration* > & translationUnit ) {
    97                 PassVisitor<FixNames> fixer;
     95        void fixNames( std::list< Declaration* > translationUnit ) {
     96                FixNames fixer;
    9897                acceptAll( translationUnit, fixer );
    9998        }
    10099
    101         void FixNames::fixDWT( DeclarationWithType * dwt ) {
     100        void FixNames::fixDWT( DeclarationWithType *dwt ) {
    102101                if ( dwt->get_name() != "" ) {
    103102                        if ( LinkageSpec::isMangled( dwt->get_linkage() ) ) {
     
    108107        }
    109108
    110         void FixNames::postvisit( ObjectDecl * objectDecl ) {
     109        void FixNames::visit( ObjectDecl *objectDecl ) {
     110                Visitor::visit( objectDecl );
    111111                fixDWT( objectDecl );
    112112        }
    113113
    114         void FixNames::postvisit( FunctionDecl * functionDecl ) {
     114        void FixNames::visit( FunctionDecl *functionDecl ) {
     115                Visitor::visit( functionDecl );
    115116                fixDWT( functionDecl );
    116117
     
    120121                                throw SemanticError("Main expected to have 0, 2 or 3 arguments\n", functionDecl);
    121122                        }
    122                         functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( new ConstantExpr( Constant::from_int( 0 ) ) ) );
     123                        functionDecl->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, new ConstantExpr( Constant::from_int( 0 ) ) ) );
    123124                        CodeGen::FixMain::registerMain( functionDecl );
    124125                }
    125126        }
    126127
    127         void FixNames::previsit( CompoundStmt * ) {
     128        void FixNames::visit( CompoundStmt *compoundStmt ) {
    128129                scopeLevel++;
    129                 GuardAction( [this](){ scopeLevel--; } );
     130                Visitor::visit( compoundStmt );
     131                scopeLevel--;
    130132        }
    131133} // namespace CodeGen
  • src/CodeGen/FixNames.h

    rc13e8dc8 r9c35431  
    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.h

    rc13e8dc8 r9c35431  
    8181        virtual void visit( FinallyStmt * finallyStmt ) override final;
    8282        virtual void visit( WaitForStmt * waitforStmt ) override final;
    83         virtual void visit( WithStmt * withStmt ) override final;
    8483        virtual void visit( NullStmt * nullStmt ) override final;
    8584        virtual void visit( DeclStmt * declStmt ) override final;
     
    173172        virtual Statement * mutate( FinallyStmt * finallyStmt ) override final;
    174173        virtual Statement * mutate( WaitForStmt * waitforStmt ) override final;
    175         virtual Statement * mutate( WithStmt * withStmt ) override final;
    176174        virtual NullStmt * mutate( NullStmt * nullStmt ) override final;
    177175        virtual Statement * mutate( DeclStmt * declStmt ) override final;
     
    298296        void indexerAddUnionFwd ( UnionDecl           * node ) { indexer_impl_addUnionFwd ( pass, 0, node ); }
    299297        void indexerAddTrait    ( TraitDecl           * node ) { indexer_impl_addTrait    ( pass, 0, node ); }
    300         void indexerAddWith     ( WithStmt            * node ) { indexer_impl_addWith    ( pass, 0, node ); }
    301 
    302298
    303299        template< typename TreeType, typename VisitorType >
  • src/Common/PassVisitor.impl.h

    rc13e8dc8 r9c35431  
    5555                it,
    5656                [](Declaration * decl) -> auto {
    57                         return new DeclStmt( decl );
     57                        return new DeclStmt( noLabels, decl );
    5858                }
    5959        );
     
    251251            || ( empty( beforeDecls ) && empty( afterDecls )) );
    252252
    253         CompoundStmt *compound = new CompoundStmt();
     253        CompoundStmt *compound = new CompoundStmt( noLabels );
    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 ); }
     
    400400        {
    401401                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    402                 // implicit add __func__ identifier as specified in the C manual 6.4.2.2
    403                 static ObjectDecl func(
    404                         "__func__", noStorageClasses, LinkageSpec::C, nullptr,
    405                         new ArrayType( Type::Qualifiers(), new BasicType( Type::Qualifiers( Type::Const ), BasicType::Char ), nullptr, true, false ),
    406                         nullptr
    407                 );
    408                 indexerAddId( &func );
    409402                maybeAccept_impl( node->type, *this );
    410403                maybeAccept_impl( node->statements, *this );
     
    425418        {
    426419                auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    427                 // implicit add __func__ identifier as specified in the C manual 6.4.2.2
    428                 static ObjectDecl func(
    429                         "__func__", noStorageClasses, LinkageSpec::C, nullptr,
    430                         new ArrayType( Type::Qualifiers(), new BasicType( Type::Qualifiers( Type::Const ), BasicType::Char ), nullptr, true, false ),
    431                         nullptr
    432                 );
    433                 indexerAddId( &func );
    434420                maybeMutate_impl( node->type, *this );
    435421                maybeMutate_impl( node->statements, *this );
     
    985971}
    986972
    987 
    988 
    989 //--------------------------------------------------------------------------
    990 // NullStmt
    991 template< typename pass_type >
    992 void PassVisitor< pass_type >::visit( WithStmt * node ) {
    993         VISIT_START( node );
    994         maybeAccept_impl( node->exprs, *this );
    995         {
    996                 // catch statements introduce a level of scope (for the caught exception)
    997                 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    998                 indexerAddWith( node );
    999                 maybeAccept_impl( node->stmt, *this );
    1000         }
    1001         VISIT_END( node );
    1002 }
    1003 
    1004 template< typename pass_type >
    1005 Statement * PassVisitor< pass_type >::mutate( WithStmt * node ) {
    1006         MUTATE_START( node );
    1007         maybeMutate_impl( node->exprs, *this );
    1008         {
    1009                 // catch statements introduce a level of scope (for the caught exception)
    1010                 auto guard = makeFuncGuard( [this]() { indexerScopeEnter(); }, [this]() { indexerScopeLeave(); } );
    1011                 indexerAddWith( node );
    1012                 maybeMutate_impl( node->stmt, *this );
    1013         }
    1014         MUTATE_END( Statement, node );
    1015 }
    1016 
    1017973//--------------------------------------------------------------------------
    1018974// NullStmt
  • src/Common/PassVisitor.proto.h

    rc13e8dc8 r9c35431  
    208208INDEXER_FUNC( addUnion  , UnionDecl *           );
    209209INDEXER_FUNC( addTrait  , TraitDecl *           );
    210 INDEXER_FUNC( addWith   , WithStmt *            );
    211210
    212211
  • src/Concurrency/Keywords.cc

    rc13e8dc8 r9c35431  
    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
    4048        //=============================================================================================
    4149        // Pass declarations
     
    288296                ObjectDecl * this_decl = new ObjectDecl(
    289297                        "this",
    290                         noStorageClasses,
     298                        noStorage,
    291299                        LinkageSpec::Cforall,
    292300                        nullptr,
     
    305313                        new ObjectDecl(
    306314                                "ret",
    307                                 noStorageClasses,
     315                                noStorage,
    308316                                LinkageSpec::Cforall,
    309317                                nullptr,
     
    338346                        main_decl = new FunctionDecl(
    339347                                "main",
    340                                 noStorageClasses,
     348                                noStorage,
    341349                                LinkageSpec::Cforall,
    342350                                main_type,
     
    355363                ObjectDecl * field = new ObjectDecl(
    356364                        field_name,
    357                         noStorageClasses,
     365                        noStorage,
    358366                        LinkageSpec::Cforall,
    359367                        nullptr,
     
    371379
    372380        void ConcurrentSueKeyword::addRoutines( ObjectDecl * field, FunctionDecl * func ) {
    373                 CompoundStmt * statement = new CompoundStmt();
     381                CompoundStmt * statement = new CompoundStmt( noLabels );
    374382                statement->push_back(
    375383                        new ReturnStmt(
     384                                noLabels,
    376385                                new AddressExpr(
    377386                                        new MemberExpr(
     
    479488                ObjectDecl * monitors = new ObjectDecl(
    480489                        "__monitor",
    481                         noStorageClasses,
     490                        noStorage,
    482491                        LinkageSpec::Cforall,
    483492                        nullptr,
     
    500509                // monitor_guard_t __guard = { __monitors, #, func };
    501510                body->push_front(
    502                         new DeclStmt( new ObjectDecl(
     511                        new DeclStmt( noLabels, new ObjectDecl(
    503512                                "__guard",
    504                                 noStorageClasses,
     513                                noStorage,
    505514                                LinkageSpec::Cforall,
    506515                                nullptr,
     
    521530
    522531                //monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) };
    523                 body->push_front( new DeclStmt( monitors) );
     532                body->push_front( new DeclStmt( noLabels, monitors) );
    524533        }
    525534
     
    527536                ObjectDecl * monitors = new ObjectDecl(
    528537                        "__monitors",
    529                         noStorageClasses,
     538                        noStorage,
    530539                        LinkageSpec::Cforall,
    531540                        nullptr,
     
    560569                // monitor_guard_t __guard = { __monitors, #, func };
    561570                body->push_front(
    562                         new DeclStmt( new ObjectDecl(
     571                        new DeclStmt( noLabels, new ObjectDecl(
    563572                                "__guard",
    564                                 noStorageClasses,
     573                                noStorage,
    565574                                LinkageSpec::Cforall,
    566575                                nullptr,
     
    582591
    583592                //monitor_desc * __monitors[] = { get_monitor(a), get_monitor(b) };
    584                 body->push_front( new DeclStmt( monitors) );
     593                body->push_front( new DeclStmt( noLabels, monitors) );
    585594        }
    586595
     
    622631                stmt->push_back(
    623632                        new ExprStmt(
     633                                noLabels,
    624634                                new UntypedExpr(
    625635                                        new NameExpr( "__thrd_start" ),
  • src/Concurrency/Waitfor.cc

    rc13e8dc8 r9c35431  
    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
    102110        //=============================================================================================
    103111        // Pass declarations
     
    195203                        ResolvExpr::findVoidExpression( expr, indexer );
    196204
    197                         return new ExprStmt( expr );
     205                        return new ExprStmt( noLabels, expr );
    198206                }
    199207
     
    251259                if( !decl_monitor || !decl_acceptable || !decl_mask ) throw SemanticError( "waitfor keyword requires monitors to be in scope, add #include <monitor>", waitfor );
    252260
    253                 CompoundStmt * stmt = new CompoundStmt();
     261                CompoundStmt * stmt = new CompoundStmt( noLabels );
    254262
    255263                ObjectDecl * acceptables = declare( waitfor->clauses.size(), stmt );
     
    273281                );
    274282
    275                 CompoundStmt * compound = new CompoundStmt();
     283                CompoundStmt * compound = new CompoundStmt( noLabels );
    276284                stmt->push_back( new IfStmt(
     285                        noLabels,
    277286                        safeCond( new VariableExpr( flag ) ),
    278287                        compound,
     
    304313                );
    305314
    306                 stmt->push_back( new DeclStmt( acceptables) );
     315                stmt->push_back( new DeclStmt( noLabels, acceptables) );
    307316
    308317                Expression * set = new UntypedExpr(
     
    317326                ResolvExpr::findVoidExpression( set, indexer );
    318327
    319                 stmt->push_back( new ExprStmt( set ) );
     328                stmt->push_back( new ExprStmt( noLabels, set ) );
    320329
    321330                return acceptables;
     
    332341                );
    333342
    334                 stmt->push_back( new DeclStmt( flag) );
     343                stmt->push_back( new DeclStmt( noLabels, flag) );
    335344
    336345                return flag;
     
    348357                ResolvExpr::findVoidExpression( expr, indexer );
    349358
    350                 return new ExprStmt( expr );
     359                return new ExprStmt( noLabels, expr );
    351360        }
    352361
     
    390399                );
    391400
    392                 stmt->push_back( new DeclStmt( mon) );
     401                stmt->push_back( new DeclStmt( noLabels, mon) );
    393402
    394403                return mon;
     
    402411
    403412                stmt->push_back( new IfStmt(
     413                        noLabels,
    404414                        safeCond( clause.condition ),
    405415                        new CompoundStmt({
     
    437447                );
    438448
    439                 stmt->push_back( new DeclStmt( timeout ) );
     449                stmt->push_back( new DeclStmt( noLabels, timeout ) );
    440450
    441451                if( time ) {
    442452                        stmt->push_back( new IfStmt(
     453                                noLabels,
    443454                                safeCond( time_cond ),
    444455                                new CompoundStmt({
    445456                                        new ExprStmt(
     457                                                noLabels,
    446458                                                makeOpAssign(
    447459                                                        new VariableExpr( timeout ),
     
    459471                if( has_else ) {
    460472                        stmt->push_back( new IfStmt(
     473                                noLabels,
    461474                                safeCond( else_cond ),
    462475                                new CompoundStmt({
    463476                                        new ExprStmt(
     477                                                noLabels,
    464478                                                makeOpAssign(
    465479                                                        new VariableExpr( timeout ),
     
    497511                );
    498512
    499                 stmt->push_back( new DeclStmt( index ) );
     513                stmt->push_back( new DeclStmt( noLabels, index ) );
    500514
    501515                ObjectDecl * mask = ObjectDecl::newObject(
     
    512526                );
    513527
    514                 stmt->push_back( new DeclStmt( mask ) );
     528                stmt->push_back( new DeclStmt( noLabels, mask ) );
    515529
    516530                stmt->push_back( new ExprStmt(
     531                        noLabels,
    517532                        new ApplicationExpr(
    518533                                VariableExpr::functionPointer( decl_waitfor ),
     
    542557        ) {
    543558                SwitchStmt * swtch = new SwitchStmt(
     559                        noLabels,
    544560                        result,
    545561                        std::list<Statement *>()
     
    550566                        swtch->statements.push_back(
    551567                                new CaseStmt(
     568                                        noLabels,
    552569                                        new ConstantExpr( Constant::from_ulong( i++ ) ),
    553570                                        {
    554571                                                clause.statement,
    555572                                                new BranchStmt(
     573                                                        noLabels,
    556574                                                        "",
    557575                                                        BranchStmt::Break
     
    565583                        swtch->statements.push_back(
    566584                                new CaseStmt(
     585                                        noLabels,
    567586                                        new ConstantExpr( Constant::from_int( -2 ) ),
    568587                                        {
    569588                                                waitfor->timeout.statement,
    570589                                                new BranchStmt(
     590                                                        noLabels,
    571591                                                        "",
    572592                                                        BranchStmt::Break
     
    580600                        swtch->statements.push_back(
    581601                                new CaseStmt(
     602                                        noLabels,
    582603                                        new ConstantExpr( Constant::from_int( -1 ) ),
    583604                                        {
    584605                                                waitfor->orelse.statement,
    585606                                                new BranchStmt(
     607                                                        noLabels,
    586608                                                        "",
    587609                                                        BranchStmt::Break
  • src/ControlStruct/ExceptTranslate.cc

    rc13e8dc8 r9c35431  
    3030#include "SynTree/Expression.h"       // for UntypedExpr, ConstantExpr, Name...
    3131#include "SynTree/Initializer.h"      // for SingleInit, ListInit
    32 #include "SynTree/Label.h"            // for Label
     32#include "SynTree/Label.h"            // for Label, noLabels
    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(item));
     59                block->push_back(new DeclStmt(noLabels, item));
    6060        }
    6161
     
    205205                throwStmt->set_expr( nullptr );
    206206                delete throwStmt;
    207                 return new ExprStmt( call );
     207                return new ExprStmt( noLabels, call );
    208208        }
    209209
     
    211211                        ThrowStmt *throwStmt ) {
    212212                // __throw_terminate( `throwStmt->get_name()` ); }
    213                 return create_given_throw( "__cfaabi_ehm__throw_terminate", throwStmt );
     213                return create_given_throw( "__cfaehm__throw_terminate", throwStmt );
    214214        }
    215215
     
    220220                assert( handler_except_decl );
    221221
    222                 CompoundStmt * result = new CompoundStmt();
    223                 result->labels =  throwStmt->labels;
    224                 result->push_back( new ExprStmt( UntypedExpr::createAssign(
     222                CompoundStmt * result = new CompoundStmt( throwStmt->get_labels() );
     223                result->push_back( new ExprStmt( noLabels, UntypedExpr::createAssign(
    225224                        nameOf( handler_except_decl ),
    226225                        new ConstantExpr( Constant::null(
     
    232231                        ) ) );
    233232                result->push_back( new ExprStmt(
    234                         new UntypedExpr( new NameExpr( "__cfaabi_ehm__rethrow_terminate" ) )
     233                        noLabels,
     234                        new UntypedExpr( new NameExpr( "__cfaehm__rethrow_terminate" ) )
    235235                        ) );
    236236                delete throwStmt;
     
    241241                        ThrowStmt *throwStmt ) {
    242242                // __throw_resume( `throwStmt->get_name` );
    243                 return create_given_throw( "__cfaabi_ehm__throw_resume", throwStmt );
     243                return create_given_throw( "__cfaehm__throw_resume", throwStmt );
    244244        }
    245245
     
    248248                // return false;
    249249                Statement * result = new ReturnStmt(
     250                        throwStmt->get_labels(),
    250251                        new ConstantExpr( Constant::from_bool( false ) )
    251252                        );
    252                 result->labels = throwStmt->labels;
    253253                delete throwStmt;
    254254                return result;
     
    291291                        // }
    292292                        // return;
    293                         CompoundStmt * block = new CompoundStmt();
     293                        CompoundStmt * block = new CompoundStmt( noLabels );
    294294
    295295                        // Just copy the exception value. (Post Validation)
     
    304304                                        ) })
    305305                                );
    306                         block->push_back( new DeclStmt( local_except ) );
     306                        block->push_back( new DeclStmt( noLabels, local_except ) );
    307307
    308308                        // Add the cleanup attribute.
    309309                        local_except->get_attributes().push_back( new Attribute(
    310310                                "cleanup",
    311                                 { new NameExpr( "__cfaabi_ehm__cleanup_terminate" ) }
     311                                { new NameExpr( "__cfaehm__cleanup_terminate" ) }
    312312                                ) );
    313313
     
    324324
    325325                        std::list<Statement *> caseBody
    326                                         { block, new ReturnStmt( nullptr ) };
     326                                        { block, new ReturnStmt( noLabels, nullptr ) };
    327327                        handler_wrappers.push_back( new CaseStmt(
     328                                noLabels,
    328329                                new ConstantExpr( Constant::from_int( index ) ),
    329330                                caseBody
     
    339340
    340341                SwitchStmt * handler_lookup = new SwitchStmt(
     342                        noLabels,
    341343                        nameOf( index_obj ),
    342344                        stmt_handlers
    343345                        );
    344                 CompoundStmt * body = new CompoundStmt();
     346                CompoundStmt * body = new CompoundStmt( noLabels );
    345347                body->push_back( handler_lookup );
    346348
     
    361363                // }
    362364
    363                 CompoundStmt * block = new CompoundStmt();
     365                CompoundStmt * block = new CompoundStmt( noLabels );
    364366
    365367                // Local Declaration
     
    367369                        dynamic_cast<ObjectDecl *>( modded_handler->get_decl() );
    368370                assert( local_except );
    369                 block->push_back( new DeclStmt( local_except ) );
     371                block->push_back( new DeclStmt( noLabels, local_except ) );
    370372
    371373                // Check for type match.
     
    379381                }
    380382                // Construct the match condition.
    381                 block->push_back( new IfStmt(
     383                block->push_back( new IfStmt( noLabels,
    382384                        cond, modded_handler->get_body(), nullptr ) );
    383385
     
    395397                // }
    396398
    397                 CompoundStmt * body = new CompoundStmt();
     399                CompoundStmt * body = new CompoundStmt( noLabels );
    398400
    399401                FunctionType * func_type = match_func_t.clone();
     
    411413
    412414                        // Create new body.
    413                         handler->set_body( new ReturnStmt(
     415                        handler->set_body( new ReturnStmt( noLabels,
    414416                                new ConstantExpr( Constant::from_int( index ) ) ) );
    415417
     
    419421                }
    420422
    421                 body->push_back( new ReturnStmt(
     423                body->push_back( new ReturnStmt( noLabels,
    422424                        new ConstantExpr( Constant::from_int( 0 ) ) ) );
    423425
     
    430432                        FunctionDecl * terminate_catch,
    431433                        FunctionDecl * terminate_match ) {
    432                 // { __cfaabi_ehm__try_terminate(`try`, `catch`, `match`); }
     434                // { __cfaehm__try_terminate(`try`, `catch`, `match`); }
    433435
    434436                UntypedExpr * caller = new UntypedExpr( new NameExpr(
    435                         "__cfaabi_ehm__try_terminate" ) );
     437                        "__cfaehm__try_terminate" ) );
    436438                std::list<Expression *>& args = caller->get_args();
    437439                args.push_back( nameOf( try_wrapper ) );
     
    439441                args.push_back( nameOf( terminate_match ) );
    440442
    441                 CompoundStmt * callStmt = new CompoundStmt();
    442                 callStmt->push_back( new ExprStmt( caller ) );
     443                CompoundStmt * callStmt = new CompoundStmt( noLabels );
     444                callStmt->push_back( new ExprStmt( noLabels, caller ) );
    443445                return callStmt;
    444446        }
     
    449451                //     HANDLER WRAPPERS { `hander->body`; return true; }
    450452                // }
    451                 CompoundStmt * body = new CompoundStmt();
     453                CompoundStmt * body = new CompoundStmt( noLabels );
    452454
    453455                FunctionType * func_type = handle_func_t.clone();
     
    462464                                dynamic_cast<CompoundStmt*>( handler->get_body() );
    463465                        if ( ! handling_code ) {
    464                                 handling_code = new CompoundStmt();
     466                                handling_code = new CompoundStmt( noLabels );
    465467                                handling_code->push_back( handler->get_body() );
    466468                        }
    467                         handling_code->push_back( new ReturnStmt(
     469                        handling_code->push_back( new ReturnStmt( noLabels,
    468470                                new ConstantExpr( Constant::from_bool( true ) ) ) );
    469471                        handler->set_body( handling_code );
     
    474476                }
    475477
    476                 body->push_back( new ReturnStmt(
     478                body->push_back( new ReturnStmt( noLabels,
    477479                        new ConstantExpr( Constant::from_bool( false ) ) ) );
    478480
     
    484486                        Statement * wraps,
    485487                        FunctionDecl * resume_handler ) {
    486                 CompoundStmt * body = new CompoundStmt();
     488                CompoundStmt * body = new CompoundStmt( noLabels );
    487489
    488490                // struct __try_resume_node __resume_node
    489                 //      __attribute__((cleanup( __cfaabi_ehm__try_resume_cleanup )));
     491                //      __attribute__((cleanup( __cfaehm__try_resume_cleanup )));
    490492                // ** unwinding of the stack here could cause problems **
    491493                // ** however I don't think that can happen currently **
    492                 // __cfaabi_ehm__try_resume_setup( &__resume_node, resume_handler );
     494                // __cfaehm__try_resume_setup( &__resume_node, resume_handler );
    493495
    494496                std::list< Attribute * > attributes;
     
    496498                        std::list< Expression * > attr_params;
    497499                        attr_params.push_back( new NameExpr(
    498                                 "__cfaabi_ehm__try_resume_cleanup" ) );
     500                                "__cfaehm__try_resume_cleanup" ) );
    499501                        attributes.push_back( new Attribute( "cleanup", attr_params ) );
    500502                }
     
    515517
    516518                UntypedExpr *setup = new UntypedExpr( new NameExpr(
    517                         "__cfaabi_ehm__try_resume_setup" ) );
     519                        "__cfaehm__try_resume_setup" ) );
    518520                setup->get_args().push_back( new AddressExpr( nameOf( obj ) ) );
    519521                setup->get_args().push_back( nameOf( resume_handler ) );
    520522
    521                 body->push_back( new ExprStmt( setup ) );
     523                body->push_back( new ExprStmt( noLabels, setup ) );
    522524
    523525                body->push_back( wraps );
     
    540542        ObjectDecl * ExceptionMutatorCore::create_finally_hook(
    541543                        FunctionDecl * finally_wrapper ) {
    542                 // struct __cfaabi_ehm__cleanup_hook __finally_hook
     544                // struct __cfaehm__cleanup_hook __finally_hook
    543545                //      __attribute__((cleanup( finally_wrapper )));
    544546
     
    594596                        // Skip children?
    595597                        return;
    596                 } else if ( structDecl->get_name() == "__cfaabi_ehm__base_exception_t" ) {
     598                } else if ( structDecl->get_name() == "__cfaehm__base_exception_t" ) {
    597599                        assert( nullptr == except_decl );
    598600                        except_decl = structDecl;
    599601                        init_func_types();
    600                 } else if ( structDecl->get_name() == "__cfaabi_ehm__try_resume_node" ) {
     602                } else if ( structDecl->get_name() == "__cfaehm__try_resume_node" ) {
    601603                        assert( nullptr == node_decl );
    602604                        node_decl = structDecl;
    603                 } else if ( structDecl->get_name() == "__cfaabi_ehm__cleanup_hook" ) {
     605                } else if ( structDecl->get_name() == "__cfaehm__cleanup_hook" ) {
    604606                        assert( nullptr == hook_decl );
    605607                        hook_decl = structDecl;
     
    644646                // Generate a prefix for the function names?
    645647
    646                 CompoundStmt * block = new CompoundStmt();
     648                CompoundStmt * block = new CompoundStmt( noLabels );
    647649                CompoundStmt * inner = take_try_block( tryStmt );
    648650
  • src/ControlStruct/ForExprMutator.cc

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

    rc13e8dc8 r9c35431  
    3737        }
    3838
    39         void LabelFixer::previsit( FunctionDecl * ) {
     39        void LabelFixer::visit( FunctionDecl *functionDecl ) {
    4040                // need to go into a nested function in a fresh state
    41                 GuardValue( labelTable );
     41                std::map < Label, Entry *> oldLabelTable = labelTable;
    4242                labelTable.clear();
    43         }
    4443
    45         void LabelFixer::postvisit( FunctionDecl * functionDecl ) {
     44                maybeAccept( functionDecl->get_statements(), *this );
     45
    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;
    4852        }
    4953
    5054        // prune to at most one label definition for each statement
    51         void LabelFixer::previsit( Statement *stmt ) {
     55        void LabelFixer::visit( Statement *stmt ) {
    5256                std::list< Label > &labels = stmt->get_labels();
    5357
     
    5862        }
    5963
    60         void LabelFixer::previsit( BranchStmt *branchStmt ) {
    61                 previsit( ( Statement *)branchStmt );
     64        void LabelFixer::visit( BranchStmt *branchStmt ) {
     65                visit ( ( Statement * )branchStmt );
    6266
    6367                // for labeled branches, add an entry to the label table
     
    6872        }
    6973
    70         void LabelFixer::previsit( LabelAddressExpr * addrExpr ) {
    71                 Label & target = addrExpr->arg;
    72                 assert( target != "" );
    73                 setLabelsUsg( target, addrExpr );
     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                }
    7485        }
    7586
  • src/ControlStruct/LabelFixer.h

    rc13e8dc8 r9c35431  
    1919#include <map>                     // for map
    2020
    21 #include "Common/PassVisitor.h"
    2221#include "Common/SemanticError.h"  // for SemanticError
    2322#include "SynTree/Label.h"         // for Label
     
    2726namespace ControlStruct {
    2827        /// normalizes label definitions and generates multi-level exit labels
    29         class LabelGenerator;
     28class LabelGenerator;
    3029
    31         class LabelFixer final : public WithGuards {
     30        class LabelFixer final : public Visitor {
     31                typedef Visitor Parent;
    3232          public:
    3333                LabelFixer( LabelGenerator *gen = 0 );
     
    3535                std::map < Label, Statement * > *resolveJumps() throw ( SemanticError );
    3636
     37                using Visitor::visit;
     38
    3739                // Declarations
    38                 void previsit( FunctionDecl *functionDecl );
    39                 void postvisit( FunctionDecl *functionDecl );
     40                virtual void visit( FunctionDecl *functionDecl ) override;
    4041
    4142                // Statements
    42                 void previsit( Statement *stmt );
    43                 void previsit( BranchStmt *branchStmt );
     43                void visit( Statement *stmt );
    4444
    45                 // Expressions
    46                 void previsit( LabelAddressExpr *addrExpr );
     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;
    4759
    4860                Label setLabelsDef( std::list< Label > &, Statement *definition );
  • src/ControlStruct/MLEMutator.cc

    rc13e8dc8 r9c35431  
    149149
    150150                        if ( CaseStmt * c = dynamic_cast< CaseStmt * >( statements.back() ) ) {
    151                                 Statement * stmt = new BranchStmt( Label("brkLabel"), BranchStmt::Break );
    152                                 stmt->labels.push_back( brkLabel );
    153                                 c->get_statements().push_back( stmt );
     151                                std::list<Label> temp; temp.push_back( brkLabel );
     152                                c->get_statements().push_back( new BranchStmt( temp, Label("brkLabel"), BranchStmt::Break ) );
    154153                        } else assert(0); // as of this point, all statements of a switch are still CaseStmts
    155154                } // if
     
    233232                // transform break/continue statements into goto to simplify later handling of branches
    234233                delete branchStmt;
    235                 return new BranchStmt( exitLabel, BranchStmt::Goto );
     234                return new BranchStmt( std::list<Label>(), exitLabel, BranchStmt::Goto );
    236235        }
    237236
     
    240239                CompoundStmt *newBody;
    241240                if ( ! (newBody = dynamic_cast<CompoundStmt *>( bodyLoop )) ) {
    242                         newBody = new CompoundStmt();
     241                        newBody = new CompoundStmt( std::list< Label >() );
    243242                        newBody->get_kids().push_back( bodyLoop );
    244243                } // if
  • src/ControlStruct/Mutate.cc

    rc13e8dc8 r9c35431  
    2424#include "SynTree/Declaration.h"   // for Declaration
    2525#include "SynTree/Mutator.h"       // for mutateAll
     26//#include "ExceptMutator.h"
    2627
    2728#include "Common/PassVisitor.h"    // for PassVisitor
     
    3637
    3738                // normalizes label definitions and generates multi-level exit labels
    38                 PassVisitor<LabelFixer> lfix;
     39                LabelFixer lfix;
     40
     41                //ExceptMutator exc;
    3942
    4043                mutateAll( translationUnit, formut );
    4144                acceptAll( translationUnit, lfix );
     45                //mutateAll( translationUnit, exc );
    4246        }
    4347} // namespace CodeGen
  • src/GenPoly/Box.cc

    rc13e8dc8 r9c35431  
    4949#include "SynTree/Expression.h"          // for ApplicationExpr, UntypedExpr
    5050#include "SynTree/Initializer.h"         // for SingleInit, Initializer, Lis...
    51 #include "SynTree/Label.h"               // for Label
     51#include "SynTree/Label.h"               // for Label, noLabels
    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(),
     295                                                                                                         LinkageSpec::AutoGen, layoutFnType, new CompoundStmt( noLabels ),
    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( cond, new ExprStmt( ifPart ), 0 );
     323                return new IfStmt( noLabels, cond, new ExprStmt( noLabels, 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( expr ) );
     342                stmts->get_kids().push_back( new ExprStmt( noLabels, 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( newObj ) );
     631                        stmtsToAddBefore.push_back( new DeclStmt( noLabels, 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( newObj ) );
     742                                stmtsToAddBefore.push_back( new DeclStmt( noLabels, 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( assign ) );
     746                                stmtsToAddBefore.push_back( new ExprStmt( noLabels, 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( adapteeApp );
     890                                bodyStmt = new ExprStmt( noLabels, 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( assign );
     902                                bodyStmt = new ExprStmt( noLabels, 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( adapteeApp );
     906                                bodyStmt = new ReturnStmt( noLabels, adapteeApp );
    907907                        } // if
    908                         CompoundStmt *adapterBody = new CompoundStmt();
     908                        CompoundStmt *adapterBody = new CompoundStmt( noLabels );
    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( newAdapter ) );
     954                                                stmtsToAddBefore.push_back( new DeclStmt( noLabels, newAdapter ) );
    955955                                        } // if
    956956                                        assert( adapter != adapters.end() );
     
    12791279                                                retval->set_name( "_retval" );
    12801280                                        }
    1281                                         functionDecl->get_statements()->get_kids().push_front( new DeclStmt( retval ) );
     1281                                        functionDecl->get_statements()->get_kids().push_front( new DeclStmt( noLabels, 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( newBuf ) );
     1521                                        stmtsToAddBefore.push_back( new DeclStmt( noLabels, 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( newObj ) );
     1600                        stmtsToAddBefore.push_back( new DeclStmt( noLabels, newObj ) );
    16011601                        return newObj;
    16021602                }
     
    16771677                                        addOtypeParamsToLayoutCall( layoutCall, otypeParams );
    16781678
    1679                                         stmtsToAddBefore.push_back( new ExprStmt( layoutCall ) );
     1679                                        stmtsToAddBefore.push_back( new ExprStmt( noLabels, layoutCall ) );
    16801680                                }
    16811681
     
    17031703                                addOtypeParamsToLayoutCall( layoutCall, otypeParams );
    17041704
    1705                                 stmtsToAddBefore.push_back( new ExprStmt( layoutCall ) );
     1705                                stmtsToAddBefore.push_back( new ExprStmt( noLabels, layoutCall ) );
    17061706
    17071707                                return true;
  • src/GenPoly/InstantiateGeneric.cc

    rc13e8dc8 r9c35431  
    453453                        return false;
    454454                }
     455
     456                AggregateDecl * getAggr( Type * t ) {
     457                        if ( StructInstType * inst = dynamic_cast< StructInstType * >( t ) ) {
     458                                return inst->baseStruct;
     459                        } else if ( UnionInstType * inst = dynamic_cast< UnionInstType * >( t ) ) {
     460                                return inst->baseUnion;
     461                        }
     462                        assertf( false, "Non-aggregate type: %s", toString( t ).c_str() );
     463                }
    455464        }
    456465
     
    460469                if ( isGenericType( memberExpr->aggregate->result ) ) {
    461470                        // find the location of the member
    462                         AggregateDecl * aggr = memberExpr->aggregate->result->getAggr();
     471                        AggregateDecl * aggr = getAggr( memberExpr->aggregate->result );
    463472                        std::list< Declaration * > & members = aggr->members;
    464473                        memberIndex = std::distance( members.begin(), std::find( members.begin(), members.end(), memberExpr->member ) );
     
    470479                if ( memberIndex != -1 ) {
    471480                        // using the location from the generic type, find the member in the instantiation and rebuild the member expression
    472                         AggregateDecl * aggr = memberExpr->aggregate->result->getAggr();
     481                        AggregateDecl * aggr = getAggr( memberExpr->aggregate->result );
    473482                        assertf( memberIndex < (int)aggr->members.size(), "Instantiation somehow has fewer members than the generic type." );
    474483                        Declaration * member = *std::next( aggr->members.begin(), memberIndex );
     
    517526                                        Expression * init = new CastExpr( new AddressExpr( memberExpr ), new PointerType( Type::Qualifiers(), concType->clone() ) );
    518527                                        ObjectDecl * tmp = ObjectDecl::newObject( tmpNamer.newName(), new ReferenceType( Type::Qualifiers(), concType ), new SingleInit( init ) );
    519                                         stmtsToAddBefore.push_back( new DeclStmt( tmp ) );
     528                                        stmtsToAddBefore.push_back( new DeclStmt( noLabels, tmp ) );
    520529                                        return new VariableExpr( tmp );
    521530                                } else {
  • src/GenPoly/Specialize.cc

    rc13e8dc8 r9c35431  
    3535#include "SynTree/Declaration.h"         // for FunctionDecl, DeclarationWit...
    3636#include "SynTree/Expression.h"          // for ApplicationExpr, Expression
    37 #include "SynTree/Label.h"               // for Label
     37#include "SynTree/Label.h"               // for Label, noLabels
    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() );
     236                FunctionDecl *thunkFunc = new FunctionDecl( thunkNamer.newName(), Type::StorageClasses(), LinkageSpec::C, newType, new CompoundStmt( noLabels ) );
    237237                thunkFunc->fixUniqueId();
    238238
     
    287287                Statement *appStmt;
    288288                if ( funType->returnVals.empty() ) {
    289                         appStmt = new ExprStmt( appExpr );
    290                 } else {
    291                         appStmt = new ReturnStmt( appExpr );
     289                        appStmt = new ExprStmt( noLabels, appExpr );
     290                } else {
     291                        appStmt = new ReturnStmt( noLabels, 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( thunkFunc ) );
     296                stmtsToAddBefore.push_back( new DeclStmt( noLabels, thunkFunc ) );
    297297                // return address of thunk function as replacement expression
    298298                return new AddressExpr( new VariableExpr( thunkFunc ) );
  • src/InitTweak/FixGlobalInit.cc

    rc13e8dc8 r9c35431  
    2020#include <algorithm>               // for replace_if
    2121
    22 #include "Common/PassVisitor.h"
    2322#include "Common/SemanticError.h"  // for SemanticError
    2423#include "Common/UniqueName.h"     // for UniqueName
     
    3029#include "SynTree/Expression.h"    // for ConstantExpr, Expression (ptr only)
    3130#include "SynTree/Initializer.h"   // for ConstructorInit, Initializer
    32 #include "SynTree/Label.h"         // for Label
     31#include "SynTree/Label.h"         // for Label, noLabels
    3332#include "SynTree/Statement.h"     // for CompoundStmt, Statement (ptr only)
    3433#include "SynTree/Type.h"          // for Type, Type::StorageClasses, Functi...
     
    3635
    3736namespace InitTweak {
    38         class GlobalFixer : public WithShortCircuiting {
     37        class GlobalFixer : public Visitor {
    3938          public:
    4039                GlobalFixer( const std::string & name, bool inLibrary );
    4140
    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 );
     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 );
    4948
    5049                UniqueName tempNamer;
     
    5453
    5554        void fixGlobalInit( std::list< Declaration * > & translationUnit, const std::string & name, bool inLibrary ) {
    56                 PassVisitor<GlobalFixer> visitor( name, inLibrary );
    57                 acceptAll( translationUnit, visitor );
    58                 GlobalFixer & fixer = visitor.pass;
     55                GlobalFixer fixer( name, inLibrary );
     56                acceptAll( translationUnit, fixer );
    5957                // don't need to include function if it's empty
    6058                if ( fixer.initFunction->get_statements()->get_kids().empty() ) {
     
    9492                        dtorParameters.push_back( new ConstantExpr( Constant::from_int( 102 ) ) );
    9593                }
    96                 initFunction = new FunctionDecl( "_init_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt() );
     94                initFunction = new FunctionDecl( "_init_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt( noLabels ) );
    9795                initFunction->get_attributes().push_back( new Attribute( "constructor", ctorParameters ) );
    98                 destroyFunction = new FunctionDecl( "_destroy_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt() );
     96                destroyFunction = new FunctionDecl( "_destroy_" + fixedName, Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt( noLabels ) );
    9997                destroyFunction->get_attributes().push_back( new Attribute( "destructor", dtorParameters ) );
    10098        }
    10199
    102         void GlobalFixer::previsit( ObjectDecl *objDecl ) {
     100        void GlobalFixer::visit( ObjectDecl *objDecl ) {
    103101                std::list< Statement * > & initStatements = initFunction->get_statements()->get_kids();
    104102                std::list< Statement * > & destroyStatements = destroyFunction->get_statements()->get_kids();
     
    136134
    137135        // only modify global variables
    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; }
     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 ) {}
    144142
    145143} // namespace InitTweak
  • src/InitTweak/FixInit.cc

    rc13e8dc8 r9c35431  
    4949#include "SynTree/Expression.h"        // for UniqueExpr, VariableExpr, Unty...
    5050#include "SynTree/Initializer.h"       // for ConstructorInit, SingleInit
    51 #include "SynTree/Label.h"             // for Label, operator<
     51#include "SynTree/Label.h"             // for Label, noLabels, operator<
    5252#include "SynTree/Mutator.h"           // for mutateAll, Mutator, maybeMutate
    5353#include "SynTree/Statement.h"         // for ExprStmt, CompoundStmt, Branch...
     
    547547                        // add all temporary declarations and their constructors
    548548                        for ( ObjectDecl * obj : tempDecls ) {
    549                                 stmtsToAddBefore.push_back( new DeclStmt( obj ) );
     549                                stmtsToAddBefore.push_back( new DeclStmt( noLabels, obj ) );
    550550                        } // for
    551551                        for ( ObjectDecl * obj : returnDecls ) {
    552                                 stmtsToAddBefore.push_back( new DeclStmt( obj ) );
     552                                stmtsToAddBefore.push_back( new DeclStmt( noLabels, obj ) );
    553553                        } // for
    554554
    555555                        // add destructors after current statement
    556556                        for ( Expression * dtor : dtors ) {
    557                                 stmtsToAddAfter.push_back( new ExprStmt( dtor ) );
     557                                stmtsToAddAfter.push_back( new ExprStmt( noLabels, dtor ) );
    558558                        } // for
    559559
     
    601601                        if ( ! result->isVoid() ) {
    602602                                for ( ObjectDecl * obj : stmtExpr->get_returnDecls() ) {
    603                                         stmtsToAddBefore.push_back( new DeclStmt( obj ) );
     603                                        stmtsToAddBefore.push_back( new DeclStmt( noLabels, obj ) );
    604604                                } // for
    605605                                // add destructors after current statement
    606606                                for ( Expression * dtor : stmtExpr->get_dtors() ) {
    607                                         stmtsToAddAfter.push_back( new ExprStmt( dtor ) );
     607                                        stmtsToAddAfter.push_back( new ExprStmt( noLabels, dtor ) );
    608608                                } // for
    609609                                // must have a non-empty body, otherwise it wouldn't have a result
    610610                                assert( ! stmts.empty() );
    611611                                assert( ! stmtExpr->get_returnDecls().empty() );
    612                                 stmts.push_back( new ExprStmt( new VariableExpr( stmtExpr->get_returnDecls().front() ) ) );
     612                                stmts.push_back( new ExprStmt( noLabels, new VariableExpr( stmtExpr->get_returnDecls().front() ) ) );
    613613                                stmtExpr->get_returnDecls().clear();
    614614                                stmtExpr->get_dtors().clear();
     
    739739
    740740                                                // generate body of if
    741                                                 CompoundStmt * initStmts = new CompoundStmt();
     741                                                CompoundStmt * initStmts = new CompoundStmt( noLabels );
    742742                                                std::list< Statement * > & body = initStmts->get_kids();
    743743                                                body.push_back( ctor );
    744                                                 body.push_back( new ExprStmt( setTrue ) );
     744                                                body.push_back( new ExprStmt( noLabels, setTrue ) );
    745745
    746746                                                // put it all together
    747                                                 IfStmt * ifStmt = new IfStmt( new VariableExpr( isUninitializedVar ), initStmts, 0 );
    748                                                 stmtsToAddAfter.push_back( new DeclStmt( isUninitializedVar ) );
     747                                                IfStmt * ifStmt = new IfStmt( noLabels, new VariableExpr( isUninitializedVar ), initStmts, 0 );
     748                                                stmtsToAddAfter.push_back( new DeclStmt( noLabels, isUninitializedVar ) );
    749749                                                stmtsToAddAfter.push_back( ifStmt );
    750750
     
    761761
    762762                                                        // void __objName_dtor_atexitN(...) {...}
    763                                                         FunctionDecl * dtorCaller = new FunctionDecl( objDecl->get_mangleName() + dtorCallerNamer.newName(), Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt() );
     763                                                        FunctionDecl * dtorCaller = new FunctionDecl( objDecl->get_mangleName() + dtorCallerNamer.newName(), Type::StorageClasses( Type::Static ), LinkageSpec::C, new FunctionType( Type::Qualifiers(), false ), new CompoundStmt( noLabels ) );
    764764                                                        dtorCaller->fixUniqueId();
    765765                                                        dtorCaller->get_statements()->push_back( dtorStmt );
     
    769769                                                        callAtexit->get_args().push_back( new VariableExpr( dtorCaller ) );
    770770
    771                                                         body.push_back( new ExprStmt( callAtexit ) );
     771                                                        body.push_back( new ExprStmt( noLabels, callAtexit ) );
    772772
    773773                                                        // hoist variable and dtor caller decls to list of decls that will be added into global scope
  • src/InitTweak/InitTweak.cc

    rc13e8dc8 r9c35431  
    55#include <memory>                  // for __shared_ptr
    66
    7 #include "Common/PassVisitor.h"
    87#include "Common/SemanticError.h"  // for SemanticError
    98#include "Common/UniqueName.h"     // for UniqueName
     
    2019#include "SynTree/Expression.h"    // for Expression, UntypedExpr, Applicati...
    2120#include "SynTree/Initializer.h"   // for Initializer, ListInit, Designation
    22 #include "SynTree/Label.h"         // for Label
     21#include "SynTree/Label.h"         // for Label, noLabels
    2322#include "SynTree/Statement.h"     // for CompoundStmt, ExprStmt, BranchStmt
    2423#include "SynTree/Type.h"          // for FunctionType, ArrayType, PointerType
     
    3029namespace InitTweak {
    3130        namespace {
    32                 struct HasDesignations : public WithShortCircuiting {
     31                class HasDesignations : public Visitor {
     32                public:
    3333                        bool hasDesignations = false;
    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                                 }
     34                        virtual void visit( Designation * des ) {
     35                                if ( ! des->get_designators().empty() ) hasDesignations = true;
     36                                else Visitor::visit( des );
    4737                        }
    4838                };
    4939
    50                 struct InitDepthChecker : public WithGuards {
     40                class InitDepthChecker : public Visitor {
     41                public:
    5142                        bool depthOkay = true;
    5243                        Type * type;
     
    6051                                maxDepth++;
    6152                        }
    62                         void previsit( ListInit * ) {
     53                        virtual void visit( ListInit * listInit ) {
    6354                                curDepth++;
    64                                 GuardAction( [this]() { curDepth--; } );
    6555                                if ( curDepth > maxDepth ) depthOkay = false;
     56                                Visitor::visit( listInit );
     57                                curDepth--;
    6658                        }
    6759                };
    6860
    69                 struct InitFlattener : public WithShortCircuiting {
    70                         void previsit( SingleInit * singleInit ) {
    71                                 visit_children = false;
    72                                 argList.push_back( singleInit->value->clone() );
    73                         }
     61                class InitFlattener : public Visitor {
     62                        public:
     63                        virtual void visit( SingleInit * singleInit );
     64                        virtual void visit( ListInit * listInit );
    7465                        std::list< Expression * > argList;
    7566                };
    7667
     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                }
    7779        }
    7880
    7981        std::list< Expression * > makeInitList( Initializer * init ) {
    80                 PassVisitor<InitFlattener> flattener;
     82                InitFlattener flattener;
    8183                maybeAccept( init, flattener );
    82                 return flattener.pass.argList;
     84                return flattener.argList;
    8385        }
    8486
    8587        bool isDesignated( Initializer * init ) {
    86                 PassVisitor<HasDesignations> finder;
     88                HasDesignations finder;
    8789                maybeAccept( init, finder );
    88                 return finder.pass.hasDesignations;
     90                return finder.hasDesignations;
    8991        }
    9092
    9193        bool checkInitDepth( ObjectDecl * objDecl ) {
    92                 PassVisitor<InitDepthChecker> checker( objDecl->type );
    93                 maybeAccept( objDecl->init, checker );
    94                 return checker.pass.depthOkay;
     94                InitDepthChecker checker( objDecl->get_type() );
     95                maybeAccept( objDecl->get_init(), checker );
     96                return checker.depthOkay;
    9597        }
    9698
     
    193195                        callExpr->get_args().splice( callExpr->get_args().end(), args );
    194196
    195                         *out++ = new IfStmt( cond, new ExprStmt( callExpr ), nullptr );
     197                        *out++ = new IfStmt( noLabels, cond, new ExprStmt( noLabels, callExpr ), nullptr );
    196198
    197199                        UntypedExpr * increment = new UntypedExpr( new NameExpr( "++?" ) );
    198200                        increment->get_args().push_back( index->clone() );
    199                         *out++ = new ExprStmt( increment );
     201                        *out++ = new ExprStmt( noLabels, increment );
    200202                }
    201203
     
    242244                                        std::list< Statement * > stmts;
    243245                                        build( callExpr, idx, idxEnd, init, back_inserter( stmts ) );
    244                                         stmts.push_back( new BranchStmt( switchLabel, BranchStmt::Break ) );
    245                                         CaseStmt * caseStmt = new CaseStmt( condition, stmts );
     246                                        stmts.push_back( new BranchStmt( noLabels, switchLabel, BranchStmt::Break ) );
     247                                        CaseStmt * caseStmt = new CaseStmt( noLabels, condition, stmts );
    246248                                        branches.push_back( caseStmt );
    247249                                }
    248                                 *out++ = new SwitchStmt( index->clone(), branches );
    249                                 *out++ = new NullStmt( { switchLabel } );
     250                                *out++ = new SwitchStmt( noLabels, index->clone(), branches );
     251                                *out++ = new NullStmt( std::list<Label>{ switchLabel } );
    250252                        }
    251253                }
     
    260262        Statement * InitImpl::buildListInit( UntypedExpr * dst, std::list< Expression * > & indices ) {
    261263                if ( ! init ) return nullptr;
    262                 CompoundStmt * block = new CompoundStmt();
     264                CompoundStmt * block = new CompoundStmt( noLabels );
    263265                build( dst, indices.begin(), indices.end(), init, back_inserter( block->get_kids() ) );
    264266                if ( block->get_kids().empty() ) {
     
    307309        }
    308310
    309         struct CallFinder {
     311        class CallFinder : public Visitor {
     312        public:
     313                typedef Visitor Parent;
    310314                CallFinder( const std::list< std::string > & names ) : names( names ) {}
    311315
    312                 void postvisit( ApplicationExpr * appExpr ) {
     316                virtual void visit( ApplicationExpr * appExpr ) {
    313317                        handleCallExpr( appExpr );
    314318                }
    315319
    316                 void postvisit( UntypedExpr * untypedExpr ) {
     320                virtual void visit( UntypedExpr * untypedExpr ) {
    317321                        handleCallExpr( untypedExpr );
    318322                }
     
    324328                template< typename CallExpr >
    325329                void handleCallExpr( CallExpr * expr ) {
     330                        Parent::visit( expr );
    326331                        std::string fname = getFunctionName( expr );
    327332                        if ( std::find( names.begin(), names.end(), fname ) != names.end() ) {
     
    332337
    333338        void collectCtorDtorCalls( Statement * stmt, std::list< Expression * > & matches ) {
    334                 static PassVisitor<CallFinder> finder( std::list< std::string >{ "?{}", "^?{}" } );
    335                 finder.pass.matches = &matches;
     339                static CallFinder finder( std::list< std::string >{ "?{}", "^?{}" } );
     340                finder.matches = &matches;
    336341                maybeAccept( stmt, finder );
    337342        }
     
    539544        }
    540545
    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 
     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 ) {
    548557                        // address of a variable or member expression is constexpr
    549558                        Expression * arg = addressExpr->get_arg();
    550559                        if ( ! dynamic_cast< NameExpr * >( arg) && ! dynamic_cast< VariableExpr * >( arg ) && ! dynamic_cast< MemberExpr * >( arg ) && ! dynamic_cast< UntypedMemberExpr * >( arg ) ) isConstExpr = false;
    551560                }
    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;
     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;
    567583        };
    568584
    569585        bool isConstExpr( Expression * expr ) {
    570586                if ( expr ) {
    571                         PassVisitor<ConstExprChecker> checker;
     587                        ConstExprChecker checker;
    572588                        expr->accept( checker );
    573                         return checker.pass.isConstExpr;
     589                        return checker.isConstExpr;
    574590                }
    575591                return true;
     
    578594        bool isConstExpr( Initializer * init ) {
    579595                if ( init ) {
    580                         PassVisitor<ConstExprChecker> checker;
     596                        ConstExprChecker checker;
    581597                        init->accept( checker );
    582                         return checker.pass.isConstExpr;
     598                        return checker.isConstExpr;
    583599                } // if
    584600                // for all intents and purposes, no initializer means const expr
  • src/MakeLibCfa.cc

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

    rc13e8dc8 r9c35431  
    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) \
    217219        SymTab/driver_cfa_cpp-Autogen.$(OBJEXT) \
    218220        SynTree/driver_cfa_cpp-Type.$(OBJEXT) \
     
    512514        ResolvExpr/CurrentObject.cc ResolvExpr/ExplodedActual.cc \
    513515        SymTab/Indexer.cc SymTab/Mangler.cc SymTab/Validate.cc \
    514         SymTab/FixFunction.cc SymTab/Autogen.cc SynTree/Type.cc \
     516        SymTab/FixFunction.cc SymTab/ImplementationType.cc \
     517        SymTab/TypeEquality.cc SymTab/Autogen.cc SynTree/Type.cc \
    515518        SynTree/VoidType.cc SynTree/BasicType.cc \
    516519        SynTree/PointerType.cc SynTree/ArrayType.cc \
     
    841844SymTab/driver_cfa_cpp-FixFunction.$(OBJEXT): SymTab/$(am__dirstamp) \
    842845        SymTab/$(DEPDIR)/$(am__dirstamp)
     846SymTab/driver_cfa_cpp-ImplementationType.$(OBJEXT):  \
     847        SymTab/$(am__dirstamp) SymTab/$(DEPDIR)/$(am__dirstamp)
     848SymTab/driver_cfa_cpp-TypeEquality.$(OBJEXT): SymTab/$(am__dirstamp) \
     849        SymTab/$(DEPDIR)/$(am__dirstamp)
    843850SymTab/driver_cfa_cpp-Autogen.$(OBJEXT): SymTab/$(am__dirstamp) \
    844851        SymTab/$(DEPDIR)/$(am__dirstamp)
     
    10331040@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-Autogen.Po@am__quote@
    10341041@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@
    10351043@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-Indexer.Po@am__quote@
    10361044@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@
    10371046@AMDEP_TRUE@@am__include@ @am__quote@SymTab/$(DEPDIR)/driver_cfa_cpp-Validate.Po@am__quote@
    10381047@AMDEP_TRUE@@am__include@ @am__quote@SynTree/$(DEPDIR)/driver_cfa_cpp-AddressExpr.Po@am__quote@
     
    20302039@AMDEP_TRUE@@am__fastdepCXX_FALSE@      DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
    20312040@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
     2042SymTab/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
     2049SymTab/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
     2056SymTab/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
     2063SymTab/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`
    20322069
    20332070SymTab/driver_cfa_cpp-Autogen.o: SymTab/Autogen.cc
  • src/Parser/DeclarationNode.cc

    rc13e8dc8 r9c35431  
    717717}
    718718
    719 DeclarationNode * DeclarationNode::addFunctionBody( StatementNode * body, StatementNode * with ) {
     719DeclarationNode * DeclarationNode::addFunctionBody( StatementNode * body ) {
    720720        assert( type );
    721721        assert( type->kind == TypeData::Function );
    722722        assert( ! type->function.body );
    723         if ( with ) {
    724                 // convert
    725                 //  void f(S s) with (s) { x = 0; }
    726                 // to
    727                 //  void f(S s) { with(s) { x = 0; } }
    728                 WithStmt * withStmt = strict_dynamic_cast< WithStmt * >( with->build() );
    729                 withStmt->stmt = body->build();
    730                 delete body;
    731                 delete with;
    732                 body = new StatementNode( new CompoundStmt( { withStmt } ) );
    733         }
    734723        type->function.body = body;
    735724        return this;
  • src/Parser/ParseNode.h

    rc13e8dc8 r9c35431  
    6969
    7070        virtual void print( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const {}
    71         virtual void printList( std::ostream &os, int indent = 0 ) const {
    72                 print( os, indent );
    73                 if ( next ) next->print( os, indent );
    74         }
     71        virtual void printList( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const {}
    7572
    7673        static int indent_by;
     
    123120        ExpressionNode * set_extension( bool exten ) { extension = exten; return this; }
    124121
    125         virtual void print( std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {
    126                 os << expr.get() << std::endl;
    127         }
     122        virtual void print( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {}
    128123        void printOneLine( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const {}
    129124
     
    262257        DeclarationNode * addBitfield( ExpressionNode * size );
    263258        DeclarationNode * addVarArgs();
    264         DeclarationNode * addFunctionBody( StatementNode * body, StatementNode * with = nullptr );
     259        DeclarationNode * addFunctionBody( StatementNode * body );
    265260        DeclarationNode * addOldDeclList( DeclarationNode * list );
    266261        DeclarationNode * setBase( TypeData * newType );
     
    364359        virtual StatementNode * append_last_case( StatementNode * );
    365360
    366         virtual void print( std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {
    367                 os << stmt.get() << std::endl;
    368         }
     361        virtual void print( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {}
     362        virtual void printList( __attribute__((unused)) std::ostream &os, __attribute__((unused)) int indent = 0 ) const override {}
    369363  private:
    370364        std::unique_ptr<Statement> stmt;
     
    414408WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when );
    415409WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when, StatementNode * else_stmt, ExpressionNode * else_when );
    416 WithStmt * build_with( ExpressionNode * exprs, StatementNode * stmt );
    417410
    418411//##############################################################################
  • src/Parser/StatementNode.cc

    rc13e8dc8 r9c35431  
    3737        DeclarationNode *agg = decl->extractAggregate();
    3838        if ( agg ) {
    39                 StatementNode *nextStmt = new StatementNode( new DeclStmt( maybeBuild< Declaration >( decl ) ) );
     39                StatementNode *nextStmt = new StatementNode( new DeclStmt( noLabels, maybeBuild< Declaration >( decl ) ) );
    4040                set_next( nextStmt );
    4141                if ( decl->get_next() ) {
     
    5050                agg = decl;
    5151        } // if
    52         stmt.reset( new DeclStmt( maybeMoveBuild< Declaration >(agg) ) );
     52        stmt.reset( new DeclStmt( noLabels, maybeMoveBuild< Declaration >(agg) ) );
    5353} // StatementNode::StatementNode
    5454
     
    7575
    7676        if ( e )
    77                 return new ExprStmt( e );
     77                return new ExprStmt( noLabels, e );
    7878        else
    79                 return new NullStmt();
     79                return new NullStmt( noLabels );
    8080}
    8181
     
    113113        }
    114114        delete ctl;
    115         return new IfStmt( cond, thenb, elseb, init );
     115        return new IfStmt( noLabels, 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( maybeMoveBuild< Expression >(ctl), branches );
     122        return new SwitchStmt( noLabels, maybeMoveBuild< Expression >(ctl), branches );
    123123}
    124124Statement *build_case( ExpressionNode *ctl ) {
    125125        std::list< Statement * > branches;
    126         return new CaseStmt( maybeMoveBuild< Expression >(ctl), branches );
     126        return new CaseStmt( noLabels, maybeMoveBuild< Expression >(ctl), branches );
    127127}
    128128Statement *build_default() {
    129129        std::list< Statement * > branches;
    130         return new CaseStmt( nullptr, branches, true );
     130        return new CaseStmt( noLabels, nullptr, branches, true );
    131131}
    132132
     
    135135        buildMoveList< Statement, StatementNode >( stmt, branches );
    136136        assert( branches.size() == 1 );
    137         return new WhileStmt( notZeroExpr( maybeMoveBuild< Expression >(ctl) ), branches.front(), kind );
     137        return new WhileStmt( noLabels, notZeroExpr( maybeMoveBuild< Expression >(ctl) ), branches.front(), kind );
    138138}
    139139
     
    157157
    158158        delete forctl;
    159         return new ForStmt( init, cond, incr, branches.front() );
     159        return new ForStmt( noLabels, init, cond, incr, branches.front() );
    160160}
    161161
    162162Statement *build_branch( BranchStmt::Type kind ) {
    163         Statement * ret = new BranchStmt( "", kind );
     163        Statement * ret = new BranchStmt( noLabels, "", kind );
    164164        return ret;
    165165}
    166166Statement *build_branch( std::string *identifier, BranchStmt::Type kind ) {
    167         Statement * ret = new BranchStmt( *identifier, kind );
     167        Statement * ret = new BranchStmt( noLabels, *identifier, kind );
    168168        delete identifier;                                                                      // allocated by lexer
    169169        return ret;
    170170}
    171171Statement *build_computedgoto( ExpressionNode *ctl ) {
    172         return new BranchStmt( maybeMoveBuild< Expression >(ctl), BranchStmt::Goto );
     172        return new BranchStmt( noLabels, maybeMoveBuild< Expression >(ctl), BranchStmt::Goto );
    173173}
    174174
     
    176176        std::list< Expression * > exps;
    177177        buildMoveList( ctl, exps );
    178         return new ReturnStmt( exps.size() > 0 ? exps.back() : nullptr );
     178        return new ReturnStmt( noLabels, 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( ThrowStmt::Terminate, !exps.empty() ? exps.back() : nullptr );
     185        return new ThrowStmt( noLabels, 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( ThrowStmt::Resume, !exps.empty() ? exps.back() : nullptr );
     192        return new ThrowStmt( noLabels, 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( tryBlock, branches, finallyBlock );
     206        return new TryStmt( noLabels, 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( kind, maybeMoveBuild< Declaration >(decl), maybeMoveBuild< Expression >(cond), branches.front() );
     212        return new CatchStmt( noLabels, 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( dynamic_cast< CompoundStmt * >( branches.front() ) );
     218        return new FinallyStmt( noLabels, dynamic_cast< CompoundStmt * >( branches.front() ) );
    219219}
    220220
     
    282282        node->timeout.condition = notZeroExpr( maybeMoveBuild<Expression>( when ) );
    283283
    284         node->orelse.statement  = maybeMoveBuild<Statement >( else_stmt );
     284        node->orelse.statement = maybeMoveBuild<Statement >( else_stmt );
    285285        node->orelse.condition  = notZeroExpr( maybeMoveBuild<Expression>( else_when ) );
    286286
     
    288288}
    289289
    290 WithStmt * build_with( ExpressionNode * exprs, StatementNode * stmt ) {
    291         std::list< Expression * > e;
    292         buildMoveList( exprs, e );
    293         Statement * s = maybeMoveBuild<Statement>( stmt );
    294         return new WithStmt( e, s );
    295 }
    296 
    297290Statement *build_compound( StatementNode *first ) {
    298         CompoundStmt *cs = new CompoundStmt();
     291        CompoundStmt *cs = new CompoundStmt( noLabels );
    299292        buildMoveList( first, cs->get_kids() );
    300293        return cs;
     
    308301        buildMoveList( input, in );
    309302        buildMoveList( clobber, clob );
    310         return new AsmStmt( voltile, instruction, out, in, clob, gotolabels ? gotolabels->labels : noLabels );
     303        return new AsmStmt( noLabels, voltile, instruction, out, in, clob, gotolabels ? gotolabels->labels : noLabels );
    311304}
    312305
  • src/Parser/parser.yy

    rc13e8dc8 r9c35431  
    10581058with_statement:
    10591059        WITH '(' tuple_expression_list ')' statement
    1060                 {
    1061                         $$ = new StatementNode( build_with( $3, $5 ) );
    1062                 }
     1060                { throw SemanticError("With clause is currently unimplemented."); $$ = nullptr; } // FIX ME
    10631061        ;
    10641062
     
    24122410                { $$ = nullptr; }
    24132411        | WITH '(' tuple_expression_list ')'
    2414                 { $$ = new StatementNode( build_with( $3, nullptr ) ); }
     2412                { throw SemanticError("With clause is currently unimplemented."); $$ = nullptr; } // FIX ME
    24152413        ;
    24162414
     
    24222420                        // Add the function body to the last identifier in the function definition list, i.e., foo3:
    24232421                        //   [const double] foo1(), foo2( int ), foo3( double ) { return 3.0; }
    2424                         $1->get_last()->addFunctionBody( $3, $2 );
     2422                        $1->get_last()->addFunctionBody( $3 );
    24252423                        $$ = $1;
    24262424                }
     
    24302428                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24312429                        typedefTable.leaveScope();
    2432                         $$ = $2->addFunctionBody( $4, $3 )->addType( $1 );
     2430                        $$ = $2->addFunctionBody( $4 )->addType( $1 );
    24332431                }
    24342432                // handles default int return type, OBSOLESCENT (see 1)
     
    24372435                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24382436                        typedefTable.leaveScope();
    2439                         $$ = $2->addFunctionBody( $4, $3 )->addQualifiers( $1 );
     2437                        $$ = $2->addFunctionBody( $4 )->addQualifiers( $1 );
    24402438                }
    24412439                // handles default int return type, OBSOLESCENT (see 1)
     
    24442442                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24452443                        typedefTable.leaveScope();
    2446                         $$ = $2->addFunctionBody( $4, $3 )->addQualifiers( $1 );
     2444                        $$ = $2->addFunctionBody( $4 )->addQualifiers( $1 );
    24472445                }
    24482446                // handles default int return type, OBSOLESCENT (see 1)
     
    24512449                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24522450                        typedefTable.leaveScope();
    2453                         $$ = $3->addFunctionBody( $5, $4 )->addQualifiers( $2 )->addQualifiers( $1 );
     2451                        $$ = $3->addFunctionBody( $5 )->addQualifiers( $2 )->addQualifiers( $1 );
    24542452                }
    24552453
     
    24602458                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24612459                        typedefTable.leaveScope();
    2462                         $$ = $2->addOldDeclList( $3 )->addFunctionBody( $5, $4 )->addType( $1 );
     2460                        $$ = $2->addOldDeclList( $3 )->addFunctionBody( $5 )->addType( $1 );
    24632461                }
    24642462                // handles default int return type, OBSOLESCENT (see 1)
     
    24672465                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24682466                        typedefTable.leaveScope();
    2469                         $$ = $2->addOldDeclList( $3 )->addFunctionBody( $5, $4 )->addQualifiers( $1 );
     2467                        $$ = $2->addOldDeclList( $3 )->addFunctionBody( $5 )->addQualifiers( $1 );
    24702468                }
    24712469                // handles default int return type, OBSOLESCENT (see 1)
     
    24742472                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24752473                        typedefTable.leaveScope();
    2476                         $$ = $2->addOldDeclList( $3 )->addFunctionBody( $5, $4 )->addQualifiers( $1 );
     2474                        $$ = $2->addOldDeclList( $3 )->addFunctionBody( $5 )->addQualifiers( $1 );
    24772475                }
    24782476                // handles default int return type, OBSOLESCENT (see 1)
     
    24812479                        typedefTable.addToEnclosingScope( TypedefTable::ID );
    24822480                        typedefTable.leaveScope();
    2483                         $$ = $3->addOldDeclList( $4 )->addFunctionBody( $6, $5 )->addQualifiers( $2 )->addQualifiers( $1 );
     2481                        $$ = $3->addOldDeclList( $4 )->addFunctionBody( $6 )->addQualifiers( $2 )->addQualifiers( $1 );
    24842482                }
    24852483        ;
  • src/ResolvExpr/AlternativeFinder.cc

    rc13e8dc8 r9c35431  
    8383        }
    8484
    85         void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt ) {
    86                 Indenter indent = { Indenter::tabsize, indentAmt };
    87                 for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) {
    88                         i->print( os, indent );
    89                         os << std::endl;
    90                 }
    91         }
    92 
    9385        namespace {
     86                void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt = 0 ) {
     87                        Indenter indent = { Indenter::tabsize, indentAmt };
     88                        for ( AltList::const_iterator i = list.begin(); i != list.end(); ++i ) {
     89                                i->print( os, indent );
     90                                os << std::endl;
     91                        }
     92                }
     93
    9494                void makeExprList( const AltList &in, std::list< Expression* > &out ) {
    9595                        for ( AltList::const_iterator i = in.begin(); i != in.end(); ++i ) {
     
    469469                        std::cerr << std::endl;
    470470                )
    471                 std::list< SymTab::Indexer::IdData > candidates;
     471                std::list< DeclarationWithType* > candidates;
    472472                decls.lookupId( curDecl->get_name(), candidates );
    473473///   if ( candidates.empty() ) { std::cerr << "no candidates!" << std::endl; }
    474                 for ( const auto & data : candidates ) {
    475                         DeclarationWithType * candidate = data.id;
     474                for ( std::list< DeclarationWithType* >::const_iterator candidate = candidates.begin(); candidate != candidates.end(); ++candidate ) {
    476475                        PRINT(
    477476                                std::cerr << "inferRecursive: candidate is ";
    478                                 candidate->print( std::cerr );
     477                                (*candidate)->print( std::cerr );
    479478                                std::cerr << std::endl;
    480479                        )
     
    483482                        TypeEnvironment newEnv( newAlt.env );
    484483                        OpenVarSet newOpenVars( openVars );
    485                         Type *adjType = candidate->get_type()->clone();
     484                        Type *adjType = (*candidate)->get_type()->clone();
    486485                        adjustExprType( adjType, newEnv, indexer );
    487486                        adjType->accept( global_renamer );
     
    501500                                Alternative newerAlt( newAlt );
    502501                                newerAlt.env = newEnv;
    503                                 assertf( candidate->get_uniqueId(), "Assertion candidate does not have a unique ID: %s", toString( candidate ).c_str() );
     502                                assertf( (*candidate)->get_uniqueId(), "Assertion candidate does not have a unique ID: %s", toString( *candidate ).c_str() );
     503                                DeclarationWithType *candDecl = static_cast< DeclarationWithType* >( Declaration::declFromId( (*candidate)->get_uniqueId() ) );
    504504
    505505                                // everything with an empty idChain was pulled in by the current assertion.
     
    516516                                // DOESN'T WORK: grandchild nodes conflict with their cousins
    517517                                //if ( newNeedParents[ curDecl->get_uniqueId() ][ candDecl->get_uniqueId() ]++ > recursionParentLimit ) continue;
    518                                 Expression *varExpr = data.combine();
     518                                Expression *varExpr = new VariableExpr( candDecl );
    519519                                delete varExpr->get_result();
    520520                                varExpr->set_result( adjType->clone() );
     
    522522                                        std::cerr << "satisfying assertion " << curDecl->get_uniqueId() << " ";
    523523                                        curDecl->print( std::cerr );
    524                                         std::cerr << " with declaration " << candidate->get_uniqueId() << " ";
    525                                         candidate->print( std::cerr );
     524                                        std::cerr << " with declaration " << (*candidate)->get_uniqueId() << " ";
     525                                        (*candidate)->print( std::cerr );
    526526                                        std::cerr << std::endl;
    527527                                )
     
    532532                                }
    533533                                // XXX: this is a memory leak, but adjType can't be deleted because it might contain assertions
    534                                 (*inferParameters)[ curDecl->get_uniqueId() ] = ParamEntry( candidate->get_uniqueId(), adjType->clone(), curDecl->get_type()->clone(), varExpr );
     534                                (*inferParameters)[ curDecl->get_uniqueId() ] = ParamEntry( (*candidate)->get_uniqueId(), adjType->clone(), curDecl->get_type()->clone(), varExpr );
    535535                                inferRecursive( begin, end, newerAlt, newOpenVars, newDecls, newerNeed, /*newNeedParents,*/ level, indexer, out );
    536536                        } else {
     
    13171317
    13181318        void AlternativeFinder::visit( NameExpr *nameExpr ) {
    1319                 std::list< SymTab::Indexer::IdData > declList;
     1319                std::list< DeclarationWithType* > declList;
    13201320                indexer.lookupId( nameExpr->get_name(), declList );
    13211321                PRINT( std::cerr << "nameExpr is " << nameExpr->get_name() << std::endl; )
    1322                 for ( auto & data : declList ) {
    1323                         Expression * newExpr = data.combine();
    1324                         // xxx - add in extra cost for with-statement exprs?
    1325                         alternatives.push_back( Alternative( newExpr, env, Cost::zero ) );
     1322                for ( std::list< DeclarationWithType* >::iterator i = declList.begin(); i != declList.end(); ++i ) {
     1323                        VariableExpr newExpr( *i );
     1324                        alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
    13261325                        PRINT(
    13271326                                std::cerr << "decl is ";
    1328                                 data.id->print( std::cerr );
     1327                                (*i)->print( std::cerr );
    13291328                                std::cerr << std::endl;
    13301329                                std::cerr << "newExpr is ";
    1331                                 newExpr->print( std::cerr );
     1330                                newExpr.print( std::cerr );
    13321331                                std::cerr << std::endl;
    13331332                        )
     
    14211420        }
    14221421
    1423         namespace {
    1424                 void resolveAttr( SymTab::Indexer::IdData data, FunctionType *function, Type *argType, const TypeEnvironment &env, AlternativeFinder & finder ) {
    1425                         // assume no polymorphism
    1426                         // assume no implicit conversions
    1427                         assert( function->get_parameters().size() == 1 );
    1428                         PRINT(
    1429                                 std::cerr << "resolvAttr: funcDecl is ";
    1430                                 data.id->print( std::cerr );
    1431                                 std::cerr << " argType is ";
    1432                                 argType->print( std::cerr );
    1433                                 std::cerr << std::endl;
    1434                         )
    1435                         const SymTab::Indexer & indexer = finder.get_indexer();
    1436                         AltList & alternatives = finder.get_alternatives();
    1437                         if ( typesCompatibleIgnoreQualifiers( argType, function->get_parameters().front()->get_type(), indexer, env ) ) {
    1438                                 alternatives.push_back( Alternative( new AttrExpr( data.combine(), argType->clone() ), env, Cost::zero ) );
    1439                                 for ( DeclarationWithType * retVal : function->returnVals ) {
    1440                                         alternatives.back().expr->result = retVal->get_type()->clone();
    1441                                 } // for
    1442                         } // if
    1443                 }
     1422        void AlternativeFinder::resolveAttr( DeclarationWithType *funcDecl, FunctionType *function, Type *argType, const TypeEnvironment &env ) {
     1423                // assume no polymorphism
     1424                // assume no implicit conversions
     1425                assert( function->get_parameters().size() == 1 );
     1426                PRINT(
     1427                        std::cerr << "resolvAttr: funcDecl is ";
     1428                        funcDecl->print( std::cerr );
     1429                        std::cerr << " argType is ";
     1430                        argType->print( std::cerr );
     1431                        std::cerr << std::endl;
     1432                )
     1433                if ( typesCompatibleIgnoreQualifiers( argType, function->get_parameters().front()->get_type(), indexer, env ) ) {
     1434                        alternatives.push_back( Alternative( new AttrExpr( new VariableExpr( funcDecl ), argType->clone() ), env, Cost::zero ) );
     1435                        for ( std::list< DeclarationWithType* >::iterator i = function->get_returnVals().begin(); i != function->get_returnVals().end(); ++i ) {
     1436                                alternatives.back().expr->set_result( (*i)->get_type()->clone() );
     1437                        } // for
     1438                } // if
    14441439        }
    14451440
     
    14481443                NameExpr *nameExpr = dynamic_cast< NameExpr* >( attrExpr->get_attr() );
    14491444                assert( nameExpr );
    1450                 std::list< SymTab::Indexer::IdData > attrList;
     1445                std::list< DeclarationWithType* > attrList;
    14511446                indexer.lookupId( nameExpr->get_name(), attrList );
    14521447                if ( attrExpr->get_isType() || attrExpr->get_expr() ) {
    1453                         for ( auto & data : attrList ) {
    1454                                 DeclarationWithType * id = data.id;
     1448                        for ( std::list< DeclarationWithType* >::iterator i = attrList.begin(); i != attrList.end(); ++i ) {
    14551449                                // check if the type is function
    1456                                 if ( FunctionType *function = dynamic_cast< FunctionType* >( id->get_type() ) ) {
     1450                                if ( FunctionType *function = dynamic_cast< FunctionType* >( (*i)->get_type() ) ) {
    14571451                                        // assume exactly one parameter
    14581452                                        if ( function->get_parameters().size() == 1 ) {
    14591453                                                if ( attrExpr->get_isType() ) {
    1460                                                         resolveAttr( data, function, attrExpr->get_type(), env, *this );
     1454                                                        resolveAttr( *i, function, attrExpr->get_type(), env );
    14611455                                                } else {
    14621456                                                        AlternativeFinder finder( indexer, env );
     
    14641458                                                        for ( AltList::iterator choice = finder.alternatives.begin(); choice != finder.alternatives.end(); ++choice ) {
    14651459                                                                if ( choice->expr->get_result()->size() == 1 ) {
    1466                                                                         resolveAttr(data, function, choice->expr->get_result(), choice->env, *this );
     1460                                                                        resolveAttr(*i, function, choice->expr->get_result(), choice->env );
    14671461                                                                } // fi
    14681462                                                        } // for
     
    14721466                        } // for
    14731467                } else {
    1474                         for ( auto & data : attrList ) {
    1475                                 alternatives.push_back( Alternative( data.combine(), env, Cost::zero ) );
     1468                        for ( std::list< DeclarationWithType* >::iterator i = attrList.begin(); i != attrList.end(); ++i ) {
     1469                                VariableExpr newExpr( *i );
     1470                                alternatives.push_back( Alternative( newExpr.clone(), env, Cost::zero ) );
    14761471                                renameTypes( alternatives.back().expr );
    14771472                        } // for
  • src/ResolvExpr/AlternativeFinder.h

    rc13e8dc8 r9c35431  
    142142                template< typename OutputIterator >
    143143                void inferParameters( const AssertionSet &need, AssertionSet &have, const Alternative &newAlt, OpenVarSet &openVars, OutputIterator out );
     144                void resolveAttr( DeclarationWithType *funcDecl, FunctionType *function, Type *argType, const TypeEnvironment &env );
    144145
    145146                const SymTab::Indexer &indexer;
     
    150151
    151152        Expression *resolveInVoidContext( Expression *expr, const SymTab::Indexer &indexer, TypeEnvironment &env );
     153        void referenceToRvalueConversion( Expression *& expr );
    152154
    153155        template< typename InputIterator, typename OutputIterator >
     
    172174
    173175        Cost sumCost( const AltList &in );
    174         void printAlts( const AltList &list, std::ostream &os, unsigned int indentAmt = 0 );
    175176
    176177        template< typename InputIterator >
     
    180181                }
    181182        }
    182 
    183183} // namespace ResolvExpr
    184184
  • src/ResolvExpr/Resolver.cc

    rc13e8dc8 r9c35431  
    2626#include "Common/utility.h"              // for ValueGuard, group_iterate
    2727#include "CurrentObject.h"               // for CurrentObject
    28 #include "InitTweak/GenInit.h"
    2928#include "InitTweak/InitTweak.h"         // for isIntrinsicSingleArgCallStmt
    3029#include "RenameVars.h"                  // for RenameVars, global_renamer
     
    4140#include "SynTree/TypeSubstitution.h"    // for TypeSubstitution
    4241#include "SynTree/Visitor.h"             // for acceptAll, maybeAccept
    43 #include "Tuples/Tuples.h"
    4442#include "typeops.h"                     // for extractResultType
    4543#include "Unify.h"                       // for unify
     
    4846
    4947namespace ResolvExpr {
    50         struct Resolver final : public WithIndexer, public WithGuards, public WithVisitorRef<Resolver>, public WithShortCircuiting, public WithStmtsToAdd {
     48        struct Resolver final : public WithIndexer, public WithGuards, public WithVisitorRef<Resolver>, public WithShortCircuiting {
    5149                Resolver() {}
    5250                Resolver( const SymTab::Indexer & other ) {
     
    7674                void previsit( CatchStmt *catchStmt );
    7775                void previsit( WaitForStmt * stmt );
    78                 void previsit( WithStmt * withStmt );
    7976
    8077                void previsit( SingleInit *singleInit );
     
    372369                if ( throwStmt->get_expr() ) {
    373370                        StructDecl * exception_decl =
    374                                 indexer.lookupStruct( "__cfaabi_ehm__base_exception_t" );
     371                                indexer.lookupStruct( "__cfaehm__base_exception_t" );
    375372                        assert( exception_decl );
    376373                        Type * exceptType = new PointerType( noQualifiers, new StructInstType( noQualifiers, exception_decl ) );
     
    571568                        findSingleExpression( stmt->orelse.condition, this->indexer );
    572569                        stmt->orelse.statement->accept( *visitor );
    573                 }
    574         }
    575 
    576         bool isStructOrUnion( Type * t ) {
    577                 t = t->stripReferences();
    578                 return dynamic_cast< StructInstType * >( t ) || dynamic_cast< UnionInstType * >( t );
    579         }
    580 
    581         void Resolver::previsit( WithStmt * withStmt ) {
    582                 for ( Expression *& expr : withStmt->exprs )  {
    583                         TypeEnvironment env;
    584                         AlternativeFinder finder( indexer, env );
    585                         finder.findWithAdjustment( expr );
    586 
    587                         // only struct- and union-typed expressions are viable candidates
    588                         AltList candidates;
    589                         for ( Alternative & alt : finder.get_alternatives() ) {
    590                                 if ( isStructOrUnion( alt.expr->result ) ) {
    591                                         candidates.push_back( std::move( alt ) );
    592                                 }
    593                         }
    594 
    595                         // choose the lowest cost expression among the candidates
    596                         AltList winners;
    597                         findMinCost( candidates.begin(), candidates.end(), back_inserter( winners ) );
    598                         if ( winners.size() == 0 ) {
    599                                 throw SemanticError( "No reasonable alternatives for with statement expression: ", expr );
    600                         } else if ( winners.size() != 1 ) {
    601                                 std::ostringstream stream;
    602                                 stream << "Cannot choose between " << winners.size() << " alternatives for with statement expression\n";
    603                                 expr->print( stream );
    604                                 stream << "Alternatives are:\n";
    605                                 printAlts( winners, stream, 1 );
    606                                 throw SemanticError( stream.str() );
    607                         }
    608 
    609                         // there is one unambiguous interpretation - move the expression into the with statement
    610                         Alternative & alt = winners.front();
    611                         finishExpr( alt.expr, alt.env, expr->env );
    612                         delete expr;
    613                         expr = alt.expr;
    614                         alt.expr = nullptr;
    615 
    616                         // if with expression might be impure, create a temporary so that it is evaluated once
    617                         if ( Tuples::maybeImpure( expr ) ) {
    618                                 static UniqueName tmpNamer( "_with_tmp_" );
    619                                 ObjectDecl * tmp = ObjectDecl::newObject( tmpNamer.newName(), expr->result->clone(), new SingleInit( expr ) );
    620                                 expr = new VariableExpr( tmp );
    621                                 stmtsToAddBefore.push_back( new DeclStmt( tmp ) );
    622                                 if ( InitTweak::isConstructable( tmp->type ) ) {
    623                                         // generate ctor/dtor and resolve them
    624                                         tmp->init = InitTweak::genCtorInit( tmp );
    625                                         tmp->accept( *visitor );
    626                                 }
    627                         }
    628570                }
    629571        }
  • src/ResolvExpr/typeops.h

    rc13e8dc8 r9c35431  
    102102        bool occurs( Type *type, std::string varName, const TypeEnvironment &env );
    103103
    104         // in AlternativeFinder.cc
    105         void referenceToRvalueConversion( Expression *& expr );
    106 
    107104        // flatten tuple type into list of types
    108105        template< typename OutputIterator >
  • src/SymTab/AddVisit.h

    rc13e8dc8 r9c35431  
    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( *decl );
     26                                DeclStmt *declStmt = new DeclStmt( noLabels, *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( *decl );
     38                                DeclStmt *declStmt = new DeclStmt( noLabels, *decl );
    3939                                stmts.insert( stmt, declStmt );
    4040                        }
  • src/SymTab/Autogen.cc

    rc13e8dc8 r9c35431  
    267267                Type::StorageClasses scs = functionNesting > 0 ? Type::StorageClasses() : Type::StorageClasses( Type::Static );
    268268                LinkageSpec::Spec spec = isIntrinsic ? LinkageSpec::Intrinsic : LinkageSpec::AutoGen;
    269                 FunctionDecl * decl = new FunctionDecl( fname, scs, spec, ftype, new CompoundStmt(),
     269                FunctionDecl * decl = new FunctionDecl( fname, scs, spec, ftype, new CompoundStmt( noLabels ),
    270270                                                                                                std::list< Attribute * >(), Type::FuncSpecifiers( Type::Inline ) );
    271271                decl->fixUniqueId();
     
    302302                                assert( assignType->returnVals.size() == 1 );
    303303                                ObjectDecl * dstParam = strict_dynamic_cast< ObjectDecl * >( assignType->parameters.front() );
    304                                 dcl->statements->push_back( new ReturnStmt( new VariableExpr( dstParam ) ) );
     304                                dcl->statements->push_back( new ReturnStmt( noLabels, new VariableExpr( dstParam ) ) );
    305305                        }
    306306                        resolve( dcl );
     
    471471                copy->args.push_back( new AddressExpr( new VariableExpr( srcParam ) ) );
    472472                copy->args.push_back( new SizeofExpr( srcParam->get_type()->clone() ) );
    473                 *out++ = new ExprStmt( copy );
     473                *out++ = new ExprStmt( noLabels, copy );
    474474        }
    475475
     
    547547                        callExpr->get_args().push_back( new VariableExpr( dstParam ) );
    548548                        callExpr->get_args().push_back( new VariableExpr( srcParam ) );
    549                         funcDecl->statements->push_back( new ExprStmt( callExpr ) );
     549                        funcDecl->statements->push_back( new ExprStmt( noLabels, callExpr ) );
    550550                } else {
    551551                        // default ctor/dtor body is empty - add unused attribute to parameter to silence warnings
     
    572572                expr->args.push_back( new CastExpr( new VariableExpr( dst ), new ReferenceType( Type::Qualifiers(), typeDecl->base->clone() ) ) );
    573573                if ( src ) expr->args.push_back( new CastExpr( new VariableExpr( src ), typeDecl->base->clone() ) );
    574                 dcl->statements->kids.push_back( new ExprStmt( expr ) );
     574                dcl->statements->kids.push_back( new ExprStmt( noLabels, expr ) );
    575575        };
    576576
     
    667667                        untyped->get_args().push_back( new VariableExpr( ftype->get_parameters().back() ) );
    668668                }
    669                 function->get_statements()->get_kids().push_back( new ExprStmt( untyped ) );
    670                 function->get_statements()->get_kids().push_back( new ReturnStmt( UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) );
     669                function->get_statements()->get_kids().push_back( new ExprStmt( noLabels, untyped ) );
     670                function->get_statements()->get_kids().push_back( new ReturnStmt( noLabels, UntypedExpr::createDeref( new VariableExpr( ftype->get_parameters().front() ) ) ) );
    671671        }
    672672
  • src/SymTab/Autogen.h

    rc13e8dc8 r9c35431  
    107107                fExpr->args.splice( fExpr->args.end(), args );
    108108
    109                 *out++ = new ExprStmt( fExpr );
     109                *out++ = new ExprStmt( noLabels, fExpr );
    110110
    111111                srcParam.clearArrayIndices();
     
    165165
    166166                // for stmt's body, eventually containing call
    167                 CompoundStmt * body = new CompoundStmt();
     167                CompoundStmt * body = new CompoundStmt( noLabels );
    168168                Statement * listInit = genCall( srcParam, dstParam, fname, back_inserter( body->kids ), array->base, addCast, forward );
    169169
    170170                // block containing for stmt and index variable
    171171                std::list<Statement *> initList;
    172                 CompoundStmt * block = new CompoundStmt();
    173                 block->push_back( new DeclStmt( index ) );
     172                CompoundStmt * block = new CompoundStmt( noLabels );
     173                block->push_back( new DeclStmt( noLabels, index ) );
    174174                if ( listInit ) block->get_kids().push_back( listInit );
    175                 block->push_back( new ForStmt( initList, cond, inc, body ) );
     175                block->push_back( new ForStmt( noLabels, initList, cond, inc, body ) );
    176176
    177177                *out++ = block;
  • src/SymTab/Indexer.cc

    rc13e8dc8 r9c35431  
    4040
    4141namespace SymTab {
    42         std::ostream & operator<<( std::ostream & out, const Indexer::IdData & data ) {
    43                 return out << "(" << data.id << "," << data.baseExpr << ")";
    44         }
    45 
    46         typedef std::unordered_map< std::string, Indexer::IdData > MangleTable;
     42        typedef std::unordered_map< std::string, DeclarationWithType* > MangleTable;
    4743        typedef std::unordered_map< std::string, MangleTable > IdTable;
    4844        typedef std::unordered_map< std::string, NamedTypeDecl* > TypeTable;
     
    10197        }
    10298
    103         void Indexer::removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const {
     99        void Indexer::removeSpecialOverrides( const std::string &id, std::list< DeclarationWithType * > & out ) const {
    104100                // only need to perform this step for constructors, destructors, and assignment functions
    105101                if ( ! CodeGen::isCtorDtorAssign( id ) ) return;
     
    108104                struct ValueType {
    109105                        struct DeclBall {
    110                                 IdData decl;
     106                                FunctionDecl * decl;
    111107                                bool isUserDefinedFunc; // properties for this particular decl
    112108                                bool isDefaultCtor;
     
    124120                        // another FunctionDecl for the current type was found - determine
    125121                        // if it has special properties and update data structure accordingly
    126                         ValueType & operator+=( IdData data ) {
    127                                 DeclarationWithType * function = data.id;
     122                        ValueType & operator+=( FunctionDecl * function ) {
    128123                                bool isUserDefinedFunc = ! LinkageSpec::isOverridable( function->get_linkage() );
    129124                                bool isDefaultCtor = InitTweak::isDefaultConstructor( function );
    130125                                bool isDtor = InitTweak::isDestructor( function );
    131126                                bool isCopyFunc = InitTweak::isCopyFunction( function, function->get_name() );
    132                                 decls.push_back( DeclBall{ data, isUserDefinedFunc, isDefaultCtor, isDtor, isCopyFunc } );
     127                                decls.push_back( DeclBall{ function, isUserDefinedFunc, isDefaultCtor, isDtor, isCopyFunc } );
    133128                                existsUserDefinedFunc = existsUserDefinedFunc || isUserDefinedFunc;
    134129                                existsUserDefinedCtor = existsUserDefinedCtor || (isUserDefinedFunc && CodeGen::isConstructor( function->get_name() ) );
     
    140135                }; // ValueType
    141136
    142                 std::list< IdData > copy;
     137                std::list< DeclarationWithType * > copy;
    143138                copy.splice( copy.end(), out );
    144139
    145140                // organize discovered declarations by type
    146141                std::unordered_map< std::string, ValueType > funcMap;
    147                 for ( auto decl : copy ) {
    148                         if ( FunctionDecl * function = dynamic_cast< FunctionDecl * >( decl.id ) ) {
     142                for ( DeclarationWithType * decl : copy ) {
     143                        if ( FunctionDecl * function = dynamic_cast< FunctionDecl * >( decl ) ) {
    149144                                std::list< DeclarationWithType * > & params = function->get_functionType()->get_parameters();
    150145                                assert( ! params.empty() );
     
    152147                                Type * base = InitTweak::getPointerBase( params.front()->get_type() );
    153148                                assert( base );
    154                                 funcMap[ Mangler::mangle( base ) ] += decl;
     149                                funcMap[ Mangler::mangle( base ) ] += function;
    155150                        } else {
    156151                                out.push_back( decl );
     
    169164                                bool noUserDefinedFunc = ! val.existsUserDefinedFunc;
    170165                                bool isUserDefinedFunc = ball.isUserDefinedFunc;
    171                                 bool isAcceptableDefaultCtor = (! val.existsUserDefinedCtor || (! val.existsUserDefinedDefaultCtor && ball.decl.id->get_linkage() == LinkageSpec::Intrinsic)) && ball.isDefaultCtor; // allow default constructors only when no user-defined constructors exist, except in the case of intrinsics, which require exact overrides
     166                                bool isAcceptableDefaultCtor = (! val.existsUserDefinedCtor || (! val.existsUserDefinedDefaultCtor && ball.decl->get_linkage() == LinkageSpec::Intrinsic)) && ball.isDefaultCtor; // allow default constructors only when no user-defined constructors exist, except in the case of intrinsics, which require exact overrides
    172167                                bool isAcceptableCopyFunc = ! val.existsUserDefinedCopyFunc && ball.isCopyFunc; // handles copy ctor and assignment operator
    173168                                bool isAcceptableDtor = ! val.existsUserDefinedDtor && ball.isDtor;
     
    224219        }
    225220
    226         void Indexer::lookupId( const std::string &id, std::list< IdData > &out ) const {
     221        void Indexer::lookupId( const std::string &id, std::list< DeclarationWithType* > &out ) const {
    227222                std::unordered_set< std::string > foundMangleNames;
    228223
     
    294289                        const MangleTable &mangleTable = decls->second;
    295290                        MangleTable::const_iterator decl = mangleTable.find( mangleName );
    296                         if ( decl != mangleTable.end() ) return decl->second.id;
     291                        if ( decl != mangleTable.end() ) return decl->second;
    297292                }
    298293
     
    309304                        for ( MangleTable::const_iterator decl = mangleTable.begin(); decl != mangleTable.end(); ++decl ) {
    310305                                // check for C decls with the same name, skipping those with a compatible type (by mangleName)
    311                                 if ( ! LinkageSpec::isMangled( decl->second.id->get_linkage() ) && decl->first != mangleName ) return true;
     306                                if ( ! LinkageSpec::isMangled( decl->second->get_linkage() ) && decl->first != mangleName ) return true;
    312307                        }
    313308                }
     
    326321                                // check for C decls with the same name, skipping
    327322                                // those with an incompatible type (by mangleName)
    328                                 if ( ! LinkageSpec::isMangled( decl->second.id->get_linkage() ) && decl->first == mangleName ) return true;
     323                                if ( ! LinkageSpec::isMangled( decl->second->get_linkage() ) && decl->first == mangleName ) return true;
    329324                        }
    330325                }
     
    408403        }
    409404
    410         void Indexer::addId( DeclarationWithType *decl, Expression * baseExpr ) {
     405        void Indexer::addId( DeclarationWithType *decl ) {
    411406                debugPrint( "Adding Id " << decl->name << std::endl );
    412407                makeWritable();
     
    444439
    445440                // add to indexer
    446                 tables->idTable[ name ][ mangleName ] = { decl, baseExpr };
     441                tables->idTable[ name ][ mangleName ] = decl;
    447442                ++tables->size;
    448443        }
     
    568563                        if ( ! addedDeclConflicts( existing->second, decl ) ) {
    569564                                existing->second = decl;
    570                         }
    571                 }
    572         }
    573 
    574         void Indexer::addWith( WithStmt * stmt ) {
    575                 for ( Expression * expr : stmt->exprs ) {
    576                         if ( expr->result ) {
    577                                 AggregateDecl * aggr = expr->result->stripReferences()->getAggr();
    578                                 assertf( aggr, "WithStmt expr has non-aggregate type: %s", toString( expr->result ).c_str() );
    579 
    580                                 for ( Declaration * decl : aggr->members ) {
    581                                         if ( DeclarationWithType * dwt = dynamic_cast< DeclarationWithType * >( decl ) ) {
    582                                                 addId( dwt, expr );
    583                                         }
    584                                 }
    585565                        }
    586566                }
     
    665645
    666646        }
    667 
    668         Expression * Indexer::IdData::combine() const {
    669                 if ( baseExpr ) {
    670                         Expression * base = baseExpr->clone();
    671                         ResolvExpr::referenceToRvalueConversion( base );
    672                         Expression * ret = new MemberExpr( id, base );
    673                         // xxx - this introduces hidden environments, for now remove them.
    674                         // std::swap( base->env, ret->env );
    675                         delete base->env;
    676                         base->env = nullptr;
    677                         return ret;
    678                 } else {
    679                         return new VariableExpr( id );
    680                 }
    681         }
    682647} // namespace SymTab
    683648
  • src/SymTab/Indexer.h

    rc13e8dc8 r9c35431  
    3939                void leaveScope();
    4040
    41                 struct IdData {
    42                         DeclarationWithType * id;
    43                         Expression * baseExpr; // WithExpr
    44 
    45                         Expression * combine() const;
    46                 };
    47 
    4841                /// Gets all declarations with the given ID
    49                 void lookupId( const std::string &id, std::list< IdData > &out ) const;
     42                void lookupId( const std::string &id, std::list< DeclarationWithType* > &out ) const;
    5043                /// Gets the top-most type declaration with the given ID
    5144                NamedTypeDecl *lookupType( const std::string &id ) const;
     
    7467                TraitDecl *lookupTraitAtScope( const std::string &id, unsigned long scope ) const;
    7568
    76                 void addId( DeclarationWithType *decl, Expression * baseExpr = nullptr );
     69                void addId( DeclarationWithType *decl );
    7770                void addType( NamedTypeDecl *decl );
    7871                void addStruct( const std::string &id );
     
    8275                void addUnion( UnionDecl *decl );
    8376                void addTrait( TraitDecl *decl );
    84 
    85                 /// adds all of the IDs from WithStmt exprs
    86                 void addWith( WithStmt * );
    8777
    8878                /// convenience function for adding a list of Ids to the indexer
     
    110100                // so that they will not be selected
    111101                // void removeSpecialOverrides( FunctionDecl *decl );
    112                 void removeSpecialOverrides( const std::string &id, std::list< IdData > & out ) const;
     102                void removeSpecialOverrides( const std::string &id, std::list< DeclarationWithType * > & out ) const;
    113103
    114104                /// Ensures that tables variable is writable (i.e. allocated, uniquely owned by this Indexer, and at the current scope)
  • src/SymTab/Validate.cc

    rc13e8dc8 r9c35431  
    8181
    8282namespace SymTab {
    83         struct HoistStruct final : public WithDeclsToAdd, public WithGuards {
     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:
    8489                /// Flattens nested struct types
    8590                static void hoistStruct( std::list< Declaration * > &translationUnit );
    8691
    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 
     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 );
    93102          private:
     103                HoistStruct();
     104
    94105                template< typename AggDecl > void handleAggregate( AggDecl *aggregateDecl );
    95106
    96                 bool inStruct = false;
     107                std::list< Declaration * > declsToAdd, declsToAddAfter;
     108                bool inStruct;
    97109        };
    98110
     
    293305
    294306        void HoistStruct::hoistStruct( std::list< Declaration * > &translationUnit ) {
    295                 PassVisitor<HoistStruct> hoister;
    296                 acceptAll( translationUnit, hoister );
     307                HoistStruct hoister;
     308                acceptAndAdd( translationUnit, hoister );
     309        }
     310
     311        HoistStruct::HoistStruct() : inStruct( false ) {
    297312        }
    298313
     
    305320                if ( inStruct ) {
    306321                        // Add elements in stack order corresponding to nesting structure.
    307                         declsToAddBefore.push_front( aggregateDecl );
     322                        declsToAdd.push_front( aggregateDecl );
     323                        Visitor::visit( aggregateDecl );
    308324                } else {
    309                         GuardValue( inStruct );
    310325                        inStruct = true;
     326                        Visitor::visit( aggregateDecl );
     327                        inStruct = false;
    311328                } // if
    312329                // Always remove the hoisted aggregate from the inner structure.
    313                 GuardAction( [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 ) {
     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 ) {
    335352                handleAggregate( aggregateDecl );
    336353        }
    337354
    338         void HoistStruct::previsit( UnionDecl * aggregateDecl ) {
     355        void HoistStruct::visit( UnionDecl *aggregateDecl ) {
    339356                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 );
    340365        }
    341366
  • src/SymTab/module.mk

    rc13e8dc8 r9c35431  
    1919       SymTab/Validate.cc \
    2020       SymTab/FixFunction.cc \
     21       SymTab/ImplementationType.cc \
     22       SymTab/TypeEquality.cc \
    2123       SymTab/Autogen.cc
  • src/SynTree/CompoundStmt.cc

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

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

    rc13e8dc8 r9c35431  
    203203}
    204204
    205 Statement * Mutator::mutate( WithStmt * withStmt ) {
    206         mutateAll( withStmt->exprs, *this );
    207         withStmt->stmt = maybeMutate( withStmt->stmt, *this );
    208         return withStmt;
    209 }
    210 
    211205NullStmt * Mutator::mutate( NullStmt *nullStmt ) {
    212206        return nullStmt;
  • src/SynTree/Mutator.h

    rc13e8dc8 r9c35431  
    5050        virtual Statement * mutate( FinallyStmt * catchStmt );
    5151        virtual Statement * mutate( WaitForStmt * waitforStmt );
    52         virtual Statement * mutate( WithStmt * withStmt );
    5352        virtual NullStmt * mutate( NullStmt * nullStmt );
    5453        virtual Statement * mutate( DeclStmt * declStmt );
  • src/SynTree/ReferenceToType.cc

    rc13e8dc8 r9c35431  
    7070bool StructInstType::isComplete() const { return baseStruct ? baseStruct->has_body() : false; }
    7171
    72 AggregateDecl * StructInstType::getAggr() { return baseStruct; }
    73 
    7472void StructInstType::lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const {
    7573        assert( baseStruct );
     
    103101
    104102bool UnionInstType::isComplete() const { return baseUnion ? baseUnion->has_body() : false; }
    105 
    106 AggregateDecl * UnionInstType::getAggr() { return baseUnion; }
    107103
    108104void UnionInstType::lookup( const std::string &name, std::list< Declaration* > &foundDecls ) const {
  • src/SynTree/Statement.cc

    rc13e8dc8 r9c35431  
    3232using std::endl;
    3333
    34 Statement::Statement( const std::list<Label> & labels ) : labels( labels ) {}
     34Statement::Statement( std::list<Label> labels ) : labels( labels ) {}
    3535
    3636void Statement::print( std::ostream & os, Indenter ) const {
     
    4646Statement::~Statement() {}
    4747
    48 ExprStmt::ExprStmt( Expression *expr ) : Statement(), expr( expr ) {}
     48ExprStmt::ExprStmt( std::list<Label> labels, Expression *expr ) : Statement( labels ), expr( expr ) {}
    4949
    5050ExprStmt::ExprStmt( const ExprStmt &other ) : Statement( other ), expr( maybeClone( other.expr ) ) {}
     
    6060
    6161
    62 AsmStmt::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 ) {}
     62AsmStmt::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 ) {}
    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( Label target, Type type ) throw ( SemanticError ) :
    99         Statement(), originalTarget( target ), target( target ), computedTarget( nullptr ), type( type ) {
     98BranchStmt::BranchStmt( std::list<Label> labels, Label target, Type type ) throw ( SemanticError ) :
     99        Statement( labels ), 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( Expression *computedTarget, Type type ) throw ( SemanticError ) :
    107         Statement(), computedTarget( computedTarget ), type( type ) {
     106BranchStmt::BranchStmt( std::list<Label> labels, Expression *computedTarget, Type type ) throw ( SemanticError ) :
     107        Statement( labels ), 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( Expression *expr ) : Statement(), expr( expr ) {}
     120ReturnStmt::ReturnStmt( std::list<Label> labels, Expression *expr ) : Statement( labels ), expr( expr ) {}
    121121
    122122ReturnStmt::ReturnStmt( const ReturnStmt & other ) : Statement( other ), expr( maybeClone( other.expr ) ) {}
     
    135135}
    136136
    137 IfStmt::IfStmt( Expression *condition, Statement *thenPart, Statement *elsePart, std::list<Statement *> initialization ):
    138         Statement(), condition( condition ), thenPart( thenPart ), elsePart( elsePart ), initialization( initialization ) {}
     137IfStmt::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 ) {}
    139139
    140140IfStmt::IfStmt( const IfStmt & other ) :
     
    176176}
    177177
    178 SwitchStmt::SwitchStmt( Expression * condition, const std::list<Statement *> &statements ):
    179         Statement(), condition( condition ), statements( statements ) {
     178SwitchStmt::SwitchStmt( std::list<Label> labels, Expression * condition, const std::list<Statement *> &statements ):
     179        Statement( labels ), condition( condition ), statements( statements ) {
    180180}
    181181
     
    201201}
    202202
    203 CaseStmt::CaseStmt( Expression *condition, const std::list<Statement *> &statements, bool deflt ) throw ( SemanticError ) :
    204         Statement(), condition( condition ), stmts( statements ), _isDefault( deflt ) {
     203CaseStmt::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 ) {
    205205        if ( isDefault() && condition != 0 ) throw SemanticError("default case with condition: ", condition);
    206206}
     
    216216}
    217217
    218 CaseStmt * 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;
     218CaseStmt * CaseStmt::makeDefault( std::list<Label> labels, std::list<Statement *> stmts ) {
     219        return new CaseStmt( labels, 0, stmts, true );
    222220}
    223221
     
    235233}
    236234
    237 WhileStmt::WhileStmt( Expression *condition, Statement *body, bool isDoWhile ):
    238         Statement(), condition( condition), body( body), isDoWhile( isDoWhile) {
     235WhileStmt::WhileStmt( std::list<Label> labels, Expression *condition, Statement *body, bool isDoWhile ):
     236        Statement( labels ), condition( condition), body( body), isDoWhile( isDoWhile) {
    239237}
    240238
     
    257255}
    258256
    259 ForStmt::ForStmt( std::list<Statement *> initialization, Expression *condition, Expression *increment, Statement *body ):
    260         Statement(), initialization( initialization ), condition( condition ), increment( increment ), body( body ) {
     257ForStmt::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 ) {
    261259}
    262260
     
    304302}
    305303
    306 ThrowStmt::ThrowStmt( Kind kind, Expression * expr, Expression * target ) :
    307                 Statement(), kind(kind), expr(expr), target(target)     {
     304ThrowStmt::ThrowStmt( std::list<Label> labels, Kind kind, Expression * expr, Expression * target ) :
     305                Statement( labels ), kind(kind), expr(expr), target(target)     {
    308306        assertf(Resume == kind || nullptr == target, "Non-local termination throw is not accepted." );
    309307}
     
    328326}
    329327
    330 TryStmt::TryStmt( CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock ) :
    331         Statement(), block( tryBlock ),  handlers( handlers ), finallyBlock( finallyBlock ) {
     328TryStmt::TryStmt( std::list<Label> labels, CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock ) :
     329        Statement( labels ), block( tryBlock ),  handlers( handlers ), finallyBlock( finallyBlock ) {
    332330}
    333331
     
    361359}
    362360
    363 CatchStmt::CatchStmt( Kind kind, Declaration *decl, Expression *cond, Statement *body ) :
    364         Statement(), kind ( kind ), decl ( decl ), cond ( cond ), body( body ) {
     361CatchStmt::CatchStmt( std::list<Label> labels, Kind kind, Declaration *decl, Expression *cond, Statement *body ) :
     362        Statement( labels ), kind ( kind ), decl ( decl ), cond ( cond ), body( body ) {
    365363                assertf( decl, "Catch clause must have a declaration." );
    366364}
     
    393391
    394392
    395 FinallyStmt::FinallyStmt( CompoundStmt *block ) : Statement(), block( block ) {
     393FinallyStmt::FinallyStmt( std::list<Label> labels, CompoundStmt *block ) : Statement( labels ), block( block ) {
     394        assert( labels.empty() ); // finally statement cannot be labeled
    396395}
    397396
     
    409408}
    410409
    411 WaitForStmt::WaitForStmt() : Statement() {
     410WaitForStmt::WaitForStmt( std::list<Label> labels ) : Statement( labels ) {
    412411        timeout.time      = nullptr;
    413412        timeout.statement = nullptr;
     
    456455}
    457456
    458 
    459 WithStmt::WithStmt( const std::list< Expression * > & exprs, Statement * stmt ) : Statement(), exprs( exprs ), stmt( stmt ) {}
    460 WithStmt::WithStmt( const WithStmt & other ) : Statement( other ), stmt( maybeClone( other.stmt ) ) {
    461         cloneAll( other.exprs, exprs );
    462 }
    463 WithStmt::~WithStmt() {
    464         deleteAll( exprs );
    465         delete stmt;
    466 }
    467 
    468 void WithStmt::print( std::ostream & os, Indenter indent ) const {
    469         os << "With statement" << endl;
    470         os << indent << "... with statement:" << endl << indent+1;
    471         stmt->print( os, indent+1 );
    472 }
    473 
    474 
    475 NullStmt::NullStmt( const std::list<Label> & labels ) : Statement( labels ) {
    476 }
     457NullStmt::NullStmt( std::list<Label> labels ) : Statement( labels ) {}
     458NullStmt::NullStmt() : Statement( std::list<Label>() ) {}
    477459
    478460void NullStmt::print( std::ostream &os, Indenter ) const {
     
    480462}
    481463
    482 ImplicitCtorDtorStmt::ImplicitCtorDtorStmt( Statement * callStmt ) : Statement(), callStmt( callStmt ) {
     464ImplicitCtorDtorStmt::ImplicitCtorDtorStmt( Statement * callStmt ) : Statement( std::list<Label>() ), callStmt( callStmt ) {
    483465        assert( callStmt );
    484466}
  • src/SynTree/Statement.h

    rc13e8dc8 r9c35431  
    3737        std::list<Label> labels;
    3838
    39         Statement( const std::list<Label> & labels = {} );
     39        Statement( std::list<Label> labels );
    4040        virtual ~Statement();
    4141
     
    5353        std::list<Statement*> kids;
    5454
    55         CompoundStmt();
     55        CompoundStmt( std::list<Label> labels );
    5656        CompoundStmt( std::list<Statement *> stmts );
    5757        CompoundStmt( const CompoundStmt &other );
     
    7070class NullStmt : public Statement {
    7171  public:
    72         NullStmt( const std::list<Label> & labels = {} );
     72        NullStmt();
     73        NullStmt( std::list<Label> labels );
    7374
    7475        virtual NullStmt *clone() const override { return new NullStmt( *this ); }
     
    8283        Expression *expr;
    8384
    84         ExprStmt( Expression *expr );
     85        ExprStmt( std::list<Label> labels, Expression *expr );
    8586        ExprStmt( const ExprStmt &other );
    8687        virtual ~ExprStmt();
     
    103104        std::list<Label> gotolabels;
    104105
    105         AsmStmt( bool voltile, Expression *instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels );
     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 );
    106107        AsmStmt( const AsmStmt &other );
    107108        virtual ~AsmStmt();
     
    133134        std::list<Statement *> initialization;
    134135
    135         IfStmt( Expression *condition, Statement *thenPart, Statement *elsePart,
     136        IfStmt( std::list<Label> labels, Expression *condition, Statement *thenPart, Statement *elsePart,
    136137                        std::list<Statement *> initialization = std::list<Statement *>() );
    137138        IfStmt( const IfStmt &other );
     
    157158        std::list<Statement *> statements;
    158159
    159         SwitchStmt( Expression *condition, const std::list<Statement *> &statements );
     160        SwitchStmt( std::list<Label> labels, Expression *condition, const std::list<Statement *> &statements );
    160161        SwitchStmt( const SwitchStmt &other );
    161162        virtual ~SwitchStmt();
     
    179180        std::list<Statement *> stmts;
    180181
    181         CaseStmt( Expression *conditions, const std::list<Statement *> &stmts, bool isdef = false ) throw(SemanticError);
     182        CaseStmt( std::list<Label> labels, Expression *conditions, const std::list<Statement *> &stmts, bool isdef = false ) throw(SemanticError);
    182183        CaseStmt( const CaseStmt &other );
    183184        virtual ~CaseStmt();
    184185
    185         static CaseStmt * makeDefault( const std::list<Label> & labels = {}, std::list<Statement *> stmts = std::list<Statement *>() );
     186        static CaseStmt * makeDefault( std::list<Label> labels = std::list<Label>(), std::list<Statement *> stmts = std::list<Statement *>() );
    186187
    187188        bool isDefault() const { return _isDefault; }
     
    209210        bool isDoWhile;
    210211
    211         WhileStmt( Expression *condition,
     212        WhileStmt( std::list<Label> labels, Expression *condition,
    212213               Statement *body, bool isDoWhile = false );
    213214        WhileStmt( const WhileStmt &other );
     
    234235        Statement *body;
    235236
    236         ForStmt( std::list<Statement *> initialization,
     237        ForStmt( std::list<Label> labels, std::list<Statement *> initialization,
    237238             Expression *condition = 0, Expression *increment = 0, Statement *body = 0 );
    238239        ForStmt( const ForStmt &other );
     
    263264        Type type;
    264265
    265         BranchStmt( Label target, Type ) throw (SemanticError);
    266         BranchStmt( Expression *computedTarget, Type ) throw (SemanticError);
     266        BranchStmt( std::list<Label> labels, Label target, Type ) throw (SemanticError);
     267        BranchStmt( std::list<Label> labels, Expression *computedTarget, Type ) throw (SemanticError);
    267268
    268269        Label get_originalTarget() { return originalTarget; }
     
    288289        Expression *expr;
    289290
    290         ReturnStmt( Expression *expr );
     291        ReturnStmt( std::list<Label> labels, Expression *expr );
    291292        ReturnStmt( const ReturnStmt &other );
    292293        virtual ~ReturnStmt();
     
    309310        Expression * target;
    310311
    311         ThrowStmt( Kind kind, Expression * expr, Expression * target = nullptr );
     312        ThrowStmt( std::list<Label> labels, Kind kind, Expression * expr, Expression * target = nullptr );
    312313        ThrowStmt( const ThrowStmt &other );
    313314        virtual ~ThrowStmt();
     
    331332        FinallyStmt * finallyBlock;
    332333
    333         TryStmt( CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock = 0 );
     334        TryStmt( std::list<Label> labels, CompoundStmt *tryBlock, std::list<CatchStmt *> &handlers, FinallyStmt *finallyBlock = 0 );
    334335        TryStmt( const TryStmt &other );
    335336        virtual ~TryStmt();
     
    357358        Statement *body;
    358359
    359         CatchStmt( Kind kind, Declaration *decl,
     360        CatchStmt( std::list<Label> labels, Kind kind, Declaration *decl,
    360361                   Expression *cond, Statement *body );
    361362        CatchStmt( const CatchStmt &other );
     
    380381        CompoundStmt *block;
    381382
    382         FinallyStmt( CompoundStmt *block );
     383        FinallyStmt( std::list<Label> labels, CompoundStmt *block );
    383384        FinallyStmt( const FinallyStmt &other );
    384385        virtual ~FinallyStmt();
     
    407408        };
    408409
    409         WaitForStmt();
     410        WaitForStmt( std::list<Label> labels = noLabels );
    410411        WaitForStmt( const WaitForStmt & );
    411412        virtual ~WaitForStmt();
     
    431432};
    432433
    433 class WithStmt : public Statement {
    434 public:
    435         std::list< Expression * > exprs;
    436         Statement * stmt;
    437 
    438         WithStmt( const std::list< Expression * > & exprs, Statement * stmt );
    439         WithStmt( const WithStmt & other );
    440         virtual ~WithStmt();
    441 
    442         virtual WithStmt * clone() const override { return new WithStmt( *this ); }
    443         virtual void accept( Visitor & v ) override { v.visit( this ); }
    444         virtual Statement * acceptMutator( Mutator & m )  override { return m.mutate( this ); }
    445         virtual void print( std::ostream & os, Indenter indent = {} ) const override;
    446 };
    447 
    448434
    449435// represents a declaration that occurs as part of a compound statement
     
    452438        Declaration *decl;
    453439
    454         DeclStmt( Declaration *decl );
     440        DeclStmt( std::list<Label> labels, Declaration *decl );
    455441        DeclStmt( const DeclStmt &other );
    456442        virtual ~DeclStmt();
  • src/SynTree/SynTree.h

    rc13e8dc8 r9c35431  
    5555class FinallyStmt;
    5656class WaitForStmt;
    57 class WithStmt;
    5857class NullStmt;
    5958class DeclStmt;
  • src/SynTree/TupleExpr.cc

    rc13e8dc8 r9c35431  
    2323#include "Declaration.h"        // for ObjectDecl
    2424#include "Expression.h"         // for Expression, TupleExpr, TupleIndexExpr
    25 #include "SynTree/Label.h"      // for Label
     25#include "SynTree/Label.h"      // for Label, noLabels
    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();
     91        CompoundStmt * compoundStmt = new CompoundStmt( noLabels );
    9292        std::list< Statement * > & stmts = compoundStmt->get_kids();
    9393        for ( ObjectDecl * obj : tempDecls ) {
    94                 stmts.push_back( new DeclStmt( obj ) );
     94                stmts.push_back( new DeclStmt( noLabels, obj ) );
    9595        }
    9696        TupleExpr * tupleExpr = new TupleExpr( assigns );
    9797        assert( tupleExpr->get_result() );
    98         stmts.push_back( new ExprStmt( tupleExpr ) );
     98        stmts.push_back( new ExprStmt( noLabels, tupleExpr ) );
    9999        stmtExpr = new StmtExpr( compoundStmt );
    100100}
  • src/SynTree/Type.h

    rc13e8dc8 r9c35431  
    178178        virtual bool isComplete() const { return true; }
    179179
    180         virtual AggregateDecl * getAggr() {     assertf( false, "Non-aggregate type: %s", toString( this ).c_str() ); }
    181 
    182180        virtual Type *clone() const = 0;
    183181        virtual void accept( Visitor & v ) = 0;
     
    407405        virtual bool isComplete() const override;
    408406
    409         virtual AggregateDecl * getAggr() override;
    410 
    411407        /// Looks up the members of this struct named "name" and places them into "foundDecls".
    412408        /// Clones declarations into "foundDecls", caller responsible for freeing
     
    440436
    441437        virtual bool isComplete() const override;
    442 
    443         virtual AggregateDecl * getAggr() override;
    444438
    445439        /// looks up the members of this union named "name" and places them into "foundDecls"
  • src/SynTree/Visitor.cc

    rc13e8dc8 r9c35431  
    174174}
    175175
    176 void Visitor::visit( WithStmt * withStmt ) {
    177         acceptAll( withStmt->exprs, *this );
    178         maybeAccept( withStmt->stmt, *this );
    179 }
    180 
    181 void Visitor::visit( NullStmt * ) {
     176void Visitor::visit( __attribute__((unused)) NullStmt *nullStmt ) {
    182177}
    183178
  • src/SynTree/Visitor.h

    rc13e8dc8 r9c35431  
    5252        virtual void visit( FinallyStmt * finallyStmt );
    5353        virtual void visit( WaitForStmt * waitforStmt );
    54         virtual void visit( WithStmt * withStmt );
    5554        virtual void visit( NullStmt * nullStmt );
    5655        virtual void visit( DeclStmt * declStmt );
  • src/Tuples/TupleAssignment.cc

    rc13e8dc8 r9c35431  
    2323
    2424#include "CodeGen/OperatorTable.h"
    25 #include "Common/PassVisitor.h"
    2625#include "Common/UniqueName.h"             // for UniqueName
    2726#include "Common/utility.h"                // for zipWith
     
    6261                struct Matcher {
    6362                  public:
    64                         Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, const
     63                        Matcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, const 
    6564                                ResolvExpr::AltList& rhs );
    6665                        virtual ~Matcher() {}
     
    7675                struct MassAssignMatcher : public Matcher {
    7776                  public:
    78                         MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs,
     77                        MassAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 
    7978                                const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
    8079                        virtual void match( std::list< Expression * > &out );
     
    8382                struct MultipleAssignMatcher : public Matcher {
    8483                  public:
    85                         MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs,
     84                        MultipleAssignMatcher( TupleAssignSpotter &spotter, const ResolvExpr::AltList& lhs, 
    8685                                const ResolvExpr::AltList& rhs ) : Matcher(spotter, lhs, rhs) {}
    8786                        virtual void match( std::list< Expression * > &out );
     
    120119        }
    121120
    122         void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr,
     121        void handleTupleAssignment( ResolvExpr::AlternativeFinder & currentFinder, UntypedExpr * expr, 
    123122                                std::vector<ResolvExpr::AlternativeFinder> &args ) {
    124123                TupleAssignSpotter spotter( currentFinder );
     
    129128                : currentFinder(f) {}
    130129
    131         void TupleAssignSpotter::spot( UntypedExpr * expr,
     130        void TupleAssignSpotter::spot( UntypedExpr * expr, 
    132131                        std::vector<ResolvExpr::AlternativeFinder> &args ) {
    133132                if (  NameExpr *op = dynamic_cast< NameExpr * >(expr->get_function()) ) {
     
    138137                                if ( args.size() == 0 ) return;
    139138
    140                                 // if an assignment only takes 1 argument, that's odd, but maybe someone wrote
     139                                // if an assignment only takes 1 argument, that's odd, but maybe someone wrote 
    141140                                // the function, in which case AlternativeFinder will handle it normally
    142141                                if ( args.size() == 1 && CodeGen::isAssignment( fname ) ) return;
     
    147146                                        if ( ! refToTuple(lhsAlt.expr) ) continue;
    148147
    149                                         // explode is aware of casts - ensure every LHS expression is sent into explode
     148                                        // explode is aware of casts - ensure every LHS expression is sent into explode 
    150149                                        // with a reference cast
    151                                         // xxx - this seems to change the alternatives before the normal
     150                                        // xxx - this seems to change the alternatives before the normal 
    152151                                        //  AlternativeFinder flow; maybe this is desired?
    153152                                        if ( ! dynamic_cast<CastExpr*>( lhsAlt.expr ) ) {
    154                                                 lhsAlt.expr = new CastExpr( lhsAlt.expr,
    155                                                                 new ReferenceType( Type::Qualifiers(),
     153                                                lhsAlt.expr = new CastExpr( lhsAlt.expr, 
     154                                                                new ReferenceType( Type::Qualifiers(), 
    156155                                                                        lhsAlt.expr->get_result()->clone() ) );
    157156                                        }
     
    161160                                        explode( lhsAlt, currentFinder.get_indexer(), back_inserter(lhs), true );
    162161                                        for ( ResolvExpr::Alternative& alt : lhs ) {
    163                                                 // each LHS value must be a reference - some come in with a cast expression,
     162                                                // each LHS value must be a reference - some come in with a cast expression, 
    164163                                                // if not just cast to reference here
    165164                                                if ( ! dynamic_cast<ReferenceType*>( alt.expr->get_result() ) ) {
    166                                                         alt.expr = new CastExpr( alt.expr,
    167                                                                 new ReferenceType( Type::Qualifiers(),
     165                                                        alt.expr = new CastExpr( alt.expr, 
     166                                                                new ReferenceType( Type::Qualifiers(), 
    168167                                                                        alt.expr->get_result()->clone() ) );
    169168                                                }
     
    179178                                                // TODO build iterative version of this instead of using combos
    180179                                                std::vector< ResolvExpr::AltList > rhsAlts;
    181                                                 combos( std::next(args.begin(), 1), args.end(),
     180                                                combos( std::next(args.begin(), 1), args.end(), 
    182181                                                        std::back_inserter( rhsAlts ) );
    183182                                                for ( const ResolvExpr::AltList& rhsAlt : rhsAlts ) {
    184183                                                        // multiple assignment
    185184                                                        ResolvExpr::AltList rhs;
    186                                                         explode( rhsAlt, currentFinder.get_indexer(),
     185                                                        explode( rhsAlt, currentFinder.get_indexer(), 
    187186                                                                std::back_inserter(rhs), true );
    188187                                                        matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );
     
    194193                                                        if ( isTuple(rhsAlt.expr) ) {
    195194                                                                // multiple assignment
    196                                                                 explode( rhsAlt, currentFinder.get_indexer(),
     195                                                                explode( rhsAlt, currentFinder.get_indexer(), 
    197196                                                                        std::back_inserter(rhs), true );
    198197                                                                matcher.reset( new MultipleAssignMatcher( *this, lhs, rhs ) );
     
    223222                ResolvExpr::AltList current;
    224223                // now resolve new assignments
    225                 for ( std::list< Expression * >::iterator i = new_assigns.begin();
     224                for ( std::list< Expression * >::iterator i = new_assigns.begin(); 
    226225                                i != new_assigns.end(); ++i ) {
    227226                        PRINT(
     
    230229                        )
    231230
    232                         ResolvExpr::AlternativeFinder finder{ currentFinder.get_indexer(),
     231                        ResolvExpr::AlternativeFinder finder{ currentFinder.get_indexer(), 
    233232                                currentFinder.get_environ() };
    234233                        try {
     
    254253                // xxx -- was push_front
    255254                currentFinder.get_alternatives().push_back( ResolvExpr::Alternative(
    256                         new TupleAssignExpr(solved_assigns, matcher->tmpDecls), matcher->compositeEnv,
     255                        new TupleAssignExpr(solved_assigns, matcher->tmpDecls), matcher->compositeEnv, 
    257256                        ResolvExpr::sumCost( current ) + matcher->baseCost ) );
    258257        }
    259258
    260         TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter,
    261                 const ResolvExpr::AltList &lhs, const ResolvExpr::AltList &rhs )
    262         : lhs(lhs), rhs(rhs), spotter(spotter),
     259        TupleAssignSpotter::Matcher::Matcher( TupleAssignSpotter &spotter, 
     260                const ResolvExpr::AltList &lhs, const ResolvExpr::AltList &rhs ) 
     261        : lhs(lhs), rhs(rhs), spotter(spotter), 
    263262          baseCost( ResolvExpr::sumCost( lhs ) + ResolvExpr::sumCost( rhs ) ) {
    264263                simpleCombineEnvironments( lhs.begin(), lhs.end(), compositeEnv );
     
    278277        // xxx - maybe this should happen in alternative finder for every StmtExpr?
    279278        // 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
    280         struct EnvRemover {
    281                 void previsit( ExprStmt * stmt ) {
    282                         delete stmt->expr->env;
    283                         stmt->expr->env = nullptr;
     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 );
    284284                }
    285285        };
     
    293293                        ret->set_init( ctorInit );
    294294                        ResolvExpr::resolveCtorInit( ctorInit, spotter.currentFinder.get_indexer() ); // resolve ctor/dtors for the new object
    295                         PassVisitor<EnvRemover> rm; // remove environments from subexpressions of StmtExprs
     295                        EnvRemover rm; // remove environments from subexpressions of StmtExprs
    296296                        ctorInit->accept( rm );
    297297                }
  • src/Tuples/TupleExpansion.cc

    rc13e8dc8 r9c35431  
    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                 struct ImpurityDetector : public WithShortCircuiting {
     317                class ImpurityDetector : public Visitor {
     318                public:
    318319                        ImpurityDetector( bool ignoreUnique ) : ignoreUnique( ignoreUnique ) {}
    319320
    320                         void previsit( ApplicationExpr * appExpr ) {
    321                                 visit_children = false;
     321                        typedef Visitor Parent;
     322                        virtual void visit( ApplicationExpr * appExpr ) {
    322323                                if ( DeclarationWithType * function = InitTweak::getFunction( appExpr ) ) {
    323324                                        if ( function->get_linkage() == LinkageSpec::Intrinsic ) {
    324325                                                if ( function->get_name() == "*?" || function->get_name() == "?[?]" ) {
    325326                                                        // intrinsic dereference, subscript are pure, but need to recursively look for impurity
    326                                                         visit_children = true;
     327                                                        Parent::visit( appExpr );
    327328                                                        return;
    328329                                                }
     
    331332                                maybeImpure = true;
    332333                        }
    333                         void previsit( UntypedExpr * ) { maybeImpure = true; visit_children = false; }
    334                         void previsit( UniqueExpr * ) {
     334                        virtual void visit( UntypedExpr * ) { maybeImpure = true; }
     335                        virtual void visit( UniqueExpr * unq ) {
    335336                                if ( ignoreUnique ) {
    336337                                        // bottom out at unique expression.
    337338                                        // The existence of a unique expression doesn't change the purity of an expression.
    338339                                        // 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 );
    342343                        }
    343344
     
    348349
    349350        bool maybeImpure( Expression * expr ) {
    350                 PassVisitor<ImpurityDetector> detector( false );
     351                ImpurityDetector detector( false );
    351352                expr->accept( detector );
    352                 return detector.pass.maybeImpure;
     353                return detector.maybeImpure;
    353354        }
    354355
    355356        bool maybeImpureIgnoreUnique( Expression * expr ) {
    356                 PassVisitor<ImpurityDetector> detector( true );
     357                ImpurityDetector detector( true );
    357358                expr->accept( detector );
    358                 return detector.pass.maybeImpure;
     359                return detector.maybeImpure;
    359360        }
    360361} // namespace Tuples
  • src/driver/cfa.cc

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

    rc13e8dc8 r9c35431  
    5555
    5656libobjs = ${headers:=.o}
    57 libsrc = libcfa-prelude.c interpose.c bits/debug.c ${headers:=.c} \
     57libsrc = libcfa-prelude.c interpose.c libhdr/libdebug.c ${headers:=.c} \
    5858         assert.c exception.c virtual.c
    5959
     
    100100        math                            \
    101101        gmp                             \
    102         bits/align.h            \
    103102        bits/containers.h               \
    104103        bits/defs.h             \
    105         bits/debug.h            \
    106104        bits/locks.h            \
    107         concurrency/invoke.h
     105        concurrency/invoke.h    \
     106        libhdr.h                        \
     107        libhdr/libalign.h       \
     108        libhdr/libdebug.h       \
     109        libhdr/libtools.h
    108110
    109111CLEANFILES = libcfa-prelude.c
  • src/libcfa/Makefile.in

    rc13e8dc8 r9c35431  
    149149libcfa_d_a_LIBADD =
    150150am__libcfa_d_a_SOURCES_DIST = libcfa-prelude.c interpose.c \
    151         bits/debug.c fstream.c iostream.c iterator.c limits.c \
     151        libhdr/libdebug.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) 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)
     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)
    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 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
     185am__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
    193194@BUILD_CONCURRENCY_TRUE@am__objects_5 = concurrency/libcfa_a-coroutine.$(OBJEXT) \
    194195@BUILD_CONCURRENCY_TRUE@        concurrency/libcfa_a-thread.$(OBJEXT) \
     
    207208@BUILD_CONCURRENCY_TRUE@        concurrency/libcfa_a-preemption.$(OBJEXT)
    208209am__objects_8 = libcfa_a-libcfa-prelude.$(OBJEXT) \
    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)
     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)
    213214am_libcfa_a_OBJECTS = $(am__objects_8)
    214215libcfa_a_OBJECTS = $(am_libcfa_a_OBJECTS)
     
    263264        containers/result containers/vector concurrency/coroutine \
    264265        concurrency/thread concurrency/kernel concurrency/monitor \
    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
     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
    267269HEADERS = $(nobase_cfa_include_HEADERS)
    268270am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
     
    422424        containers/vector $(am__append_3)
    423425libobjs = ${headers:=.o}
    424 libsrc = libcfa-prelude.c interpose.c bits/debug.c ${headers:=.c} \
     426libsrc = libcfa-prelude.c interpose.c libhdr/libdebug.c ${headers:=.c} \
    425427        assert.c exception.c virtual.c $(am__append_4)
    426428libcfa_a_SOURCES = ${libsrc}
     
    435437        math                            \
    436438        gmp                             \
    437         bits/align.h            \
    438439        bits/containers.h               \
    439440        bits/defs.h             \
    440         bits/debug.h            \
    441441        bits/locks.h            \
    442         concurrency/invoke.h
     442        concurrency/invoke.h    \
     443        libhdr.h                        \
     444        libhdr/libalign.h       \
     445        libhdr/libdebug.h       \
     446        libhdr/libtools.h
    443447
    444448CLEANFILES = libcfa-prelude.c
     
    507511clean-libLIBRARIES:
    508512        -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES)
    509 bits/$(am__dirstamp):
    510         @$(MKDIR_P) bits
    511         @: > bits/$(am__dirstamp)
    512 bits/$(DEPDIR)/$(am__dirstamp):
    513         @$(MKDIR_P) bits/$(DEPDIR)
    514         @: > bits/$(DEPDIR)/$(am__dirstamp)
    515 bits/libcfa_d_a-debug.$(OBJEXT): bits/$(am__dirstamp) \
    516         bits/$(DEPDIR)/$(am__dirstamp)
     513libhdr/$(am__dirstamp):
     514        @$(MKDIR_P) libhdr
     515        @: > libhdr/$(am__dirstamp)
     516libhdr/$(DEPDIR)/$(am__dirstamp):
     517        @$(MKDIR_P) libhdr/$(DEPDIR)
     518        @: > libhdr/$(DEPDIR)/$(am__dirstamp)
     519libhdr/libcfa_d_a-libdebug.$(OBJEXT): libhdr/$(am__dirstamp) \
     520        libhdr/$(DEPDIR)/$(am__dirstamp)
    517521containers/$(am__dirstamp):
    518522        @$(MKDIR_P) containers
     
    559563        $(AM_V_AR)$(libcfa_d_a_AR) libcfa-d.a $(libcfa_d_a_OBJECTS) $(libcfa_d_a_LIBADD)
    560564        $(AM_V_at)$(RANLIB) libcfa-d.a
    561 bits/libcfa_a-debug.$(OBJEXT): bits/$(am__dirstamp) \
    562         bits/$(DEPDIR)/$(am__dirstamp)
     565libhdr/libcfa_a-libdebug.$(OBJEXT): libhdr/$(am__dirstamp) \
     566        libhdr/$(DEPDIR)/$(am__dirstamp)
    563567containers/libcfa_a-maybe.$(OBJEXT): containers/$(am__dirstamp) \
    564568        containers/$(DEPDIR)/$(am__dirstamp)
     
    592596mostlyclean-compile:
    593597        -rm -f *.$(OBJEXT)
    594         -rm -f bits/*.$(OBJEXT)
    595598        -rm -f concurrency/*.$(OBJEXT)
    596599        -rm -f containers/*.$(OBJEXT)
     600        -rm -f libhdr/*.$(OBJEXT)
    597601
    598602distclean-compile:
     
    621625@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcfa_d_a-stdlib.Po@am__quote@
    622626@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@
    625627@AMDEP_TRUE@@am__include@ @am__quote@concurrency/$(DEPDIR)/CtxSwitch-@MACHINE_TYPE@.Po@am__quote@
    626628@AMDEP_TRUE@@am__include@ @am__quote@concurrency/$(DEPDIR)/libcfa_a-alarm.Po@am__quote@
     
    646648@AMDEP_TRUE@@am__include@ @am__quote@containers/$(DEPDIR)/libcfa_d_a-result.Po@am__quote@
    647649@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@
    648652
    649653.S.o:
     
    700704@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`
    701705
    702 bits/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 
    709 bits/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`
     706libhdr/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
     713libhdr/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`
    715719
    716720libcfa_d_a-fstream.o: fstream.c
     
    994998@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`
    995999
    996 bits/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 
    1003 bits/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`
     1000libhdr/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
     1007libhdr/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`
    10091013
    10101014libcfa_a-fstream.o: fstream.c
     
    14071411        -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
    14081412        -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)
    14111413        -rm -f concurrency/$(DEPDIR)/$(am__dirstamp)
    14121414        -rm -f concurrency/$(am__dirstamp)
    14131415        -rm -f containers/$(DEPDIR)/$(am__dirstamp)
    14141416        -rm -f containers/$(am__dirstamp)
     1417        -rm -f libhdr/$(DEPDIR)/$(am__dirstamp)
     1418        -rm -f libhdr/$(am__dirstamp)
    14151419
    14161420maintainer-clean-generic:
     
    14221426
    14231427distclean: distclean-am
    1424         -rm -rf ./$(DEPDIR) bits/$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR)
     1428        -rm -rf ./$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR) libhdr/$(DEPDIR)
    14251429        -rm -f Makefile
    14261430distclean-am: clean-am distclean-compile distclean-generic \
     
    14681472
    14691473maintainer-clean: maintainer-clean-am
    1470         -rm -rf ./$(DEPDIR) bits/$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR)
     1474        -rm -rf ./$(DEPDIR) concurrency/$(DEPDIR) containers/$(DEPDIR) libhdr/$(DEPDIR)
    14711475        -rm -f Makefile
    14721476maintainer-clean-am: distclean-am maintainer-clean-generic \
  • src/libcfa/assert.c

    rc13e8dc8 r9c35431  
    1717#include <stdarg.h>                                                             // varargs
    1818#include <stdio.h>                                                              // fprintf
    19 #include "bits/debug.h"
     19#include "libhdr/libdebug.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                 __cfaabi_dbg_bits_print_safe( CFA_ASSERT_FMT ".\n", __progname, function, line, file );
     28                __lib_debug_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                 __cfaabi_dbg_bits_acquire();
    35                 __cfaabi_dbg_bits_print_nolock( CFA_ASSERT_FMT ": ", __progname, function, line, file );
     34                __lib_debug_acquire();
     35                __lib_debug_print_nolock( CFA_ASSERT_FMT ": ", __progname, function, line, file );
    3636
    3737                va_list args;
    3838                va_start( args, fmt );
    39                 __cfaabi_dbg_bits_print_vararg( fmt, args );
     39                __lib_debug_print_vararg( fmt, args );
    4040                va_end( args );
    4141
    42                 __cfaabi_dbg_bits_print_nolock( "\n" );
    43                 __cfaabi_dbg_bits_release();
     42                __lib_debug_print_nolock( "\n" );
     43                __lib_debug_release();
    4444                abort();
    4545        }
  • src/libcfa/bits/containers.h

    rc13e8dc8 r9c35431  
    1515#pragma once
    1616
    17 #include "bits/align.h"
    1817#include "bits/defs.h"
     18#include "libhdr.h"
    1919
    2020//-----------------------------------------------------------------------------
  • src/libcfa/bits/defs.h

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

    rc13e8dc8 r9c35431  
    1616#pragma once
    1717
    18 #include "bits/debug.h"
    1918#include "bits/defs.h"
     19
     20#include "libhdr.h"
    2021
    2122// pause to prevent excess processor bus usage
     
    6465
    6566        // Lock the spinlock, return false if already acquired
    66         static inline _Bool try_lock  ( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
     67        static inline _Bool try_lock  ( __spinlock_t & this DEBUG_CTX_PARAM2 ) {
    6768                _Bool result = __lock_test_and_test_and_set( this.lock );
    68                 __cfaabi_dbg_debug_do(
     69                LIB_DEBUG_DO(
    6970                        if( result ) {
    7071                                this.prev_name = caller;
     
    7677
    7778        // Lock the spinlock, spin if already acquired
    78         static inline void lock( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
     79        static inline void lock( __spinlock_t & this DEBUG_CTX_PARAM2 ) {
    7980                #ifndef NOEXPBACK
    8081                        enum { SPIN_START = 4, SPIN_END = 64 * 1024, };
     
    9798                        #endif
    9899                }
    99                 __cfaabi_dbg_debug_do(
     100                LIB_DEBUG_DO(
    100101                        this.prev_name = caller;
    101102                        this.prev_thrd = this_thread;
     
    104105
    105106        // Lock the spinlock, spin if already acquired
    106         static inline void lock_yield( __spinlock_t & this __cfaabi_dbg_ctx_param2 ) {
     107        static inline void lock_yield( __spinlock_t & this DEBUG_CTX_PARAM2 ) {
    107108                for ( unsigned int i = 1;; i += 1 ) {
    108109                        if ( __lock_test_and_test_and_set( this.lock ) ) break;
    109110                        yield( i );
    110111                }
    111                 __cfaabi_dbg_debug_do(
     112                LIB_DEBUG_DO(
    112113                        this.prev_name = caller;
    113114                        this.prev_thrd = this_thread;
  • src/libcfa/concurrency/alarm.c

    rc13e8dc8 r9c35431  
    2323}
    2424
     25#include "libhdr.h"
     26
    2527#include "alarm.h"
    2628#include "kernel_private.h"
     
    108110}
    109111
    110 __cfaabi_dbg_debug_do( bool validate( alarm_list_t * this ) {
     112LIB_DEBUG_DO( bool validate( alarm_list_t * this ) {
    111113        alarm_node_t ** it = &this->head;
    112114        while( (*it) ) {
     
    184186
    185187        disable_interrupts();
    186         lock( event_kernel->lock __cfaabi_dbg_ctx2 );
     188        lock( event_kernel->lock DEBUG_CTX2 );
    187189        {
    188190                verify( validate( alarms ) );
     
    196198        unlock( event_kernel->lock );
    197199        this->set = true;
    198         enable_interrupts( __cfaabi_dbg_ctx );
     200        enable_interrupts( DEBUG_CTX );
    199201}
    200202
    201203void unregister_self( alarm_node_t * this ) {
    202204        disable_interrupts();
    203         lock( event_kernel->lock __cfaabi_dbg_ctx2 );
     205        lock( event_kernel->lock DEBUG_CTX2 );
    204206        {
    205207                verify( validate( &event_kernel->alarms ) );
     
    207209        }
    208210        unlock( event_kernel->lock );
    209         enable_interrupts( __cfaabi_dbg_ctx );
     211        enable_interrupts( DEBUG_CTX );
    210212        this->set = false;
    211213}
  • src/libcfa/concurrency/coroutine.c

    rc13e8dc8 r9c35431  
    2929#define __CFA_INVOKE_PRIVATE__
    3030#include "invoke.h"
     31
    3132
    3233//-----------------------------------------------------------------------------
     
    7576void ^?{}(coStack_t & this) {
    7677        if ( ! this.userStack && this.storage ) {
    77                 __cfaabi_dbg_debug_do(
     78                LIB_DEBUG_DO(
    7879                        if ( mprotect( this.storage, pageSize, PROT_READ | PROT_WRITE ) == -1 ) {
    7980                                abortf( "(coStack_t *)%p.^?{}() : internal error, mprotect failure, error(%d) %s.", &this, errno, strerror( errno ) );
     
    130131
    131132                // assume malloc has 8 byte alignment so add 8 to allow rounding up to 16 byte alignment
    132                 __cfaabi_dbg_debug_do( this->storage = memalign( pageSize, cxtSize + this->size + pageSize ) );
    133                 __cfaabi_dbg_no_debug_do( this->storage = malloc( cxtSize + this->size + 8 ) );
     133                LIB_DEBUG_DO( this->storage = memalign( pageSize, cxtSize + this->size + pageSize ) );
     134                LIB_NO_DEBUG_DO( this->storage = malloc( cxtSize + this->size + 8 ) );
    134135
    135                 __cfaabi_dbg_debug_do(
     136                LIB_DEBUG_DO(
    136137                        if ( mprotect( this->storage, pageSize, PROT_NONE ) == -1 ) {
    137138                                abortf( "(uMachContext &)%p.createContext() : internal error, mprotect failure, error(%d) %s.", this, (int)errno, strerror( (int)errno ) );
     
    143144                } // if
    144145
    145                 __cfaabi_dbg_debug_do( this->limit = (char *)this->storage + pageSize );
    146                 __cfaabi_dbg_no_debug_do( this->limit = (char *)libCeiling( (unsigned long)this->storage, 16 ) ); // minimum alignment
     146                LIB_DEBUG_DO( this->limit = (char *)this->storage + pageSize );
     147                LIB_NO_DEBUG_DO( this->limit = (char *)libCeiling( (unsigned long)this->storage, 16 ) ); // minimum alignment
    147148
    148149        } else {
  • src/libcfa/concurrency/invoke.c

    rc13e8dc8 r9c35431  
    1818#include <stdio.h>
    1919
     20#include "libhdr.h"
    2021#include "invoke.h"
    2122
     
    3031extern void __leave_thread_monitor( struct thread_desc * this );
    3132extern void disable_interrupts();
    32 extern void enable_interrupts( __cfaabi_dbg_ctx_param );
     33extern void enable_interrupts( DEBUG_CTX_PARAM );
    3334
    3435void CtxInvokeCoroutine(
    35         void (*main)(void *),
    36         struct coroutine_desc *(*get_coroutine)(void *),
    37         void *this
     36      void (*main)(void *),
     37      struct coroutine_desc *(*get_coroutine)(void *),
     38      void *this
    3839) {
    39         struct coroutine_desc* cor = get_coroutine( this );
     40      // LIB_DEBUG_PRINTF("Invoke Coroutine : Received %p (main %p, get_c %p)\n", this, main, get_coroutine);
    4041
    41         if(cor->state == Primed) {
    42                 __suspend_internal();
    43         }
     42      struct coroutine_desc* cor = get_coroutine( this );
    4443
    45         cor->state = Active;
     44      if(cor->state == Primed) {
     45            __suspend_internal();
     46      }
    4647
    47         main( this );
     48      cor->state = Active;
    4849
    49         cor->state = Halted;
     50      main( this );
    5051
    51         //Final suspend, should never return
    52         __leave_coroutine();
    53         abortf("Resumed dead coroutine");
     52      cor->state = Halted;
     53
     54      //Final suspend, should never return
     55      __leave_coroutine();
     56      abortf("Resumed dead coroutine");
    5457}
    5558
    5659void CtxInvokeThread(
    57         void (*dtor)(void *),
    58         void (*main)(void *),
    59         struct thread_desc *(*get_thread)(void *),
    60         void *this
     60      void (*dtor)(void *),
     61      void (*main)(void *),
     62      struct thread_desc *(*get_thread)(void *),
     63      void *this
    6164) {
    62         // First suspend, once the thread arrives here,
    63         // the function pointer to main can be invalidated without risk
    64         __suspend_internal();
     65      // First suspend, once the thread arrives here,
     66      // the function pointer to main can be invalidated without risk
     67      __suspend_internal();
    6568
    66         // Fetch the thread handle from the user defined thread structure
    67         struct thread_desc* thrd = get_thread( this );
     69      // Fetch the thread handle from the user defined thread structure
     70      struct thread_desc* thrd = get_thread( this );
    6871
    69         // Officially start the thread by enabling preemption
    70         enable_interrupts( __cfaabi_dbg_ctx );
     72      // Officially start the thread by enabling preemption
     73      enable_interrupts( DEBUG_CTX );
    7174
    72         // Call the main of the thread
    73         main( this );
     75      // Call the main of the thread
     76      main( this );
    7477
    75         // To exit a thread we must :
    76         // 1 - Mark it as halted
    77         // 2 - Leave its monitor
    78         // 3 - Disable the interupts
    79         // 4 - Final suspend
    80         // The order of these 4 operations is very important
    81         //Final suspend, should never return
    82         __leave_thread_monitor( thrd );
    83         abortf("Resumed dead thread");
     78      // To exit a thread we must :
     79      // 1 - Mark it as halted
     80      // 2 - Leave its monitor
     81      // 3 - Disable the interupts
     82      // 4 - Final suspend
     83      // The order of these 4 operations is very important
     84      //Final suspend, should never return
     85      __leave_thread_monitor( thrd );
     86      abortf("Resumed dead thread");
    8487}
    8588
    8689
    8790void CtxStart(
    88         void (*main)(void *),
    89         struct coroutine_desc *(*get_coroutine)(void *),
    90         void *this,
    91         void (*invoke)(void *)
     91      void (*main)(void *),
     92      struct coroutine_desc *(*get_coroutine)(void *),
     93      void *this,
     94      void (*invoke)(void *)
    9295) {
    93         struct coStack_t* stack = &get_coroutine( this )->stack;
     96      // LIB_DEBUG_PRINTF("StartCoroutine : Passing in %p (main %p) to invoke (%p) from start (%p)\n", this, main, invoke, CtxStart);
     97
     98      struct coStack_t* stack = &get_coroutine( this )->stack;
    9499
    95100#if defined( __i386__ )
     
    98103            void *fixedRegisters[3];                    // fixed registers ebx, edi, esi (popped on 1st uSwitch, values unimportant)
    99104            uint32_t mxcr;                        // SSE Status and Control bits (control bits are preserved across function calls)
    100             uint16_t fcw;                         // X97 FPU control word (preserved across function calls)
     105          uint16_t fcw;                         // X97 FPU control word (preserved across function calls)
    101106            void *rturn;                          // where to go on return from uSwitch
    102107            void *dummyReturn;                          // fake return compiler would have pushed on call to uInvoke
     
    111116        ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->argument[0] = this;     // argument to invoke
    112117        ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->rturn = invoke;
    113         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->mxcr = 0x1F80; //Vol. 2A 3-520
    114         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fcw = 0x037F;  //Vol. 1 8-7
     118      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->mxcr = 0x1F80; //Vol. 2A 3-520
     119      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fcw = 0x037F;  //Vol. 1 8-7
    115120
    116121#elif defined( __x86_64__ )
    117122
    118         struct FakeStack {
    119                 void *fixedRegisters[5];            // fixed registers rbx, r12, r13, r14, r15
    120                 uint32_t mxcr;                      // SSE Status and Control bits (control bits are preserved across function calls)
    121                 uint16_t fcw;                       // X97 FPU control word (preserved across function calls)
    122                 void *rturn;                        // where to go on return from uSwitch
    123                 void *dummyReturn;                  // NULL return address to provide proper alignment
    124         };
     123      struct FakeStack {
     124            void *fixedRegisters[5];            // fixed registers rbx, r12, r13, r14, r15
     125            uint32_t mxcr;                      // SSE Status and Control bits (control bits are preserved across function calls)
     126            uint16_t fcw;                       // X97 FPU control word (preserved across function calls)
     127            void *rturn;                        // where to go on return from uSwitch
     128            void *dummyReturn;                  // NULL return address to provide proper alignment
     129      };
    125130
    126         ((struct machine_context_t *)stack->context)->SP = (char *)stack->base - sizeof( struct FakeStack );
    127         ((struct machine_context_t *)stack->context)->FP = NULL;                // terminate stack with NULL fp
     131      ((struct machine_context_t *)stack->context)->SP = (char *)stack->base - sizeof( struct FakeStack );
     132      ((struct machine_context_t *)stack->context)->FP = NULL;          // terminate stack with NULL fp
    128133
    129         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->dummyReturn = NULL;
    130         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->rturn = CtxInvokeStub;
    131         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fixedRegisters[0] = this;
    132         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fixedRegisters[1] = invoke;
    133         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->mxcr = 0x1F80; //Vol. 2A 3-520
    134         ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fcw = 0x037F;  //Vol. 1 8-7
     134      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->dummyReturn = NULL;
     135      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->rturn = CtxInvokeStub;
     136      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fixedRegisters[0] = this;
     137      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fixedRegisters[1] = invoke;
     138      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->mxcr = 0x1F80; //Vol. 2A 3-520
     139      ((struct FakeStack *)(((struct machine_context_t *)stack->context)->SP))->fcw = 0x037F;  //Vol. 1 8-7
    135140#else
    136         #error Only __i386__ and __x86_64__ is supported for threads in cfa
     141      #error Only __i386__ and __x86_64__ is supported for threads in cfa
    137142#endif
    138143}
  • src/libcfa/concurrency/kernel.c

    rc13e8dc8 r9c35431  
    1414//
    1515
     16#include "libhdr.h"
     17
    1618//C Includes
    1719#include <stddef.h>
     
    148150
    149151        this.runner = &runner;
    150         __cfaabi_dbg_print_safe("Kernel : constructing main processor context %p\n", &runner);
     152        LIB_DEBUG_PRINT_SAFE("Kernel : constructing main processor context %p\n", &runner);
    151153        runner{ &this };
    152154}
     
    154156void ^?{}(processor & this) {
    155157        if( ! this.do_terminate ) {
    156                 __cfaabi_dbg_print_safe("Kernel : core %p signaling termination\n", &this);
     158                LIB_DEBUG_PRINT_SAFE("Kernel : core %p signaling termination\n", &this);
    157159                this.do_terminate = true;
    158160                P( this.terminated );
     
    179181        processor * this = runner.proc;
    180182
    181         __cfaabi_dbg_print_safe("Kernel : core %p starting\n", this);
     183        LIB_DEBUG_PRINT_SAFE("Kernel : core %p starting\n", this);
    182184
    183185        {
     
    185187                preemption_scope scope = { this };
    186188
    187                 __cfaabi_dbg_print_safe("Kernel : core %p started\n", this);
     189                LIB_DEBUG_PRINT_SAFE("Kernel : core %p started\n", this);
    188190
    189191                thread_desc * readyThread = NULL;
     
    211213                }
    212214
    213                 __cfaabi_dbg_print_safe("Kernel : core %p stopping\n", this);
     215                LIB_DEBUG_PRINT_SAFE("Kernel : core %p stopping\n", this);
    214216        }
    215217
    216218        V( this->terminated );
    217219
    218         __cfaabi_dbg_print_safe("Kernel : core %p terminated\n", this);
     220        LIB_DEBUG_PRINT_SAFE("Kernel : core %p terminated\n", this);
    219221}
    220222
     
    290292        processorCtx_t proc_cor_storage = { proc, &info };
    291293
    292         __cfaabi_dbg_print_safe("Coroutine : created stack %p\n", proc_cor_storage.__cor.stack.base);
     294        LIB_DEBUG_PRINT_SAFE("Coroutine : created stack %p\n", proc_cor_storage.__cor.stack.base);
    293295
    294296        //Set global state
     
    297299
    298300        //We now have a proper context from which to schedule threads
    299         __cfaabi_dbg_print_safe("Kernel : core %p created (%p, %p)\n", proc, proc->runner, &ctx);
     301        LIB_DEBUG_PRINT_SAFE("Kernel : core %p created (%p, %p)\n", proc, proc->runner, &ctx);
    300302
    301303        // SKULLDUGGERY: Since the coroutine doesn't have its own stack, we can't
     
    308310
    309311        // Main routine of the core returned, the core is now fully terminated
    310         __cfaabi_dbg_print_safe("Kernel : core %p main ended (%p)\n", proc, proc->runner);
     312        LIB_DEBUG_PRINT_SAFE("Kernel : core %p main ended (%p)\n", proc, proc->runner);
    311313
    312314        return NULL;
     
    314316
    315317void start(processor * this) {
    316         __cfaabi_dbg_print_safe("Kernel : Starting core %p\n", this);
     318        LIB_DEBUG_PRINT_SAFE("Kernel : Starting core %p\n", this);
    317319
    318320        pthread_create( &this->kernel_thread, NULL, CtxInvokeProcessor, (void*)this );
    319321
    320         __cfaabi_dbg_print_safe("Kernel : core %p started\n", this);
     322        LIB_DEBUG_PRINT_SAFE("Kernel : core %p started\n", this);
    321323}
    322324
     
    332334        verifyf( thrd->next == NULL, "Expected null got %p", thrd->next );
    333335
    334         lock(   this_processor->cltr->ready_queue_lock __cfaabi_dbg_ctx2 );
     336        lock(   this_processor->cltr->ready_queue_lock DEBUG_CTX2 );
    335337        append( this_processor->cltr->ready_queue, thrd );
    336338        unlock( this_processor->cltr->ready_queue_lock );
     
    341343thread_desc * nextThread(cluster * this) {
    342344        verify( disable_preempt_count > 0 );
    343         lock( this->ready_queue_lock __cfaabi_dbg_ctx2 );
     345        lock( this->ready_queue_lock DEBUG_CTX2 );
    344346        thread_desc * head = pop_head( this->ready_queue );
    345347        unlock( this->ready_queue_lock );
     
    353355        suspend();
    354356        verify( disable_preempt_count > 0 );
    355         enable_interrupts( __cfaabi_dbg_ctx );
     357        enable_interrupts( DEBUG_CTX );
    356358}
    357359
     
    365367        verify( disable_preempt_count > 0 );
    366368
    367         enable_interrupts( __cfaabi_dbg_ctx );
     369        enable_interrupts( DEBUG_CTX );
    368370}
    369371
     
    379381        verify( disable_preempt_count > 0 );
    380382
    381         enable_interrupts( __cfaabi_dbg_ctx );
     383        enable_interrupts( DEBUG_CTX );
    382384}
    383385
     
    393395        verify( disable_preempt_count > 0 );
    394396
    395         enable_interrupts( __cfaabi_dbg_ctx );
     397        enable_interrupts( DEBUG_CTX );
    396398}
    397399
     
    406408        verify( disable_preempt_count > 0 );
    407409
    408         enable_interrupts( __cfaabi_dbg_ctx );
     410        enable_interrupts( DEBUG_CTX );
    409411}
    410412
     
    421423        verify( disable_preempt_count > 0 );
    422424
    423         enable_interrupts( __cfaabi_dbg_ctx );
     425        enable_interrupts( DEBUG_CTX );
    424426}
    425427
     
    439441// Kernel boot procedures
    440442void kernel_startup(void) {
    441         __cfaabi_dbg_print_safe("Kernel : Starting\n");
     443        LIB_DEBUG_PRINT_SAFE("Kernel : Starting\n");
    442444
    443445        // Start by initializing the main thread
     
    448450        (*mainThread){ &info };
    449451
    450         __cfaabi_dbg_print_safe("Kernel : Main thread ready\n");
     452        LIB_DEBUG_PRINT_SAFE("Kernel : Main thread ready\n");
    451453
    452454        // Initialize the main cluster
     
    454456        (*mainCluster){};
    455457
    456         __cfaabi_dbg_print_safe("Kernel : main cluster ready\n");
     458        LIB_DEBUG_PRINT_SAFE("Kernel : main cluster ready\n");
    457459
    458460        // Initialize the main processor and the main processor ctx
     
    481483
    482484        // THE SYSTEM IS NOW COMPLETELY RUNNING
    483         __cfaabi_dbg_print_safe("Kernel : Started\n--------------------------------------------------\n\n");
    484 
    485         enable_interrupts( __cfaabi_dbg_ctx );
     485        LIB_DEBUG_PRINT_SAFE("Kernel : Started\n--------------------------------------------------\n\n");
     486
     487        enable_interrupts( DEBUG_CTX );
    486488}
    487489
    488490void kernel_shutdown(void) {
    489         __cfaabi_dbg_print_safe("\n--------------------------------------------------\nKernel : Shutting down\n");
     491        LIB_DEBUG_PRINT_SAFE("\n--------------------------------------------------\nKernel : Shutting down\n");
    490492
    491493        disable_interrupts();
     
    511513        ^(mainThread){};
    512514
    513         __cfaabi_dbg_print_safe("Kernel : Shutdown complete\n");
     515        LIB_DEBUG_PRINT_SAFE("Kernel : Shutdown complete\n");
    514516}
    515517
     
    521523        // abort cannot be recursively entered by the same or different processors because all signal handlers return when
    522524        // the globalAbort flag is true.
    523         lock( kernel_abort_lock __cfaabi_dbg_ctx2 );
     525        lock( kernel_abort_lock DEBUG_CTX2 );
    524526
    525527        // first task to abort ?
     
    546548
    547549        int len = snprintf( abort_text, abort_text_size, "Error occurred while executing task %.256s (%p)", thrd->self_cor.name, thrd );
    548         __cfaabi_dbg_bits_write( abort_text, len );
     550        __lib_debug_write( abort_text, len );
    549551
    550552        if ( thrd != this_coroutine ) {
    551553                len = snprintf( abort_text, abort_text_size, " in coroutine %.256s (%p).\n", this_coroutine->name, this_coroutine );
    552                 __cfaabi_dbg_bits_write( abort_text, len );
     554                __lib_debug_write( abort_text, len );
    553555        }
    554556        else {
    555                 __cfaabi_dbg_bits_write( ".\n", 2 );
     557                __lib_debug_write( ".\n", 2 );
    556558        }
    557559}
    558560
    559561extern "C" {
    560         void __cfaabi_dbg_bits_acquire() {
    561                 lock( kernel_debug_lock __cfaabi_dbg_ctx2 );
    562         }
    563 
    564         void __cfaabi_dbg_bits_release() {
     562        void __lib_debug_acquire() {
     563                lock( kernel_debug_lock DEBUG_CTX2 );
     564        }
     565
     566        void __lib_debug_release() {
    565567                unlock( kernel_debug_lock );
    566568        }
     
    580582
    581583void P(semaphore & this) {
    582         lock( this.lock __cfaabi_dbg_ctx2 );
     584        lock( this.lock DEBUG_CTX2 );
    583585        this.count -= 1;
    584586        if ( this.count < 0 ) {
     
    596598void V(semaphore & this) {
    597599        thread_desc * thrd = NULL;
    598         lock( this.lock __cfaabi_dbg_ctx2 );
     600        lock( this.lock DEBUG_CTX2 );
    599601        this.count += 1;
    600602        if ( this.count <= 0 ) {
  • src/libcfa/concurrency/kernel_private.h

    rc13e8dc8 r9c35431  
    1616#pragma once
    1717
     18#include "libhdr.h"
     19
    1820#include "kernel"
    1921#include "thread"
     
    2830        void disable_interrupts();
    2931        void enable_interrupts_noPoll();
    30         void enable_interrupts( __cfaabi_dbg_ctx_param );
     32        void enable_interrupts( DEBUG_CTX_PARAM );
    3133}
    3234
     
    3739        disable_interrupts();
    3840        ScheduleThread( thrd );
    39         enable_interrupts( __cfaabi_dbg_ctx );
     41        enable_interrupts( DEBUG_CTX );
    4042}
    4143thread_desc * nextThread(cluster * this);
  • src/libcfa/concurrency/monitor.c

    rc13e8dc8 r9c35431  
    1919#include <inttypes.h>
    2020
     21#include "libhdr.h"
    2122#include "kernel_private.h"
    2223
     
    9091        static void __enter_monitor_desc( monitor_desc * this, const __monitor_group_t & group ) {
    9192                // Lock the monitor spinlock
    92                 DO_LOCK( this->lock __cfaabi_dbg_ctx2 );
     93                DO_LOCK( this->lock DEBUG_CTX2 );
    9394                thread_desc * thrd = this_thread;
    9495
    95                 __cfaabi_dbg_print_safe("Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner);
     96                LIB_DEBUG_PRINT_SAFE("Kernel : %10p Entering mon %p (%p)\n", thrd, this, this->owner);
    9697
    9798                if( !this->owner ) {
     
    99100                        set_owner( this, thrd );
    100101
    101                         __cfaabi_dbg_print_safe("Kernel :  mon is free \n");
     102                        LIB_DEBUG_PRINT_SAFE("Kernel :  mon is free \n");
    102103                }
    103104                else if( this->owner == thrd) {
     
    105106                        this->recursion += 1;
    106107
    107                         __cfaabi_dbg_print_safe("Kernel :  mon already owned \n");
     108                        LIB_DEBUG_PRINT_SAFE("Kernel :  mon already owned \n");
    108109                }
    109110                else if( is_accepted( this, group) ) {
     
    114115                        reset_mask( this );
    115116
    116                         __cfaabi_dbg_print_safe("Kernel :  mon accepts \n");
     117                        LIB_DEBUG_PRINT_SAFE("Kernel :  mon accepts \n");
    117118                }
    118119                else {
    119                         __cfaabi_dbg_print_safe("Kernel :  blocking \n");
     120                        LIB_DEBUG_PRINT_SAFE("Kernel :  blocking \n");
    120121
    121122                        // Some one else has the monitor, wait in line for it
     
    123124                        BlockInternal( &this->lock );
    124125
    125                         __cfaabi_dbg_print_safe("Kernel : %10p Entered  mon %p\n", thrd, this);
     126                        LIB_DEBUG_PRINT_SAFE("Kernel : %10p Entered  mon %p\n", thrd, this);
    126127
    127128                        // BlockInternal will unlock spinlock, no need to unlock ourselves
     
    129130                }
    130131
    131                 __cfaabi_dbg_print_safe("Kernel : %10p Entered  mon %p\n", thrd, this);
     132                LIB_DEBUG_PRINT_SAFE("Kernel : %10p Entered  mon %p\n", thrd, this);
    132133
    133134                // Release the lock and leave
     
    138139        static void __enter_monitor_dtor( monitor_desc * this, fptr_t func ) {
    139140                // Lock the monitor spinlock
    140                 DO_LOCK( this->lock __cfaabi_dbg_ctx2 );
     141                DO_LOCK( this->lock DEBUG_CTX2 );
    141142                thread_desc * thrd = this_thread;
    142143
    143                 __cfaabi_dbg_print_safe("Kernel : %10p Entering dtor for mon %p (%p)\n", thrd, this, this->owner);
     144                LIB_DEBUG_PRINT_SAFE("Kernel : %10p Entering dtor for mon %p (%p)\n", thrd, this, this->owner);
    144145
    145146
    146147                if( !this->owner ) {
    147                         __cfaabi_dbg_print_safe("Kernel : Destroying free mon %p\n", this);
     148                        LIB_DEBUG_PRINT_SAFE("Kernel : Destroying free mon %p\n", this);
    148149
    149150                        // No one has the monitor, just take it
     
    163164                __monitor_group_t group = { &this, 1, func };
    164165                if( is_accepted( this, group) ) {
    165                         __cfaabi_dbg_print_safe("Kernel :  mon accepts dtor, block and signal it \n");
     166                        LIB_DEBUG_PRINT_SAFE("Kernel :  mon accepts dtor, block and signal it \n");
    166167
    167168                        // Wake the thread that is waiting for this
     
    182183                }
    183184                else {
    184                         __cfaabi_dbg_print_safe("Kernel :  blocking \n");
     185                        LIB_DEBUG_PRINT_SAFE("Kernel :  blocking \n");
    185186
    186187                        wait_ctx( this_thread, 0 )
     
    195196                }
    196197
    197                 __cfaabi_dbg_print_safe("Kernel : Destroying %p\n", this);
     198                LIB_DEBUG_PRINT_SAFE("Kernel : Destroying %p\n", this);
    198199
    199200        }
     
    202203        void __leave_monitor_desc( monitor_desc * this ) {
    203204                // Lock the monitor spinlock, DO_LOCK to reduce contention
    204                 DO_LOCK( this->lock __cfaabi_dbg_ctx2 );
    205 
    206                 __cfaabi_dbg_print_safe("Kernel : %10p Leaving mon %p (%p)\n", this_thread, this, this->owner);
     205                DO_LOCK( this->lock DEBUG_CTX2 );
     206
     207                LIB_DEBUG_PRINT_SAFE("Kernel : %10p Leaving mon %p (%p)\n", this_thread, this, this->owner);
    207208
    208209                verifyf( this_thread == this->owner, "Expected owner to be %p, got %p (r: %i, m: %p)", this_thread, this->owner, this->recursion, this );
     
    214215                // it means we don't need to do anything
    215216                if( this->recursion != 0) {
    216                         __cfaabi_dbg_print_safe("Kernel :  recursion still %d\n", this->recursion);
     217                        LIB_DEBUG_PRINT_SAFE("Kernel :  recursion still %d\n", this->recursion);
    217218                        unlock( this->lock );
    218219                        return;
     
    231232        // Leave single monitor for the last time
    232233        void __leave_dtor_monitor_desc( monitor_desc * this ) {
    233                 __cfaabi_dbg_debug_do(
     234                LIB_DEBUG_DO(
    234235                        if( this_thread != this->owner ) {
    235236                                abortf("Destroyed monitor %p has inconsistent owner, expected %p got %p.\n", this, this_thread, this->owner);
     
    248249
    249250                // Lock the monitor now
    250                 DO_LOCK( this->lock __cfaabi_dbg_ctx2 );
     251                DO_LOCK( this->lock DEBUG_CTX2 );
    251252
    252253                disable_interrupts();
     
    307308        (this_thread->monitors){m, count, func};
    308309
    309         // __cfaabi_dbg_print_safe("MGUARD : enter %d\n", count);
     310        // LIB_DEBUG_PRINT_SAFE("MGUARD : enter %d\n", count);
    310311
    311312        // Enter the monitors in order
     
    313314        enter( group );
    314315
    315         // __cfaabi_dbg_print_safe("MGUARD : entered\n");
     316        // LIB_DEBUG_PRINT_SAFE("MGUARD : entered\n");
    316317}
    317318
     
    319320// Dtor for monitor guard
    320321void ^?{}( monitor_guard_t & this ) {
    321         // __cfaabi_dbg_print_safe("MGUARD : leaving %d\n", this.count);
     322        // LIB_DEBUG_PRINT_SAFE("MGUARD : leaving %d\n", this.count);
    322323
    323324        // Leave the monitors in order
    324325        leave( this.m, this.count );
    325326
    326         // __cfaabi_dbg_print_safe("MGUARD : left\n");
     327        // LIB_DEBUG_PRINT_SAFE("MGUARD : left\n");
    327328
    328329        // Restore thread context
     
    429430
    430431        //Some more checking in debug
    431         __cfaabi_dbg_debug_do(
     432        LIB_DEBUG_DO(
    432433                thread_desc * this_thrd = this_thread;
    433434                if ( this.monitor_count != this_thrd->monitors.size ) {
     
    486487        set_owner( monitors, count, signallee );
    487488
    488         __cfaabi_dbg_print_buffer_decl( "Kernel : signal_block condition %p (s: %p)\n", &this, signallee );
     489        LIB_DEBUG_PRINT_BUFFER_DECL( "Kernel : signal_block condition %p (s: %p)\n", &this, signallee );
    489490
    490491        //Everything is ready to go to sleep
     
    495496
    496497
    497         __cfaabi_dbg_print_buffer_local( "Kernel :   signal_block returned\n" );
     498        LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel :   signal_block returned\n" );
    498499
    499500        //We are back, restore the masks and recursions
     
    534535        __lock_size_t actual_count = aggregate( mon_storage, mask );
    535536
    536         __cfaabi_dbg_print_buffer_decl( "Kernel : waitfor %d (s: %d, m: %d)\n", actual_count, mask.size, (__lock_size_t)max);
     537        LIB_DEBUG_PRINT_BUFFER_DECL( "Kernel : waitfor %d (s: %d, m: %d)\n", actual_count, mask.size, (__lock_size_t)max);
    537538
    538539        if(actual_count == 0) return;
    539540
    540         __cfaabi_dbg_print_buffer_local( "Kernel : waitfor internal proceeding\n");
     541        LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : waitfor internal proceeding\n");
    541542
    542543        // Create storage for monitor context
     
    555556                        __acceptable_t& accepted = mask[index];
    556557                        if( accepted.is_dtor ) {
    557                                 __cfaabi_dbg_print_buffer_local( "Kernel : dtor already there\n");
     558                                LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : dtor already there\n");
    558559                                verifyf( accepted.size == 1,  "ERROR: Accepted dtor has more than 1 mutex parameter." );
    559560
     
    567568                        }
    568569                        else {
    569                                 __cfaabi_dbg_print_buffer_local( "Kernel : thread present, baton-passing\n");
     570                                LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : thread present, baton-passing\n");
    570571
    571572                                // Create the node specific to this wait operation
     
    575576                                monitor_save;
    576577
    577                                 __cfaabi_dbg_print_buffer_local( "Kernel :  baton of %d monitors : ", count );
     578                                LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel :  baton of %d monitors : ", count );
    578579                                #ifdef __CFA_DEBUG_PRINT__
    579580                                        for( int i = 0; i < count; i++) {
    580                                                 __cfaabi_dbg_print_buffer_local( "%p %p ", monitors[i], monitors[i]->signal_stack.top );
     581                                                LIB_DEBUG_PRINT_BUFFER_LOCAL( "%p %p ", monitors[i], monitors[i]->signal_stack.top );
    581582                                        }
    582583                                #endif
    583                                 __cfaabi_dbg_print_buffer_local( "\n");
     584                                LIB_DEBUG_PRINT_BUFFER_LOCAL( "\n");
    584585
    585586                                // Set the owners to be the next thread
     
    592593                                monitor_restore;
    593594
    594                                 __cfaabi_dbg_print_buffer_local( "Kernel : thread present, returned\n");
     595                                LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : thread present, returned\n");
    595596                        }
    596597
    597                         __cfaabi_dbg_print_buffer_local( "Kernel : accepted %d\n", *mask.accepted);
     598                        LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : accepted %d\n", *mask.accepted);
    598599                        return;
    599600                }
     
    602603
    603604        if( duration == 0 ) {
    604                 __cfaabi_dbg_print_buffer_local( "Kernel : non-blocking, exiting\n");
     605                LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : non-blocking, exiting\n");
    605606
    606607                unlock_all( locks, count );
    607608
    608                 __cfaabi_dbg_print_buffer_local( "Kernel : accepted %d\n", *mask.accepted);
     609                LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : accepted %d\n", *mask.accepted);
    609610                return;
    610611        }
     
    613614        verifyf( duration < 0, "Timeout on waitfor statments not supported yet.");
    614615
    615         __cfaabi_dbg_print_buffer_local( "Kernel : blocking waitfor\n");
     616        LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : blocking waitfor\n");
    616617
    617618        // Create the node specific to this wait operation
     
    635636        monitor_restore;
    636637
    637         __cfaabi_dbg_print_buffer_local( "Kernel : exiting\n");
    638 
    639         __cfaabi_dbg_print_buffer_local( "Kernel : accepted %d\n", *mask.accepted);
     638        LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : exiting\n");
     639
     640        LIB_DEBUG_PRINT_BUFFER_LOCAL( "Kernel : accepted %d\n", *mask.accepted);
    640641}
    641642
     
    644645
    645646static inline void set_owner( monitor_desc * this, thread_desc * owner ) {
    646         // __cfaabi_dbg_print_safe("Kernal :   Setting owner of %p to %p ( was %p)\n", this, owner, this->owner );
     647        // LIB_DEBUG_PRINT_SAFE("Kernal :   Setting owner of %p to %p ( was %p)\n", this, owner, this->owner );
    647648
    648649        //Pass the monitor appropriately
     
    676677static inline thread_desc * next_thread( monitor_desc * this ) {
    677678        //Check the signaller stack
    678         __cfaabi_dbg_print_safe("Kernel :  mon %p AS-stack top %p\n", this, this->signal_stack.top);
     679        LIB_DEBUG_PRINT_SAFE("Kernel :  mon %p AS-stack top %p\n", this, this->signal_stack.top);
    679680        __condition_criterion_t * urgent = pop( this->signal_stack );
    680681        if( urgent ) {
     
    728729        for( __lock_size_t i = 0; i < count; i++) {
    729730                (criteria[i]){ monitors[i], waiter };
    730                 __cfaabi_dbg_print_safe( "Kernel :  target %p = %p\n", criteria[i].target, &criteria[i] );
     731                LIB_DEBUG_PRINT_SAFE( "Kernel :  target %p = %p\n", criteria[i].target, &criteria[i] );
    731732                push( criteria[i].target->signal_stack, &criteria[i] );
    732733        }
     
    737738static inline void lock_all( __spinlock_t * locks [], __lock_size_t count ) {
    738739        for( __lock_size_t i = 0; i < count; i++ ) {
    739                 DO_LOCK( *locks[i] __cfaabi_dbg_ctx2 );
     740                DO_LOCK( *locks[i] DEBUG_CTX2 );
    740741        }
    741742}
     
    744745        for( __lock_size_t i = 0; i < count; i++ ) {
    745746                __spinlock_t * l = &source[i]->lock;
    746                 DO_LOCK( *l __cfaabi_dbg_ctx2 );
     747                DO_LOCK( *l DEBUG_CTX2 );
    747748                if(locks) locks[i] = l;
    748749        }
     
    802803        for(    int i = 0; i < count; i++ ) {
    803804
    804                 // __cfaabi_dbg_print_safe( "Checking %p for %p\n", &criteria[i], target );
     805                // LIB_DEBUG_PRINT_SAFE( "Checking %p for %p\n", &criteria[i], target );
    805806                if( &criteria[i] == target ) {
    806807                        criteria[i].ready = true;
    807                         // __cfaabi_dbg_print_safe( "True\n" );
     808                        // LIB_DEBUG_PRINT_SAFE( "True\n" );
    808809                }
    809810
     
    811812        }
    812813
    813         __cfaabi_dbg_print_safe( "Kernel :  Runing %i (%p)\n", ready2run, ready2run ? node->waiting_thread : NULL );
     814        LIB_DEBUG_PRINT_SAFE( "Kernel :  Runing %i (%p)\n", ready2run, ready2run ? node->waiting_thread : NULL );
    814815        return ready2run ? node->waiting_thread : NULL;
    815816}
     
    818819        thread_desc * thrd = this_thread;
    819820        if( !this.monitors ) {
    820                 // __cfaabi_dbg_print_safe("Branding\n");
     821                // LIB_DEBUG_PRINT_SAFE("Branding\n");
    821822                assertf( thrd->monitors.data != NULL, "No current monitor to brand condition %p", thrd->monitors.data );
    822823                this.monitor_count = thrd->monitors.size;
  • src/libcfa/concurrency/preemption.c

    rc13e8dc8 r9c35431  
    1414//
    1515
     16#include "libhdr.h"
    1617#include "preemption.h"
    1718
     
    147148//=============================================================================================
    148149
    149 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )
     150LIB_DEBUG_DO( static thread_local void * last_interrupt = 0; )
    150151
    151152extern "C" {
     
    158159        // Enable interrupts by decrementing the counter
    159160        // If counter reaches 0, execute any pending CtxSwitch
    160         void enable_interrupts( __cfaabi_dbg_ctx_param ) {
     161        void enable_interrupts( DEBUG_CTX_PARAM ) {
    161162                processor * proc   = this_processor;      // Cache the processor now since interrupts can start happening after the atomic add
    162163                thread_desc * thrd = this_thread;         // Cache the thread now since interrupts can start happening after the atomic add
     
    172173
    173174                // For debugging purposes : keep track of the last person to enable the interrupts
    174                 __cfaabi_dbg_debug_do( proc->last_enable = caller; )
     175                LIB_DEBUG_DO( proc->last_enable = caller; )
    175176        }
    176177
     
    232233// Called from kernel_startup
    233234void kernel_start_preemption() {
    234         __cfaabi_dbg_print_safe("Kernel : Starting preemption\n");
     235        LIB_DEBUG_PRINT_SAFE("Kernel : Starting preemption\n");
    235236
    236237        // Start with preemption disabled until ready
     
    254255// Called from kernel_shutdown
    255256void kernel_stop_preemption() {
    256         __cfaabi_dbg_print_safe("Kernel : Preemption stopping\n");
     257        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopping\n");
    257258
    258259        // Block all signals since we are already shutting down
     
    270271        // Preemption is now fully stopped
    271272
    272         __cfaabi_dbg_print_safe("Kernel : Preemption stopped\n");
     273        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption stopped\n");
    273274}
    274275
     
    296297// Receives SIGUSR1 signal and causes the current thread to yield
    297298void sigHandler_ctxSwitch( __CFA_SIGPARMS__ ) {
    298         __cfaabi_dbg_debug_do( last_interrupt = (void *)(cxt->uc_mcontext.gregs[CFA_REG_IP]); )
     299        LIB_DEBUG_DO( last_interrupt = (void *)(cxt->uc_mcontext.gregs[CFA_REG_IP]); )
    299300
    300301        // Check if it is safe to preempt here
     
    345346                assertf(sig == SIGALRM, "Kernel Internal Error, sigwait: Unexpected signal %d (%d : %d)\n", sig, info.si_code, info.si_value.sival_int);
    346347
    347                 // __cfaabi_dbg_print_safe("Kernel : Caught alarm from %d with %d\n", info.si_code, info.si_value.sival_int );
     348                // LIB_DEBUG_PRINT_SAFE("Kernel : Caught alarm from %d with %d\n", info.si_code, info.si_value.sival_int );
    348349                // Switch on the code (a.k.a. the sender) to
    349350                switch( info.si_code )
     
    353354                case SI_TIMER:
    354355                case SI_KERNEL:
    355                         // __cfaabi_dbg_print_safe("Kernel : Preemption thread tick\n");
    356                         lock( event_kernel->lock __cfaabi_dbg_ctx2 );
     356                        // LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread tick\n");
     357                        lock( event_kernel->lock DEBUG_CTX2 );
    357358                        tick_preemption();
    358359                        unlock( event_kernel->lock );
     
    367368
    368369EXIT:
    369         __cfaabi_dbg_print_safe("Kernel : Preemption thread stopping\n");
     370        LIB_DEBUG_PRINT_SAFE("Kernel : Preemption thread stopping\n");
    370371        return NULL;
    371372}
     
    379380
    380381        if ( sigaction( sig, &act, NULL ) == -1 ) {
    381                 __cfaabi_dbg_print_buffer_decl(
     382                LIB_DEBUG_PRINT_BUFFER_DECL(
    382383                        " __kernel_sigaction( sig:%d, handler:%p, flags:%d ), problem installing signal handler, error(%d) %s.\n",
    383384                        sig, handler, flags, errno, strerror( errno )
     
    396397
    397398        if ( sigaction( sig, &act, NULL ) == -1 ) {
    398                 __cfaabi_dbg_print_buffer_decl(
     399                LIB_DEBUG_PRINT_BUFFER_DECL(
    399400                        " __kernel_sigdefault( sig:%d ), problem reseting signal handler, error(%d) %s.\n",
    400401                        sig, errno, strerror( errno )
     
    408409//=============================================================================================
    409410
    410 __cfaabi_dbg_debug_do(
     411LIB_DEBUG_DO(
    411412        static void __kernel_backtrace( int start ) {
    412413                // skip first N stack frames
     
    475476
    476477// void sigHandler_segv( __CFA_SIGPARMS__ ) {
    477 //      __cfaabi_dbg_debug_do(
     478//      LIB_DEBUG_DO(
    478479//              #ifdef __USE_STREAM__
    479480//              serr    | "*CFA runtime error* program cfa-cpp terminated with"
     
    492493// void sigHandler_abort( __CFA_SIGPARMS__ ) {
    493494//      // skip first 6 stack frames
    494 //      __cfaabi_dbg_debug_do( __kernel_backtrace( 6 ); )
     495//      LIB_DEBUG_DO( __kernel_backtrace( 6 ); )
    495496
    496497//      // reset default signal handler
  • src/libcfa/concurrency/thread.c

    rc13e8dc8 r9c35431  
    1717
    1818#include "kernel_private.h"
     19#include "libhdr.h"
    1920
    2021#define __CFA_INVOKE_PRIVATE__
     
    7172        thrd_c->last = this_coroutine;
    7273
    73         // __cfaabi_dbg_print_safe("Thread start : %p (t %p, c %p)\n", this, thrd_c, thrd_h);
     74        // LIB_DEBUG_PRINT_SAFE("Thread start : %p (t %p, c %p)\n", this, thrd_c, thrd_h);
    7475
    7576        disable_interrupts();
     
    8182
    8283        ScheduleThread(thrd_h);
    83         enable_interrupts( __cfaabi_dbg_ctx );
     84        enable_interrupts( DEBUG_CTX );
    8485}
    8586
  • src/libcfa/exception.c

    rc13e8dc8 r9c35431  
    2323#include <stdio.h>
    2424#include <unwind.h>
    25 #include <bits/debug.h>
     25#include <libhdr/libdebug.h>
    2626
    2727// FIX ME: temporary hack to keep ARM build working
     
    3737
    3838// Base exception vtable is abstract, you should not have base exceptions.
    39 struct __cfaabi_ehm__base_exception_t_vtable
    40                 ___cfaabi_ehm__base_exception_t_vtable_instance = {
     39struct __cfaehm__base_exception_t_vtable
     40                ___cfaehm__base_exception_t_vtable_instance = {
    4141        .parent = NULL,
    4242        .size = 0,
     
    4949// Temperary global exception context. Does not work with concurency.
    5050struct exception_context_t {
    51     struct __cfaabi_ehm__try_resume_node * top_resume;
    52     struct __cfaabi_ehm__try_resume_node * current_resume;
     51    struct __cfaehm__try_resume_node * top_resume;
     52    struct __cfaehm__try_resume_node * current_resume;
    5353
    5454    exception * current_exception;
     
    7878// RESUMPTION ================================================================
    7979
    80 void __cfaabi_ehm__throw_resume(exception * except) {
    81 
    82         __cfaabi_dbg_print_safe("Throwing resumption exception\n");
    83 
    84         struct __cfaabi_ehm__try_resume_node * original_head = shared_stack.current_resume;
    85         struct __cfaabi_ehm__try_resume_node * current =
     80void __cfaehm__throw_resume(exception * except) {
     81
     82        LIB_DEBUG_PRINT_SAFE("Throwing resumption exception\n");
     83
     84        struct __cfaehm__try_resume_node * original_head = shared_stack.current_resume;
     85        struct __cfaehm__try_resume_node * current =
    8686                (original_head) ? original_head->next : shared_stack.top_resume;
    8787
     
    9494        }
    9595
    96         __cfaabi_dbg_print_safe("Unhandled exception\n");
     96        LIB_DEBUG_PRINT_SAFE("Unhandled exception\n");
    9797        shared_stack.current_resume = original_head;
    9898
    9999        // Fall back to termination:
    100         __cfaabi_ehm__throw_terminate(except);
     100        __cfaehm__throw_terminate(except);
    101101        // TODO: Default handler for resumption.
    102102}
     
    105105// hook has to be added after the node is built but before it is made the top node.
    106106
    107 void __cfaabi_ehm__try_resume_setup(struct __cfaabi_ehm__try_resume_node * node,
     107void __cfaehm__try_resume_setup(struct __cfaehm__try_resume_node * node,
    108108                        _Bool (*handler)(exception * except)) {
    109109        node->next = shared_stack.top_resume;
     
    112112}
    113113
    114 void __cfaabi_ehm__try_resume_cleanup(struct __cfaabi_ehm__try_resume_node * node) {
     114void __cfaehm__try_resume_cleanup(struct __cfaehm__try_resume_node * node) {
    115115        shared_stack.top_resume = node->next;
    116116}
     
    122122// May have to move to cfa for constructors and destructors (references).
    123123
    124 struct __cfaabi_ehm__node {
    125         struct __cfaabi_ehm__node * next;
     124struct __cfaehm__node {
     125        struct __cfaehm__node * next;
    126126};
    127127
    128128#define NODE_TO_EXCEPT(node) ((exception *)(1 + (node)))
    129 #define EXCEPT_TO_NODE(except) ((struct __cfaabi_ehm__node *)(except) - 1)
     129#define EXCEPT_TO_NODE(except) ((struct __cfaehm__node *)(except) - 1)
    130130
    131131// Creates a copy of the indicated exception and sets current_exception to it.
    132 static void __cfaabi_ehm__allocate_exception( exception * except ) {
     132static void __cfaehm__allocate_exception( exception * except ) {
    133133        struct exception_context_t * context = this_exception_context();
    134134
    135135        // Allocate memory for the exception.
    136         struct __cfaabi_ehm__node * store = malloc(
    137                 sizeof( struct __cfaabi_ehm__node ) + except->virtual_table->size );
     136        struct __cfaehm__node * store = malloc(
     137                sizeof( struct __cfaehm__node ) + except->virtual_table->size );
    138138
    139139        if ( ! store ) {
     
    151151
    152152// Delete the provided exception, unsetting current_exception if relivant.
    153 static void __cfaabi_ehm__delete_exception( exception * except ) {
     153static void __cfaehm__delete_exception( exception * except ) {
    154154        struct exception_context_t * context = this_exception_context();
    155155
    156         __cfaabi_dbg_print_safe("Deleting Exception\n");
     156        LIB_DEBUG_PRINT_SAFE("Deleting Exception\n");
    157157
    158158        // Remove the exception from the list.
    159         struct __cfaabi_ehm__node * to_free = EXCEPT_TO_NODE(except);
    160         struct __cfaabi_ehm__node * node;
     159        struct __cfaehm__node * to_free = EXCEPT_TO_NODE(except);
     160        struct __cfaehm__node * node;
    161161
    162162        if ( context->current_exception == except ) {
     
    178178
    179179// If this isn't a rethrow (*except==0), delete the provided exception.
    180 void __cfaabi_ehm__cleanup_terminate( void * except ) {
    181         if ( *(void**)except ) __cfaabi_ehm__delete_exception( *(exception**)except );
     180void __cfaehm__cleanup_terminate( void * except ) {
     181        if ( *(void**)except ) __cfaehm__delete_exception( *(exception**)except );
    182182}
    183183
     
    202202
    203203// The exception that is being thrown must already be stored.
    204 __attribute__((noreturn)) void __cfaabi_ehm__begin_unwind(void) {
     204__attribute__((noreturn)) void __cfaehm__begin_unwind(void) {
    205205        if ( ! this_exception_context()->current_exception ) {
    206206                printf("UNWIND ERROR missing exception in begin unwind\n");
     
    233233}
    234234
    235 void __cfaabi_ehm__throw_terminate( exception * val ) {
    236         __cfaabi_dbg_print_safe("Throwing termination exception\n");
    237 
    238         __cfaabi_ehm__allocate_exception( val );
    239         __cfaabi_ehm__begin_unwind();
    240 }
    241 
    242 void __cfaabi_ehm__rethrow_terminate(void) {
    243         __cfaabi_dbg_print_safe("Rethrowing termination exception\n");
    244 
    245         __cfaabi_ehm__begin_unwind();
     235void __cfaehm__throw_terminate( exception * val ) {
     236        LIB_DEBUG_PRINT_SAFE("Throwing termination exception\n");
     237
     238        __cfaehm__allocate_exception( val );
     239        __cfaehm__begin_unwind();
     240}
     241
     242void __cfaehm__rethrow_terminate(void) {
     243        LIB_DEBUG_PRINT_SAFE("Rethrowing termination exception\n");
     244
     245        __cfaehm__begin_unwind();
    246246}
    247247
     
    254254{
    255255
    256         //__cfaabi_dbg_print_safe("CFA: 0x%lx\n", _Unwind_GetCFA(context));
    257         __cfaabi_dbg_print_safe("Personality function (%d, %x, %llu, %p, %p):", version, actions, exceptionClass, unwind_exception, context);
     256        //LIB_DEBUG_PRINT_SAFE("CFA: 0x%lx\n", _Unwind_GetCFA(context));
     257        LIB_DEBUG_PRINT_SAFE("Personality function (%d, %x, %llu, %p, %p):", version, actions, exceptionClass, unwind_exception, context);
    258258
    259259        // If we've reached the end of the stack then there is nothing much we can do...
     
    261261
    262262        if (actions & _UA_SEARCH_PHASE) {
    263                 __cfaabi_dbg_print_safe(" lookup phase");
     263                LIB_DEBUG_PRINT_SAFE(" lookup phase");
    264264        }
    265265        else if (actions & _UA_CLEANUP_PHASE) {
    266                 __cfaabi_dbg_print_safe(" cleanup phase");
     266                LIB_DEBUG_PRINT_SAFE(" cleanup phase");
    267267        }
    268268        // Just in case, probably can't actually happen
     
    307307                        void * ep = (void*)lsd_info.Start + callsite_start + callsite_len;
    308308                        void * ip = (void*)instruction_ptr;
    309                         __cfaabi_dbg_print_safe("\nfound %p - %p (%p, %p, %p), looking for %p\n", bp, ep, ls, cs, cl, ip);
     309                        LIB_DEBUG_PRINT_SAFE("\nfound %p - %p (%p, %p, %p), looking for %p\n", bp, ep, ls, cs, cl, ip);
    310310#endif // __CFA_DEBUG_PRINT__
    311311                        continue;
     
    346346
    347347                                        // Get a function pointer from the relative offset and call it
    348                                         // _Unwind_Reason_Code (*matcher)() = (_Unwind_Reason_Code (*)())lsd_info.LPStart + imatcher;
     348                                        // _Unwind_Reason_Code (*matcher)() = (_Unwind_Reason_Code (*)())lsd_info.LPStart + imatcher;                                   
    349349
    350350                                        _Unwind_Reason_Code (*matcher)(exception *) =
     
    357357                                        // Based on the return value, check if we matched the exception
    358358                                        if( ret == _URC_HANDLER_FOUND) {
    359                                                 __cfaabi_dbg_print_safe(" handler found\n");
     359                                                LIB_DEBUG_PRINT_SAFE(" handler found\n");
    360360                                        } else {
    361                                                 __cfaabi_dbg_print_safe(" no handler\n");
     361                                                LIB_DEBUG_PRINT_SAFE(" no handler\n");
    362362                                        }
    363363                                        return ret;
     
    365365
    366366                                // This is only a cleanup handler, ignore it
    367                                 __cfaabi_dbg_print_safe(" no action");
     367                                LIB_DEBUG_PRINT_SAFE(" no action");
    368368                        }
    369369                        else if (actions & _UA_CLEANUP_PHASE) {
     
    385385                                _Unwind_SetIP( context, ((lsd_info.LPStart) + (callsite_landing_pad)) );
    386386
    387                                 __cfaabi_dbg_print_safe(" action\n");
     387                                LIB_DEBUG_PRINT_SAFE(" action\n");
    388388
    389389                                // Return have some action to run
     
    393393
    394394                // Nothing to do, move along
    395                 __cfaabi_dbg_print_safe(" no landing pad");
     395                LIB_DEBUG_PRINT_SAFE(" no landing pad");
    396396        }
    397397        // No handling found
    398         __cfaabi_dbg_print_safe(" table end reached\n");
     398        LIB_DEBUG_PRINT_SAFE(" table end reached\n");
    399399
    400400        UNWIND:
    401         __cfaabi_dbg_print_safe(" unwind\n");
     401        LIB_DEBUG_PRINT_SAFE(" unwind\n");
    402402
    403403        // Keep unwinding the stack
     
    408408// libcfa but there is one problem left, see the exception table for details
    409409__attribute__((noinline))
    410 void __cfaabi_ehm__try_terminate(void (*try_block)(),
     410void __cfaehm__try_terminate(void (*try_block)(),
    411411                void (*catch_block)(int index, exception * except),
    412412                __attribute__((unused)) int (*match_block)(exception * except)) {
     
    466466        // Body uses language specific data and therefore could be modified arbitrarily
    467467        ".LLSDACSBCFA2:\n"                                              // BODY start
    468         "       .uleb128 .TRYSTART-__cfaabi_ehm__try_terminate\n"               // Handled area start  (relative to start of function)
     468        "       .uleb128 .TRYSTART-__cfaehm__try_terminate\n"           // Handled area start  (relative to start of function)
    469469        "       .uleb128 .TRYEND-.TRYSTART\n"                           // Handled area length
    470         "       .uleb128 .CATCH-__cfaabi_ehm__try_terminate\n"                          // Hanlder landing pad adress  (relative to start of function)
     470        "       .uleb128 .CATCH-__cfaehm__try_terminate\n"                              // Hanlder landing pad adress  (relative to start of function)
    471471        "       .uleb128 1\n"                                           // Action code, gcc seems to use always 0
    472472        ".LLSDACSECFA2:\n"                                              // BODY end
    473473        "       .text\n"                                                        // TABLE footer
    474         "       .size   __cfaabi_ehm__try_terminate, .-__cfaabi_ehm__try_terminate\n"
     474        "       .size   __cfaehm__try_terminate, .-__cfaehm__try_terminate\n"
    475475        "       .ident  \"GCC: (Ubuntu 6.2.0-3ubuntu11~16.04) 6.2.0 20160901\"\n"
    476476//      "       .section        .note.GNU-stack,\"x\",@progbits\n"
  • src/libcfa/exception.h

    rc13e8dc8 r9c35431  
    2121#endif
    2222
    23 struct __cfaabi_ehm__base_exception_t;
    24 typedef struct __cfaabi_ehm__base_exception_t exception;
    25 struct __cfaabi_ehm__base_exception_t_vtable {
    26         const struct __cfaabi_ehm__base_exception_t_vtable * parent;
     23struct __cfaehm__base_exception_t;
     24typedef struct __cfaehm__base_exception_t exception;
     25struct __cfaehm__base_exception_t_vtable {
     26        const struct __cfaehm__base_exception_t_vtable * parent;
    2727        size_t size;
    28         void (*copy)(struct __cfaabi_ehm__base_exception_t *this,
    29                      struct __cfaabi_ehm__base_exception_t * other);
    30         void (*free)(struct __cfaabi_ehm__base_exception_t *this);
    31         const char * (*msg)(struct __cfaabi_ehm__base_exception_t *this);
     28        void (*copy)(struct __cfaehm__base_exception_t *this,
     29                     struct __cfaehm__base_exception_t * other);
     30        void (*free)(struct __cfaehm__base_exception_t *this);
     31        const char * (*msg)(struct __cfaehm__base_exception_t *this);
    3232};
    33 struct __cfaabi_ehm__base_exception_t {
    34         struct __cfaabi_ehm__base_exception_t_vtable const * virtual_table;
     33struct __cfaehm__base_exception_t {
     34        struct __cfaehm__base_exception_t_vtable const * virtual_table;
    3535};
    36 extern struct __cfaabi_ehm__base_exception_t_vtable
    37         ___cfaabi_ehm__base_exception_t_vtable_instance;
     36extern struct __cfaehm__base_exception_t_vtable
     37        ___cfaehm__base_exception_t_vtable_instance;
    3838
    3939
    4040// Used in throw statement translation.
    41 void __cfaabi_ehm__throw_terminate(exception * except) __attribute__((noreturn));
    42 void __cfaabi_ehm__rethrow_terminate() __attribute__((noreturn));
    43 void __cfaabi_ehm__throw_resume(exception * except);
     41void __cfaehm__throw_terminate(exception * except) __attribute__((noreturn));
     42void __cfaehm__rethrow_terminate() __attribute__((noreturn));
     43void __cfaehm__throw_resume(exception * except);
    4444
    4545// Function catches termination exceptions.
    46 void __cfaabi_ehm__try_terminate(
     46void __cfaehm__try_terminate(
    4747    void (*try_block)(),
    4848    void (*catch_block)(int index, exception * except),
     
    5050
    5151// Clean-up the exception in catch blocks.
    52 void __cfaabi_ehm__cleanup_terminate(void * except);
     52void __cfaehm__cleanup_terminate(void * except);
    5353
    5454// Data structure creates a list of resume handlers.
    55 struct __cfaabi_ehm__try_resume_node {
    56     struct __cfaabi_ehm__try_resume_node * next;
     55struct __cfaehm__try_resume_node {
     56    struct __cfaehm__try_resume_node * next;
    5757    _Bool (*handler)(exception * except);
    5858};
    5959
    6060// These act as constructor and destructor for the resume node.
    61 void __cfaabi_ehm__try_resume_setup(
    62     struct __cfaabi_ehm__try_resume_node * node,
     61void __cfaehm__try_resume_setup(
     62    struct __cfaehm__try_resume_node * node,
    6363    _Bool (*handler)(exception * except));
    64 void __cfaabi_ehm__try_resume_cleanup(
    65     struct __cfaabi_ehm__try_resume_node * node);
     64void __cfaehm__try_resume_cleanup(
     65    struct __cfaehm__try_resume_node * node);
    6666
    6767// Check for a standard way to call fake deconstructors.
    68 struct __cfaabi_ehm__cleanup_hook {};
     68struct __cfaehm__cleanup_hook {};
    6969
    7070#ifdef __cforall
  • src/libcfa/interpose.c

    rc13e8dc8 r9c35431  
    2424}
    2525
    26 #include "bits/debug.h"
    27 #include "bits/defs.h"
     26#include "libhdr/libdebug.h"
     27#include "libhdr/libtools.h"
    2828#include "startup.h"
    2929
     
    6969__typeof__( exit ) libc_exit __attribute__(( noreturn ));
    7070__typeof__( abort ) libc_abort __attribute__(( noreturn ));
     71
     72// #define INIT_REALRTN( x, ver ) libc_##x = (__typeof__(libc_##x))interpose_symbol( #x, ver )
    7173
    7274forall(dtype T)
     
    125127                        va_end( args );
    126128
    127                         __cfaabi_dbg_bits_write( abort_text, len );
    128                         __cfaabi_dbg_bits_write( "\n", 1 );
     129                        __lib_debug_write( abort_text, len );
     130                        __lib_debug_write( "\n", 1 );
    129131                }
    130132
    131133                len = snprintf( abort_text, abort_text_size, "Cforall Runtime error (UNIX pid:%ld)\n", (long int)getpid() ); // use UNIX pid (versus getPid)
    132                 __cfaabi_dbg_bits_write( abort_text, len );
     134                __lib_debug_write( abort_text, len );
    133135
    134136
  • src/libcfa/stdhdr/assert.h

    rc13e8dc8 r9c35431  
    3030#endif
    3131
    32 #if !defined(NDEBUG) && (defined(__CFA_DEBUG__) || defined(__CFA_VERIFY__))
    33         #define verify(x) assert(x)
    34         #define verifyf(x, ...) assertf(x, __VA_ARGS__)
    35 #else
    36         #define verify(x)
    37         #define verifyf(x, ...)
    38 #endif
    39 
    4032#ifdef __cforall
    4133} // extern "C"
  • src/prelude/builtins.c

    rc13e8dc8 r9c35431  
    1616// exception implementation
    1717
    18 typedef unsigned long long __cfaabi_abi_exception_type_t;
     18typedef unsigned long long __cfaabi_exception_type_t;
    1919
    2020#include "../libcfa/virtual.h"
     
    8080} // ?\?
    8181
    82 // FIXME (x \ (unsigned long int)y) relies on X ?\?(T, unsigned long) a function that is neither
    83 // defined, nor passed as an assertion parameter. Without user-defined conversions, cannot specify
    84 // X as a type that casts to double, yet it doesn't make sense to write functions with that type
     82// FIXME (x \ (unsigned long int)y) relies on X ?\?(T, unsigned long) a function that is neither 
     83// defined, nor passed as an assertion parameter. Without user-defined conversions, cannot specify 
     84// X as a type that casts to double, yet it doesn't make sense to write functions with that type 
    8585// signature where X is double.
    8686
  • src/tests/.expect/declarationErrors.txt

    rc13e8dc8 r9c35431  
    77declarationErrors.c:19:1 error: duplicate static in declaration of x4: static const volatile instance of const volatile struct __anonymous0
    88  with members
    9     i: int
    109   with body
    1110
     
    1312declarationErrors.c:20:1 error: duplicate const, duplicate static, duplicate volatile in declaration of x5: static const volatile instance of const volatile struct __anonymous1
    1413  with members
    15     i: int
    1614   with body
    1715
  • src/tests/Makefile.am

    rc13e8dc8 r9c35431  
    2020
    2121if BUILD_CONCURRENCY
    22 concurrent=
     22concurrent = yes
     23quick_test += coroutine thread monitor
     24concurrent_test =               \
     25        coroutine               \
     26        fmtLines                \
     27        pingpong                \
     28        prodcons                \
     29        thread                  \
     30        matrixSum               \
     31        monitor                 \
     32        multi-monitor           \
     33        boundedBuffer           \
     34        preempt                 \
     35        sched-int-block         \
     36        sched-int-disjoint      \
     37        sched-int-wait          \
     38        sched-ext-barge         \
     39        sched-ext-dtor          \
     40        sched-ext-else          \
     41        sched-ext-parse         \
     42        sched-ext-recurse       \
     43        sched-ext-statment      \
     44        sched-ext-when
    2345else
    24 concurrent='-Econcurrent'
     46concurrent=no
     47concurrent_test=
    2548endif
    2649
     
    3962endif
    4063
    41 TEST_FLAGS = $(if $(test), 2> $(test), )
     64TEST_FLAGS = $(if $(test), 2> .err/${@}.log, )
    4265AM_CFLAGS = ${TEST_FLAGS} ${BUILD_FLAGS}
    4366CC = @CFA_BINDIR@/@CFA_NAME@
     
    4770
    4871fstream_test_SOURCES = fstream_test.c
    49 fstream_test_CFLAGS = $(if $(test), 2>> $(test), ) ${BUILD_FLAGS}
     72fstream_test_CFLAGS = $(if $(test), 2>> .err/fstream_test.log, ) ${BUILD_FLAGS}
    5073
    5174vector_test_SOURCES = vector/vector_int.c vector/array.c vector/vector_test.c
    52 vector_test_CFLAGS = $(if $(test), 2>> $(test), ) ${BUILD_FLAGS}
     75vector_test_CFLAGS = $(if $(test), 2>> .err/vector_test.log, ) ${BUILD_FLAGS}
    5376
    5477avl_test_SOURCES = avltree/avl_test.c avltree/avl0.c avltree/avl1.c avltree/avl2.c avltree/avl3.c avltree/avl4.c avltree/avl-private.c
    55 avl_test_CFLAGS = $(if $(test), 2>> $(test), ) ${BUILD_FLAGS}
     78avl_test_CFLAGS = $(if $(test), 2>> .err/avl_test.log, ) ${BUILD_FLAGS}
    5679
    5780all-local :
    58         @+python test.py --debug=${debug} ${concurrent} ${quick_test}
     81        @+python test.py --debug=${debug} --concurrent=${concurrent} ${quick_test}
    5982
    6083all-tests :
    61         @+python test.py --all --debug=${debug} ${concurrent}           # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
     84        @+python test.py --all --debug=${debug} --concurrent=${concurrent}              # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
    6285
    6386clean-local :
     
    6588
    6689list :
    67         @+python test.py --list ${concurrent}
     90        @+python test.py --list --concurrent=${concurrent}
    6891
    6992concurrency :
    70         @+python test.py --debug=${debug} ${concurrent} ${concurrent_test}
     93        @+python test.py --debug=${debug} --concurrent=${concurrent} ${concurrent_test}
    7194
    7295.dummy : .dummy.c @CFA_BINDIR@/@CFA_NAME@
  • src/tests/Makefile.in

    rc13e8dc8 r9c35431  
    9191build_triplet = @build@
    9292host_triplet = @host@
    93 @BUILD_DEBUG_FALSE@am__append_1 = -nodebug
    94 @BUILD_DEBUG_TRUE@@BUILD_RELEASE_FALSE@am__append_2 = -debug
    95 @BUILD_DEBUG_TRUE@@BUILD_RELEASE_TRUE@am__append_3 = ${DEBUG_FLAGS}
     93@BUILD_CONCURRENCY_TRUE@am__append_1 = coroutine thread monitor
     94@BUILD_DEBUG_FALSE@am__append_2 = -nodebug
     95@BUILD_DEBUG_TRUE@@BUILD_RELEASE_FALSE@am__append_3 = -debug
     96@BUILD_DEBUG_TRUE@@BUILD_RELEASE_TRUE@am__append_4 = ${DEBUG_FLAGS}
    9697EXTRA_PROGRAMS = fstream_test$(EXEEXT) vector_test$(EXEEXT) \
    9798        avl_test$(EXEEXT)
     
    313314top_srcdir = @top_srcdir@
    314315debug = yes
    315 quick_test = vector_test avl_test operators numericConstants expression enum array typeof cast dtor-early-exit init_once attributes
    316 @BUILD_CONCURRENCY_FALSE@concurrent = '-Econcurrent'
    317 @BUILD_CONCURRENCY_TRUE@concurrent =
     316quick_test = vector_test avl_test operators numericConstants \
     317        expression enum array typeof cast dtor-early-exit init_once \
     318        attributes $(am__append_1)
     319@BUILD_CONCURRENCY_FALSE@concurrent = no
     320@BUILD_CONCURRENCY_TRUE@concurrent = yes
     321@BUILD_CONCURRENCY_FALSE@concurrent_test =
     322@BUILD_CONCURRENCY_TRUE@concurrent_test = \
     323@BUILD_CONCURRENCY_TRUE@        coroutine               \
     324@BUILD_CONCURRENCY_TRUE@        fmtLines                \
     325@BUILD_CONCURRENCY_TRUE@        pingpong                \
     326@BUILD_CONCURRENCY_TRUE@        prodcons                \
     327@BUILD_CONCURRENCY_TRUE@        thread                  \
     328@BUILD_CONCURRENCY_TRUE@        matrixSum               \
     329@BUILD_CONCURRENCY_TRUE@        monitor                 \
     330@BUILD_CONCURRENCY_TRUE@        multi-monitor           \
     331@BUILD_CONCURRENCY_TRUE@        boundedBuffer           \
     332@BUILD_CONCURRENCY_TRUE@        preempt                 \
     333@BUILD_CONCURRENCY_TRUE@        sched-int-block         \
     334@BUILD_CONCURRENCY_TRUE@        sched-int-disjoint      \
     335@BUILD_CONCURRENCY_TRUE@        sched-int-wait          \
     336@BUILD_CONCURRENCY_TRUE@        sched-ext-barge         \
     337@BUILD_CONCURRENCY_TRUE@        sched-ext-dtor          \
     338@BUILD_CONCURRENCY_TRUE@        sched-ext-else          \
     339@BUILD_CONCURRENCY_TRUE@        sched-ext-parse         \
     340@BUILD_CONCURRENCY_TRUE@        sched-ext-recurse       \
     341@BUILD_CONCURRENCY_TRUE@        sched-ext-statment      \
     342@BUILD_CONCURRENCY_TRUE@        sched-ext-when
     343
    318344
    319345# applies to both programs
    320346DEBUG_FLAGS =
    321347BUILD_FLAGS = -g -Wall -Wno-unused-function -quiet @CFA_FLAGS@ \
    322         $(am__append_1) $(am__append_2) $(am__append_3)
    323 TEST_FLAGS = $(if $(test), 2> $(test), )
     348        $(am__append_2) $(am__append_3) $(am__append_4)
     349TEST_FLAGS = $(if $(test), 2> .err/${@}.log, )
    324350AM_CFLAGS = ${TEST_FLAGS} ${BUILD_FLAGS}
    325351fstream_test_SOURCES = fstream_test.c
    326 fstream_test_CFLAGS = $(if $(test), 2>> $(test), ) ${BUILD_FLAGS}
     352fstream_test_CFLAGS = $(if $(test), 2>> .err/fstream_test.log, ) ${BUILD_FLAGS}
    327353vector_test_SOURCES = vector/vector_int.c vector/array.c vector/vector_test.c
    328 vector_test_CFLAGS = $(if $(test), 2>> $(test), ) ${BUILD_FLAGS}
     354vector_test_CFLAGS = $(if $(test), 2>> .err/vector_test.log, ) ${BUILD_FLAGS}
    329355avl_test_SOURCES = avltree/avl_test.c avltree/avl0.c avltree/avl1.c avltree/avl2.c avltree/avl3.c avltree/avl4.c avltree/avl-private.c
    330 avl_test_CFLAGS = $(if $(test), 2>> $(test), ) ${BUILD_FLAGS}
     356avl_test_CFLAGS = $(if $(test), 2>> .err/avl_test.log, ) ${BUILD_FLAGS}
    331357all: all-am
    332358
     
    807833
    808834all-local :
    809         @+python test.py --debug=${debug} ${concurrent} ${quick_test}
     835        @+python test.py --debug=${debug} --concurrent=${concurrent} ${quick_test}
    810836
    811837all-tests :
    812         @+python test.py --all --debug=${debug} ${concurrent}           # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
     838        @+python test.py --all --debug=${debug} --concurrent=${concurrent}              # '@' => do not echo command (SILENT), '+' => allows recursive make from within python program
    813839
    814840clean-local :
     
    816842
    817843list :
    818         @+python test.py --list ${concurrent}
     844        @+python test.py --list --concurrent=${concurrent}
    819845
    820846concurrency :
    821         @+python test.py --debug=${debug} ${concurrent} ${concurrent_test}
     847        @+python test.py --debug=${debug} --concurrent=${concurrent} ${concurrent_test}
    822848
    823849.dummy : .dummy.c @CFA_BINDIR@/@CFA_NAME@
  • src/tests/except-mac.h

    rc13e8dc8 r9c35431  
    77
    88// The fully (perhaps overly) qualified name of the base exception type:
    9 #define BASE_EXCEPT __cfaabi_ehm__base_exception_t
     9#define BASE_EXCEPT __cfaehm__base_exception_t
    1010
    1111// Get the name of the vtable type and vtable instance for an exception type:
     
    3030        size_t size; \
    3131        void (*copy)(except_name *this, except_name * other); \
    32         void (*free)(except_name &this); \
     32        void (*free)(except_name *this); \
    3333        const char * (*msg)(except_name *this); \
    3434        __VA_ARGS__ \
     
    4242// In each constructor the vtable must be initialized.
    4343#define VTABLE_INIT(this_name,except_name) \
    44 this_name.virtual_table = &INSTANCE(except_name)
     44this_name->virtual_table = &INSTANCE(except_name)
    4545
    4646// Declare the vtable instance. This should end an exception declaration.
     
    7373    this->virtual_table = other->virtual_table; \
    7474} \
    75 void ?{}(name & this) { \
     75void ?{}(name * this) { \
    7676        VTABLE_INIT(this,name); \
    7777} \
  • src/tests/pybin/tools.py

    rc13e8dc8 r9c35431  
    1 from __future__ import print_function
    2 
    31import __main__
    42import argparse
    5 import multiprocessing
    63import os
    74import re
    8 import signal
    95import stat
    10 import sys
    116
    12 from pybin import settings
    137from subprocess import Popen, PIPE, STDOUT
    148
    15 ################################################################################
    16 #               shell helpers
    17 ################################################################################
    18 
    199# helper functions to run terminal commands
    20 def sh(cmd, print2stdout = True, input = None):
    21         # add input redirection if needed
    22         if input and os.path.isfile(input):
    23                 cmd += " < %s" % input
    24 
    25         # if this is a dry_run, only print the commands that would be ran
    26         if settings.dry_run :
     10def sh(cmd, dry_run = False, print2stdout = True):
     11        if dry_run :    # if this is a dry_run, only print the commands that would be ran
    2712                print("cmd: %s" % cmd)
    2813                return 0, None
    29 
    30         # otherwise create a pipe and run the desired command
    31         else :
     14        else :                  # otherwise create a pipe and run the desired command
    3215                proc = Popen(cmd, stdout=None if print2stdout else PIPE, stderr=STDOUT, shell=True)
    3316                out, err = proc.communicate()
     
    3518
    3619# Remove 1 or more files silently
    37 def rm( files ):
     20def rm( files, dry_run = False ):
    3821        try:
    3922                for file in files:
    40                         sh("rm -f %s > /dev/null 2>&1" % file )
     23                        sh("rm -f %s > /dev/null 2>&1" % file, dry_run)
    4124        except TypeError:
    42                 sh("rm -f %s > /dev/null 2>&1" % files )
     25                sh("rm -f %s > /dev/null 2>&1" % files, dry_run)
    4326
    4427def chdir( dest = __main__.__file__ ):
     
    4730        os.chdir(dname)
    4831
     32# helper function to replace patterns in a file
     33def file_replace(fname, pat, s_after):
     34    # first, see if the pattern is even in the file.
     35    with open(fname) as f:
     36        if not any(re.search(pat, line) for line in f):
     37            return # pattern does not occur in file so we are done.
     38
     39    # pattern is in the file, so perform replace operation.
     40    with open(fname) as f:
     41        out_fname = fname + ".tmp"
     42        out = open(out_fname, "w")
     43        for line in f:
     44            out.write(re.sub(pat, s_after, line))
     45        out.close()
     46        os.rename(out_fname, fname)
     47
     48# helper function to check if a files contains only a spacific string
     49def fileContainsOnly(file, text) :
     50        with open(file) as f:
     51                ff = f.read().strip()
     52                result = ff == text.strip()
     53
     54                return result;
     55
     56# check whether or not a file is executable
     57def fileIsExecutable(file) :
     58        try :
     59                fileinfo = os.stat(file)
     60                return bool(fileinfo.st_mode & stat.S_IXUSR)
     61        except Exception as inst:
     62                print(type(inst))    # the exception instance
     63                print(inst.args)     # arguments stored in .args
     64                print(inst)
     65                return False
     66
     67# check if arguments is yes or no
     68def yes_no(string):
     69        if string == "yes" :
     70                return True
     71        if string == "no" :
     72                return False
     73        raise argparse.ArgumentTypeError(msg)
     74        return False
     75
    4976# diff two files
    50 def diff( lhs, rhs ):
     77def diff( lhs, rhs, dry_run ):
    5178        # diff the output of the files
    5279        diff_cmd = ("diff --ignore-all-space "
     
    6794
    6895        # fetch return code and error from the diff command
    69         return sh(diff_cmd % (lhs, rhs), False)
    70 
    71 # call make
    72 def make(target, flags = '', redirects = '', error_file = None, silent = False):
    73         test_param = """test="%s" """ % (error_file) if error_file else ''
    74         cmd = ' '.join([
    75                 settings.make,
    76                 '-s' if silent else '',
    77                 test_param,
    78                 settings.debug.flags,
    79                 flags,
    80                 target,
    81                 redirects
    82         ])
    83         return sh(cmd)
    84 
    85 ################################################################################
    86 #               file handling
    87 ################################################################################
    88 
    89 # helper function to replace patterns in a file
    90 def file_replace(fname, pat, s_after):
    91     # first, see if the pattern is even in the file.
    92     with open(fname) as f:
    93         if not any(re.search(pat, line) for line in f):
    94             return # pattern does not occur in file so we are done.
    95 
    96     # pattern is in the file, so perform replace operation.
    97     with open(fname) as f:
    98         out_fname = fname + ".tmp"
    99         out = open(out_fname, "w")
    100         for line in f:
    101             out.write(re.sub(pat, s_after, line))
    102         out.close()
    103         os.rename(out_fname, fname)
    104 
    105 # helper function to check if a files contains only a specific string
    106 def fileContainsOnly(file, text) :
    107         with open(file) as f:
    108                 ff = f.read().strip()
    109                 result = ff == text.strip()
    110 
    111                 return result;
    112 
    113 # check whether or not a file is executable
    114 def fileIsExecutable(file) :
    115         try :
    116                 fileinfo = os.stat(file)
    117                 return bool(fileinfo.st_mode & stat.S_IXUSR)
    118         except Exception as inst:
    119                 print(type(inst))    # the exception instance
    120                 print(inst.args)     # arguments stored in .args
    121                 print(inst)
    122                 return False
    123 
    124 # transform path to canonical form
    125 def canonicalPath(path):
    126         return os.path.join('.', os.path.normpath(path) )
    127 
    128 # compare path even if form is different
    129 def pathCmp(lhs, rhs):
    130         return canonicalPath( lhs ) == canonicalPath( rhs )
    131 
    132 # walk all files in a path
    133 def pathWalk( op ):
    134         def step(_, dirname, names):
    135                 for name in names:
    136                         path = os.path.join(dirname, name)
    137 
    138                         op( path )
    139 
    140         # Start the walk
    141         os.path.walk('.', step, '')
    142 
    143 ################################################################################
    144 #               system
    145 ################################################################################
    146 
    147 # parses the Makefile to find the machine type (32-bit / 64-bit)
    148 def getMachineType():
    149         sh('echo "void ?{}(int&a,int b){}int main(){return 0;}" > .dummy.c')
    150         ret, out = make('.dummy', silent = True)
    151 
    152         if ret != 0:
    153                 print("Failed to identify architecture:")
    154                 print(out)
    155                 print("Stopping")
    156                 rm( (".dummy.c",".dummy") )
    157                 sys.exit(1)
    158 
    159         _, out = sh("file .dummy", print2stdout=False)
    160         rm( (".dummy.c",".dummy") )
    161 
    162         if settings.dry_run :
    163                 return 'x64'
    164 
    165         return re.search(r"[^,]+,([^,]+),", out).group(1).strip()
    166 
    167 # count number of jobs to create
    168 def jobCount( options, tests ):
    169         # check if the user already passed in a number of jobs for multi-threading
    170         make_flags = os.environ.get('MAKEFLAGS')
    171         make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
    172         if make_jobs_fds :
    173                 tokens = os.read(int(make_jobs_fds.group(2)), 1024)
    174                 options.jobs = len(tokens)
    175                 os.write(int(make_jobs_fds.group(3)), tokens)
    176         else :
    177                 options.jobs = multiprocessing.cpu_count()
    178 
    179         # make sure we have a valid number of jobs that corresponds to user input
    180         if options.jobs <= 0 :
    181                 print('ERROR: Invalid number of jobs', file=sys.stderr)
    182                 sys.exit(1)
    183 
    184         return min( options.jobs, len(tests) ), True if make_flags else False
    185 
    186 # setup a proper processor pool with correct signal handling
    187 def setupPool(jobs):
    188         original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
    189         pool = multiprocessing.Pool(jobs)
    190         signal.signal(signal.SIGINT, original_sigint_handler)
    191 
    192         return pool
    193 
    194 # handle signals in scope
    195 class SignalHandling():
    196         def __enter__(self):
    197                 # enable signal handling
    198                 signal.signal(signal.SIGINT, signal.SIG_DFL)
    199 
    200         def __exit__(self, type, value, traceback):
    201                 # disable signal handling
    202                 signal.signal(signal.SIGINT, signal.SIG_IGN)
    203 
    204 ################################################################################
    205 #               misc
    206 ################################################################################
    207 
    208 # check if arguments is yes or no
    209 def yes_no(string):
    210         if string == "yes" :
    211                 return True
    212         if string == "no" :
    213                 return False
    214         raise argparse.ArgumentTypeError(msg)
    215         return False
    216 
    217 
    218 settings.set_machine_default( getMachineType )
     96        return sh(diff_cmd % (lhs, rhs), dry_run, False)
  • src/tests/test.py

    rc13e8dc8 r9c35431  
    22from __future__ import print_function
    33
     4from functools import partial
     5from multiprocessing import Pool
     6from os import listdir, environ
     7from os.path import isfile, join, splitext
    48from pybin.tools import *
    5 from pybin.test_run import *
    6 from pybin import settings
    79
    810import argparse
     11import multiprocessing
     12import os
    913import re
     14import signal
    1015import sys
    1116
     
    1419################################################################################
    1520
    16 def findTests():
    17         expected = []
    18 
    19         def matchTest(path):
    20                 match = re.search("(\.[\w\/\-_]*)\/.expect\/([\w\-_]+)(\.[\w\-_]+)?\.txt", path)
    21                 if match :
    22                         test = Test()
    23                         test.name = match.group(2)
    24                         test.path = match.group(1)
    25                         test.arch = match.group(3)[1:] if match.group(3) else None
    26                         if settings.arch.match(test.arch):
    27                                 expected.append(test)
    28 
    29         pathWalk( matchTest )
    30 
    31         return expected
     21# Test class that defines what a test is
     22class Test:
     23    def __init__(self, name, path):
     24        self.name, self.path = name, path
     25
     26class TestResult:
     27        SUCCESS = 0
     28        FAILURE = 1
     29        TIMEOUT = 124
     30
     31# parses the Makefile to find the machine type (32-bit / 64-bit)
     32def getMachineType():
     33        sh('echo "void ?{}(int&a,int b){}int main(){return 0;}" > .dummy.c')
     34        ret, out = sh("make .dummy -s", print2stdout=True)
     35
     36        if ret != 0:
     37                print("Failed to identify architecture:")
     38                print(out)
     39                print("Stopping")
     40                rm( (".dummy.c",".dummy") )
     41                sys.exit(1)
     42
     43        _, out = sh("file .dummy", print2stdout=False)
     44        rm( (".dummy.c",".dummy") )
     45
     46        return re.search("ELF\s([0-9]+)-bit", out).group(1)
     47
     48def listTestsFolder(folder) :
     49        path = ('./.expect/%s/' % folder) if folder else './.expect/'
     50        subpath = "%s/" % folder if folder else ""
     51
     52        # tests directly in the .expect folder will always be processed
     53        return map(lambda fname: Test(fname, subpath + fname),
     54                [splitext(f)[0] for f in listdir( path )
     55                if not f.startswith('.') and f.endswith('.txt')
     56                ])
    3257
    3358# reads the directory ./.expect and indentifies the tests
    34 def listTests( includes, excludes ):
    35         includes = [canonicalPath( i ) for i in includes] if includes else None
    36         excludes = [canonicalPath( i ) for i in excludes] if excludes else None
     59def listTests( concurrent ):
     60        machineType = getMachineType()
    3761
    3862        # tests directly in the .expect folder will always be processed
    39         test_list = findTests()
    40 
    41         # if we have a limited number of includes, filter by them
    42         if includes:
    43                 test_list = [x for x in test_list if
    44                         x.path.startswith( tuple(includes) )
    45                 ]
    46 
    47         # # if we have a folders to excludes, filter by them
    48         if excludes:
    49                 test_list = [x for x in test_list if not
    50                         x.path.startswith( tuple(excludes) )
    51                 ]
    52 
    53         return test_list
     63        generic_list = listTestsFolder( "" )
     64
     65        # tests in the machineType folder will be ran only for the corresponding compiler
     66        typed_list = listTestsFolder( machineType )
     67
     68        # tests in the concurrent folder will be ran only if concurrency is enabled
     69        concurrent_list = listTestsFolder( "concurrent" ) if concurrent else []
     70
     71        # append both lists to get
     72        return generic_list + typed_list + concurrent_list;
    5473
    5574# from the found tests, filter all the valid tests/desired tests
     
    6180        if options.regenerate_expected :
    6281                for testname in options.tests :
    63                         if Test.valid_name(testname):
    64                                 found = [test for test in allTests if test.target() == testname]
    65                                 tests.append( found[0] if len(found) == 1 else Test.from_target(testname) )
     82                        if testname.endswith( (".c", ".cc", ".cpp") ):
     83                                print('ERROR: "%s", tests are not allowed to end with a C/C++/CFA extension, ignoring it' % testname, file=sys.stderr)
    6684                        else :
    67                                 print('ERROR: "%s", tests are not allowed to end with a C/C++/CFA extension, ignoring it' % testname, file=sys.stderr)
     85                                found = [test for test in allTests if test.name == testname]
     86                                tests.append( found[0] if len(found) == 1 else Test(testname, testname) )
    6887
    6988        else :
    7089                # otherwise we only need to validate that all tests are present in the complete list
    7190                for testname in options.tests:
    72                         test = [t for t in allTests if pathCmp( t.target(), testname )]
    73 
    74                         if test :
     91                        test = [t for t in allTests if t.name == testname]
     92
     93                        if len(test) != 0 :
    7594                                tests.append( test[0] )
    7695                        else :
     
    7897
    7998        # make sure we have at least some test to run
    80         if not tests :
     99        if len(tests) == 0 :
    81100                print('ERROR: No valid test to run', file=sys.stderr)
    82101                sys.exit(1)
     
    89108        parser = argparse.ArgumentParser(description='Script which runs cforall tests')
    90109        parser.add_argument('--debug', help='Run all tests in debug or release', type=yes_no, default='no')
    91         parser.add_argument('--arch', help='Test for specific architecture', type=str, default='')
     110        parser.add_argument('--concurrent', help='Run concurrent tests', type=yes_no, default='yes')
    92111        parser.add_argument('--dry-run', help='Don\'t run the tests, only output the commands', action='store_true')
    93112        parser.add_argument('--list', help='List all test available', action='store_true')
     
    96115        parser.add_argument('-j', '--jobs', help='Number of tests to run simultaneously', type=int, default='8')
    97116        parser.add_argument('--list-comp', help='List all valide arguments', action='store_true')
    98         parser.add_argument('-I','--include', help='Directory of test to include, can be used multiple time, All  if omitted', action='append')
    99         parser.add_argument('-E','--exclude', help='Directory of test to exclude, can be used multiple time, None if omitted', action='append')
    100117        parser.add_argument('tests', metavar='test', type=str, nargs='*', help='a list of tests to run')
    101118
     
    106123        all_tests  = options.all
    107124        some_tests = len(options.tests) > 0
    108         some_dirs  = len(options.include) > 0 if options.include else 0
    109125
    110126        # check that exactly one of the booleans is set to true
    111         if not sum( (listing, all_tests, some_tests, some_dirs) ) > 0 :
    112                 print('ERROR: must have option \'--all\', \'--list\', \'--include\', \'-I\' or non-empty test list', file=sys.stderr)
     127        if not sum( (listing, all_tests, some_tests) ) == 1 :
     128                print('ERROR: must have option \'--all\', \'--list\' or non-empty test list', file=sys.stderr)
    113129                parser.print_help()
    114130                sys.exit(1)
     
    116132        return options
    117133
     134def jobCount( options ):
     135        # check if the user already passed in a number of jobs for multi-threading
     136        make_flags = environ.get('MAKEFLAGS')
     137        make_jobs_fds = re.search("--jobserver-(auth|fds)=\s*([0-9]+),([0-9]+)", make_flags) if make_flags else None
     138        if make_jobs_fds :
     139                tokens = os.read(int(make_jobs_fds.group(2)), 1024)
     140                options.jobs = len(tokens)
     141                os.write(int(make_jobs_fds.group(3)), tokens)
     142        else :
     143                options.jobs = multiprocessing.cpu_count()
     144
     145        # make sure we have a valid number of jobs that corresponds to user input
     146        if options.jobs <= 0 :
     147                print('ERROR: Invalid number of jobs', file=sys.stderr)
     148                sys.exit(1)
     149
     150        return min( options.jobs, len(tests) ), True if make_flags else False
     151
    118152################################################################################
    119153#               running test functions
    120154################################################################################
    121155# logic to run a single test and return the result (No handling of printing or other test framework logic)
    122 def run_single_test(test):
     156def run_single_test(test, generate, dry_run, debug):
    123157
    124158        # find the output file based on the test name and options flag
    125         out_file = test.target_output()
    126         err_file = test.error_log()
    127         cmp_file = test.expect()
    128         in_file  = test.input()
    129 
    130         # prepare the proper directories
    131         test.prepare()
     159        out_file = (".out/%s.log" % test.name) if not generate else (".expect/%s.txt" % test.path)
     160        err_file = ".err/%s.log" % test.name
    132161
    133162        # remove any outputs from the previous tests to prevent side effects
    134         rm( (out_file, err_file, test.target()) )
     163        rm( (out_file, err_file, test.name), dry_run )
     164
     165        options = "-debug" if debug else "-nodebug"
    135166
    136167        # build, skipping to next test on error
    137         make_ret, _ = make( test.target(),
    138                 redirects  = "2> %s 1> /dev/null" % out_file,
    139                 error_file = err_file
    140         )
     168        make_ret, _ = sh("""%s test=yes DEBUG_FLAGS="%s" %s 2> %s 1> /dev/null""" % (make_cmd, options, test.name, out_file), dry_run)
     169
     170        retcode = 0
     171        error = None
    141172
    142173        # if the make command succeds continue otherwise skip to diff
    143         if make_ret == 0 or settings.dry_run:
    144                 if settings.dry_run or fileIsExecutable(test.target()) :
     174        if make_ret == 0 :
     175                # fetch optional input
     176                stdinput = "< .in/%s.txt" % test.name if isfile(".in/%s.txt" % test.name) else ""
     177
     178                if fileIsExecutable(test.name) :
    145179                        # run test
    146                         retcode, _ = sh("timeout 60 %s > %s 2>&1" % (test.target(), out_file), input = in_file)
     180                        retcode, _ = sh("timeout 60 ./%s %s > %s 2>&1" % (test.name, stdinput, out_file), dry_run)
    147181                else :
    148182                        # simply cat the result into the output
    149                         retcode, _ = sh("cat %s > %s" % (test.target(), out_file))
    150         else:
    151                 retcode, _ = sh("mv %s %s" % (err_file, out_file))
    152 
     183                        sh("cat %s > %s" % (test.name, out_file), dry_run)
     184
     185        else :
     186                # command failed save the log to less temporary file
     187                sh("mv %s %s" % (err_file, out_file), dry_run)
    153188
    154189        if retcode == 0:
    155                 if settings.generating :
     190                if generate :
    156191                        # if we are ounly generating the output we still need to check that the test actually exists
    157                         if not settings.dry_run and fileContainsOnly(out_file, "make: *** No rule to make target `%s'.  Stop." % test.target()) :
     192                        if not dry_run and fileContainsOnly(out_file, "make: *** No rule to make target `%s'.  Stop." % test.name) :
    158193                                retcode = 1;
    159                                 error = "\t\tNo make target for test %s!" % test.target()
     194                                error = "\t\tNo make target for test %s!" % test.name
    160195                                sh("rm %s" % out_file, False)
    161                         else:
    162                                 error = None
    163196                else :
    164197                        # fetch return code and error from the diff command
    165                         retcode, error = diff(cmp_file, out_file)
     198                        retcode, error = diff(".expect/%s.txt" % test.path, ".out/%s.log" % test.name, dry_run)
    166199
    167200        else:
     
    171204
    172205        # clean the executable
    173         sh("rm -f %s > /dev/null 2>&1" % test.target())
     206        sh("rm -f %s > /dev/null 2>&1" % test.name, dry_run)
    174207
    175208        return retcode, error
    176209
    177210# run a single test and handle the errors, outputs, printing, exception handling, etc.
    178 def run_test_worker(t) :
    179 
    180         with SignalHandling():
    181                 # print formated name
    182                 name_txt = "%20s  " % t.name
    183 
    184                 retcode, error = run_single_test(t)
    185 
    186                 # update output based on current action
    187                 result_txt = TestResult.toString( retcode )
    188 
    189                 #print result with error if needed
    190                 text = name_txt + result_txt
    191                 out = sys.stdout
    192                 if error :
    193                         text = text + "\n" + error
    194                         out = sys.stderr
    195 
    196                 print(text, file = out)
    197                 sys.stdout.flush()
    198                 sys.stderr.flush()
     211def run_test_worker(t, generate, dry_run, debug) :
     212
     213        signal.signal(signal.SIGINT, signal.SIG_DFL)
     214        # print formated name
     215        name_txt = "%20s  " % t.name
     216
     217        retcode, error = run_single_test(t, generate, dry_run, debug)
     218
     219        # update output based on current action
     220        if generate :
     221                if   retcode == TestResult.SUCCESS:     result_txt = "Done"
     222                elif retcode == TestResult.TIMEOUT:     result_txt = "TIMEOUT"
     223                else :                                          result_txt = "ERROR code %d" % retcode
     224        else :
     225                if   retcode == TestResult.SUCCESS:     result_txt = "PASSED"
     226                elif retcode == TestResult.TIMEOUT:     result_txt = "TIMEOUT"
     227                else :                                          result_txt = "FAILED with code %d" % retcode
     228
     229        #print result with error if needed
     230        text = name_txt + result_txt
     231        out = sys.stdout
     232        if error :
     233                text = text + "\n" + error
     234                out = sys.stderr
     235
     236        print(text, file = out)
     237        sys.stdout.flush()
     238        sys.stderr.flush()
     239        signal.signal(signal.SIGINT, signal.SIG_IGN)
    199240
    200241        return retcode != TestResult.SUCCESS
    201242
    202243# run the given list of tests with the given parameters
    203 def run_tests(tests, jobs) :
     244def run_tests(tests, generate, dry_run, jobs, debug) :
    204245        # clean the sandbox from previous commands
    205         make('clean', redirects = '> /dev/null 2>&1')
     246        sh("%s clean > /dev/null 2>&1" % make_cmd, dry_run)
     247
     248        # make sure the required folder are present
     249        sh('mkdir -p .out .expect .err', dry_run)
     250
     251        if generate :
     252                print( "Regenerate tests for: " )
    206253
    207254        # create the executor for our jobs and handle the signal properly
    208         pool = setupPool(jobs)
     255        original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
     256        pool = Pool(jobs)
     257        signal.signal(signal.SIGINT, original_sigint_handler)
    209258
    210259        # for each test to run
    211260        try :
    212                 results = pool.map_async(
    213                         run_test_worker,
    214                         tests,
    215                         chunksize = 1
    216                 ).get(7200)
     261                results = pool.map_async(partial(run_test_worker, generate=generate, dry_run=dry_run, debug=debug), tests, chunksize = 1 ).get(7200)
    217262        except KeyboardInterrupt:
    218263                pool.terminate()
     
    221266
    222267        # clean the workspace
    223         make('clean', redirects = '> /dev/null 2>&1')
     268        sh("%s clean > /dev/null 2>&1" % make_cmd, dry_run)
    224269
    225270        for failed in results:
     
    240285        options = getOptions()
    241286
    242         # init global settings
    243         settings.init( options )
    244 
    245287        # fetch the liest of all valid tests
    246         allTests = listTests( options.include, options.exclude )
     288        allTests = listTests( options.concurrent )
    247289
    248290        # if user wants all tests than no other treatement of the test list is required
    249         if options.all or options.list or options.list_comp or options.include :
     291        if options.all or options.list or options.list_comp :
    250292                tests = allTests
    251293
    252         #otherwise we need to validate that the test list that was entered is valid
    253         else :
     294        else :
     295                #otherwise we need to validate that the test list that was entered is valid
    254296                tests = validTests( options )
    255297
    256298        # sort the test alphabetically for convenience
    257         tests.sort(key=lambda t: (t.arch if t.arch else '') + t.target())
     299        tests.sort(key=lambda t: t.name)
    258300
    259301        # users may want to simply list the tests
    260302        if options.list_comp :
    261                 print("-h --help --debug --dry-run --list --arch --all --regenerate-expected -j --jobs ", end='')
    262                 print(" ".join(map(lambda t: "%s" % (t.target()), tests)))
     303                print("-h --help --debug --concurrent --dry-run --list --all --regenerate-expected -j --jobs ", end='')
     304                print(" ".join(map(lambda t: "%s" % (t.name), tests)))
    263305
    264306        elif options.list :
    265                 print("Listing for %s:%s"% (settings.arch.string, settings.debug.string))
    266                 print("\n".join(map(lambda t: "%s" % (t.toString()), tests)))
    267 
    268         else :
    269                 options.jobs, forceJobs = jobCount( options, tests )
    270                 settings.updateMakeCmd(forceJobs, options.jobs)
    271 
    272                 print('%s (%s:%s) on %i cores' % (
    273                         'Regenerate tests' if settings.generating else 'Running',
    274                         settings.arch.string,
    275                         settings.debug.string,
    276                         options.jobs
    277                 ))
     307                print("\n".join(map(lambda t: "%s (%s)" % (t.name, t.path), tests)))
     308
     309        else :
     310                options.jobs, forceJobs = jobCount( options )
     311
     312                print('Running (%s) on %i cores' % ("debug" if options.debug else "no debug", options.jobs))
     313                make_cmd = "make" if forceJobs else ("make -j%i" % options.jobs)
    278314
    279315                # otherwise run all tests and make sure to return the correct error code
    280                 sys.exit( run_tests(tests, options.jobs) )
     316                sys.exit( run_tests(tests, options.regenerate_expected, options.dry_run, options.jobs, options.debug) )
Note: See TracChangeset for help on using the changeset viewer.