Index: doc/theses/thierry_delisle_PhD/thesis/fig/base.fig
===================================================================
--- doc/theses/thierry_delisle_PhD/thesis/fig/base.fig	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ doc/theses/thierry_delisle_PhD/thesis/fig/base.fig	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -12,4 +12,9 @@
 1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6900 4200 20 20 6900 4200 6920 4200
 1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6975 4200 20 20 6975 4200 6995 4200
+-6
+6 6375 5100 6675 5250
+1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6450 5175 20 20 6450 5175 6470 5175
+1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6525 5175 20 20 6525 5175 6545 5175
+1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6600 5175 20 20 6600 5175 6620 5175
 -6
 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3900 2400 300 300 3900 2400 4200 2400
@@ -75,4 +80,13 @@
 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
 	 2400 2475 3000 2475
+2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7
+	 3300 5210 3150 4950 2850 4950 2700 5210 2850 5470 3150 5470
+	 3300 5210
+2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7
+	 4500 5210 4350 4950 4050 4950 3900 5210 4050 5470 4350 5470
+	 4500 5210
+2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 7
+	 5700 5210 5550 4950 5250 4950 5100 5210 5250 5470 5550 5470
+	 5700 5210
 4 2 -1 50 -1 0 12 0.0000 2 135 630 2100 3075 Threads\001
 4 2 -1 50 -1 0 12 0.0000 2 165 450 2100 2850 Ready\001
@@ -82,2 +96,3 @@
 4 1 -1 50 -1 0 11 0.0000 2 135 180 2700 3550 TS\001
 4 1 -1 50 -1 0 11 0.0000 2 135 180 2700 2650 TS\001
+4 2 -1 50 -1 0 12 0.0000 2 135 900 2100 5175 Processors\001
Index: doc/theses/thierry_delisle_PhD/thesis/glossary.tex
===================================================================
--- doc/theses/thierry_delisle_PhD/thesis/glossary.tex	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ doc/theses/thierry_delisle_PhD/thesis/glossary.tex	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -40,4 +40,10 @@
 
 \textit{Synonyms : User threads, Lightweight threads, Green threads, Virtual threads, Tasks.}
+}
+
+\longnewglossaryentry{rmr}
+{name={remote memory reference}}
+{
+
 }
 
Index: doc/theses/thierry_delisle_PhD/thesis/text/core.tex
===================================================================
--- doc/theses/thierry_delisle_PhD/thesis/text/core.tex	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ doc/theses/thierry_delisle_PhD/thesis/text/core.tex	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -49,73 +49,113 @@
 
 \section{Design}
-In general, a na\"{i}ve \glsxtrshort{fifo} ready-queue does not scale with increased parallelism from \glspl{hthrd}, resulting in decreased performance. The problem is adding/removing \glspl{thrd} is a single point of contention. As shown in the evaluation sections, most production schedulers do scale when adding \glspl{hthrd}. The common solution to the single point of contention is to shard the ready-queue so each \gls{hthrd} can access the ready-queue without contention, increasing performance.
+In general, a na\"{i}ve \glsxtrshort{fifo} ready-queue does not scale with increased parallelism from \glspl{hthrd}, resulting in decreased performance. The problem is adding/removing \glspl{thrd} is a single point of contention. As shown in the evaluation sections, most production schedulers do scale when adding \glspl{hthrd}. The solution to this problem is to shard the ready-queue : create multiple sub-ready-queues that multiple \glspl{hthrd} can access and modify without interfering.
 
-\subsection{Sharding} \label{sec:sharding}
-An interesting approach to sharding a queue is presented in \cit{Trevors paper}. This algorithm presents a queue with a relaxed \glsxtrshort{fifo} guarantee using an array of strictly \glsxtrshort{fifo} sublists as shown in Figure~\ref{fig:base}. Each \emph{cell} of the array has a timestamp for the last operation and a pointer to a linked-list with a lock. Each node in the list is marked with a timestamp indicating when it is added to the list. A push operation is done by picking a random cell, acquiring the list lock, and pushing to the list. If the cell is locked, the operation is simply retried on another random cell until a lock is acquired. A pop operation is done in a similar fashion except two random cells are picked. If both cells are unlocked with non-empty lists, the operation pops the node with the oldest timestamp. If one of the cells is unlocked and non-empty, the operation pops from that cell. If both cells are either locked or empty, the operation picks two new random cells and tries again.
+Before going into the design of \CFA's scheduler proper, I want to discuss two sharding solutions which served as the inspiration scheduler in this thesis.
 
+\subsection{Work-Stealing}
+
+As I mentioned in \ref{existing:workstealing}, a popular pattern shard the ready-queue is work-stealing. As mentionned, in this pattern each \gls{proc} has its own ready-queue and \glspl{proc} only access each other's ready-queue if they run out of work.
+The interesting aspect of workstealing happen in easier scheduling cases, \ie enough work for everyone but no more and no load balancing needed. In these cases, work-stealing is close to optimal scheduling: it can achieve perfect locality and have no contention.
+On the other hand, work-stealing schedulers only attempt to do load-balancing when a \gls{proc} runs out of work.
+This means that the scheduler may never balance unfairness that does not result in a \gls{proc} running out of work.
+Chapter~\ref{microbench} shows that in pathological cases this problem can lead to indefinite starvation.
+
+
+Based on these observation, I conclude that \emph{perfect} scheduler should behave very similarly to work-stealing in the easy cases, but should have more proactive load-balancing if the need arises.
+
+\subsection{Relaxed-Fifo}
+An entirely different scheme is to create a ``relaxed-FIFO'' queue as in \todo{cite Trevor's paper}. This approach forgos any ownership between \gls{proc} and ready-queue, and simply creates a pool of ready-queues from which the \glspl{proc} can pick from.
+\Glspl{proc} choose ready-queus at random, but timestamps are added to all elements of the queue and dequeues are done by picking two queues and dequeing the oldest element.
+The result is a queue that has both decent scalability and sufficient fairness.
+The lack of ownership means that as long as one \gls{proc} is still able to repeatedly dequeue elements, it is unlikely that any element will stay on the queue for much longer than any other element.
+This contrasts with work-stealing, where \emph{any} \gls{proc} busy for an extended period of time results in all the elements on its local queue to have to wait. Unless another \gls{proc} runs out of work.
+
+An important aspects of this scheme's fairness approach is that the timestamps make it possible to evaluate how long elements have been on the queue.
+However, another major aspect is that \glspl{proc} will eagerly search for these older elements instead of focusing on specific queues.
+
+While the fairness, of this scheme is good, it does suffer in terms of performance.
+It requires very wide sharding, \eg at least 4 queues per \gls{hthrd}, and the randomness means locality can suffer significantly and finding non-empty queues can be difficult.
+
+\section{\CFA}
+The \CFA is effectively attempting to merge these two approaches, keeping the best of both.
+It is based on the
 \begin{figure}
 	\centering
 	\input{base.pstex_t}
-	\caption[Relaxed FIFO list]{Relaxed FIFO list \smallskip\newline List at the base of the scheduler: an array of strictly FIFO lists. The timestamp is in all nodes and cell arrays.}
+	\caption[Base \CFA design]{Base \CFA design \smallskip\newline A list of sub-ready queues offers the sharding, two per \glspl{proc}. However, \glspl{proc} can access any of the sub-queues.}
 	\label{fig:base}
 \end{figure}
 
-\subsection{Finding threads}
-Once threads have been distributed onto multiple queues, identifying empty queues becomes a problem. Indeed, if the number of \glspl{thrd} does not far exceed the number of queues, it is probable that several of the cell queues are empty. Figure~\ref{fig:empty} shows an example with 2 \glspl{thrd} running on 8 queues, where the chances of getting an empty queue is 75\% per pick, meaning two random picks yield a \gls{thrd} only half the time. This scenario leads to performance problems since picks that do not yield a \gls{thrd} are not useful and do not necessarily help make more informed guesses.
 
-\begin{figure}
-	\centering
-	\input{empty.pstex_t}
-	\caption[``More empty'' Relaxed FIFO list]{``More empty'' Relaxed FIFO list \smallskip\newline Emptier state of the queue: the array contains many empty cells, that is strictly FIFO lists containing no elements.}
-	\label{fig:empty}
-\end{figure}
 
-There are several solutions to this problem, but they ultimately all have to encode if a cell has an empty list. My results show the density and locality of this encoding is generally the dominating factor in these scheme. Classic solutions to this problem use one of three techniques to encode the information:
+% The common solution to the single point of contention is to shard the ready-queue so each \gls{hthrd} can access the ready-queue without contention, increasing performance.
 
-\paragraph{Dense Information} Figure~\ref{fig:emptybit} shows a dense bitmask to identify the cell queues currently in use. This approach means processors can often find \glspl{thrd} in constant time, regardless of how many underlying queues are empty. Furthermore, modern x86 CPUs have extended bit manipulation instructions (BMI2) that allow searching the bitmask with very little overhead compared to the randomized selection approach for a filled ready queue, offering good performance even in cases with many empty inner queues. However, this technique has its limits: with a single word\footnote{Word refers here to however many bits can be written atomically.} bitmask, the total amount of ready-queue sharding is limited to the number of bits in the word. With a multi-word bitmask, this maximum limit can be increased arbitrarily, but the look-up time increases. Finally, a dense bitmap, either single or multi-word, causes additional contention problems that reduces performance because of cache misses after updates. This central update bottleneck also means the information in the bitmask is more often stale before a processor can use it to find an item, \ie mask read says there are available \glspl{thrd} but none on queue when the subsequent atomic check is done.
+% \subsection{Sharding} \label{sec:sharding}
+% An interesting approach to sharding a queue is presented in \cit{Trevors paper}. This algorithm presents a queue with a relaxed \glsxtrshort{fifo} guarantee using an array of strictly \glsxtrshort{fifo} sublists as shown in Figure~\ref{fig:base}. Each \emph{cell} of the array has a timestamp for the last operation and a pointer to a linked-list with a lock. Each node in the list is marked with a timestamp indicating when it is added to the list. A push operation is done by picking a random cell, acquiring the list lock, and pushing to the list. If the cell is locked, the operation is simply retried on another random cell until a lock is acquired. A pop operation is done in a similar fashion except two random cells are picked. If both cells are unlocked with non-empty lists, the operation pops the node with the oldest timestamp. If one of the cells is unlocked and non-empty, the operation pops from that cell. If both cells are either locked or empty, the operation picks two new random cells and tries again.
 
-\begin{figure}
-	\centering
-	\vspace*{-5pt}
-	{\resizebox{0.75\textwidth}{!}{\input{emptybit.pstex_t}}}
-	\vspace*{-5pt}
-	\caption[Underloaded queue with bitmask]{Underloaded queue with bitmask indicating array cells with items.}
-	\label{fig:emptybit}
+% \begin{figure}
+% 	\centering
+% 	\input{base.pstex_t}
+% 	\caption[Relaxed FIFO list]{Relaxed FIFO list \smallskip\newline List at the base of the scheduler: an array of strictly FIFO lists. The timestamp is in all nodes and cell arrays.}
+% 	\label{fig:base}
+% \end{figure}
 
-	\vspace*{10pt}
-	{\resizebox{0.75\textwidth}{!}{\input{emptytree.pstex_t}}}
-	\vspace*{-5pt}
-	\caption[Underloaded queue with binary search-tree]{Underloaded queue with binary search-tree indicating array cells with items.}
-	\label{fig:emptytree}
+% \subsection{Finding threads}
+% Once threads have been distributed onto multiple queues, identifying empty queues becomes a problem. Indeed, if the number of \glspl{thrd} does not far exceed the number of queues, it is probable that several of the cell queues are empty. Figure~\ref{fig:empty} shows an example with 2 \glspl{thrd} running on 8 queues, where the chances of getting an empty queue is 75\% per pick, meaning two random picks yield a \gls{thrd} only half the time. This scenario leads to performance problems since picks that do not yield a \gls{thrd} are not useful and do not necessarily help make more informed guesses.
 
-	\vspace*{10pt}
-	{\resizebox{0.95\textwidth}{!}{\input{emptytls.pstex_t}}}
-	\vspace*{-5pt}
-	\caption[Underloaded queue with per processor bitmask]{Underloaded queue with per processor bitmask indicating array cells with items.}
-	\label{fig:emptytls}
-\end{figure}
+% \begin{figure}
+% 	\centering
+% 	\input{empty.pstex_t}
+% 	\caption[``More empty'' Relaxed FIFO list]{``More empty'' Relaxed FIFO list \smallskip\newline Emptier state of the queue: the array contains many empty cells, that is strictly FIFO lists containing no elements.}
+% 	\label{fig:empty}
+% \end{figure}
 
-\paragraph{Sparse Information} Figure~\ref{fig:emptytree} shows an approach using a hierarchical tree data-structure to reduce contention and has been shown to work in similar cases~\cite{ellen2007snzi}. However, this approach may lead to poorer performance due to the inherent pointer chasing cost while still allowing significant contention on the nodes of the tree if the tree is shallow.
+% There are several solutions to this problem, but they ultimately all have to encode if a cell has an empty list. My results show the density and locality of this encoding is generally the dominating factor in these scheme. Classic solutions to this problem use one of three techniques to encode the information:
 
-\paragraph{Local Information} Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but each \gls{hthrd} keeps its own independent copy. While this approach can offer good scalability \emph{and} low latency, the liveliness and discovery of the information can become a problem. This case is made worst in systems with few processors where even blind random picks can find \glspl{thrd} in a few tries.
+% \paragraph{Dense Information} Figure~\ref{fig:emptybit} shows a dense bitmask to identify the cell queues currently in use. This approach means processors can often find \glspl{thrd} in constant time, regardless of how many underlying queues are empty. Furthermore, modern x86 CPUs have extended bit manipulation instructions (BMI2) that allow searching the bitmask with very little overhead compared to the randomized selection approach for a filled ready queue, offering good performance even in cases with many empty inner queues. However, this technique has its limits: with a single word\footnote{Word refers here to however many bits can be written atomically.} bitmask, the total amount of ready-queue sharding is limited to the number of bits in the word. With a multi-word bitmask, this maximum limit can be increased arbitrarily, but the look-up time increases. Finally, a dense bitmap, either single or multi-word, causes additional contention problems that reduces performance because of cache misses after updates. This central update bottleneck also means the information in the bitmask is more often stale before a processor can use it to find an item, \ie mask read says there are available \glspl{thrd} but none on queue when the subsequent atomic check is done.
 
-I built a prototype of these approaches and none of these techniques offer satisfying performance when few threads are present. All of these approach hit the same 2 problems. First, randomly picking sub-queues is very fast. That speed means any improvement to the hit rate can easily be countered by a slow-down in look-up speed, whether or not there are empty lists. Second, the array is already sharded to avoid contention bottlenecks, so any denser data structure tends to become a bottleneck. In all cases, these factors meant the best cases scenario, \ie many threads, would get worst throughput, and the worst-case scenario, few threads, would get a better hit rate, but an equivalent poor throughput. As a result I tried an entirely different approach.
+% \begin{figure}
+% 	\centering
+% 	\vspace*{-5pt}
+% 	{\resizebox{0.75\textwidth}{!}{\input{emptybit.pstex_t}}}
+% 	\vspace*{-5pt}
+% 	\caption[Underloaded queue with bitmask]{Underloaded queue with bitmask indicating array cells with items.}
+% 	\label{fig:emptybit}
 
-\subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/}
-In the worst-case scenario there are only few \glspl{thrd} ready to run, or more precisely given $P$ \glspl{proc}\footnote{For simplicity, this assumes there is a one-to-one match between \glspl{proc} and \glspl{hthrd}.}, $T$ \glspl{thrd} and $\epsilon$ a very small number, than the worst case scenario can be represented by $T = P + \epsilon$, with $\epsilon \ll P$. It is important to note in this case that fairness is effectively irrelevant. Indeed, this case is close to \emph{actually matching} the model of the ``Ideal multi-tasking CPU'' on page \pageref{q:LinuxCFS}. In this context, it is possible to use a purely internal-locality based approach and still meet the fairness requirements. This approach simply has each \gls{proc} running a single \gls{thrd} repeatedly. Or from the shared ready-queue viewpoint, each \gls{proc} pushes to a given sub-queue and then pops from the \emph{same} subqueue. The challenge is for the the scheduler to achieve good performance in both the $T = P + \epsilon$ case and the $T \gg P$ case, without affecting the fairness guarantees in the later.
+% 	\vspace*{10pt}
+% 	{\resizebox{0.75\textwidth}{!}{\input{emptytree.pstex_t}}}
+% 	\vspace*{-5pt}
+% 	\caption[Underloaded queue with binary search-tree]{Underloaded queue with binary search-tree indicating array cells with items.}
+% 	\label{fig:emptytree}
 
-To handle this case, I use a \glsxtrshort{prng}\todo{Fix missing long form} in a novel way. There exist \glsxtrshort{prng}s that are fast, compact and can be run forward \emph{and} backwards.  Linear congruential generators~\cite{wiki:lcg} are an example of \glsxtrshort{prng}s of such \glsxtrshort{prng}s. The novel approach is to use the ability to run backwards to ``replay'' the \glsxtrshort{prng}. The scheduler uses an exclusive \glsxtrshort{prng} instance per \gls{proc}, the random-number seed effectively starts an encoding that produces a list of all accessed subqueues, from latest to oldest. Replaying the \glsxtrshort{prng} to identify cells accessed recently and which probably have data still cached.
+% 	\vspace*{10pt}
+% 	{\resizebox{0.95\textwidth}{!}{\input{emptytls.pstex_t}}}
+% 	\vspace*{-5pt}
+% 	\caption[Underloaded queue with per processor bitmask]{Underloaded queue with per processor bitmask indicating array cells with items.}
+% 	\label{fig:emptytls}
+% \end{figure}
 
-The algorithm works as follows:
-\begin{itemize}
-	\item Each \gls{proc} has two \glsxtrshort{prng} instances, $F$ and $B$.
-	\item Push and Pop operations occur as discussed in Section~\ref{sec:sharding} with the following exceptions:
-	\begin{itemize}
-		\item Push operations use $F$ going forward on each try and on success $F$ is copied into $B$.
-		\item Pop operations use $B$ going backwards on each try.
-	\end{itemize}
-\end{itemize}
+% \paragraph{Sparse Information} Figure~\ref{fig:emptytree} shows an approach using a hierarchical tree data-structure to reduce contention and has been shown to work in similar cases~\cite{ellen2007snzi}. However, this approach may lead to poorer performance due to the inherent pointer chasing cost while still allowing significant contention on the nodes of the tree if the tree is shallow.
 
-The main benefit of this technique is that it basically respects the desired properties of Figure~\ref{fig:fair}. When looking for work, a \gls{proc} first looks at the last cell they pushed to, if any, and then move backwards through its accessed cells. As the \gls{proc} continues looking for work, $F$ moves backwards and $B$ stays in place. As a result, the relation between the two becomes weaker, which means that the probablisitic fairness of the algorithm reverts to normal. Chapter~\ref{proofs} discusses more formally the fairness guarantees of this algorithm.
+% \paragraph{Local Information} Figure~\ref{fig:emptytls} shows an approach using dense information, similar to the bitmap, but each \gls{hthrd} keeps its own independent copy. While this approach can offer good scalability \emph{and} low latency, the liveliness and discovery of the information can become a problem. This case is made worst in systems with few processors where even blind random picks can find \glspl{thrd} in a few tries.
 
-\section{Details}
+% I built a prototype of these approaches and none of these techniques offer satisfying performance when few threads are present. All of these approach hit the same 2 problems. First, randomly picking sub-queues is very fast. That speed means any improvement to the hit rate can easily be countered by a slow-down in look-up speed, whether or not there are empty lists. Second, the array is already sharded to avoid contention bottlenecks, so any denser data structure tends to become a bottleneck. In all cases, these factors meant the best cases scenario, \ie many threads, would get worst throughput, and the worst-case scenario, few threads, would get a better hit rate, but an equivalent poor throughput. As a result I tried an entirely different approach.
+
+% \subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/}
+% In the worst-case scenario there are only few \glspl{thrd} ready to run, or more precisely given $P$ \glspl{proc}\footnote{For simplicity, this assumes there is a one-to-one match between \glspl{proc} and \glspl{hthrd}.}, $T$ \glspl{thrd} and $\epsilon$ a very small number, than the worst case scenario can be represented by $T = P + \epsilon$, with $\epsilon \ll P$. It is important to note in this case that fairness is effectively irrelevant. Indeed, this case is close to \emph{actually matching} the model of the ``Ideal multi-tasking CPU'' on page \pageref{q:LinuxCFS}. In this context, it is possible to use a purely internal-locality based approach and still meet the fairness requirements. This approach simply has each \gls{proc} running a single \gls{thrd} repeatedly. Or from the shared ready-queue viewpoint, each \gls{proc} pushes to a given sub-queue and then pops from the \emph{same} subqueue. The challenge is for the the scheduler to achieve good performance in both the $T = P + \epsilon$ case and the $T \gg P$ case, without affecting the fairness guarantees in the later.
+
+% To handle this case, I use a \glsxtrshort{prng}\todo{Fix missing long form} in a novel way. There exist \glsxtrshort{prng}s that are fast, compact and can be run forward \emph{and} backwards.  Linear congruential generators~\cite{wiki:lcg} are an example of \glsxtrshort{prng}s of such \glsxtrshort{prng}s. The novel approach is to use the ability to run backwards to ``replay'' the \glsxtrshort{prng}. The scheduler uses an exclusive \glsxtrshort{prng} instance per \gls{proc}, the random-number seed effectively starts an encoding that produces a list of all accessed subqueues, from latest to oldest. Replaying the \glsxtrshort{prng} to identify cells accessed recently and which probably have data still cached.
+
+% The algorithm works as follows:
+% \begin{itemize}
+% 	\item Each \gls{proc} has two \glsxtrshort{prng} instances, $F$ and $B$.
+% 	\item Push and Pop operations occur as discussed in Section~\ref{sec:sharding} with the following exceptions:
+% 	\begin{itemize}
+% 		\item Push operations use $F$ going forward on each try and on success $F$ is copied into $B$.
+% 		\item Pop operations use $B$ going backwards on each try.
+% 	\end{itemize}
+% \end{itemize}
+
+% The main benefit of this technique is that it basically respects the desired properties of Figure~\ref{fig:fair}. When looking for work, a \gls{proc} first looks at the last cell they pushed to, if any, and then move backwards through its accessed cells. As the \gls{proc} continues looking for work, $F$ moves backwards and $B$ stays in place. As a result, the relation between the two becomes weaker, which means that the probablisitic fairness of the algorithm reverts to normal. Chapter~\ref{proofs} discusses more formally the fairness guarantees of this algorithm.
+
+% \section{Details}
Index: doc/theses/thierry_delisle_PhD/thesis/text/eval_macro.tex
===================================================================
--- doc/theses/thierry_delisle_PhD/thesis/text/eval_macro.tex	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ doc/theses/thierry_delisle_PhD/thesis/text/eval_macro.tex	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -4,6 +4,4 @@
 
 In Memory Plain Text
-
-Networked Plain Text
 
 Networked ZIPF
Index: doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex
===================================================================
--- doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -2,26 +2,24 @@
 
 The first step of evaluation is always to test-out small controlled cases, to ensure that the basics are working properly.
-This sections presents four different experimental setup, evaluating some of the basic features of \CFA's scheduler.
+This sections presents five different experimental setup, evaluating some of the basic features of \CFA's scheduler.
 
 \section{Cycling latency}
 The most basic evaluation of any ready queue is to evaluate the latency needed to push and pop one element from the ready-queue.
-While these two operation also describe a \texttt{yield} operation, many systems use this as the most basic benchmark.
-However, yielding can be treated as a special case, since it also carries the information that the length of the ready queue will not change.
+Since these two operation also describe a \texttt{yield} operation, many systems use this as the most basic benchmark.
+However, yielding can be treated as a special case, since it also carries the information that the number of the ready \glspl{at} will not change.
 Not all systems use this information, but those which do may appear to have better performance than they would for disconnected push/pop pairs.
 For this reason, I chose a different first benchmark, which I call the Cycle Benchmark.
-This benchmark arranges many threads into multiple rings of threads.
+This benchmark arranges many \glspl{at} into multiple rings of \glspl{at}.
 Each ring is effectively a circular singly-linked list.
-At runtime, each thread unparks the next thread before parking itself.
+At runtime, each \gls{at} unparks the next \gls{at} before parking itself.
 This corresponds to the desired pair of ready queue operations.
-Unparking the next thread requires pushing that thread onto the ready queue and the ensuing park will cause the runtime to pop a thread from the ready-queue.
+Unparking the next \gls{at} requires pushing that \gls{at} onto the ready queue and the ensuing park will cause the runtime to pop a \gls{at} from the ready-queue.
 Figure~\ref{fig:cycle} shows a visual representation of this arrangement.
 
-The goal of this ring is that the underlying runtime cannot rely on the guarantee that the number of ready threads will stay constant over the duration of the experiment.
-In fact, the total number of threads waiting on the ready is expected to vary a little because of the race between the next thread unparking and the current thread parking.
-The size of the cycle is also decided based on this race: cycles that are too small may see the
-chain of unparks go full circle before the first thread can park.
+The goal of this ring is that the underlying runtime cannot rely on the guarantee that the number of ready \glspl{at} will stay constant over the duration of the experiment.
+In fact, the total number of \glspl{at} waiting on the ready queue is expected to vary because of the race between the next \gls{at} unparking and the current \gls{at} parking.
+The size of the cycle is also decided based on this race: cycles that are too small may see the chain of unparks go full circle before the first \gls{at} can park.
 While this would not be a correctness problem, every runtime system must handle that race, it could lead to pushes and pops being optimized away.
-Since silently omitting ready-queue operations would throw off the measuring of these operations.
-Therefore the ring of threads must be big enough so the threads have the time to fully park before they are unparked.
+Since silently omitting ready-queue operations would throw off the measuring of these operations, the ring of \glspl{at} must be big enough so the \glspl{at} have the time to fully park before they are unparked.
 Note that this problem is only present on SMP machines and is significantly mitigated by the fact that there are multiple rings in the system.
 
@@ -29,22 +27,144 @@
 	\centering
 	\input{cycle.pstex_t}
-	\caption[Cycle benchmark]{Cycle benchmark\smallskip\newline Each thread unparks the next thread in the cycle before parking itself.}
+	\caption[Cycle benchmark]{Cycle benchmark\smallskip\newline Each \gls{at} unparks the next \gls{at} in the cycle before parking itself.}
 	\label{fig:cycle}
 \end{figure}
 
 \todo{check term ``idle sleep handling''}
-To avoid this benchmark from being dominated by the idle sleep handling, the number of rings is kept at least as high as the number of processors available.
+To avoid this benchmark from being dominated by the idle sleep handling, the number of rings is kept at least as high as the number of \glspl{proc} available.
 Beyond this point, adding more rings serves to mitigate even more the idle sleep handling.
-This is to avoid the case where one of the worker threads runs out of work because of the variation on the number of ready threads mentionned above.
+This is to avoid the case where one of the worker \glspl{at} runs out of work because of the variation on the number of ready \glspl{at} mentionned above.
 
 The actual benchmark is more complicated to handle termination, but that simply requires using a binary semphore or a channel instead of raw \texttt{park}/\texttt{unpark} and carefully picking the order of the \texttt{P} and \texttt{V} with respect to the loop condition.
 
-\todo{mention where to get the code.}
+\todo{code, setup, results}
+\begin{lstlisting}
+	Thread.main() {
+		count := 0
+		for {
+			wait()
+			this.next.wake()
+			count ++
+			if must_stop() { break }
+		}
+		global.count += count
+	}
+\end{lstlisting}
+
 
 \section{Yield}
 For completion, I also include the yield benchmark.
-This benchmark is much simpler than the cycle tests, it simply creates many threads that call \texttt{yield}.
+This benchmark is much simpler than the cycle tests, it simply creates many \glspl{at} that call \texttt{yield}.
+As mentionned in the previous section, this benchmark may be less representative of usages that only make limited use of \texttt{yield}, due to potential shortcuts in the routine.
+Its only interesting variable is the number of \glspl{at} per \glspl{proc}, where ratios close to 1 means the ready queue(s) could be empty.
+This sometimes puts more strain on the idle sleep handling, compared to scenarios where there is clearly plenty of work to be done.
+
+\todo{code, setup, results}
+
+\begin{lstlisting}
+	Thread.main() {
+		count := 0
+		while !stop {
+			yield()
+			count ++
+		}
+		global.count += count
+	}
+\end{lstlisting}
+
+
+\section{Churn}
+The Cycle and Yield benchmark represents an ``easy'' scenario for a scheduler, \eg, an embarrassingly parallel application.
+In these benchmarks, \glspl{at} can be easily partitioned over the different \glspl{proc} up-front and none of the \glspl{at} communicate with each other.
+
+The Churn benchmark represents more chaotic usages, where there is no relation between the last \gls{proc} on which a \gls{at} ran and the \gls{proc} that unblocked it.
+When a \gls{at} is unblocked from a different \gls{proc} than the one on which it last ran, the unblocking \gls{proc} must either ``steal'' the \gls{at} or place it on a remote queue.
+This results can result in either contention on the remote queue or \glspl{rmr} on \gls{at} data structure.
+In either case, this benchmark aims to highlight how each scheduler handles these cases, since both cases can lead to performance degradation if they are not handled correctly.
+
+To achieve this the benchmark uses a fixed size array of \newterm{chair}s, where a chair is a data structure that holds a single blocked \gls{at}.
+When a \gls{at} attempts to block on the chair, it must first unblocked the \gls{at} currently blocked on said chair, if any.
+This creates a flow where \glspl{at} push each other out of the chairs before being pushed out themselves.
+For this benchmark to work however, the number of \glspl{at} must be equal or greater to the number of chairs plus the number of \glspl{proc}.
+
+\todo{code, setup, results}
+\begin{lstlisting}
+	Thread.main() {
+		count := 0
+		for {
+			r := random() % len(spots)
+			next := xchg(spots[r], this)
+			if next { next.wake() }
+			wait()
+			count ++
+			if must_stop() { break }
+		}
+		global.count += count
+	}
+\end{lstlisting}
 
 \section{Locality}
 
+\todo{code, setup, results}
+
 \section{Transfer}
+The last benchmark is more exactly characterize as an experiment than a benchmark.
+It tests the behavior of the schedulers for a particularly misbehaved workload.
+In this workload, one of the \gls{at} is selected at random to be the leader.
+The leader then spins in a tight loop until it has observed that all other \glspl{at} have acknowledged its leadership.
+The leader \gls{at} then picks a new \gls{at} to be the ``spinner'' and the cycle repeats.
+
+The benchmark comes in two flavours for the behavior of the non-leader \glspl{at}:
+once they acknowledged the leader, they either block on a semaphore or yield repeatadly.
+
+This experiment is designed to evaluate the short term load balancing of the scheduler.
+Indeed, schedulers where the runnable \glspl{at} are partitioned on the \glspl{proc} may need to balance the \glspl{at} for this experient to terminate.
+This is because the spinning \gls{at} is effectively preventing the \gls{proc} from runnning any other \glspl{thrd}.
+In the semaphore flavour, the number of runnable \glspl{at} will eventually dwindle down to only the leader.
+This is a simpler case to handle for schedulers since \glspl{proc} eventually run out of work.
+In the yielding flavour, the number of runnable \glspl{at} stays constant.
+This is a harder case to handle because corrective measures must be taken even if work is still available.
+Note that languages that have mandatory preemption do circumvent this problem by forcing the spinner to yield.
+
+\todo{code, setup, results}
+\begin{lstlisting}
+	Thread.lead() {
+		this.idx_seen = ++lead_idx
+		if lead_idx > stop_idx {
+			done := true
+			return
+		}
+
+		// Wait for everyone to acknowledge my leadership
+		start: = timeNow()
+		for t in threads {
+			while t.idx_seen != lead_idx {
+				asm pause
+				if (timeNow() - start) > 5 seconds { error() }
+			}
+		}
+
+		// pick next leader
+		leader := threads[ prng() % len(threads) ]
+
+		// wake every one
+		if !exhaust {
+			for t in threads {
+				if t != me { t.wake() }
+			}
+		}
+	}
+
+	Thread.wait() {
+		this.idx_seen := lead_idx
+		if exhaust { wait() }
+		else { yield() }
+	}
+
+	Thread.main() {
+		while !done  {
+			if leader == me { this.lead() }
+			else { this.wait() }
+		}
+	}
+\end{lstlisting}
Index: doc/theses/thierry_delisle_PhD/thesis/text/existing.tex
===================================================================
--- doc/theses/thierry_delisle_PhD/thesis/text/existing.tex	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ doc/theses/thierry_delisle_PhD/thesis/text/existing.tex	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -33,5 +33,5 @@
 
 
-\section{Work Stealing}
+\section{Work Stealing}\label{existing:workstealing}
 One of the most popular scheduling algorithm in practice (see~\ref{existing:prod}) is work-stealing. This idea, introduce by \cite{DBLP:conf/fpca/BurtonS81}, effectively has each worker work on its local tasks first, but allows the possibility for other workers to steal local tasks if they run out of tasks. \cite{DBLP:conf/focs/Blumofe94} introduced the more familiar incarnation of this, where each workers has queue of tasks to accomplish and workers without tasks steal tasks from random workers. (The Burton and Sleep algorithm had trees of tasks and stole only among neighbours). Blumofe and Leiserson also prove worst case space and time requirements for well-structured computations.
 
Index: libcfa/src/concurrency/io.cfa
===================================================================
--- libcfa/src/concurrency/io.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/io.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -306,5 +306,10 @@
 		ctx->proc->io.pending = true;
 		ctx->proc->io.dirty   = true;
-		if(sq.to_submit > 30 || !lazy) {
+		if(sq.to_submit > 30) {
+			__tls_stats()->io.flush.full++;
+			__cfa_io_flush( ctx->proc, 0 );
+		}
+		if(!lazy) {
+			__tls_stats()->io.flush.eager++;
 			__cfa_io_flush( ctx->proc, 0 );
 		}
Index: libcfa/src/concurrency/kernel.cfa
===================================================================
--- libcfa/src/concurrency/kernel.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/kernel.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -42,7 +42,7 @@
 
 #if !defined(__CFA_NO_STATISTICS__)
-	#define __STATS( ...) __VA_ARGS__
+	#define __STATS_DEF( ...) __VA_ARGS__
 #else
-	#define __STATS( ...)
+	#define __STATS_DEF( ...)
 #endif
 
@@ -122,4 +122,5 @@
 static thread$ * __next_thread(cluster * this);
 static thread$ * __next_thread_slow(cluster * this);
+static thread$ * __next_thread_search(cluster * this);
 static inline bool __must_unpark( thread$ * thrd ) __attribute((nonnull(1)));
 static void __run_thread(processor * this, thread$ * dst);
@@ -187,6 +188,4 @@
 		MAIN_LOOP:
 		for() {
-			#define OLD_MAIN 1
-			#if OLD_MAIN
 			// Check if there is pending io
 			__maybe_io_drain( this );
@@ -196,7 +195,18 @@
 
 			if( !readyThread ) {
+				__IO_STATS__(true, io.flush.idle++; )
 				__cfa_io_flush( this, 0 );
 
+				readyThread = __next_thread( this->cltr );
+			}
+
+			if( !readyThread ) for(5) {
+				__IO_STATS__(true, io.flush.idle++; )
+
 				readyThread = __next_thread_slow( this->cltr );
+
+				if( readyThread ) break;
+
+				__cfa_io_flush( this, 0 );
 			}
 
@@ -206,20 +216,14 @@
 				if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
 
-				#if !defined(__CFA_NO_STATISTICS__)
-					__tls_stats()->ready.sleep.halts++;
-				#endif
-
 				// Push self to idle stack
 				if(!mark_idle(this->cltr->procs, * this)) continue MAIN_LOOP;
 
 				// Confirm the ready-queue is empty
-				readyThread = __next_thread_slow( this->cltr );
+				readyThread = __next_thread_search( this->cltr );
 				if( readyThread ) {
 					// A thread was found, cancel the halt
 					mark_awake(this->cltr->procs, * this);
 
-					#if !defined(__CFA_NO_STATISTICS__)
-						__tls_stats()->ready.sleep.cancels++;
-					#endif
+					__STATS__(true, ready.sleep.cancels++; )
 
 					// continue the mai loop
@@ -248,122 +252,7 @@
 
 			if(this->io.pending && !this->io.dirty) {
+				__IO_STATS__(true, io.flush.dirty++; )
 				__cfa_io_flush( this, 0 );
 			}
-
-			#else
-				#warning new kernel loop
-			SEARCH: {
-				/* paranoid */ verify( ! __preemption_enabled() );
-
-				// First, lock the scheduler since we are searching for a thread
-				ready_schedule_lock();
-
-				// Try to get the next thread
-				readyThread = pop_fast( this->cltr );
-				if(readyThread) { ready_schedule_unlock(); break SEARCH; }
-
-				// If we can't find a thread, might as well flush any outstanding I/O
-				if(this->io.pending) { __cfa_io_flush( this, 0 ); }
-
-				// Spin a little on I/O, just in case
-				for(5) {
-					__maybe_io_drain( this );
-					readyThread = pop_fast( this->cltr );
-					if(readyThread) { ready_schedule_unlock(); break SEARCH; }
-				}
-
-				// no luck, try stealing a few times
-				for(5) {
-					if( __maybe_io_drain( this ) ) {
-						readyThread = pop_fast( this->cltr );
-					} else {
-						readyThread = pop_slow( this->cltr );
-					}
-					if(readyThread) { ready_schedule_unlock(); break SEARCH; }
-				}
-
-				// still no luck, search for a thread
-				readyThread = pop_search( this->cltr );
-				if(readyThread) { ready_schedule_unlock(); break SEARCH; }
-
-				// Don't block if we are done
-				if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) {
-					ready_schedule_unlock();
-					break MAIN_LOOP;
-				}
-
-				__STATS( __tls_stats()->ready.sleep.halts++; )
-
-				// Push self to idle stack
-				ready_schedule_unlock();
-				if(!mark_idle(this->cltr->procs, * this)) goto SEARCH;
-				ready_schedule_lock();
-
-				// Confirm the ready-queue is empty
-				__maybe_io_drain( this );
-				readyThread = pop_search( this->cltr );
-				ready_schedule_unlock();
-
-				if( readyThread ) {
-					// A thread was found, cancel the halt
-					mark_awake(this->cltr->procs, * this);
-
-					__STATS( __tls_stats()->ready.sleep.cancels++; )
-
-					// continue the main loop
-					break SEARCH;
-				}
-
-				__STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); )
-				__cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle_fd);
-
-				{
-					eventfd_t val;
-					ssize_t ret = read( this->idle_fd, &val, sizeof(val) );
-					if(ret < 0) {
-						switch((int)errno) {
-						case EAGAIN:
-						#if EAGAIN != EWOULDBLOCK
-							case EWOULDBLOCK:
-						#endif
-						case EINTR:
-							// No need to do anything special here, just assume it's a legitimate wake-up
-							break;
-						default:
-							abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) );
-						}
-					}
-				}
-
-					__STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); )
-
-				// We were woken up, remove self from idle
-				mark_awake(this->cltr->procs, * this);
-
-				// DON'T just proceed, start looking again
-				continue MAIN_LOOP;
-			}
-
-		RUN_THREAD:
-			/* paranoid */ verify( ! __preemption_enabled() );
-			/* paranoid */ verify( readyThread );
-
-			// Reset io dirty bit
-			this->io.dirty = false;
-
-			// We found a thread run it
-			__run_thread(this, readyThread);
-
-			// Are we done?
-			if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP;
-
-			if(this->io.pending && !this->io.dirty) {
-				__cfa_io_flush( this, 0 );
-			}
-
-			ready_schedule_lock();
-			__maybe_io_drain( this );
-			ready_schedule_unlock();
-			#endif
 		}
 
@@ -476,7 +365,5 @@
 				break RUNNING;
 			case TICKET_UNBLOCK:
-				#if !defined(__CFA_NO_STATISTICS__)
-					__tls_stats()->ready.threads.threads++;
-				#endif
+				__STATS__(true, ready.threads.threads++; )
 				// This is case 2, the racy case, someone tried to run this thread before it finished blocking
 				// In this case, just run it again.
@@ -493,7 +380,5 @@
 	__cfadbg_print_safe(runtime_core, "Kernel : core %p finished running thread %p\n", this, thrd_dst);
 
-	#if !defined(__CFA_NO_STATISTICS__)
-		__tls_stats()->ready.threads.threads--;
-	#endif
+	__STATS__(true, ready.threads.threads--; )
 
 	/* paranoid */ verify( ! __preemption_enabled() );
@@ -506,5 +391,5 @@
 	thread$ * thrd_src = kernelTLS().this_thread;
 
-	__STATS( thrd_src->last_proc = kernelTLS().this_processor; )
+	__STATS_DEF( thrd_src->last_proc = kernelTLS().this_processor; )
 
 	// Run the thread on this processor
@@ -558,5 +443,5 @@
 	// Dereference the thread now because once we push it, there is not guaranteed it's still valid.
 	struct cluster * cl = thrd->curr_cluster;
-	__STATS(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
+	__STATS_DEF(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )
 
 	// push the thread to the cluster ready-queue
@@ -609,12 +494,17 @@
 
 	ready_schedule_lock();
-		thread$ * thrd;
-		for(25) {
-			thrd = pop_slow( this );
-			if(thrd) goto RET;
-		}
-		thrd = pop_search( this );
-
-		RET:
+		thread$ * thrd = pop_slow( this );
+	ready_schedule_unlock();
+
+	/* paranoid */ verify( ! __preemption_enabled() );
+	return thrd;
+}
+
+// KERNEL ONLY
+static inline thread$ * __next_thread_search(cluster * this) with( *this ) {
+	/* paranoid */ verify( ! __preemption_enabled() );
+
+	ready_schedule_lock();
+		thread$ * thrd = pop_search( this );
 	ready_schedule_unlock();
 
@@ -732,30 +622,46 @@
 // Wake a thread from the front if there are any
 static void __wake_one(cluster * this) {
+	eventfd_t val;
+
 	/* paranoid */ verify( ! __preemption_enabled() );
 	/* paranoid */ verify( ready_schedule_islocked() );
 
 	// Check if there is a sleeping processor
-	// int fd = __atomic_load_n(&this->procs.fd, __ATOMIC_SEQ_CST);
-	int fd = 0;
-	if( __atomic_load_n(&this->procs.fd, __ATOMIC_SEQ_CST) != 0 ) {
-		fd = __atomic_exchange_n(&this->procs.fd, 0, __ATOMIC_RELAXED);
-	}
-
-	// If no one is sleeping, we are done
-	if( fd == 0 ) return;
-
-	// We found a processor, wake it up
-	eventfd_t val;
-	val = 1;
-	eventfd_write( fd, val );
-
-	#if !defined(__CFA_NO_STATISTICS__)
-		if( kernelTLS().this_stats ) {
-			__tls_stats()->ready.sleep.wakes++;
-		}
-		else {
-			__atomic_fetch_add(&this->stats->ready.sleep.wakes, 1, __ATOMIC_RELAXED);
-		}
-	#endif
+	struct __fd_waitctx * fdp = __atomic_load_n(&this->procs.fdw, __ATOMIC_SEQ_CST);
+
+	// If no one is sleeping: we are done
+	if( fdp == 0p ) return;
+
+	int fd = 1;
+	if( __atomic_load_n(&fdp->fd, __ATOMIC_SEQ_CST) != 1 ) {
+		fd = __atomic_exchange_n(&fdp->fd, 1, __ATOMIC_RELAXED);
+	}
+
+	switch(fd) {
+	case 0:
+		// If the processor isn't ready to sleep then the exchange will already wake it up
+		#if !defined(__CFA_NO_STATISTICS__)
+			if( kernelTLS().this_stats ) { __tls_stats()->ready.sleep.early++;
+			} else { __atomic_fetch_add(&this->stats->ready.sleep.early, 1, __ATOMIC_RELAXED); }
+		#endif
+		break;
+	case 1:
+		// If someone else already said they will wake them: we are done
+		#if !defined(__CFA_NO_STATISTICS__)
+			if( kernelTLS().this_stats ) { __tls_stats()->ready.sleep.seen++;
+			} else { __atomic_fetch_add(&this->stats->ready.sleep.seen, 1, __ATOMIC_RELAXED); }
+		#endif
+		break;
+	default:
+		// If the processor was ready to sleep, we need to wake it up with an actual write
+		val = 1;
+		eventfd_write( fd, val );
+
+		#if !defined(__CFA_NO_STATISTICS__)
+			if( kernelTLS().this_stats ) { __tls_stats()->ready.sleep.wakes++;
+			} else { __atomic_fetch_add(&this->stats->ready.sleep.wakes, 1, __ATOMIC_RELAXED); }
+		#endif
+		break;
+	}
 
 	/* paranoid */ verify( ready_schedule_islocked() );
@@ -770,4 +676,6 @@
 
 	__cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this);
+
+	this->idle_wctx.fd = 1;
 
 	eventfd_t val;
@@ -779,4 +687,19 @@
 
 static void idle_sleep(processor * this, io_future_t & future, iovec & iov) {
+	// Tell everyone we are ready to go do sleep
+	for() {
+		int expected = this->idle_wctx.fd;
+
+		// Someone already told us to wake-up! No time for a nap.
+		if(expected == 1) { return; }
+
+		// Try to mark that we are going to sleep
+		if(__atomic_compare_exchange_n(&this->idle_wctx.fd, &expected, this->idle_fd, false,  __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) {
+			// Every one agreed, taking a nap
+			break;
+		}
+	}
+
+
 	#if !defined(CFA_WITH_IO_URING_IDLE)
 		#if !defined(__CFA_NO_STATISTICS__)
@@ -825,4 +748,8 @@
 
 static bool mark_idle(__cluster_proc_list & this, processor & proc) {
+	__STATS__(true, ready.sleep.halts++; )
+
+	proc.idle_wctx.fd = 0;
+
 	/* paranoid */ verify( ! __preemption_enabled() );
 	if(!try_lock( this )) return false;
@@ -832,5 +759,5 @@
 		insert_first(this.idles, proc);
 
-		__atomic_store_n(&this.fd, proc.idle_fd, __ATOMIC_SEQ_CST);
+		__atomic_store_n(&this.fdw, &proc.idle_wctx, __ATOMIC_SEQ_CST);
 	unlock( this );
 	/* paranoid */ verify( ! __preemption_enabled() );
@@ -848,7 +775,7 @@
 
 		{
-			int fd = 0;
-			if(!this.idles`isEmpty) fd = this.idles`first.idle_fd;
-			__atomic_store_n(&this.fd, fd, __ATOMIC_SEQ_CST);
+			struct __fd_waitctx * wctx = 0;
+			if(!this.idles`isEmpty) wctx = &this.idles`first.idle_wctx;
+			__atomic_store_n(&this.fdw, wctx, __ATOMIC_SEQ_CST);
 		}
 
@@ -914,11 +841,7 @@
 		unsigned tail = *ctx->cq.tail;
 		if(head == tail) return false;
-		#if OLD_MAIN
-			ready_schedule_lock();
-			ret = __cfa_io_drain( proc );
-			ready_schedule_unlock();
-		#else
-			ret = __cfa_io_drain( proc );
-		#endif
+		ready_schedule_lock();
+		ret = __cfa_io_drain( proc );
+		ready_schedule_unlock();
 	#endif
 	return ret;
Index: libcfa/src/concurrency/kernel.hfa
===================================================================
--- libcfa/src/concurrency/kernel.hfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/kernel.hfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -53,4 +53,9 @@
 coroutine processorCtx_t {
 	struct processor * proc;
+};
+
+
+struct __fd_waitctx {
+	volatile int fd;
 };
 
@@ -101,4 +106,7 @@
 	int idle_fd;
 
+	// Idle waitctx
+	struct __fd_waitctx idle_wctx;
+
 	// Termination synchronisation (user semaphore)
 	oneshot terminated;
@@ -207,5 +215,5 @@
 
 	// FD to use to wake a processor
-	volatile int fd;
+	struct __fd_waitctx * volatile fdw;
 
 	// Total number of processors
Index: libcfa/src/concurrency/kernel/fwd.hfa
===================================================================
--- libcfa/src/concurrency/kernel/fwd.hfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/kernel/fwd.hfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -396,6 +396,18 @@
 				if( !(in_kernel) ) enable_interrupts(); \
 			}
+			#if defined(CFA_HAVE_LINUX_IO_URING_H)
+				#define __IO_STATS__(in_kernel, ...) { \
+					if( !(in_kernel) ) disable_interrupts(); \
+					with( *__tls_stats() ) { \
+						__VA_ARGS__ \
+					} \
+					if( !(in_kernel) ) enable_interrupts(); \
+				}
+			#else
+				#define __IO_STATS__(in_kernel, ...)
+			#endif
 		#else
 			#define __STATS__(in_kernel, ...)
+			#define __IO_STATS__(in_kernel, ...)
 		#endif
 	}
Index: libcfa/src/concurrency/kernel/startup.cfa
===================================================================
--- libcfa/src/concurrency/kernel/startup.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/kernel/startup.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -537,4 +537,11 @@
 	}
 
+	this.idle_wctx.fd = 0;
+
+	// I'm assuming these two are reserved for standard input and output
+	// so I'm using them as sentinels with idle_wctx.
+	/* paranoid */ verify( this.idle_fd != 0 );
+	/* paranoid */ verify( this.idle_fd != 1 );
+
 	#if !defined(__CFA_NO_STATISTICS__)
 		print_stats = 0;
@@ -590,5 +597,5 @@
 // Cluster
 static void ?{}(__cluster_proc_list & this) {
-	this.fd    = 0;
+	this.fdw   = 0p;
 	this.idle  = 0;
 	this.total = 0;
Index: libcfa/src/concurrency/mutex_stmt.hfa
===================================================================
--- libcfa/src/concurrency/mutex_stmt.hfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/mutex_stmt.hfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -38,4 +38,17 @@
     }
 
+    struct scoped_lock {
+        L * internal_lock;
+    };
+
+    static inline void ?{}( scoped_lock(L) & this, L & internal_lock ) {
+        this.internal_lock = &internal_lock;
+        lock(internal_lock);
+    }
+    
+    static inline void ^?{}( scoped_lock(L) & this ) with(this) {
+        unlock(*internal_lock);
+    }
+
     static inline L * __get_ptr( L & this ) {
         return &this;
Index: libcfa/src/concurrency/preemption.cfa
===================================================================
--- libcfa/src/concurrency/preemption.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/preemption.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -251,7 +251,18 @@
 	bool enabled = __cfaabi_tls.preemption_state.enabled;
 
+	// Check if there is a pending preemption
+	processor   * proc = __cfaabi_tls.this_processor;
+	bool pending = proc ? proc->pending_preemption : false;
+	if( enabled && pending ) proc->pending_preemption = false;
+
 	// create a assembler label after
 	// marked as clobber all to avoid movement
 	__cfaasm_label(check, after);
+
+	// If we can preempt and there is a pending one
+	// this is a good time to yield
+	if( enabled && pending ) {
+		force_yield( __POLL_PREEMPTION );
+	}
 	return enabled;
 }
@@ -282,4 +293,6 @@
 	// marked as clobber all to avoid movement
 	__cfaasm_label(get, after);
+
+	// This is used everywhere, to avoid cost, we DO NOT poll pending preemption
 	return val;
 }
@@ -358,5 +371,5 @@
 	if(!ready) { abort("Preemption should be ready"); }
 
-	__cfaasm_label(debug, before);
+	// __cfaasm_label(debug, before);
 
 		sigset_t oldset;
@@ -377,5 +390,5 @@
 		if(ret == 1) { abort("ERROR SIGTERM is disabled"); }
 
-	__cfaasm_label(debug, after);
+	// __cfaasm_label(debug, after);
 }
 
@@ -548,5 +561,5 @@
 	__cfaasm_label( check  );
 	__cfaasm_label( dsable );
-	__cfaasm_label( debug  );
+	// __cfaasm_label( debug  );
 
 	// Check if preemption is safe
@@ -555,5 +568,5 @@
 	if( __cfaasm_in( ip, check  ) ) { ready = false; goto EXIT; };
 	if( __cfaasm_in( ip, dsable ) ) { ready = false; goto EXIT; };
-	if( __cfaasm_in( ip, debug  ) ) { ready = false; goto EXIT; };
+	// if( __cfaasm_in( ip, debug  ) ) { ready = false; goto EXIT; };
 	if( !__cfaabi_tls.preemption_state.enabled) { ready = false; goto EXIT; };
 	if( __cfaabi_tls.preemption_state.in_progress ) { ready = false; goto EXIT; };
@@ -661,5 +674,10 @@
 
 	// Check if it is safe to preempt here
-	if( !preemption_ready( ip ) ) { return; }
+	if( !preemption_ready( ip ) ) {
+		#if !defined(__CFA_NO_STATISTICS__)
+			__cfaabi_tls.this_stats->ready.threads.preempt.rllfwd++;
+		#endif
+		return;
+	}
 
 	__cfaabi_dbg_print_buffer_decl( " KERNEL: preempting core %p (%p @ %p).\n", __cfaabi_tls.this_processor, __cfaabi_tls.this_thread, (void *)(cxt->uc_mcontext.CFA_REG_IP) );
@@ -680,4 +698,8 @@
 
 	// Preemption can occur here
+
+	#if !defined(__CFA_NO_STATISTICS__)
+		__cfaabi_tls.this_stats->ready.threads.preempt.yield++;
+	#endif
 
 	force_yield( __ALARM_PREEMPTION ); // Do the actual __cfactx_switch
Index: libcfa/src/concurrency/stats.cfa
===================================================================
--- libcfa/src/concurrency/stats.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/stats.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -29,7 +29,11 @@
 		stats->ready.threads.threads   = 0;
 		stats->ready.threads.cthreads  = 0;
+		stats->ready.threads.preempt.yield  = 0;
+		stats->ready.threads.preempt.rllfwd = 0;
 		stats->ready.sleep.halts   = 0;
 		stats->ready.sleep.cancels = 0;
+		stats->ready.sleep.early   = 0;
 		stats->ready.sleep.wakes   = 0;
+		stats->ready.sleep.seen    = 0;
 		stats->ready.sleep.exits   = 0;
 
@@ -43,4 +47,8 @@
 			stats->io.submit.slow       = 0;
 			stats->io.flush.external    = 0;
+			stats->io.flush.dirty       = 0;
+			stats->io.flush.full        = 0;
+			stats->io.flush.idle        = 0;
+			stats->io.flush.eager       = 0;
 			stats->io.calls.flush       = 0;
 			stats->io.calls.submitted   = 0;
@@ -71,26 +79,30 @@
 
 	void __tally_stats( struct __stats_t * cltr, struct __stats_t * proc ) {
-		tally_one( &cltr->ready.push.local.attempt, &proc->ready.push.local.attempt );
-		tally_one( &cltr->ready.push.local.success, &proc->ready.push.local.success );
-		tally_one( &cltr->ready.push.share.attempt, &proc->ready.push.share.attempt );
-		tally_one( &cltr->ready.push.share.success, &proc->ready.push.share.success );
-		tally_one( &cltr->ready.push.extrn.attempt, &proc->ready.push.extrn.attempt );
-		tally_one( &cltr->ready.push.extrn.success, &proc->ready.push.extrn.success );
-		tally_one( &cltr->ready.pop.local .attempt, &proc->ready.pop.local .attempt );
-		tally_one( &cltr->ready.pop.local .success, &proc->ready.pop.local .success );
-		tally_one( &cltr->ready.pop.help  .attempt, &proc->ready.pop.help  .attempt );
-		tally_one( &cltr->ready.pop.help  .success, &proc->ready.pop.help  .success );
-		tally_one( &cltr->ready.pop.steal .attempt, &proc->ready.pop.steal .attempt );
-		tally_one( &cltr->ready.pop.steal .success, &proc->ready.pop.steal .success );
-		tally_one( &cltr->ready.pop.search.attempt, &proc->ready.pop.search.attempt );
-		tally_one( &cltr->ready.pop.search.success, &proc->ready.pop.search.success );
-		tally_one( &cltr->ready.threads.migration , &proc->ready.threads.migration  );
-		tally_one( &cltr->ready.threads.extunpark , &proc->ready.threads.extunpark  );
-		tally_one( &cltr->ready.threads.threads   , &proc->ready.threads.threads    );
-		tally_one( &cltr->ready.threads.cthreads  , &proc->ready.threads.cthreads   );
-		tally_one( &cltr->ready.sleep.halts       , &proc->ready.sleep.halts        );
-		tally_one( &cltr->ready.sleep.cancels     , &proc->ready.sleep.cancels      );
-		tally_one( &cltr->ready.sleep.wakes       , &proc->ready.sleep.wakes        );
-		tally_one( &cltr->ready.sleep.exits       , &proc->ready.sleep.exits        );
+		tally_one( &cltr->ready.push.local.attempt    , &proc->ready.push.local.attempt     );
+		tally_one( &cltr->ready.push.local.success    , &proc->ready.push.local.success     );
+		tally_one( &cltr->ready.push.share.attempt    , &proc->ready.push.share.attempt     );
+		tally_one( &cltr->ready.push.share.success    , &proc->ready.push.share.success     );
+		tally_one( &cltr->ready.push.extrn.attempt    , &proc->ready.push.extrn.attempt     );
+		tally_one( &cltr->ready.push.extrn.success    , &proc->ready.push.extrn.success     );
+		tally_one( &cltr->ready.pop.local .attempt    , &proc->ready.pop.local .attempt     );
+		tally_one( &cltr->ready.pop.local .success    , &proc->ready.pop.local .success     );
+		tally_one( &cltr->ready.pop.help  .attempt    , &proc->ready.pop.help  .attempt     );
+		tally_one( &cltr->ready.pop.help  .success    , &proc->ready.pop.help  .success     );
+		tally_one( &cltr->ready.pop.steal .attempt    , &proc->ready.pop.steal .attempt     );
+		tally_one( &cltr->ready.pop.steal .success    , &proc->ready.pop.steal .success     );
+		tally_one( &cltr->ready.pop.search.attempt    , &proc->ready.pop.search.attempt     );
+		tally_one( &cltr->ready.pop.search.success    , &proc->ready.pop.search.success     );
+		tally_one( &cltr->ready.threads.migration     , &proc->ready.threads.migration      );
+		tally_one( &cltr->ready.threads.extunpark     , &proc->ready.threads.extunpark      );
+		tally_one( &cltr->ready.threads.threads       , &proc->ready.threads.threads        );
+		tally_one( &cltr->ready.threads.cthreads      , &proc->ready.threads.cthreads       );
+		tally_one( &cltr->ready.threads.preempt.yield , &proc->ready.threads.preempt.yield  );
+		tally_one( &cltr->ready.threads.preempt.rllfwd, &proc->ready.threads.preempt.rllfwd );
+		tally_one( &cltr->ready.sleep.halts           , &proc->ready.sleep.halts            );
+		tally_one( &cltr->ready.sleep.cancels         , &proc->ready.sleep.cancels          );
+		tally_one( &cltr->ready.sleep.early           , &proc->ready.sleep.early            );
+		tally_one( &cltr->ready.sleep.wakes           , &proc->ready.sleep.wakes            );
+		tally_one( &cltr->ready.sleep.seen            , &proc->ready.sleep.wakes            );
+		tally_one( &cltr->ready.sleep.exits           , &proc->ready.sleep.exits            );
 
 		#if defined(CFA_HAVE_LINUX_IO_URING_H)
@@ -103,4 +115,8 @@
 			tally_one( &cltr->io.submit.slow      , &proc->io.submit.slow       );
 			tally_one( &cltr->io.flush.external   , &proc->io.flush.external    );
+			tally_one( &cltr->io.flush.dirty      , &proc->io.flush.dirty       );
+			tally_one( &cltr->io.flush.full       , &proc->io.flush.full        );
+			tally_one( &cltr->io.flush.idle       , &proc->io.flush.idle        );
+			tally_one( &cltr->io.flush.eager      , &proc->io.flush.eager       );
 			tally_one( &cltr->io.calls.flush      , &proc->io.calls.flush       );
 			tally_one( &cltr->io.calls.submitted  , &proc->io.calls.submitted   );
@@ -153,5 +169,8 @@
 			     | " (" | eng3(ready.pop.search.attempt) | " try)";
 
-			sstr | "- Idle Slp : " | eng3(ready.sleep.halts) | "halt," | eng3(ready.sleep.cancels) | "cancel," | eng3(ready.sleep.wakes) | "wake," | eng3(ready.sleep.exits) | "exit";
+			sstr | "- Idle Slp : " | eng3(ready.sleep.halts) | "halt," | eng3(ready.sleep.cancels) | "cancel,"
+			     | eng3(ready.sleep.wakes + ready.sleep.early) | '(' | eng3(ready.sleep.early) | ',' | eng3(ready.sleep.seen) | ')' | " wake(early, seen),"
+			     | eng3(ready.sleep.exits) | "exit";
+			sstr | "- Preemption : " | eng3(ready.threads.preempt.yield) | "yields," | eng3(ready.threads.preempt.rllfwd) | "delayed";
 			sstr | nl;
 		}
@@ -178,13 +197,14 @@
 				if(io.alloc.fail || io.alloc.revoke || io.alloc.block)
 					sstr | "-     failures      : " | eng3(io.alloc.fail) | "oom, " | eng3(io.alloc.revoke) | "rvk, " | eng3(io.alloc.block) | "blk";
-				if(io.flush.external)
-					sstr | "- flush external    : " | eng3(io.flush.external);
+				// if(io.flush.external)
+				// 	sstr | "- flush external    : " | eng3(io.flush.external);
 
 				double avgsubs = ((double)io.calls.submitted) / io.calls.flush;
 				double avgcomp = ((double)io.calls.completed) / io.calls.drain;
 				sstr | "- syscll : "
-				     |   " sub " | eng3(io.calls.flush) | "/" | eng3(io.calls.submitted) | "(" | ws(3, 3, avgsubs) | "/flush)"
-				     | " - cmp " | eng3(io.calls.drain) | "/" | eng3(io.calls.completed) | "(" | ws(3, 3, avgcomp) | "/drain)"
+				     |   " sub " | eng3(io.calls.submitted) | "/" | eng3(io.calls.flush) | "(" | ws(3, 3, avgsubs) | "/flush)"
+				     | " - cmp " | eng3(io.calls.completed) | "/" | eng3(io.calls.drain) | "(" | ws(3, 3, avgcomp) | "/drain)"
 				     | " - " | eng3(io.calls.errors.busy) | " EBUSY";
+				sstr | " - sub: " | eng3(io.flush.full) | "full, " | eng3(io.flush.dirty) | "drty, " | eng3(io.flush.idle) | "idle, " | eng3(io.flush.eager) | "eagr, " | eng3(io.flush.external) | "ext";
 				sstr | "- ops blk: "
 				     |   " sk rd: " | eng3(io.ops.sockread)  | "epll: " | eng3(io.ops.epllread)
Index: libcfa/src/concurrency/stats.hfa
===================================================================
--- libcfa/src/concurrency/stats.hfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/concurrency/stats.hfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -65,9 +65,15 @@
 			volatile  int64_t threads;  // number of threads in the system, includes only local change
 			volatile  int64_t cthreads; // number of threads in the system, includes only local change
+			struct {
+				volatile uint64_t yield;
+				volatile uint64_t rllfwd;
+			} preempt;
 		} threads;
 		struct {
 			volatile uint64_t halts;
 			volatile uint64_t cancels;
+			volatile uint64_t early;
 			volatile uint64_t wakes;
+			volatile uint64_t seen;
 			volatile uint64_t exits;
 		} sleep;
@@ -89,4 +95,8 @@
 			struct {
 				volatile uint64_t external;
+				volatile uint64_t dirty;
+				volatile uint64_t full;
+				volatile uint64_t idle;
+				volatile uint64_t eager;
 			} flush;
 			struct {
Index: libcfa/src/stdhdr/pthread.h
===================================================================
--- libcfa/src/stdhdr/pthread.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/stdhdr/pthread.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,15 +10,34 @@
 // Created On       : Wed Jun 16 13:39:06 2021
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Jun 16 13:39:42 2021
-// Update Count     : 1
+// Last Modified On : Thu Feb  3 21:53:26 2022
+// Update Count     : 13
 // 
 
+// pthread.h and setjmp.h cannot agree on the type of __sigsetjmp:
+//
+//   extern int __sigsetjmp (struct __jmp_buf_tag *__env, int __savemask) __attribute__ ((__nothrow__));
+//   extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __attribute__ ((__nothrow__));
+//
+// With -Wall, gcc-11 warns about the disagreement unless the CPP directive
+//
+//    # 1 "/usr/include/pthread.h" 1 3 4
+//
+// appears, which appears to be witchcraft. Unfortunately, this directive is removed by the CFA preprocessor, so the
+// batchtest fails because of the spurious warning message. Hence, the warning is elided.
+
 extern "C" {
+#if defined(__GNUC__) && __GNUC__ == 11
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Warray-parameter"
+#endif // defined(__GNUC__) && __GNUC__ == 11
+
 #include_next <pthread.h>								// has internal check for multiple expansion
+
+#if defined(__GNUC__) && __GNUC__ == 11
+	#pragma GCC diagnostic pop
+#endif // defined(__GNUC__) && __GNUC__ == 11
 } // extern "C"
 
 // Local Variables: //
-// tab-width: 4 //
 // mode: c++ //
-// compile-command: "make install" //
 // End: //
Index: libcfa/src/stdhdr/setjmp.h
===================================================================
--- libcfa/src/stdhdr/setjmp.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ libcfa/src/stdhdr/setjmp.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,15 +10,34 @@
 // Created On       : Mon Jul  4 23:25:26 2016
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Tue Jul  5 20:38:33 2016
-// Update Count     : 12
+// Last Modified On : Thu Feb  3 21:53:28 2022
+// Update Count     : 18
 // 
 
+// pthread.h and setjmp.h cannot agree on the type of __sigsetjmp:
+//
+//   extern int __sigsetjmp (struct __jmp_buf_tag *__env, int __savemask) __attribute__ ((__nothrow__));
+//   extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __attribute__ ((__nothrow__));
+//
+// With -Wall, gcc-11 warns about the disagreement unless the CPP directive
+//
+//    # 1 "/usr/include/pthread.h" 1 3 4
+//
+// appears, which appears to be witchcraft. Unfortunately, this directive is removed by the CFA preprocessor, so the
+// batchtest fails because of the spurious warning message. Hence, the warning is elided.
+
 extern "C" {
+#if defined(__GNUC__) && __GNUC__ == 11
+	#pragma GCC diagnostic push
+	#pragma GCC diagnostic ignored "-Warray-parameter"
+#endif // defined(__GNUC__) && __GNUC__ == 11
+
 #include_next <setjmp.h>								// has internal check for multiple expansion
+
+#if defined(__GNUC__) && __GNUC__ == 11
+	#pragma GCC diagnostic pop
+#endif // defined(__GNUC__) && __GNUC__ == 11
 } // extern "C"
 
 // Local Variables: //
-// tab-width: 4 //
 // mode: c++ //
-// compile-command: "make install" //
 // End: //
Index: src/AST/Convert.cpp
===================================================================
--- src/AST/Convert.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Convert.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Thierry Delisle
 // Created On       : Thu May 09 15::37::05 2019
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jul 14 16:15:00 2021
-// Update Count     : 37
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Feb  2 13:19:22 2022
+// Update Count     : 41
 //
 
@@ -393,6 +393,6 @@
 		auto stmt = new IfStmt(
 			get<Expression>().accept1( node->cond ),
-			get<Statement>().accept1( node->thenPart ),
-			get<Statement>().accept1( node->elsePart ),
+			get<Statement>().accept1( node->then ),
+			get<Statement>().accept1( node->else_ ),
 			get<Statement>().acceptL( node->inits )
 		);
@@ -419,10 +419,11 @@
 	}
 
-	const ast::Stmt * visit( const ast::WhileStmt * node ) override final {
+	const ast::Stmt * visit( const ast::WhileDoStmt * node ) override final {
 		if ( inCache( node ) ) return nullptr;
 		auto inits = get<Statement>().acceptL( node->inits );
-		auto stmt = new WhileStmt(
+		auto stmt = new WhileDoStmt(
 			get<Expression>().accept1( node->cond ),
 			get<Statement>().accept1( node->body ),
+			get<Statement>().accept1( node->else_ ),
 			inits,
 			node->isDoWhile
@@ -437,5 +438,6 @@
 			get<Expression>().accept1( node->cond ),
 			get<Expression>().accept1( node->inc ),
-			get<Statement>().accept1( node->body )
+			get<Statement>().accept1( node->body ),
+			get<Statement>().accept1( node->else_ )
 		);
 		return stmtPostamble( stmt, node );
@@ -1872,6 +1874,6 @@
 			old->location,
 			GET_ACCEPT_1(condition, Expr),
-			GET_ACCEPT_1(thenPart, Stmt),
-			GET_ACCEPT_1(elsePart, Stmt),
+			GET_ACCEPT_1(then, Stmt),
+			GET_ACCEPT_1(else_, Stmt),
 			GET_ACCEPT_V(initialization, Stmt),
 			GET_LABELS_V(old->labels)
@@ -1902,10 +1904,11 @@
 	}
 
-	virtual void visit( const WhileStmt * old ) override final {
+	virtual void visit( const WhileDoStmt * old ) override final {
 		if ( inCache( old ) ) return;
-		this->node = new ast::WhileStmt(
+		this->node = new ast::WhileDoStmt(
 			old->location,
 			GET_ACCEPT_1(condition, Expr),
 			GET_ACCEPT_1(body, Stmt),
+			GET_ACCEPT_1(else_, Stmt),
 			GET_ACCEPT_V(initialization, Stmt),
 			old->isDoWhile,
@@ -1923,4 +1926,5 @@
 			GET_ACCEPT_1(increment, Expr),
 			GET_ACCEPT_1(body, Stmt),
+			GET_ACCEPT_1(else_, Stmt),
 			GET_LABELS_V(old->labels)
 		);
Index: src/AST/Copy.hpp
===================================================================
--- src/AST/Copy.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Copy.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Wed Jul 10 16:13:00 2019
 // Last Modified By : Andrew Beach
-// Last Modified On : Thr Nov 11  9:22:00 2021
-// Update Count     : 2
+// Last Modified On : Wed Dec 15 11:07:00 2021
+// Update Count     : 3
 //
 
@@ -52,4 +52,14 @@
 Node * deepCopy<Node>( const Node * localRoot );
 
+template<typename node_t, enum Node::ref_type ref_t>
+node_t * shallowCopy( const ptr_base<node_t, ref_t> & localRoot ) {
+	return shallowCopy( localRoot.get() );
+}
+
+template<typename node_t, enum Node::ref_type ref_t>
+node_t * deepCopy( const ptr_base<node_t, ref_t> & localRoot ) {
+	return deepCopy( localRoot.get() );
+}
+
 }
 
Index: src/AST/Fwd.hpp
===================================================================
--- src/AST/Fwd.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Fwd.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Wed May  8 16:05:00 2019
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:37:39 2021
-// Update Count     : 4
+// Last Modified On : Tue Feb  1 09:08:33 2022
+// Update Count     : 5
 //
 
@@ -44,5 +44,5 @@
 class DirectiveStmt;
 class IfStmt;
-class WhileStmt;
+class WhileDoStmt;
 class ForStmt;
 class SwitchStmt;
Index: src/AST/Node.cpp
===================================================================
--- src/AST/Node.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Node.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Thu May 16 14:16:00 2019
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:25:06 2021
-// Update Count     : 2
+// Last Modified On : Tue Feb  1 09:09:39 2022
+// Update Count     : 3
 //
 
@@ -146,6 +146,6 @@
 template class ast::ptr_base< ast::IfStmt, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::IfStmt, ast::Node::ref_type::strong >;
-template class ast::ptr_base< ast::WhileStmt, ast::Node::ref_type::weak >;
-template class ast::ptr_base< ast::WhileStmt, ast::Node::ref_type::strong >;
+template class ast::ptr_base< ast::WhileDoStmt, ast::Node::ref_type::weak >;
+template class ast::ptr_base< ast::WhileDoStmt, ast::Node::ref_type::strong >;
 template class ast::ptr_base< ast::ForStmt, ast::Node::ref_type::weak >;
 template class ast::ptr_base< ast::ForStmt, ast::Node::ref_type::strong >;
Index: src/AST/Node.hpp
===================================================================
--- src/AST/Node.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Node.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -188,4 +188,9 @@
 	}
 
+	ptr_base & operator=( const node_t * node ) {
+		assign( node );
+		return *this;
+	}
+
 	template<typename o_node_t>
 	ptr_base & operator=( const o_node_t * node ) {
Index: src/AST/Pass.hpp
===================================================================
--- src/AST/Pass.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Pass.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -146,5 +146,5 @@
 	const ast::Stmt *             visit( const ast::DirectiveStmt        * ) override final;
 	const ast::Stmt *             visit( const ast::IfStmt               * ) override final;
-	const ast::Stmt *             visit( const ast::WhileStmt            * ) override final;
+	const ast::Stmt *             visit( const ast::WhileDoStmt          * ) override final;
 	const ast::Stmt *             visit( const ast::ForStmt              * ) override final;
 	const ast::Stmt *             visit( const ast::SwitchStmt           * ) override final;
Index: src/AST/Pass.impl.hpp
===================================================================
--- src/AST/Pass.impl.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Pass.impl.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -33,11 +33,4 @@
 	/* call the implementation of the previsit of this pass */ \
 	__pass::previsit( core, node, 0 );
-
-#define VISIT( code... ) \
-	/* if this node should visit its children */ \
-	if ( __visit_children() ) { \
-		/* visit the children */ \
-		code \
-	}
 
 #define VISIT_END( type, node ) \
@@ -452,5 +445,5 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		{
 			guard_symtab guard { *this };
@@ -460,5 +453,5 @@
 		maybe_accept( node, &ObjectDecl::bitfieldWidth );
 		maybe_accept( node, &ObjectDecl::attributes    );
-	)
+	}
 
 	__pass::symtab::addId( core, 0, node );
@@ -475,5 +468,7 @@
 	__pass::symtab::addId( core, 0, node );
 
-	VISIT(maybe_accept( node, &FunctionDecl::withExprs );)
+	if ( __visit_children() ) {
+		maybe_accept( node, &FunctionDecl::withExprs );
+	}
 	{
 		// with clause introduces a level of scope (for the with expression members).
@@ -493,5 +488,5 @@
 			} };
 			__pass::symtab::addId( core, 0, func );
-			VISIT(
+			if ( __visit_children() ) {
 				// parameter declarations
 				maybe_accept( node, &FunctionDecl::params );
@@ -509,5 +504,5 @@
 				maybe_accept( node, &FunctionDecl::stmts );
 				maybe_accept( node, &FunctionDecl::attributes );
-			)
+			}
 		}
 	}
@@ -526,10 +521,10 @@
 	__pass::symtab::addStructFwd( core, 0, node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { * this };
 		maybe_accept( node, &StructDecl::params     );
 		maybe_accept( node, &StructDecl::members    );
 		maybe_accept( node, &StructDecl::attributes );
-	})
+	}
 
 	// this addition replaces the forward declaration
@@ -548,10 +543,10 @@
 	__pass::symtab::addUnionFwd( core, 0, node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { * this };
 		maybe_accept( node, &UnionDecl::params     );
 		maybe_accept( node, &UnionDecl::members    );
 		maybe_accept( node, &UnionDecl::attributes );
-	})
+	}
 
 	__pass::symtab::addUnion( core, 0, node );
@@ -568,10 +563,10 @@
 	__pass::symtab::addEnum( core, 0, node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		// unlike structs, traits, and unions, enums inject their members into the global scope
 		maybe_accept( node, &EnumDecl::params     );
 		maybe_accept( node, &EnumDecl::members    );
 		maybe_accept( node, &EnumDecl::attributes );
-	)
+	}
 
 	VISIT_END( Decl, node );
@@ -584,10 +579,10 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &TraitDecl::params     );
 		maybe_accept( node, &TraitDecl::members    );
 		maybe_accept( node, &TraitDecl::attributes );
-	})
+	}
 
 	__pass::symtab::addTrait( core, 0, node );
@@ -602,8 +597,8 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &TypeDecl::base   );
-	})
+	}
 
 	// see A NOTE ON THE ORDER OF TRAVERSAL, above
@@ -612,5 +607,5 @@
 	__pass::symtab::addType( core, 0, node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &TypeDecl::assertions );
 
@@ -619,5 +614,5 @@
 			maybe_accept( node, &TypeDecl::init );
 		}
-	)
+	}
 
 	VISIT_END( Decl, node );
@@ -630,12 +625,14 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &TypedefDecl::base   );
-	})
+	}
 
 	__pass::symtab::addType( core, 0, node );
 
-	VISIT( maybe_accept( node, &TypedefDecl::assertions ); )
+	if ( __visit_children() ) {
+		maybe_accept( node, &TypedefDecl::assertions );
+	}
 
 	VISIT_END( Decl, node );
@@ -648,7 +645,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &AsmDecl::stmt );
-	)
+	}
 
 	VISIT_END( AsmDecl, node );
@@ -661,7 +658,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &DirectiveDecl::stmt );
-	)
+	}
 
 	VISIT_END( DirectiveDecl, node );
@@ -674,8 +671,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &StaticAssertDecl::cond );
 		maybe_accept( node, &StaticAssertDecl::msg  );
-	)
+	}
 
 	VISIT_END( StaticAssertDecl, node );
@@ -687,5 +684,6 @@
 const ast::CompoundStmt * ast::Pass< core_t >::visit( const ast::CompoundStmt * node ) {
 	VISIT_START( node );
-	VISIT(
+
+	if ( __visit_children() ) {
 		// Do not enter (or leave) a new scope if atFunctionTop. Remember to save the result.
 		auto guard1 = makeFuncGuard( [this, enterScope = !this->atFunctionTop]() {
@@ -704,5 +702,6 @@
 		guard_scope guard3 { *this };
 		maybe_accept( node, &CompoundStmt::kids );
-	)
+	}
+
 	VISIT_END( CompoundStmt, node );
 }
@@ -714,7 +713,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ExprStmt::expr );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -727,10 +726,10 @@
 	VISIT_START( node )
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &AsmStmt::instruction );
 		maybe_accept( node, &AsmStmt::output      );
 		maybe_accept( node, &AsmStmt::input       );
 		maybe_accept( node, &AsmStmt::clobber     );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -752,12 +751,12 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		// if statements introduce a level of scope (for the initialization)
 		guard_symtab guard { *this };
 		maybe_accept( node, &IfStmt::inits    );
 		maybe_accept( node, &IfStmt::cond     );
-		maybe_accept_as_compound( node, &IfStmt::thenPart );
-		maybe_accept_as_compound( node, &IfStmt::elsePart );
-	})
+		maybe_accept_as_compound( node, &IfStmt::then );
+		maybe_accept_as_compound( node, &IfStmt::else_ );
+	}
 
 	VISIT_END( Stmt, node );
@@ -765,16 +764,16 @@
 
 //--------------------------------------------------------------------------
-// WhileStmt
-template< typename core_t >
-const ast::Stmt * ast::Pass< core_t >::visit( const ast::WhileStmt * node ) {
-	VISIT_START( node );
-
-	VISIT({
+// WhileDoStmt
+template< typename core_t >
+const ast::Stmt * ast::Pass< core_t >::visit( const ast::WhileDoStmt * node ) {
+	VISIT_START( node );
+
+	if ( __visit_children() ) {
 		// while statements introduce a level of scope (for the initialization)
 		guard_symtab guard { *this };
-		maybe_accept( node, &WhileStmt::inits );
-		maybe_accept( node, &WhileStmt::cond  );
-		maybe_accept_as_compound( node, &WhileStmt::body  );
-	})
+		maybe_accept( node, &WhileDoStmt::inits );
+		maybe_accept( node, &WhileDoStmt::cond  );
+		maybe_accept_as_compound( node, &WhileDoStmt::body  );
+	}
 
 	VISIT_END( Stmt, node );
@@ -787,5 +786,5 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		// for statements introduce a level of scope (for the initialization)
 		guard_symtab guard { *this };
@@ -795,5 +794,5 @@
 		maybe_accept( node, &ForStmt::inc   );
 		maybe_accept_as_compound( node, &ForStmt::body  );
-	})
+	}
 
 	VISIT_END( Stmt, node );
@@ -806,8 +805,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &SwitchStmt::cond  );
 		maybe_accept( node, &SwitchStmt::stmts );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -820,8 +819,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &CaseStmt::cond  );
 		maybe_accept( node, &CaseStmt::stmts );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -842,7 +841,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ReturnStmt::expr );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -855,8 +854,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ThrowStmt::expr   );
 		maybe_accept( node, &ThrowStmt::target );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -869,9 +868,9 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &TryStmt::body     );
 		maybe_accept( node, &TryStmt::handlers );
 		maybe_accept( node, &TryStmt::finally  );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -884,5 +883,5 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		// catch statements introduce a level of scope (for the caught exception)
 		guard_symtab guard { *this };
@@ -890,5 +889,5 @@
 		maybe_accept( node, &CatchStmt::cond );
 		maybe_accept_as_compound( node, &CatchStmt::body );
-	})
+	}
 
 	VISIT_END( Stmt, node );
@@ -901,7 +900,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &FinallyStmt::body );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -914,7 +913,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &SuspendStmt::then   );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -934,5 +933,5 @@
 		// }
 
-	VISIT({
+	if ( __visit_children() ) {
 		std::vector<WaitForStmt::Clause> new_clauses;
 		new_clauses.reserve( node->clauses.size() );
@@ -965,5 +964,5 @@
 			node = n;
 		}
-	})
+	}
 
 	#define maybe_accept(field) \
@@ -977,5 +976,5 @@
 		}
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( timeout.time );
 		maybe_accept( timeout.stmt );
@@ -983,5 +982,5 @@
 		maybe_accept( orElse.stmt  );
 		maybe_accept( orElse.cond  );
-	)
+	}
 
 	#undef maybe_accept
@@ -996,5 +995,5 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &WithStmt::exprs );
 		{
@@ -1004,5 +1003,6 @@
 			maybe_accept( node, &WithStmt::stmt );
 		}
-	)
+	}
+
 	VISIT_END( Stmt, node );
 }
@@ -1022,7 +1022,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &DeclStmt::decl );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -1037,7 +1037,7 @@
 	// For now this isn't visited, it is unclear if this causes problem
 	// if all tests are known to pass, remove this code
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ImplicitCtorDtorStmt::callStmt );
-	)
+	}
 
 	VISIT_END( Stmt, node );
@@ -1050,10 +1050,10 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		// mutex statements introduce a level of scope (for the initialization)
 		guard_symtab guard { *this };
 		maybe_accept( node, &MutexStmt::stmt );
 		maybe_accept( node, &MutexStmt::mutexObjs );
-	})
+	}
 
 	VISIT_END( Stmt, node );
@@ -1066,5 +1066,5 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		{
 			guard_symtab guard { *this };
@@ -1073,5 +1073,5 @@
 		maybe_accept( node, &ApplicationExpr::func );
 		maybe_accept( node, &ApplicationExpr::args );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1084,5 +1084,5 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		{
 			guard_symtab guard { *this };
@@ -1091,5 +1091,5 @@
 
 		maybe_accept( node, &UntypedExpr::args );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1102,8 +1102,8 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &NameExpr::result );
-	})
+	}
 
 	VISIT_END( Expr, node );
@@ -1116,10 +1116,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &CastExpr::result );
 		}
 		maybe_accept( node, &CastExpr::arg );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1132,10 +1133,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &KeywordCastExpr::result );
 		}
 		maybe_accept( node, &KeywordCastExpr::arg );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1148,10 +1150,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &VirtualCastExpr::result );
 		}
 		maybe_accept( node, &VirtualCastExpr::arg );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1164,10 +1167,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &AddressExpr::result );
 		}
 		maybe_accept( node, &AddressExpr::arg );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1180,8 +1184,8 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &LabelAddressExpr::result );
-	})
+	}
 
 	VISIT_END( Expr, node );
@@ -1194,5 +1198,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedMemberExpr::result );
@@ -1200,5 +1205,5 @@
 		maybe_accept( node, &UntypedMemberExpr::aggregate );
 		maybe_accept( node, &UntypedMemberExpr::member    );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1211,10 +1216,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &MemberExpr::result );
 		}
 		maybe_accept( node, &MemberExpr::aggregate );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1227,8 +1233,8 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &VariableExpr::result );
-	})
+	}
 
 	VISIT_END( Expr, node );
@@ -1241,8 +1247,8 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &ConstantExpr::result );
-	})
+	}
 
 	VISIT_END( Expr, node );
@@ -1255,5 +1261,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &SizeofExpr::result );
@@ -1264,5 +1271,5 @@
 			maybe_accept( node, &SizeofExpr::expr );
 		}
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1275,5 +1282,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &AlignofExpr::result );
@@ -1284,5 +1292,5 @@
 			maybe_accept( node, &AlignofExpr::expr );
 		}
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1295,10 +1303,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedOffsetofExpr::result );
 		}
 		maybe_accept( node, &UntypedOffsetofExpr::type   );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1311,10 +1320,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &OffsetofExpr::result );
 		}
 		maybe_accept( node, &OffsetofExpr::type   );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1327,10 +1337,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &OffsetPackExpr::result );
 		}
 		maybe_accept( node, &OffsetPackExpr::type   );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1343,5 +1354,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &LogicalExpr::result );
@@ -1349,5 +1361,5 @@
 		maybe_accept( node, &LogicalExpr::arg1 );
 		maybe_accept( node, &LogicalExpr::arg2 );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1360,5 +1372,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &ConditionalExpr::result );
@@ -1367,5 +1380,5 @@
 		maybe_accept( node, &ConditionalExpr::arg2 );
 		maybe_accept( node, &ConditionalExpr::arg3 );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1378,5 +1391,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &CommaExpr::result );
@@ -1384,5 +1398,5 @@
 		maybe_accept( node, &CommaExpr::arg1 );
 		maybe_accept( node, &CommaExpr::arg2 );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1395,10 +1409,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &TypeExpr::result );
 		}
 		maybe_accept( node, &TypeExpr::type );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1411,5 +1426,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &AsmExpr::result );
@@ -1417,5 +1433,5 @@
 		maybe_accept( node, &AsmExpr::constraint );
 		maybe_accept( node, &AsmExpr::operand    );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1428,10 +1444,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &ImplicitCopyCtorExpr::result );
 		}
 		maybe_accept( node, &ImplicitCopyCtorExpr::callExpr    );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1444,10 +1461,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &ConstructorExpr::result );
 		}
 		maybe_accept( node, &ConstructorExpr::callExpr );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1460,10 +1478,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &CompoundLiteralExpr::result );
 		}
 		maybe_accept( node, &CompoundLiteralExpr::init );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1476,5 +1495,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &RangeExpr::result );
@@ -1482,5 +1502,5 @@
 		maybe_accept( node, &RangeExpr::low    );
 		maybe_accept( node, &RangeExpr::high   );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1493,10 +1513,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedTupleExpr::result );
 		}
 		maybe_accept( node, &UntypedTupleExpr::exprs  );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1509,10 +1530,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &TupleExpr::result );
 		}
 		maybe_accept( node, &TupleExpr::exprs  );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1525,10 +1547,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &TupleIndexExpr::result );
 		}
 		maybe_accept( node, &TupleIndexExpr::tuple  );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1541,10 +1564,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &TupleAssignExpr::result );
 		}
 		maybe_accept( node, &TupleAssignExpr::stmtExpr );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1557,5 +1581,6 @@
 	VISIT_START( node );
 
-	VISIT(// don't want statements from outer CompoundStmts to be added to this StmtExpr
+	if ( __visit_children() ) {
+		// don't want statements from outer CompoundStmts to be added to this StmtExpr
 		// get the stmts that will need to be spliced in
 		auto stmts_before = __pass::stmtsToAddBefore( core, 0);
@@ -1574,5 +1599,5 @@
 		maybe_accept( node, &StmtExpr::returnDecls );
 		maybe_accept( node, &StmtExpr::dtors       );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1585,10 +1610,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &UniqueExpr::result );
 		}
 		maybe_accept( node, &UniqueExpr::expr   );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1601,5 +1627,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &UntypedInitExpr::result );
@@ -1607,5 +1634,5 @@
 		maybe_accept( node, &UntypedInitExpr::expr   );
 		// not currently visiting initAlts, but this doesn't matter since this node is only used in the resolver.
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1618,5 +1645,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &InitExpr::result );
@@ -1624,5 +1652,5 @@
 		maybe_accept( node, &InitExpr::expr   );
 		maybe_accept( node, &InitExpr::designation );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1635,5 +1663,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &DeletedExpr::result );
@@ -1641,5 +1670,5 @@
 		maybe_accept( node, &DeletedExpr::expr );
 		// don't visit deleteStmt, because it is a pointer to somewhere else in the tree.
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1652,10 +1681,11 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &DefaultArgExpr::result );
 		}
 		maybe_accept( node, &DefaultArgExpr::expr );
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1668,5 +1698,6 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
+		{
 			guard_symtab guard { *this };
 			maybe_accept( node, &GenericExpr::result );
@@ -1697,5 +1728,5 @@
 			node = n;
 		}
-	)
+	}
 
 	VISIT_END( Expr, node );
@@ -1726,8 +1757,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		// xxx - should PointerType visit/mutate dimension?
 		maybe_accept( node, &PointerType::base );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1740,8 +1771,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ArrayType::dimension );
 		maybe_accept( node, &ArrayType::base );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1754,7 +1785,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ReferenceType::base );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1767,8 +1798,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &QualifiedType::parent );
 		maybe_accept( node, &QualifiedType::child );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1781,5 +1812,5 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		// guard_forall_subs forall_guard { *this, node };
 		// mutate_forall( node );
@@ -1787,5 +1818,5 @@
 		maybe_accept( node, &FunctionType::returns );
 		maybe_accept( node, &FunctionType::params  );
-	})
+	}
 
 	VISIT_END( Type, node );
@@ -1800,8 +1831,8 @@
 	__pass::symtab::addStruct( core, 0, node->name );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &StructInstType::params );
-	})
+	}
 
 	VISIT_END( Type, node );
@@ -1816,8 +1847,8 @@
 	__pass::symtab::addUnion( core, 0, node->name );
 
-	VISIT({
+	if ( __visit_children() ) {
 		guard_symtab guard { *this };
 		maybe_accept( node, &UnionInstType::params );
-	})
+	}
 
 	VISIT_END( Type, node );
@@ -1830,7 +1861,7 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		maybe_accept( node, &EnumInstType::params );
-	})
+	}
 
 	VISIT_END( Type, node );
@@ -1843,7 +1874,7 @@
 	VISIT_START( node );
 
-	VISIT({
+	if ( __visit_children() ) {
 		maybe_accept( node, &TraitInstType::params );
-	})
+	}
 
 	VISIT_END( Type, node );
@@ -1856,5 +1887,5 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		{
 			maybe_accept( node, &TypeInstType::params );
@@ -1862,5 +1893,5 @@
 		// ensure that base re-bound if doing substitution
 		__pass::forall::replace( core, 0, node );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1873,8 +1904,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &TupleType::types );
 		maybe_accept( node, &TupleType::members );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1887,7 +1918,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &TypeofType::expr );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1900,7 +1931,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &VTableType::base );
-	)
+	}
 
 	VISIT_END( Type, node );
@@ -1950,5 +1981,7 @@
 	VISIT_START( node );
 
-	VISIT( maybe_accept( node, &Designation::designators ); )
+	if ( __visit_children() ) {
+		maybe_accept( node, &Designation::designators );
+	}
 
 	VISIT_END( Designation, node );
@@ -1961,7 +1994,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &SingleInit::value );
-	)
+	}
 
 	VISIT_END( Init, node );
@@ -1974,8 +2007,8 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ListInit::designations );
 		maybe_accept( node, &ListInit::initializers );
-	)
+	}
 
 	VISIT_END( Init, node );
@@ -1988,9 +2021,9 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &ConstructorInit::ctor );
 		maybe_accept( node, &ConstructorInit::dtor );
 		maybe_accept( node, &ConstructorInit::init );
-	)
+	}
 
 	VISIT_END( Init, node );
@@ -2003,7 +2036,7 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		maybe_accept( node, &Attribute::params );
-	)
+	}
 
 	VISIT_END( Attribute, node );
@@ -2016,5 +2049,5 @@
 	VISIT_START( node );
 
-	VISIT(
+	if ( __visit_children() ) {
 		{
 			bool mutated = false;
@@ -2032,5 +2065,5 @@
 			}
 		}
-	)
+	}
 
 	VISIT_END( TypeSubstitution, node );
@@ -2038,4 +2071,3 @@
 
 #undef VISIT_START
-#undef VISIT
 #undef VISIT_END
Index: src/AST/Print.cpp
===================================================================
--- src/AST/Print.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Print.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -511,12 +511,12 @@
 		++indent;
 		os << indent;
-		safe_print( node->thenPart );
-		--indent;
-
-		if ( node->elsePart != 0 ) {
+		safe_print( node->then );
+		--indent;
+
+		if ( node->else_ != 0 ) {
 			os << indent << "... else:" << endl;
 			++indent;
 			os << indent;
-			node->elsePart->accept( *this );
+			node->else_->accept( *this );
 			--indent;
 		} // if
@@ -524,5 +524,5 @@
 	}
 
-	virtual const ast::Stmt * visit( const ast::WhileStmt * node ) override final {
+	virtual const ast::Stmt * visit( const ast::WhileDoStmt * node ) override final {
 		if ( node->isDoWhile ) { os << "Do-"; }
 		os << "While on condition:" << endl;
Index: src/AST/Stmt.cpp
===================================================================
--- src/AST/Stmt.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Stmt.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Aaron B. Moss
 // Created On       : Wed May  8 13:00:00 2019
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed May 15 15:53:00 2019
-// Update Count     : 2
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Feb  2 19:01:20 2022
+// Update Count     : 3
 //
 
@@ -56,6 +56,6 @@
 
 // --- BranchStmt
-BranchStmt::BranchStmt( const CodeLocation& loc, Kind kind, Label target, std::vector<Label>&& labels )
-: Stmt(loc, std::move(labels)), originalTarget(target), target(target), kind(kind) {
+BranchStmt::BranchStmt( const CodeLocation& loc, Kind kind, Label target, const std::vector<Label>&& labels )
+		: Stmt(loc, std::move(labels)), originalTarget(target), target(target), kind(kind) {
 	// Make sure a syntax error hasn't slipped through.
 	assert( Goto != kind || !target.empty() );
Index: src/AST/Stmt.hpp
===================================================================
--- src/AST/Stmt.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Stmt.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Aaron B. Moss
 // Created On       : Wed May  8 13:00:00 2019
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri May 17 12:45:00 2019
-// Update Count     : 5
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Feb  2 20:06:41 2022
+// Update Count     : 34
 //
 
@@ -17,9 +17,9 @@
 
 #include <list>
-#include <utility>                // for move
+#include <utility>										// for move
 #include <vector>
 
 #include "Label.hpp"
-#include "Node.hpp"               // for node, ptr
+#include "Node.hpp"										// for node, ptr
 #include "ParseNode.hpp"
 #include "Visitor.hpp"
@@ -27,39 +27,37 @@
 
 // Must be included in *all* AST classes; should be #undef'd at the end of the file
-#define MUTATE_FRIEND \
+#define MUTATE_FRIEND													\
     template<typename node_t> friend node_t * mutate(const node_t * node); \
 	template<typename node_t> friend node_t * shallowCopy(const node_t * node);
 
 namespace ast {
-
 class Expr;
 
-/// Base statement node
+// Base statement node
 class Stmt : public ParseNode {
-public:
+  public:
 	std::vector<Label> labels;
 
-	Stmt( const CodeLocation & loc, std::vector<Label> && labels = {} )
-	: ParseNode(loc), labels(std::move(labels)) {}
-
-	Stmt(const Stmt& o) : ParseNode(o), labels(o.labels) {}
+	Stmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
+		: ParseNode(loc), labels(std::move(labels)) {}
+
+	Stmt(const Stmt & o) : ParseNode(o), labels(o.labels) {}
 
 	const Stmt * accept( Visitor & v ) const override = 0;
-private:
+  private:
 	Stmt * clone() const override = 0;
 	MUTATE_FRIEND
 };
 
-/// Compound statement `{ ... }`
+// Compound statement: { ... }
 class CompoundStmt final : public Stmt {
-public:
+  public:
 	std::list<ptr<Stmt>> kids;
 
-	CompoundStmt(const CodeLocation & loc, std::list<ptr<Stmt>> && ks = {},
-		std::vector<Label>&& labels = {} )
-	: Stmt(loc, std::move(labels)), kids(std::move(ks)) {}
-
-	CompoundStmt( const CompoundStmt& o );
-	CompoundStmt( CompoundStmt&& o ) = default;
+	CompoundStmt(const CodeLocation & loc, const std::list<ptr<Stmt>> && ks = {}, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), kids(std::move(ks)) {}
+
+	CompoundStmt( const CompoundStmt & o );
+	CompoundStmt( CompoundStmt && o ) = default;
 
 	void push_back( const Stmt * s ) { kids.emplace_back( s ); }
@@ -67,38 +65,38 @@
 
 	const CompoundStmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+  private:
 	CompoundStmt * clone() const override { return new CompoundStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Empty statment `;`
+// Empty statment: ;
 class NullStmt final : public Stmt {
-public:
-	NullStmt( const CodeLocation & loc, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)) {}
+  public:
+	NullStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)) {}
 
 	const NullStmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+  private:
 	NullStmt * clone() const override { return new NullStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Expression wrapped by statement
+// Expression wrapped by statement
 class ExprStmt final : public Stmt {
-public:
+  public:
 	ptr<Expr> expr;
 
-	ExprStmt( const CodeLocation& loc, const Expr* e, std::vector<Label>&& labels = {} )
-	: Stmt(loc, std::move(labels)), expr(e) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	ExprStmt( const CodeLocation & loc, const Expr* e, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), expr(e) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	ExprStmt * clone() const override { return new ExprStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Assembly statement `asm ... ( "..." : ... )`
+// Assembly statement: asm ... ( "..." : ... )
 class AsmStmt final : public Stmt {
-public:
+  public:
 	bool isVolatile;
 	ptr<Expr> instruction;
@@ -108,128 +106,136 @@
 
 	AsmStmt( const CodeLocation & loc, bool isVolatile, const Expr * instruction,
-		std::vector<ptr<Expr>> && output, std::vector<ptr<Expr>> && input,
-		std::vector<ptr<ConstantExpr>> && clobber, std::vector<Label> && gotoLabels,
-		std::vector<Label> && labels = {})
-	: Stmt(loc, std::move(labels)), isVolatile(isVolatile), instruction(instruction),
-	  output(std::move(output)), input(std::move(input)), clobber(std::move(clobber)),
-	  gotoLabels(std::move(gotoLabels)) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+			 const std::vector<ptr<Expr>> && output, const std::vector<ptr<Expr>> && input,
+			 const std::vector<ptr<ConstantExpr>> && clobber, const std::vector<Label> && gotoLabels,
+			 const std::vector<Label> && labels = {})
+		: Stmt(loc, std::move(labels)), isVolatile(isVolatile), instruction(instruction),
+		  output(std::move(output)), input(std::move(input)), clobber(std::move(clobber)),
+		  gotoLabels(std::move(gotoLabels)) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	AsmStmt * clone() const override { return new AsmStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// C-preprocessor directive `#...`
+// C-preprocessor directive: #...
 class DirectiveStmt final : public Stmt {
-public:
+  public:
 	std::string directive;
 
 	DirectiveStmt( const CodeLocation & loc, const std::string & directive,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), directive(directive) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+				   std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), directive(directive) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	DirectiveStmt * clone() const override { return new DirectiveStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// If conditional statement `if (...) ... else ...`
+// If statement: if (...) ... else ...
 class IfStmt final : public Stmt {
-public:
-	ptr<Expr> cond;
-	ptr<Stmt> thenPart;
-	ptr<Stmt> elsePart;
+  public:
+	ptr<Expr> cond;
+	ptr<Stmt> then;
+	ptr<Stmt> else_;
 	std::vector<ptr<Stmt>> inits;
 
-	IfStmt( const CodeLocation & loc, const Expr * cond, const Stmt * thenPart,
-		const Stmt * elsePart = nullptr, std::vector<ptr<Stmt>> && inits = {},
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), cond(cond), thenPart(thenPart), elsePart(elsePart),
-	  inits(std::move(inits)) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	IfStmt( const CodeLocation & loc, const Expr * cond, const Stmt * then,
+			const Stmt * else_ = nullptr, const std::vector<ptr<Stmt>> && inits = {},
+			const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), cond(cond), then(then), else_(else_),
+		  inits(std::move(inits)) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	IfStmt * clone() const override { return new IfStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Switch or choose conditional statement `switch (...) { ... }`
+// Switch or choose statement: switch (...) { ... }
 class SwitchStmt final : public Stmt {
-public:
+  public:
 	ptr<Expr> cond;
 	std::vector<ptr<Stmt>> stmts;
 
-	SwitchStmt( const CodeLocation & loc, const Expr * cond, std::vector<ptr<Stmt>> && stmts,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	SwitchStmt( const CodeLocation & loc, const Expr * cond, const std::vector<ptr<Stmt>> && stmts,
+				const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	SwitchStmt * clone() const override { return new SwitchStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Case label `case ...:` `default:`
+// Case label: case ...: or default:
 class CaseStmt final : public Stmt {
-public:
-	/// Null for the default label.
+  public:
+	// Null for the default label.
 	ptr<Expr> cond;
 	std::vector<ptr<Stmt>> stmts;
 
-	CaseStmt( const CodeLocation & loc, const Expr * cond, std::vector<ptr<Stmt>> && stmts,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {}
+	CaseStmt( const CodeLocation & loc, const Expr * cond, const std::vector<ptr<Stmt>> && stmts,
+			  const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {}
 
 	bool isDefault() const { return !cond; }
 
 	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+  private:
 	CaseStmt * clone() const override { return new CaseStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// While loop `while (...) ...` `do ... while (...);
-class WhileStmt final : public Stmt {
-public:
+// While loop: while (...) ... else ... or do ... while (...) else ...;
+class WhileDoStmt final : public Stmt {
+  public:
 	ptr<Expr> cond;
 	ptr<Stmt> body;
+	ptr<Stmt> else_;
 	std::vector<ptr<Stmt>> inits;
 	bool isDoWhile;
 
-	WhileStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body,
-		std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), cond(cond), body(body), inits(std::move(inits)),
-	  isDoWhile(isDoWhile) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
-	WhileStmt * clone() const override { return new WhileStmt{ *this }; }
-	MUTATE_FRIEND
-};
-
-/// For loop `for (... ; ... ; ...) ...`
+	WhileDoStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body,
+				 const std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), cond(cond), body(body), else_(nullptr), inits(std::move(inits)), isDoWhile(isDoWhile) {}
+
+	WhileDoStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body, const Stmt * else_,
+				 const std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), cond(cond), body(body), else_(else_), inits(std::move(inits)), isDoWhile(isDoWhile) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
+	WhileDoStmt * clone() const override { return new WhileDoStmt{ *this }; }
+	MUTATE_FRIEND
+};
+
+// For loop: for (... ; ... ; ...) ... else ...
 class ForStmt final : public Stmt {
-public:
+  public:
 	std::vector<ptr<Stmt>> inits;
 	ptr<Expr> cond;
 	ptr<Expr> inc;
 	ptr<Stmt> body;
-
-	ForStmt( const CodeLocation & loc, std::vector<ptr<Stmt>> && inits, const Expr * cond,
-		const Expr * inc, const Stmt * body, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), inits(std::move(inits)), cond(cond), inc(inc),
-	  body(body) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	ptr<Stmt> else_;
+
+	ForStmt( const CodeLocation & loc, const std::vector<ptr<Stmt>> && inits, const Expr * cond,
+			 const Expr * inc, const Stmt * body, const std::vector<Label> && label = {} )
+		: Stmt(loc, std::move(label)), inits(std::move(inits)), cond(cond), inc(inc), body(body), else_(nullptr) {}
+
+	ForStmt( const CodeLocation & loc, const std::vector<ptr<Stmt>> && inits, const Expr * cond,
+			 const Expr * inc, const Stmt * body, const Stmt * else_, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), inits(std::move(inits)), cond(cond), inc(inc), body(body), else_(else_) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	ForStmt * clone() const override { return new ForStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Branch control flow statement `goto ...` `break` `continue` `fallthru`
+// Branch control flow statement: goto ... or break or continue or fallthru
 class BranchStmt final : public Stmt {
-public:
+  public:
 	enum Kind { Goto, Break, Continue, FallThrough, FallThroughDefault };
 	static constexpr size_t kindEnd = 1 + (size_t)FallThroughDefault;
@@ -240,15 +246,12 @@
 	Kind kind;
 
-	BranchStmt( const CodeLocation & loc, Kind kind, Label target,
-		std::vector<Label> && labels = {} );
-	BranchStmt( const CodeLocation & loc, const Expr * computedTarget,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), originalTarget(loc), target(loc),
-	  computedTarget(computedTarget), kind(Goto) {}
+	BranchStmt( const CodeLocation & loc, Kind kind, Label target, const std::vector<Label> && labels = {} );
+	BranchStmt( const CodeLocation & loc, const Expr * computedTarget, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), originalTarget(loc), target(loc), computedTarget(computedTarget), kind(Goto) {}
 
 	const char * kindName() const { return kindNames[kind]; }
 
 	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+  private:
 	BranchStmt * clone() const override { return new BranchStmt{ *this }; }
 	MUTATE_FRIEND
@@ -257,61 +260,59 @@
 };
 
-/// Return statement `return ...`
+// Return statement: return ...
 class ReturnStmt final : public Stmt {
-public:
+  public:
 	ptr<Expr> expr;
 
-	ReturnStmt( const CodeLocation & loc, const Expr * expr, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), expr(expr) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	ReturnStmt( const CodeLocation & loc, const Expr * expr, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), expr(expr) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	ReturnStmt * clone() const override { return new ReturnStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Kind of exception
+// Kind of exception
 enum ExceptionKind { Terminate, Resume };
 
-/// Throw statement `throw ...`
+// Throw statement: throw ...
 class ThrowStmt final : public Stmt {
-public:
+  public:
 	ptr<Expr> expr;
 	ptr<Expr> target;
 	ExceptionKind kind;
 
-	ThrowStmt(
-		const CodeLocation & loc, ExceptionKind kind, const Expr * expr, const Expr * target,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), expr(expr), target(target), kind(kind) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	ThrowStmt( const CodeLocation & loc, ExceptionKind kind, const Expr * expr,
+			   const Expr * target, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), expr(expr), target(target), kind(kind) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	ThrowStmt * clone() const override { return new ThrowStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Try statement `try { ... } ...`
+// Try statement: try { ... } ...
 class TryStmt final : public Stmt {
-public:
+  public:
 	ptr<CompoundStmt> body;
 	std::vector<ptr<CatchStmt>> handlers;
 	ptr<FinallyStmt> finally;
 
-	TryStmt(
-		const CodeLocation & loc, const CompoundStmt * body,
-		std::vector<ptr<CatchStmt>> && handlers, const FinallyStmt * finally,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), body(body), handlers(std::move(handlers)), finally(finally) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	TryStmt( const CodeLocation & loc, const CompoundStmt * body,
+			 const std::vector<ptr<CatchStmt>> && handlers, const FinallyStmt * finally,
+			 const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), body(body), handlers(std::move(handlers)), finally(finally) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	TryStmt * clone() const override { return new TryStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Catch clause of try statement
+// Catch clause of try statement
 class CatchStmt final : public Stmt {
-public:
+  public:
 	ptr<Decl> decl;
 	ptr<Expr> cond;
@@ -319,48 +320,47 @@
 	ExceptionKind kind;
 
-	CatchStmt(
-		const CodeLocation & loc, ExceptionKind kind, const Decl * decl, const Expr * cond,
-		const Stmt * body, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), decl(decl), cond(cond), body(body), kind(kind) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	CatchStmt( const CodeLocation & loc, ExceptionKind kind, const Decl * decl, const Expr * cond,
+			   const Stmt * body, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), decl(decl), cond(cond), body(body), kind(kind) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	CatchStmt * clone() const override { return new CatchStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Finally clause of try statement
+// Finally clause of try statement
 class FinallyStmt final : public Stmt {
-public:
+  public:
 	ptr<CompoundStmt> body;
 
 	FinallyStmt( const CodeLocation & loc, const CompoundStmt * body,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), body(body) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+				 std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), body(body) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	FinallyStmt * clone() const override { return new FinallyStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Suspend statement
+// Suspend statement
 class SuspendStmt final : public Stmt {
-public:
+  public:
 	ptr<CompoundStmt> then;
 	enum Type { None, Coroutine, Generator } type = None;
 
-	SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Type type, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), then(then), type(type) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Type type, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), then(then), type(type) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	SuspendStmt * clone() const override { return new SuspendStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Wait for concurrency statement `when (...) waitfor (... , ...) ... timeout(...) ... else ...`
+// Waitfor statement: when (...) waitfor (... , ...) ... timeout(...) ... else ...
 class WaitForStmt final : public Stmt {
-public:
+  public:
 	struct Target {
 		ptr<Expr> func;
@@ -389,65 +389,62 @@
 	OrElse orElse;
 
-	WaitForStmt( const CodeLocation & loc, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	WaitForStmt( const CodeLocation & loc, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	WaitForStmt * clone() const override { return new WaitForStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Any declaration in a (compound) statement.
+// Any declaration in a (compound) statement.
 class DeclStmt final : public Stmt {
-public:
+  public:
 	ptr<Decl> decl;
 
-	DeclStmt( const CodeLocation & loc, const Decl * decl, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), decl(decl) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+	DeclStmt( const CodeLocation & loc, const Decl * decl, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), decl(decl) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	DeclStmt * clone() const override { return new DeclStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Represents an implicit application of a constructor or destructor.
+// Represents an implicit application of a constructor or destructor.
 class ImplicitCtorDtorStmt final : public Stmt {
-public:
+  public:
 	ptr<Stmt> callStmt;
 
 	ImplicitCtorDtorStmt( const CodeLocation & loc, const Stmt * callStmt,
-		std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), callStmt(callStmt) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+						  std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), callStmt(callStmt) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	ImplicitCtorDtorStmt * clone() const override { return new ImplicitCtorDtorStmt{ *this }; }
 	MUTATE_FRIEND
 };
 
-/// Mutex Statement
+// Mutex Statement
 class MutexStmt final : public Stmt {
-public:
+  public:
 	ptr<Stmt> stmt;
 	std::vector<ptr<Expr>> mutexObjs;
 
 	MutexStmt( const CodeLocation & loc, const Stmt * stmt, 
-		std::vector<ptr<Expr>> && mutexes, std::vector<Label> && labels = {} )
-	: Stmt(loc, std::move(labels)), stmt(stmt), mutexObjs(std::move(mutexes)) {}
-
-	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
-private:
+			   const std::vector<ptr<Expr>> && mutexes, const std::vector<Label> && labels = {} )
+		: Stmt(loc, std::move(labels)), stmt(stmt), mutexObjs(std::move(mutexes)) {}
+
+	const Stmt * accept( Visitor & v ) const override { return v.visit( this ); }
+  private:
 	MutexStmt * clone() const override { return new MutexStmt{ *this }; }
 	MUTATE_FRIEND
 };
-
-}
+} // namespace ast
 
 #undef MUTATE_FRIEND
 
 // Local Variables: //
-// tab-width: 4 //
 // mode: c++ //
-// compile-command: "make install" //
 // End: //
Index: src/AST/Visitor.hpp
===================================================================
--- src/AST/Visitor.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/AST/Visitor.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Thr May 9 15:28:00 2019
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:25:07 2021
-// Update Count     : 1
+// Last Modified On : Tue Feb  1 09:09:34 2022
+// Update Count     : 2
 //
 
@@ -38,5 +38,5 @@
     virtual const ast::Stmt *             visit( const ast::DirectiveStmt        * ) = 0;
     virtual const ast::Stmt *             visit( const ast::IfStmt               * ) = 0;
-    virtual const ast::Stmt *             visit( const ast::WhileStmt            * ) = 0;
+    virtual const ast::Stmt *             visit( const ast::WhileDoStmt          * ) = 0;
     virtual const ast::Stmt *             visit( const ast::ForStmt              * ) = 0;
     virtual const ast::Stmt *             visit( const ast::SwitchStmt           * ) = 0;
Index: src/CodeGen/CodeGenerator.cc
===================================================================
--- src/CodeGen/CodeGenerator.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/CodeGen/CodeGenerator.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 19:00:42 2021
-// Update Count     : 536
+// Last Modified On : Wed Feb  2 20:30:30 2022
+// Update Count     : 541
 //
 #include "CodeGenerator.h"
@@ -42,5 +42,5 @@
 	bool wantSpacing( Statement * stmt) {
 		return dynamic_cast< IfStmt * >( stmt ) || dynamic_cast< CompoundStmt * >( stmt ) ||
-			dynamic_cast< WhileStmt * >( stmt ) || dynamic_cast< ForStmt * >( stmt ) || dynamic_cast< SwitchStmt *>( stmt );
+			dynamic_cast< WhileDoStmt * >( stmt ) || dynamic_cast< ForStmt * >( stmt ) || dynamic_cast< SwitchStmt *>( stmt );
 	}
 
@@ -955,9 +955,9 @@
 		output << " ) ";
 
-		ifStmt->get_thenPart()->accept( *visitor );
-
-		if ( ifStmt->get_elsePart() != 0) {
+		ifStmt->get_then()->accept( *visitor );
+
+		if ( ifStmt->get_else() != 0) {
 			output << " else ";
-			ifStmt->get_elsePart()->accept( *visitor );
+			ifStmt->get_else()->accept( *visitor );
 		} // if
 	}
@@ -1020,4 +1020,5 @@
 			output << "fallthru";
 			break;
+		  default: ;									// prevent warning
 		} // switch
 		// print branch target for labelled break/continue/fallthru in debug mode
@@ -1125,22 +1126,22 @@
 	}
 
-	void CodeGenerator::postvisit( WhileStmt * whileStmt ) {
-		if ( whileStmt->get_isDoWhile() ) {
+	void CodeGenerator::postvisit( WhileDoStmt * whileDoStmt ) {
+		if ( whileDoStmt->get_isDoWhile() ) {
 			output << "do";
 		} else {
 			output << "while (";
-			whileStmt->get_condition()->accept( *visitor );
+			whileDoStmt->get_condition()->accept( *visitor );
 			output << ")";
 		} // if
 		output << " ";
 
-		output << CodeGenerator::printLabels( whileStmt->get_body()->get_labels() );
-		whileStmt->get_body()->accept( *visitor );
+		output << CodeGenerator::printLabels( whileDoStmt->get_body()->get_labels() );
+		whileDoStmt->get_body()->accept( *visitor );
 
 		output << indent;
 
-		if ( whileStmt->get_isDoWhile() ) {
+		if ( whileDoStmt->get_isDoWhile() ) {
 			output << " while (";
-			whileStmt->get_condition()->accept( *visitor );
+			whileDoStmt->get_condition()->accept( *visitor );
 			output << ");";
 		} // if
Index: src/CodeGen/CodeGenerator.h
===================================================================
--- src/CodeGen/CodeGenerator.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/CodeGen/CodeGenerator.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:35:38 2021
-// Update Count     : 63
+// Last Modified On : Tue Feb  1 09:23:21 2022
+// Update Count     : 64
 //
 
@@ -116,5 +116,5 @@
 		void postvisit( WaitForStmt * );
 		void postvisit( WithStmt * );
-		void postvisit( WhileStmt * );
+		void postvisit( WhileDoStmt * );
 		void postvisit( ForStmt * );
 		void postvisit( NullStmt * );
Index: src/Common/CodeLocationTools.cpp
===================================================================
--- src/Common/CodeLocationTools.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/Common/CodeLocationTools.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Fri Dec  4 15:42:00 2020
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:35:37 2021
-// Update Count     : 2
+// Last Modified On : Tue Feb  1 09:14:39 2022
+// Update Count     : 3
 //
 
@@ -109,5 +109,5 @@
     macro(DirectiveStmt, Stmt) \
     macro(IfStmt, Stmt) \
-    macro(WhileStmt, Stmt) \
+    macro(WhileDoStmt, Stmt) \
     macro(ForStmt, Stmt) \
     macro(SwitchStmt, Stmt) \
Index: src/Common/PassVisitor.h
===================================================================
--- src/Common/PassVisitor.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/Common/PassVisitor.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -92,6 +92,6 @@
 	virtual void visit( IfStmt * ifStmt ) override final;
 	virtual void visit( const IfStmt * ifStmt ) override final;
-	virtual void visit( WhileStmt * whileStmt ) override final;
-	virtual void visit( const WhileStmt * whileStmt ) override final;
+	virtual void visit( WhileDoStmt * whileDoStmt ) override final;
+	virtual void visit( const WhileDoStmt * whileDoStmt ) override final;
 	virtual void visit( ForStmt * forStmt ) override final;
 	virtual void visit( const ForStmt * forStmt ) override final;
@@ -277,5 +277,5 @@
 	virtual Statement * mutate( DirectiveStmt * dirStmt ) override final;
 	virtual Statement * mutate( IfStmt * ifStmt ) override final;
-	virtual Statement * mutate( WhileStmt * whileStmt ) override final;
+	virtual Statement * mutate( WhileDoStmt * whileDoStmt ) override final;
 	virtual Statement * mutate( ForStmt * forStmt ) override final;
 	virtual Statement * mutate( SwitchStmt * switchStmt ) override final;
Index: src/Common/PassVisitor.impl.h
===================================================================
--- src/Common/PassVisitor.impl.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/Common/PassVisitor.impl.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -1189,6 +1189,6 @@
 		maybeAccept_impl( node->initialization, *this );
 		visitExpression ( node->condition );
-		node->thenPart = visitStatement( node->thenPart );
-		node->elsePart = visitStatement( node->elsePart );
+		node->then = visitStatement( node->then );
+		node->else_ = visitStatement( node->else_ );
 	}
 	VISIT_END( node );
@@ -1203,6 +1203,6 @@
 		maybeAccept_impl( node->initialization, *this );
 		visitExpression ( node->condition );
-		visitStatement  ( node->thenPart );
-		visitStatement  ( node->elsePart );
+		visitStatement  ( node->then );
+		visitStatement  ( node->else_ );
 	}
 	VISIT_END( node );
@@ -1217,6 +1217,6 @@
 		maybeMutate_impl( node->initialization, *this );
 		node->condition = mutateExpression( node->condition );
-		node->thenPart  = mutateStatement ( node->thenPart  );
-		node->elsePart  = mutateStatement ( node->elsePart  );
+		node->then  = mutateStatement ( node->then  );
+		node->else_  = mutateStatement ( node->else_  );
 	}
 	MUTATE_END( Statement, node );
@@ -1224,7 +1224,7 @@
 
 //--------------------------------------------------------------------------
-// WhileStmt
-template< typename pass_type >
-void PassVisitor< pass_type >::visit( WhileStmt * node ) {
+// WhileDoStmt
+template< typename pass_type >
+void PassVisitor< pass_type >::visit( WhileDoStmt * node ) {
 	VISIT_START( node );
 
@@ -1241,5 +1241,5 @@
 
 template< typename pass_type >
-void PassVisitor< pass_type >::visit( const WhileStmt * node ) {
+void PassVisitor< pass_type >::visit( const WhileDoStmt * node ) {
 	VISIT_START( node );
 
@@ -1256,5 +1256,5 @@
 
 template< typename pass_type >
-Statement * PassVisitor< pass_type >::mutate( WhileStmt * node ) {
+Statement * PassVisitor< pass_type >::mutate( WhileDoStmt * node ) {
 	MUTATE_START( node );
 
Index: src/ControlStruct/ExceptTranslateNew.cpp
===================================================================
--- src/ControlStruct/ExceptTranslateNew.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/ExceptTranslateNew.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Andrew Beach
 // Created On       : Mon Nov  8 11:53:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  8 16:50:00 2021
-// Update Count     : 0
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 18:49:58 2022
+// Update Count     : 1
 //
 
Index: src/ControlStruct/FixLabels.cpp
===================================================================
--- src/ControlStruct/FixLabels.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/FixLabels.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Andrew Beach
 // Created On       : Mon Nov  1 09:39:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  8 10:53:00 2021
-// Update Count     : 3
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 22:19:17 2022
+// Update Count     : 9
 //
 
@@ -20,42 +20,40 @@
 #include "AST/Stmt.hpp"
 #include "ControlStruct/MultiLevelExit.hpp"
+using namespace ast;
 
 namespace ControlStruct {
-
-namespace {
-
-class FixLabelsCore final : public ast::WithGuards {
+class FixLabelsCore final : public WithGuards {
 	LabelToStmt labelTable;
-public:
+  public:
 	FixLabelsCore() : labelTable() {}
 
-	void previsit( const ast::FunctionDecl * );
-	const ast::FunctionDecl * postvisit( const ast::FunctionDecl * );
-	void previsit( const ast::Stmt * );
-	void previsit( const ast::BranchStmt * );
-	void previsit( const ast::LabelAddressExpr * );
+	void previsit( const FunctionDecl * );
+	const FunctionDecl * postvisit( const FunctionDecl * );
+	void previsit( const Stmt * );
+	void previsit( const BranchStmt * );
+	void previsit( const LabelAddressExpr * );
 
-	void setLabelsDef( const std::vector<ast::Label> &, const ast::Stmt * );
-	void setLabelsUsage( const ast::Label & );
+	void setLabelsDef( const std::vector<Label> &, const Stmt * );
+	void setLabelsUsage( const Label & );
 };
 
-void FixLabelsCore::previsit( const ast::FunctionDecl * ) {
+void FixLabelsCore::previsit( const FunctionDecl * ) {
 	GuardValue( labelTable ).clear();
 }
 
-const ast::FunctionDecl * FixLabelsCore::postvisit(
-		const ast::FunctionDecl * decl ) {
+const FunctionDecl * FixLabelsCore::postvisit(
+	const FunctionDecl * decl ) {
 	if ( nullptr == decl->stmts ) return decl;
 	for ( auto kvp : labelTable ) {
 		if ( nullptr == kvp.second ) {
 			SemanticError( kvp.first.location,
-				"Use of undefined label: " + kvp.first.name );
+						   "Use of undefined label: " + kvp.first.name );
 		}
 	}
-	return ast::mutate_field( decl, &ast::FunctionDecl::stmts,
-		multiLevelExitUpdate( decl->stmts.get(), labelTable ) );
+	return mutate_field( decl, &FunctionDecl::stmts,
+						 multiLevelExitUpdate( decl->stmts.get(), labelTable ) );
 }
 
-void FixLabelsCore::previsit( const ast::Stmt * stmt ) {
+void FixLabelsCore::previsit( const Stmt * stmt ) {
 	if ( !stmt->labels.empty() ) {
 		setLabelsDef( stmt->labels, stmt );
@@ -63,5 +61,5 @@
 }
 
-void FixLabelsCore::previsit( const ast::BranchStmt * stmt ) {
+void FixLabelsCore::previsit( const BranchStmt * stmt ) {
 	if ( !stmt->labels.empty() ) {
 		setLabelsDef( stmt->labels, stmt );
@@ -72,5 +70,5 @@
 }
 
-void FixLabelsCore::previsit( const ast::LabelAddressExpr * expr ) {
+void FixLabelsCore::previsit( const LabelAddressExpr * expr ) {
 	assert( !expr->arg.empty() );
 	setLabelsUsage( expr->arg );
@@ -78,5 +76,5 @@
 
 void FixLabelsCore::setLabelsDef(
-		const std::vector<ast::Label> & labels, const ast::Stmt * stmt ) {
+	const std::vector<Label> & labels, const Stmt * stmt ) {
 	assert( !labels.empty() );
 	assert( stmt );
@@ -89,5 +87,5 @@
 			// Duplicate definition, this is an error.
 			SemanticError( label.location,
-				"Duplicate definition of label: " + label.name );
+						   "Duplicate definition of label: " + label.name );
 		} else {
 			// Perviously used, but not defined until now.
@@ -98,5 +96,5 @@
 
 // Label was used, if it is new add it to the table.
-void FixLabelsCore::setLabelsUsage( const ast::Label & label ) {
+void FixLabelsCore::setLabelsUsage( const Label & label ) {
 	if ( labelTable.find( label ) == labelTable.end() ) {
 		labelTable[ label ] = nullptr;
@@ -104,10 +102,7 @@
 }
 
-} // namespace
-
-void fixLabels( ast::TranslationUnit & translationUnit ) {
-	ast::Pass<FixLabelsCore>::run( translationUnit );
+void fixLabels( TranslationUnit & translationUnit ) {
+	Pass<FixLabelsCore>::run( translationUnit );
 }
-
 } // namespace ControlStruct
 
Index: src/ControlStruct/FixLabels.hpp
===================================================================
--- src/ControlStruct/FixLabels.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/FixLabels.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Andrew Beach
 // Created On       : Mon Nov  1 09:36:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  1 09:40:00 2021
-// Update Count     : 0
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 22:18:43 2022
+// Update Count     : 2
 //
 
@@ -17,12 +17,10 @@
 
 namespace ast {
-	class TranslationUnit;
+class TranslationUnit;
 }
 
 namespace ControlStruct {
-
-/// normalizes label definitions and generates multi-level exit labels
+// normalizes label definitions and generates multi-level exit labels
 void fixLabels( ast::TranslationUnit & translationUnit );
-
 }
 
Index: src/ControlStruct/ForExprMutator.cc
===================================================================
--- src/ControlStruct/ForExprMutator.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/ForExprMutator.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Mon Mar 11 22:26:52 2019
-// Update Count     : 14
+// Last Modified On : Tue Feb  1 09:26:12 2022
+// Update Count     : 16
 //
 
@@ -45,6 +45,6 @@
 		return hoist( forStmt, forStmt->initialization );
 	}
-	Statement * ForExprMutator::postmutate( WhileStmt * whileStmt ) {
-		return hoist( whileStmt, whileStmt->initialization );
+	Statement * ForExprMutator::postmutate( WhileDoStmt * whileDoStmt ) {
+		return hoist( whileDoStmt, whileDoStmt->initialization );
 	}
 } // namespace ControlStruct
Index: src/ControlStruct/ForExprMutator.h
===================================================================
--- src/ControlStruct/ForExprMutator.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/ForExprMutator.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Thu Aug 17 15:32:48 2017
-// Update Count     : 5
+// Last Modified On : Tue Feb  1 09:18:50 2022
+// Update Count     : 7
 //
 
@@ -18,5 +18,5 @@
 class IfStmt;
 class ForStmt;
-class WhileStmt;
+class WhileDoStmt;
 class Statement;
 
@@ -24,7 +24,7 @@
 	class ForExprMutator {
 	  public:
-		Statement *postmutate( IfStmt * );
-		Statement *postmutate( ForStmt * );
-		Statement *postmutate( WhileStmt * );
+		Statement * postmutate( IfStmt * );
+		Statement * postmutate( ForStmt * );
+		Statement * postmutate( WhileDoStmt * );
 	};
 } // namespace ControlStruct
Index: src/ControlStruct/HoistControlDecls.cpp
===================================================================
--- src/ControlStruct/HoistControlDecls.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ src/ControlStruct/HoistControlDecls.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,86 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// HoistControlDecls.cpp -- Desugar Cforall control structures.
+//
+// Author           : Andrew Beach
+// Created On       : Fri Dec  3 15:34:00 2021
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Tue Feb  1 18:59:47 2022
+// Update Count     : 25
+//
+
+#include "HoistControlDecls.hpp"
+
+#include "AST/Decl.hpp"
+#include "AST/Pass.hpp"
+#include "AST/TranslationUnit.hpp"
+using namespace ast;
+
+namespace ControlStruct {
+
+template<typename StmtT>
+const Stmt * hoist( const StmtT * stmt ) {
+	// If no hoisting is needed, then make no changes.
+
+	if ( stmt->inits.size() == 0 ) {					// no declarations ?
+		// if ( /* no conditional declarations */ ...  ) ...
+		return stmt;
+	} // if
+
+	// Put hoist declarations and modified statement in a compound statement.
+
+	CompoundStmt * block = new CompoundStmt( stmt->location ); // create empty compound statement
+	//    {}
+
+	for ( const Stmt * next : stmt->inits ) {			// link conditional declarations into compound
+		block->kids.push_back( next );
+	}
+	//    if ( int x = f(), y = g(); ... ) ...
+	// link declarations into compound statement
+	//    {
+	//         int x = f();
+	//         int y = g();
+	//    }
+
+	StmtT * mutStmt = mutate( stmt );					// create mutate handle to change statement
+	mutStmt->inits.clear();								// remove declarations
+	//    if ( ... ) ...
+
+	block->kids.push_back( mutStmt );					// link modified statement into compound
+	//    {
+	//        int x = f();
+	//        int y = g();
+	//        if ( ... ) ...
+	//    }
+	return block;
+}
+
+struct hoistControlDeclsCore {
+	// Statements with declarations in conditional.
+	const Stmt * postvisit( const IfStmt * stmt ) {
+		return hoist<IfStmt>( stmt );
+	}
+	const Stmt * postvisit( const ForStmt * stmt ) {
+		return hoist<ForStmt>( stmt );
+	}
+	const Stmt * postvisit( const WhileDoStmt * stmt ) {
+		return hoist<WhileDoStmt>( stmt );
+	}
+};
+
+// Hoist initialization out of for statements.
+void hoistControlDecls( TranslationUnit & translationUnit ) {
+	Pass<hoistControlDeclsCore>::run( translationUnit );
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/HoistControlDecls.hpp
===================================================================
--- src/ControlStruct/HoistControlDecls.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ src/ControlStruct/HoistControlDecls.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,31 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// HoistControlDecls.hpp -- Desugar Cforall control structures.
+//
+// Author           : Andrew Beach
+// Created On       : Fri Dec  3 15:31:00 2021
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 22:25:07 2022
+// Update Count     : 3
+//
+
+#pragma once
+
+namespace ast {
+class TranslationUnit;
+}
+
+namespace ControlStruct {
+// Hoist declarations out of control flow statements into compound statement.
+void hoistControlDecls( ast::TranslationUnit & translationUnit );
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/LabelFixer.cc
===================================================================
--- src/ControlStruct/LabelFixer.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/LabelFixer.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Tue Jan 21 10:32:00 2020
-// Update Count     : 160
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Tue Feb  1 09:12:09 2022
+// Update Count     : 162
 //
 
@@ -27,107 +27,107 @@
 
 namespace ControlStruct {
-	bool LabelFixer::Entry::insideLoop() {
-		return ( dynamic_cast< ForStmt * > ( definition ) ||
-			dynamic_cast< WhileStmt * > ( definition )  );
+bool LabelFixer::Entry::insideLoop() {
+	return ( dynamic_cast< ForStmt * > ( definition ) ||
+		dynamic_cast< WhileDoStmt * > ( definition )  );
+}
+
+LabelFixer::LabelFixer( LabelGenerator * gen ) : generator ( gen ) {
+	if ( generator == 0 )
+		generator = LabelGenerator::getGenerator();
+}
+
+void LabelFixer::previsit( FunctionDecl * ) {
+	// need to go into a nested function in a fresh state
+	GuardValue( labelTable );
+	labelTable.clear();
+}
+
+void LabelFixer::postvisit( FunctionDecl * functionDecl ) {
+	PassVisitor<MultiLevelExitMutator> mlem( resolveJumps(), generator );
+	// We start in the body so we can stop when we hit another FunctionDecl.
+	maybeMutate( functionDecl->statements, mlem );
+}
+
+// prune to at most one label definition for each statement
+void LabelFixer::previsit( Statement * stmt ) {
+	std::list< Label > &labels = stmt->get_labels();
+
+	if ( ! labels.empty() ) {
+		// only remember one label for each statement
+		Label current = setLabelsDef( labels, stmt );
+	} // if
+}
+
+void LabelFixer::previsit( BranchStmt * branchStmt ) {
+	previsit( ( Statement *)branchStmt );
+
+	// for labeled branches, add an entry to the label table
+	Label target = branchStmt->get_target();
+	if ( target != "" ) {
+		setLabelsUsg( target, branchStmt );
+	}
+}
+
+void LabelFixer::previsit( LabelAddressExpr * addrExpr ) {
+	Label & target = addrExpr->arg;
+	assert( target != "" );
+	setLabelsUsg( target, addrExpr );
+}
+
+
+// Sets the definition of the labelTable entry to be the provided statement for every label in
+// the list parameter. Happens for every kind of statement.
+Label LabelFixer::setLabelsDef( std::list< Label > & llabel, Statement * definition ) {
+	assert( definition != 0 );
+	assert( llabel.size() > 0 );
+
+	for ( std::list< Label >::iterator i = llabel.begin(); i != llabel.end(); i++ ) {
+		Label & l = *i;
+		l.set_statement( definition ); // attach statement to the label to be used later
+		if ( labelTable.find( l ) == labelTable.end() ) {
+			// All labels on this statement need to use the same entry,
+			// so this should only be created once.
+			// undefined and unused until now, add an entry
+			labelTable[ l ] = new Entry( definition );
+		} else if ( labelTable[ l ]->defined() ) {
+			// defined twice, error
+			SemanticError( l.get_statement()->location,
+				"Duplicate definition of label: " + l.get_name() );
+		} else {
+			// used previously, but undefined until now -> link with this entry
+			// Question: Is changing objects important?
+			delete labelTable[ l ];
+			labelTable[ l ] = new Entry( definition );
+		} // if
+	} // for
+
+	// Produce one of the labels attached to this statement to be temporarily used as the
+	// canonical label.
+	return labelTable[ llabel.front() ]->get_label();
+}
+
+// A label was used, add it to the table if it isn't already there
+template< typename UsageNode >
+void LabelFixer::setLabelsUsg( Label orgValue, UsageNode *use ) {
+	assert( use != 0 );
+
+	// add label with an unknown origin
+	if ( labelTable.find( orgValue ) == labelTable.end() ) {
+		labelTable[ orgValue ] = new Entry( 0 );
+	}
+}
+
+// Builds a table that maps a label to its defining statement.
+std::map<Label, Statement * > * LabelFixer::resolveJumps() throw ( SemanticErrorException ) {
+	std::map< Label, Statement * > *ret = new std::map< Label, Statement * >();
+	for ( std::map< Label, Entry * >::iterator i = labelTable.begin(); i != labelTable.end(); ++i ) {
+		if ( ! i->second->defined() ) {
+			SemanticError( i->first.get_statement()->location, "Use of undefined label: " + i->first.get_name() );
+		}
+		(*ret)[ i->first ] = i->second->get_definition();
 	}
 
-	LabelFixer::LabelFixer( LabelGenerator * gen ) : generator ( gen ) {
-		if ( generator == 0 )
-			generator = LabelGenerator::getGenerator();
-	}
-
-	void LabelFixer::previsit( FunctionDecl * ) {
-		// need to go into a nested function in a fresh state
-		GuardValue( labelTable );
-		labelTable.clear();
-	}
-
-	void LabelFixer::postvisit( FunctionDecl * functionDecl ) {
-		PassVisitor<MultiLevelExitMutator> mlem( resolveJumps(), generator );
-		// We start in the body so we can stop when we hit another FunctionDecl.
-		maybeMutate( functionDecl->statements, mlem );
-	}
-
-	// prune to at most one label definition for each statement
-	void LabelFixer::previsit( Statement * stmt ) {
-		std::list< Label > &labels = stmt->get_labels();
-
-		if ( ! labels.empty() ) {
-			// only remember one label for each statement
-			Label current = setLabelsDef( labels, stmt );
-		} // if
-	}
-
-	void LabelFixer::previsit( BranchStmt * branchStmt ) {
-		previsit( ( Statement *)branchStmt );
-
-		// for labeled branches, add an entry to the label table
-		Label target = branchStmt->get_target();
-		if ( target != "" ) {
-			setLabelsUsg( target, branchStmt );
-		}
-	}
-
-	void LabelFixer::previsit( LabelAddressExpr * addrExpr ) {
-		Label & target = addrExpr->arg;
-		assert( target != "" );
-		setLabelsUsg( target, addrExpr );
-	}
-
-
-	// Sets the definition of the labelTable entry to be the provided statement for every label in
-	// the list parameter. Happens for every kind of statement.
-	Label LabelFixer::setLabelsDef( std::list< Label > & llabel, Statement * definition ) {
-		assert( definition != 0 );
-		assert( llabel.size() > 0 );
-
-		for ( std::list< Label >::iterator i = llabel.begin(); i != llabel.end(); i++ ) {
-			Label & l = *i;
-			l.set_statement( definition ); // attach statement to the label to be used later
-			if ( labelTable.find( l ) == labelTable.end() ) {
-				// All labels on this statement need to use the same entry,
-				// so this should only be created once.
-				// undefined and unused until now, add an entry
-				labelTable[ l ] = new Entry( definition );
-			} else if ( labelTable[ l ]->defined() ) {
-				// defined twice, error
-				SemanticError( l.get_statement()->location,
-					"Duplicate definition of label: " + l.get_name() );
-			} else {
-				// used previously, but undefined until now -> link with this entry
-				// Question: Is changing objects important?
-				delete labelTable[ l ];
-				labelTable[ l ] = new Entry( definition );
-			} // if
-		} // for
-
-		// Produce one of the labels attached to this statement to be temporarily used as the
-		// canonical label.
-		return labelTable[ llabel.front() ]->get_label();
-	}
-
-	// A label was used, add it to the table if it isn't already there
-	template< typename UsageNode >
-	void LabelFixer::setLabelsUsg( Label orgValue, UsageNode *use ) {
-		assert( use != 0 );
-
-		// add label with an unknown origin
-		if ( labelTable.find( orgValue ) == labelTable.end() ) {
-			labelTable[ orgValue ] = new Entry( 0 );
-		}
-	}
-
-	// Builds a table that maps a label to its defining statement.
-	std::map<Label, Statement * > * LabelFixer::resolveJumps() throw ( SemanticErrorException ) {
-		std::map< Label, Statement * > *ret = new std::map< Label, Statement * >();
-		for ( std::map< Label, Entry * >::iterator i = labelTable.begin(); i != labelTable.end(); ++i ) {
-			if ( ! i->second->defined() ) {
-				SemanticError( i->first.get_statement()->location, "Use of undefined label: " + i->first.get_name() );
-			}
-			(*ret)[ i->first ] = i->second->get_definition();
-		}
-
-		return ret;
-	}
+	return ret;
+}
 }  // namespace ControlStruct
 
Index: src/ControlStruct/LabelFixer.h
===================================================================
--- src/ControlStruct/LabelFixer.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/LabelFixer.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jul 22 09:17:24 2017
-// Update Count     : 34
+// Last Modified On : Mon Jan 31 22:28:04 2022
+// Update Count     : 35
 //
 
@@ -26,49 +26,49 @@
 
 namespace ControlStruct {
-	/// normalizes label definitions and generates multi-level exit labels
-	class LabelGenerator;
+// normalizes label definitions and generates multi-level exit labels
+class LabelGenerator;
 
-	class LabelFixer final : public WithGuards {
-	  public:
-		LabelFixer( LabelGenerator *gen = 0 );
+class LabelFixer final : public WithGuards {
+  public:
+	LabelFixer( LabelGenerator *gen = 0 );
 
-		std::map < Label, Statement * > *resolveJumps() throw ( SemanticErrorException );
+	std::map < Label, Statement * > *resolveJumps() throw ( SemanticErrorException );
 
-		// Declarations
-		void previsit( FunctionDecl *functionDecl );
-		void postvisit( FunctionDecl *functionDecl );
+	// Declarations
+	void previsit( FunctionDecl *functionDecl );
+	void postvisit( FunctionDecl *functionDecl );
 
-		// Statements
-		void previsit( Statement *stmt );
-		void previsit( BranchStmt *branchStmt );
+	// Statements
+	void previsit( Statement *stmt );
+	void previsit( BranchStmt *branchStmt );
 
-		// Expressions
-		void previsit( LabelAddressExpr *addrExpr );
+	// Expressions
+	void previsit( LabelAddressExpr *addrExpr );
 
-		Label setLabelsDef( std::list< Label > &, Statement *definition );
-		template< typename UsageNode >
-		void setLabelsUsg( Label, UsageNode *usage = 0 );
+	Label setLabelsDef( std::list< Label > &, Statement *definition );
+	template< typename UsageNode >
+	void setLabelsUsg( Label, UsageNode *usage = 0 );
+
+  private:
+	class Entry {
+		public:
+		Entry( Statement *to ) : definition( to ) {}
+		bool defined() { return ( definition != 0 ); }
+		bool insideLoop();
+
+		Label get_label() const { return label; }
+		void set_label( Label lab ) { label = lab; }
+
+		Statement *get_definition() const { return definition; }
+		void set_definition( Statement *def ) { definition = def; }
 
 	  private:
-		class Entry {
-			public:
-			Entry( Statement *to ) : definition( to ) {}
-			bool defined() { return ( definition != 0 ); }
-			bool insideLoop();
+		Label label;
+		Statement *definition;
+	};
 
-			Label get_label() const { return label; }
-			void set_label( Label lab ) { label = lab; }
-
-			Statement *get_definition() const { return definition; }
-			void set_definition( Statement *def ) { definition = def; }
-
-		  private:
-			Label label;
-			Statement *definition;
-		};
-
-		std::map < Label, Entry *> labelTable;
-		LabelGenerator *generator;
-	};
+	std::map < Label, Entry *> labelTable;
+	LabelGenerator *generator;
+};
 } // namespace ControlStruct
 
Index: src/ControlStruct/LabelGenerator.cc
===================================================================
--- src/ControlStruct/LabelGenerator.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/LabelGenerator.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  8 10:18:00 2021
-// Update Count     : 17
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 22:30:26 2022
+// Update Count     : 28
 //
 
@@ -17,10 +17,8 @@
 #include <sstream>              // for ostringstream
 #include <list>                 // for list
+using namespace std;
 
 #include "LabelGenerator.h"
 
-#include "AST/Attribute.hpp"
-#include "AST/Label.hpp"
-#include "AST/Stmt.hpp"
 #include "SynTree/Attribute.h"  // for Attribute
 #include "SynTree/Label.h"      // for Label, operator<<
@@ -28,45 +26,27 @@
 
 namespace ControlStruct {
-
 int LabelGenerator::current = 0;
 LabelGenerator * LabelGenerator::labelGenerator = nullptr;
 
-	LabelGenerator * LabelGenerator::getGenerator() {
-		if ( LabelGenerator::labelGenerator == 0 )
-			LabelGenerator::labelGenerator = new LabelGenerator();
-		return labelGenerator;
-	}
-
-	Label LabelGenerator::newLabel( std::string suffix, Statement * stmt ) {
-		std::ostringstream os;
-		os << "__L" << current++ << "__" << suffix;
-		if ( stmt && ! stmt->get_labels().empty() ) {
-			os << "_" << stmt->get_labels().front() << "__";
-		} // if
-		std::string ret = os.str();
-		Label l( ret );
-		l.get_attributes().push_back( new Attribute("unused") );
-		return l;
-	}
-
-ast::Label LabelGenerator::newLabel(
-		const std::string & suffix, const ast::Stmt * stmt ) {
-	assert( stmt );
-
-	std::ostringstream os;
-	os << "__L" << current++ << "__" << suffix;
-	if ( stmt && !stmt->labels.empty() ) {
-		os << "_" << stmt->labels.front() << "__";
-	}
-	ast::Label ret_label( stmt->location, os.str() );
-	ret_label.attributes.push_back( new ast::Attribute( "unused" ) );
-	return ret_label;
+LabelGenerator * LabelGenerator::getGenerator() {
+	if ( LabelGenerator::labelGenerator == 0 )
+		LabelGenerator::labelGenerator = new LabelGenerator();
+	return labelGenerator;
 }
 
+Label LabelGenerator::newLabel( string suffix, Statement * stmt ) {
+	ostringstream os;
+	os << "__L_OLD" << current++ << "__" << suffix;
+	if ( stmt && ! stmt->get_labels().empty() ) {
+		os << "_" << stmt->get_labels().front() << "__";
+	} // if
+	string ret = os.str();
+	Label l( ret );
+	l.get_attributes().push_back( new Attribute( "unused" ) );
+	return l;
+}
 } // namespace ControlStruct
 
 // Local Variables: //
-// tab-width: 4 //
 // mode: c++ //
-// compile-command: "make install" //
 // End: //
Index: src/ControlStruct/LabelGenerator.h
===================================================================
--- src/ControlStruct/LabelGenerator.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/LabelGenerator.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  8 10:16:00 2021
-// Update Count     : 8
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 22:30:10 2022
+// Update Count     : 16
 //
 
@@ -21,24 +21,20 @@
 
 class Statement;
+
 namespace ast {
-	class Stmt;
-	class Label;
+class Stmt;
+class Label;
 }
 
 namespace ControlStruct {
-
 class LabelGenerator {
 	static int current;
 	static LabelGenerator *labelGenerator;
-protected:
+  protected:
 	LabelGenerator() {}
-public:
+  public:
 	static LabelGenerator *getGenerator();
 	static Label newLabel(std::string suffix, Statement * stmt = nullptr);
-	static ast::Label newLabel( const std::string&, const ast::Stmt * );
-	static void reset() { current = 0; }
-	static void rewind() { current--; }
 };
-
 } // namespace ControlStruct
 
Index: src/ControlStruct/LabelGeneratorNew.cpp
===================================================================
--- src/ControlStruct/LabelGeneratorNew.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ src/ControlStruct/LabelGeneratorNew.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,52 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// LabelGenerator.cc --
+//
+// Author           : Peter A. Buhr
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Feb  2 09:11:17 2022
+// Update Count     : 72
+//
+
+using namespace std;
+
+#include "LabelGeneratorNew.hpp"
+
+#include "AST/Attribute.hpp"
+#include "AST/Label.hpp"
+#include "AST/Stmt.hpp"
+using namespace ast;
+
+namespace ControlStruct {
+
+Label newLabel( const string & suffix, const Stmt * stmt ) {
+	static int current = 0;
+
+	assertf( stmt, "CFA internal error: parameter statement cannot be null pointer" );
+
+	enum { size = 128 };
+	char buf[size];										// space to build label
+	int len = snprintf( buf, size, "__L%d__%s", current++, suffix.c_str() );
+	assertf( len < size, "CFA Internal error: buffer overflow creating label" );
+
+	// What does this do?
+	if ( ! stmt->labels.empty() ) {
+		len = snprintf( buf + len, size - len, "_%s__", stmt->labels.front().name.c_str() );
+		assertf( len < size - len, "CFA Internal error: buffer overflow creating label" );
+	} // if
+
+	Label ret_label( stmt->location, buf );
+	ret_label.attributes.push_back( new Attribute( "unused" ) );
+	return ret_label;
+}
+
+} // namespace ControlStruct
+
+// Local Variables: //
+// mode: c++ //
+// End: //
Index: src/ControlStruct/LabelGeneratorNew.hpp
===================================================================
--- src/ControlStruct/LabelGeneratorNew.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ src/ControlStruct/LabelGeneratorNew.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,35 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// LabelGenerator.h --
+//
+// Author           : Rodolfo G. Esteves
+// Created On       : Mon May 18 07:44:20 2015
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 18:03:09 2022
+// Update Count     : 27
+//
+
+#pragma once
+
+#include <string>										// for string
+
+class Statement;
+
+namespace ast {
+	class Stmt;
+	class Label;
+} // namespace ast
+
+namespace ControlStruct {
+	ast::Label newLabel( const std::string &, const ast::Stmt * );
+} // namespace ControlStruct
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/ControlStruct/MLEMutator.cc
===================================================================
--- src/ControlStruct/MLEMutator.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/MLEMutator.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jan 22 11:50:00 2020
-// Update Count     : 223
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Feb  2 20:18:57 2022
+// Update Count     : 227
 //
 
@@ -39,5 +39,5 @@
 	namespace {
 		bool isLoop( const MultiLevelExitMutator::Entry & e ) {
-			return dynamic_cast< WhileStmt * >( e.get_controlStructure() )
+			return dynamic_cast< WhileDoStmt * >( e.get_controlStructure() )
 				|| dynamic_cast< ForStmt * >( e.get_controlStructure() );
 		}
@@ -136,5 +136,5 @@
 			}
 		}
-		assertf( false, "Could not find label '%s' on statement %s",
+		assertf( false, "CFA internal error: could not find label '%s' on statement %s",
 			originalTarget.get_name().c_str(), toString( stmt ).c_str() );
 	}
@@ -295,6 +295,6 @@
 	}
 
-	void MultiLevelExitMutator::premutate( WhileStmt * whileStmt ) {
-		return prehandleLoopStmt( whileStmt );
+	void MultiLevelExitMutator::premutate( WhileDoStmt * whileDoStmt ) {
+		return prehandleLoopStmt( whileDoStmt );
 	}
 
@@ -303,6 +303,6 @@
 	}
 
-	Statement * MultiLevelExitMutator::postmutate( WhileStmt * whileStmt ) {
-		return posthandleLoopStmt( whileStmt );
+	Statement * MultiLevelExitMutator::postmutate( WhileDoStmt * whileDoStmt ) {
+		return posthandleLoopStmt( whileDoStmt );
 	}
 
@@ -395,5 +395,7 @@
 		}
 		assert( ! enclosingControlStructures.empty() );
-		assertf( dynamic_cast<SwitchStmt *>( enclosingControlStructures.back().get_controlStructure() ), "Control structure enclosing a case clause must be a switch, but is: %s", toCString( enclosingControlStructures.back().get_controlStructure() ) );
+		assertf( dynamic_cast<SwitchStmt *>( enclosingControlStructures.back().get_controlStructure() ),
+				 "CFA internal error: control structure enclosing a case clause must be a switch, but is: %s",
+				 toCString( enclosingControlStructures.back().get_controlStructure() ) );
 		if ( caseStmt->isDefault() ) {
 			if ( enclosingControlStructures.back().isFallDefaultUsed() ) {
Index: src/ControlStruct/MLEMutator.h
===================================================================
--- src/ControlStruct/MLEMutator.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/MLEMutator.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Rodolfo G. Esteves
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Wed Jan 22 11:50:00 2020
-// Update Count     : 48
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Tue Feb  1 09:27:24 2022
+// Update Count     : 50
 //
 
@@ -42,6 +42,6 @@
 		void premutate( CompoundStmt *cmpndStmt );
 		Statement * postmutate( BranchStmt *branchStmt ) throw ( SemanticErrorException );
-		void premutate( WhileStmt *whileStmt );
-		Statement * postmutate( WhileStmt *whileStmt );
+		void premutate( WhileDoStmt *whileDoStmt );
+		Statement * postmutate( WhileDoStmt *whileDoStmt );
 		void premutate( ForStmt *forStmt );
 		Statement * postmutate( ForStmt *forStmt );
@@ -67,5 +67,5 @@
 				stmt( stmt ), breakExit( breakExit ), contExit( contExit ) {}
 
-			explicit Entry( WhileStmt *stmt, Label breakExit, Label contExit ) :
+			explicit Entry( WhileDoStmt *stmt, Label breakExit, Label contExit ) :
 				stmt( stmt ), breakExit( breakExit ), contExit( contExit ) {}
 
Index: src/ControlStruct/MultiLevelExit.cpp
===================================================================
--- src/ControlStruct/MultiLevelExit.cpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/MultiLevelExit.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Andrew Beach
 // Created On       : Mon Nov  1 13:48:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  8 10:56:00 2021
-// Update Count     : 2
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Feb  2 23:07:54 2022
+// Update Count     : 33
 //
 
@@ -18,21 +18,20 @@
 #include "AST/Pass.hpp"
 #include "AST/Stmt.hpp"
-#include "ControlStruct/LabelGenerator.h"
+#include "LabelGeneratorNew.hpp"
 
 #include <set>
+using namespace std;
+using namespace ast;
 
 namespace ControlStruct {
-
-namespace {
-
 class Entry {
-public:
-	const ast::Stmt * stmt;
-private:
+  public:
+	const Stmt * stmt;
+  private:
 	// Organized like a manual ADT. Avoids creating a bunch of dead data.
 	struct Target {
-		ast::Label label;
+		Label label;
 		bool used = false;
-		Target( const ast::Label & label ) : label( label ) {}
+		Target( const Label & label ) : label( label ) {}
 		Target() : label( CodeLocation() ) {}
 	};
@@ -41,49 +40,50 @@
 
 	enum Kind {
-		ForStmt, WhileStmt, CompoundStmt, IfStmt, CaseStmt, SwitchStmt, TryStmt
+		ForStmtK, WhileDoStmtK, CompoundStmtK, IfStmtK, CaseStmtK, SwitchStmtK, TryStmtK
 	} kind;
 
 	bool fallDefaultValid = true;
 
-	static ast::Label & useTarget( Target & target ) {
+	static Label & useTarget( Target & target ) {
 		target.used = true;
 		return target.label;
 	}
-
-public:
-	Entry( const ast::ForStmt * stmt, ast::Label breakExit, ast::Label contExit ) :
-		stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( ForStmt ) {}
-	Entry( const ast::WhileStmt * stmt, ast::Label breakExit, ast::Label contExit ) :
-		stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( WhileStmt ) {}
-	Entry( const ast::CompoundStmt *stmt, ast::Label breakExit ) :
-		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( CompoundStmt ) {}
-	Entry( const ast::IfStmt *stmt, ast::Label breakExit ) :
-		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmt ) {}
-	Entry( const ast::CaseStmt *stmt, ast::Label fallExit ) :
-		stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseStmt ) {}
-	Entry( const ast::SwitchStmt *stmt, ast::Label breakExit, ast::Label fallDefaultExit ) :
-		stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmt ) {}
-	Entry( const ast::TryStmt *stmt, ast::Label breakExit ) :
-		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( TryStmt ) {}
-
-	bool isContTarget() const { return kind <= WhileStmt; }
-	bool isBreakTarget() const { return CaseStmt != kind; }
-	bool isFallTarget() const { return CaseStmt == kind; }
-	bool isFallDefaultTarget() const { return SwitchStmt == kind; }
-
-	ast::Label useContExit() { assert( kind <= WhileStmt ); return useTarget(secondTarget); }
-	ast::Label useBreakExit() { assert( CaseStmt != kind ); return useTarget(firstTarget); }
-	ast::Label useFallExit() { assert( CaseStmt == kind );  return useTarget(firstTarget); }
-	ast::Label useFallDefaultExit() { assert( SwitchStmt == kind ); return useTarget(secondTarget); }
-
-	bool isContUsed() const { assert( kind <= WhileStmt ); return secondTarget.used; }
-	bool isBreakUsed() const { assert( CaseStmt != kind ); return firstTarget.used; }
-	bool isFallUsed() const { assert( CaseStmt == kind ); return firstTarget.used; }
-	bool isFallDefaultUsed() const { assert( SwitchStmt == kind ); return secondTarget.used; }
+  public:
+	Entry( const ForStmt * stmt, Label breakExit, Label contExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( ForStmtK ) {}
+	Entry( const WhileDoStmt * stmt, Label breakExit, Label contExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( WhileDoStmtK ) {}
+	Entry( const CompoundStmt *stmt, Label breakExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( CompoundStmtK ) {}
+	Entry( const IfStmt *stmt, Label breakExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmtK ) {}
+	Entry( const CaseStmt *stmt, Label fallExit ) :
+		stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseStmtK ) {}
+	Entry( const SwitchStmt *stmt, Label breakExit, Label fallDefaultExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmtK ) {}
+	Entry( const TryStmt *stmt, Label breakExit ) :
+		stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( TryStmtK ) {}
+
+	bool isContTarget() const { return kind <= WhileDoStmtK; }
+	bool isBreakTarget() const { return kind != CaseStmtK; }
+	bool isFallTarget() const { return kind == CaseStmtK; }
+	bool isFallDefaultTarget() const { return kind == SwitchStmtK; }
+
+	// These routines set a target as being "used" by a BranchStmt
+	Label useContExit() { assert( kind <= WhileDoStmtK ); return useTarget(secondTarget); }
+	Label useBreakExit() { assert( kind != CaseStmtK ); return useTarget(firstTarget); }
+	Label useFallExit() { assert( kind == CaseStmtK );  return useTarget(firstTarget); }
+	Label useFallDefaultExit() { assert( kind == SwitchStmtK ); return useTarget(secondTarget); }
+
+	// These routines check if a specific label for a statement is used by a BranchStmt
+	bool isContUsed() const { assert( kind <= WhileDoStmtK ); return secondTarget.used; }
+	bool isBreakUsed() const { assert( kind != CaseStmtK ); return firstTarget.used; }
+	bool isFallUsed() const { assert( kind == CaseStmtK ); return firstTarget.used; }
+	bool isFallDefaultUsed() const { assert( kind == SwitchStmtK ); return secondTarget.used; }
 	void seenDefault() { fallDefaultValid = false; }
 	bool isFallDefaultValid() const { return fallDefaultValid; }
 };
 
-// Helper predicates used in std::find_if calls (it doesn't take methods):
+// Helper predicates used in find_if calls (it doesn't take methods):
 bool isBreakTarget( const Entry & entry ) {
 	return entry.isBreakTarget();
@@ -103,32 +103,32 @@
 
 struct MultiLevelExitCore final :
-		public ast::WithVisitorRef<MultiLevelExitCore>,
-		public ast::WithShortCircuiting, public ast::WithGuards {
+	public WithVisitorRef<MultiLevelExitCore>,
+	public WithShortCircuiting, public WithGuards {
 	MultiLevelExitCore( const LabelToStmt & lt );
 
-	void previsit( const ast::FunctionDecl * );
-
-	const ast::CompoundStmt * previsit( const ast::CompoundStmt * );
-	const ast::BranchStmt * postvisit( const ast::BranchStmt * );
-	void previsit( const ast::WhileStmt * );
-	const ast::WhileStmt * postvisit( const ast::WhileStmt * );
-	void previsit( const ast::ForStmt * );
-	const ast::ForStmt * postvisit( const ast::ForStmt * );
-	const ast::CaseStmt * previsit( const ast::CaseStmt * );
-	void previsit( const ast::IfStmt * );
-	const ast::IfStmt * postvisit( const ast::IfStmt * );
-	void previsit( const ast::SwitchStmt * );
-	const ast::SwitchStmt * postvisit( const ast::SwitchStmt * );
-	void previsit( const ast::ReturnStmt * );
-	void previsit( const ast::TryStmt * );
-	void postvisit( const ast::TryStmt * );
-	void previsit( const ast::FinallyStmt * );
-
-	const ast::Stmt * mutateLoop( const ast::Stmt * body, Entry& );
+	void previsit( const FunctionDecl * );
+
+	const CompoundStmt * previsit( const CompoundStmt * );
+	const BranchStmt * postvisit( const BranchStmt * );
+	void previsit( const WhileDoStmt * );
+	const WhileDoStmt * postvisit( const WhileDoStmt * );
+	void previsit( const ForStmt * );
+	const ForStmt * postvisit( const ForStmt * );
+	const CaseStmt * previsit( const CaseStmt * );
+	void previsit( const IfStmt * );
+	const IfStmt * postvisit( const IfStmt * );
+	void previsit( const SwitchStmt * );
+	const SwitchStmt * postvisit( const SwitchStmt * );
+	void previsit( const ReturnStmt * );
+	void previsit( const TryStmt * );
+	void postvisit( const TryStmt * );
+	void previsit( const FinallyStmt * );
+
+	const Stmt * mutateLoop( const Stmt * body, Entry& );
 
 	const LabelToStmt & target_table;
-	std::set<ast::Label> fallthrough_labels;
-	std::vector<Entry> enclosing_control_structures;
-	ast::Label break_label;
+	set<Label> fallthrough_labels;
+	vector<Entry> enclosing_control_structures;
+	Label break_label;
 	bool inFinally;
 
@@ -138,17 +138,17 @@
 	const LoopNode * posthandleLoopStmt( const LoopNode * loopStmt );
 
-	std::list<ast::ptr<ast::Stmt>> fixBlock(
-		const std::list<ast::ptr<ast::Stmt>> & kids, bool caseClause );
+	list<ptr<Stmt>> fixBlock(
+		const list<ptr<Stmt>> & kids, bool caseClause );
 
 	template<typename UnaryPredicate>
 	auto findEnclosingControlStructure( UnaryPredicate pred ) {
-		return std::find_if( enclosing_control_structures.rbegin(),
-			enclosing_control_structures.rend(), pred );
+		return find_if( enclosing_control_structures.rbegin(),
+						enclosing_control_structures.rend(), pred );
 	}
 };
 
-ast::NullStmt * labelledNullStmt(
-		const CodeLocation & cl, const ast::Label & label ) {
-	return new ast::NullStmt( cl, std::vector<ast::Label>{ label } );
+NullStmt * labelledNullStmt(
+	const CodeLocation & cl, const Label & label ) {
+	return new NullStmt( cl, vector<Label>{ label } );
 }
 
@@ -158,26 +158,28 @@
 {}
 
-void MultiLevelExitCore::previsit( const ast::FunctionDecl * ) {
+void MultiLevelExitCore::previsit( const FunctionDecl * ) {
 	visit_children = false;
 }
 
-const ast::CompoundStmt * MultiLevelExitCore::previsit(
-		const ast::CompoundStmt * stmt ) {
+const CompoundStmt * MultiLevelExitCore::previsit(
+	const CompoundStmt * stmt ) {
 	visit_children = false;
-	bool isLabeled = !stmt->labels.empty();
+
+	// if the stmt is labelled then generate a label to check in postvisit if the label is used
+	bool isLabeled = ! stmt->labels.empty();
 	if ( isLabeled ) {
-		ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt );
+		Label breakLabel = newLabel( "blockBreak", stmt );
 		enclosing_control_structures.emplace_back( stmt, breakLabel );
 		GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
 	}
 
-	auto mutStmt = ast::mutate( stmt );
+	auto mutStmt = mutate( stmt );
 	// A child statement may set the break label.
-	mutStmt->kids = std::move( fixBlock( stmt->kids, false ) );
+	mutStmt->kids = move( fixBlock( stmt->kids, false ) );
 
 	if ( isLabeled ) {
-		assert( !enclosing_control_structures.empty() );
+		assert( ! enclosing_control_structures.empty() );
 		Entry & entry = enclosing_control_structures.back();
-		if ( !entry.useBreakExit().empty() ) {
+		if ( ! entry.useBreakExit().empty() ) {
 			break_label = entry.useBreakExit();
 		}
@@ -187,15 +189,15 @@
 
 size_t getUnusedIndex(
-		const ast::Stmt * stmt, const ast::Label & originalTarget ) {
+	const Stmt * stmt, const Label & originalTarget ) {
 	const size_t size = stmt->labels.size();
 
-	// If the label is empty, we can skip adding the unused attribute:
-	if ( originalTarget.empty() ) return size;
+	// If the label is empty, do not add unused attribute.
+  if ( originalTarget.empty() ) return size;
 
 	// Search for a label that matches the originalTarget.
 	for ( size_t i = 0 ; i < size ; ++i ) {
-		const ast::Label & label = stmt->labels[i];
+		const Label & label = stmt->labels[i];
 		if ( label == originalTarget ) {
-			for ( const ast::Attribute * attr : label.attributes ) {
+			for ( const Attribute * attr : label.attributes ) {
 				if ( attr->name == "unused" ) return size;
 			}
@@ -203,128 +205,128 @@
 		}
 	}
-	assertf( false, "Could not find label '%s' on statement %s",
-		originalTarget.name.c_str(), toString( stmt ).c_str() );
-}
-
-const ast::Stmt * addUnused(
-		const ast::Stmt * stmt, const ast::Label & originalTarget ) {
+	assertf( false, "CFA internal error: could not find label '%s' on statement %s",
+			 originalTarget.name.c_str(), toString( stmt ).c_str() );
+}
+
+const Stmt * addUnused(
+	const Stmt * stmt, const Label & originalTarget ) {
 	size_t i = getUnusedIndex( stmt, originalTarget );
 	if ( i == stmt->labels.size() ) {
 		return stmt;
 	}
-	ast::Stmt * mutStmt = ast::mutate( stmt );
-	mutStmt->labels[i].attributes.push_back( new ast::Attribute( "unused" ) );
+	Stmt * mutStmt = mutate( stmt );
+	mutStmt->labels[i].attributes.push_back( new Attribute( "unused" ) );
 	return mutStmt;
 }
 
-const ast::BranchStmt * MultiLevelExitCore::postvisit( const ast::BranchStmt * stmt ) {
-	std::vector<Entry>::reverse_iterator targetEntry =
+// This routine updates targets on enclosing control structures to indicate which
+//     label is used by the BranchStmt that is passed
+const BranchStmt * MultiLevelExitCore::postvisit( const BranchStmt * stmt ) {
+	vector<Entry>::reverse_iterator targetEntry =
 		enclosing_control_structures.rend();
+
+	// Labels on different stmts require different approaches to access
 	switch ( stmt->kind ) {
-	case ast::BranchStmt::Goto:
+	  case BranchStmt::Goto:
 		return stmt;
-	case ast::BranchStmt::Continue:
-	case ast::BranchStmt::Break: {
-		bool isContinue = stmt->kind == ast::BranchStmt::Continue;
-		// Handle unlabeled break and continue.
-		if ( stmt->target.empty() ) {
-			if ( isContinue ) {
-				targetEntry = findEnclosingControlStructure( isContinueTarget );
-			} else {
-				if ( enclosing_control_structures.empty() ) {
-					SemanticError( stmt->location,
-						"'break' outside a loop, 'switch', or labelled block" );
-				}
-				targetEntry = findEnclosingControlStructure( isBreakTarget );
-			}
-		// Handle labeled break and continue.
-		} else {
-			// Lookup label in table to find attached control structure.
-			targetEntry = findEnclosingControlStructure(
-				[ targetStmt = target_table.at(stmt->target) ](auto entry){
-					return entry.stmt == targetStmt;
-				} );
-		}
-		// Ensure that selected target is valid.
-		if ( targetEntry == enclosing_control_structures.rend() || ( isContinue && !isContinueTarget( *targetEntry ) ) ) {
-			SemanticError(
-				stmt->location,
-				toString( (isContinue ? "'continue'" : "'break'"),
-					" target must be an enclosing ",
-					(isContinue ? "loop: " : "control structure: "),
-					stmt->originalTarget ) );
-		}
-		break;
-	}
-	case ast::BranchStmt::FallThrough: {
-		targetEntry = findEnclosingControlStructure( isFallthroughTarget );
-		// Check that target is valid.
-		if ( targetEntry == enclosing_control_structures.rend() ) {
-			SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
-		}
-		if ( !stmt->target.empty() ) {
-			// Labelled fallthrough: target must be a valid fallthough label.
-			if ( !fallthrough_labels.count( stmt->target ) ) {
-				SemanticError( stmt->location, toString( "'fallthrough' target must be a later case statement: ", stmt->originalTarget ) );
-			}
-			return new ast::BranchStmt(
-				stmt->location, ast::BranchStmt::Goto, stmt->originalTarget );
-		}
-		break;
-	}
-	case ast::BranchStmt::FallThroughDefault: {
-		targetEntry = findEnclosingControlStructure( isFallthroughDefaultTarget );
-
-		// Check that this is in a switch or choose statement.
-		if ( targetEntry == enclosing_control_structures.rend() ) {
-			SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
-		}
-
-		// Check that the switch or choose has a default clause.
-		auto switchStmt = strict_dynamic_cast< const ast::SwitchStmt * >(
-			targetEntry->stmt );
-		bool foundDefault = false;
-		for ( auto subStmt : switchStmt->stmts ) {
-			const ast::CaseStmt * caseStmt = subStmt.strict_as<ast::CaseStmt>();
-			if ( caseStmt->isDefault() ) {
-				foundDefault = true;
-				break;
-			}
-		}
-		if ( !foundDefault ) {
-			SemanticError( stmt->location, "'fallthrough default' must be enclosed in a 'switch' or 'choose' control structure with a 'default' clause" );
-		}
-		break;
-	}
-	default:
+	  case BranchStmt::Continue:
+	  case BranchStmt::Break: {
+		  bool isContinue = stmt->kind == BranchStmt::Continue;
+		  // Handle unlabeled break and continue.
+		  if ( stmt->target.empty() ) {
+			  if ( isContinue ) {
+				  targetEntry = findEnclosingControlStructure( isContinueTarget );
+			  } else {
+				  if ( enclosing_control_structures.empty() ) {
+					  SemanticError( stmt->location,
+									 "'break' outside a loop, 'switch', or labelled block" );
+				  }
+				  targetEntry = findEnclosingControlStructure( isBreakTarget );
+			  }
+			  // Handle labeled break and continue.
+		  } else {
+			  // Lookup label in table to find attached control structure.
+			  targetEntry = findEnclosingControlStructure(
+				  [ targetStmt = target_table.at(stmt->target) ](auto entry){
+					  return entry.stmt == targetStmt;
+				  } );
+		  }
+		  // Ensure that selected target is valid.
+		  if ( targetEntry == enclosing_control_structures.rend() || ( isContinue && ! isContinueTarget( *targetEntry ) ) ) {
+			  SemanticError( stmt->location, toString( (isContinue ? "'continue'" : "'break'"),
+							" target must be an enclosing ", (isContinue ? "loop: " : "control structure: "),
+							stmt->originalTarget ) );
+		  }
+		  break;
+	  }
+	  // handle fallthrough in case/switch stmts
+	  case BranchStmt::FallThrough: {
+		  targetEntry = findEnclosingControlStructure( isFallthroughTarget );
+		  // Check that target is valid.
+		  if ( targetEntry == enclosing_control_structures.rend() ) {
+			  SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
+		  }
+		  if ( ! stmt->target.empty() ) {
+			  // Labelled fallthrough: target must be a valid fallthough label.
+			  if ( ! fallthrough_labels.count( stmt->target ) ) {
+				  SemanticError( stmt->location, toString( "'fallthrough' target must be a later case statement: ",
+														   stmt->originalTarget ) );
+			  }
+			  return new BranchStmt( stmt->location, BranchStmt::Goto, stmt->originalTarget );
+		  }
+		  break;
+	  }
+	  case BranchStmt::FallThroughDefault: {
+		  targetEntry = findEnclosingControlStructure( isFallthroughDefaultTarget );
+
+		  // Check if in switch or choose statement.
+		  if ( targetEntry == enclosing_control_structures.rend() ) {
+			  SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" );
+		  }
+
+		  // Check if switch or choose has default clause.
+		  auto switchStmt = strict_dynamic_cast< const SwitchStmt * >( targetEntry->stmt );
+		  bool foundDefault = false;
+		  for ( auto subStmt : switchStmt->stmts ) {
+			  const CaseStmt * caseStmt = subStmt.strict_as<CaseStmt>();
+			  if ( caseStmt->isDefault() ) {
+				  foundDefault = true;
+				  break;
+			  }
+		  }
+		  if ( ! foundDefault ) {
+			  SemanticError( stmt->location, "'fallthrough default' must be enclosed in a 'switch' or 'choose'"
+							 "control structure with a 'default' clause" );
+		  }
+		  break;
+	  }
+	  default:
 		assert( false );
 	}
 
-	// Branch error checks: get the appropriate label name:
-	// (This label will always be replaced.)
-	ast::Label exitLabel( CodeLocation(), "" );
+	// Branch error checks: get the appropriate label name, which is always replaced.
+	Label exitLabel( CodeLocation(), "" );
 	switch ( stmt->kind ) {
-	case ast::BranchStmt::Break:
-		assert( !targetEntry->useBreakExit().empty() );
+	  case BranchStmt::Break:
+		assert( ! targetEntry->useBreakExit().empty() );
 		exitLabel = targetEntry->useBreakExit();
 		break;
-	case ast::BranchStmt::Continue:
-		assert( !targetEntry->useContExit().empty() );
+	  case BranchStmt::Continue:
+		assert( ! targetEntry->useContExit().empty() );
 		exitLabel = targetEntry->useContExit();
 		break;
-	case ast::BranchStmt::FallThrough:
-		assert( !targetEntry->useFallExit().empty() );
+	  case BranchStmt::FallThrough:
+		assert( ! targetEntry->useFallExit().empty() );
 		exitLabel = targetEntry->useFallExit();
 		break;
-	case ast::BranchStmt::FallThroughDefault:
-		assert( !targetEntry->useFallDefaultExit().empty() );
+	  case BranchStmt::FallThroughDefault:
+		assert( ! targetEntry->useFallDefaultExit().empty() );
 		exitLabel = targetEntry->useFallDefaultExit();
 		// Check that fallthrough default comes before the default clause.
-		if ( !targetEntry->isFallDefaultValid() ) {
-			SemanticError( stmt->location,
-				"'fallthrough default' must precede the 'default' clause" );
+		if ( ! targetEntry->isFallDefaultValid() ) {
+			SemanticError( stmt->location, "'fallthrough default' must precede the 'default' clause" );
 		}
 		break;
-	default:
+	  default:
 		assert(0);
 	}
@@ -333,21 +335,21 @@
 	targetEntry->stmt = addUnused( targetEntry->stmt, stmt->originalTarget );
 
-	// Replace this with a goto to make later passes more uniform.
-	return new ast::BranchStmt( stmt->location, ast::BranchStmt::Goto, exitLabel );
-}
-
-void MultiLevelExitCore::previsit( const ast::WhileStmt * stmt ) {
+	// Replace with goto to make later passes more uniform.
+	return new BranchStmt( stmt->location, BranchStmt::Goto, exitLabel );
+}
+
+void MultiLevelExitCore::previsit( const WhileDoStmt * stmt ) {
 	return prehandleLoopStmt( stmt );
 }
 
-const ast::WhileStmt * MultiLevelExitCore::postvisit( const ast::WhileStmt * stmt ) {
+const WhileDoStmt * MultiLevelExitCore::postvisit( const WhileDoStmt * stmt ) {
 	return posthandleLoopStmt( stmt );
 }
 
-void MultiLevelExitCore::previsit( const ast::ForStmt * stmt ) {
+void MultiLevelExitCore::previsit( const ForStmt * stmt ) {
 	return prehandleLoopStmt( stmt );
 }
 
-const ast::ForStmt * MultiLevelExitCore::postvisit( const ast::ForStmt * stmt ) {
+const ForStmt * MultiLevelExitCore::postvisit( const ForStmt * stmt ) {
 	return posthandleLoopStmt( stmt );
 }
@@ -355,58 +357,55 @@
 // Mimic what the built-in push_front would do anyways. It is O(n).
 void push_front(
-		std::vector<ast::ptr<ast::Stmt>> & vec, const ast::Stmt * element ) {
+	vector<ptr<Stmt>> & vec, const Stmt * element ) {
 	vec.emplace_back( nullptr );
 	for ( size_t i = vec.size() - 1 ; 0 < i ; --i ) {
-		vec[ i ] = std::move( vec[ i - 1 ] );
+		vec[ i ] = move( vec[ i - 1 ] );
 	}
 	vec[ 0 ] = element;
 }
 
-const ast::CaseStmt * MultiLevelExitCore::previsit( const ast::CaseStmt * stmt ) {
+const CaseStmt * MultiLevelExitCore::previsit( const CaseStmt * stmt ) {
 	visit_children = false;
 
-	// If it is the default, mark the default as seen.
+	// If default, mark seen.
 	if ( stmt->isDefault() ) {
-		assert( !enclosing_control_structures.empty() );
+		assert( ! enclosing_control_structures.empty() );
 		enclosing_control_structures.back().seenDefault();
 	}
 
 	// The cond may not exist, but if it does update it now.
-	visitor->maybe_accept( stmt, &ast::CaseStmt::cond );
+	visitor->maybe_accept( stmt, &CaseStmt::cond );
 
 	// Just save the mutated node for simplicity.
-	ast::CaseStmt * mutStmt = ast::mutate( stmt );
-
-	ast::Label fallLabel = LabelGenerator::newLabel( "fallThrough", stmt );
-	if ( !mutStmt->stmts.empty() ) {
+	CaseStmt * mutStmt = mutate( stmt );
+
+	Label fallLabel = newLabel( "fallThrough", stmt );
+	if ( ! mutStmt->stmts.empty() ) {
 		// Ensure that the stack isn't corrupted by exceptions in fixBlock.
 		auto guard = makeFuncGuard(
 			[&](){ enclosing_control_structures.emplace_back( mutStmt, fallLabel ); },
 			[this](){ enclosing_control_structures.pop_back(); }
-		);
+			);
 
 		// These should already be in a block.
-		auto block = ast::mutate( mutStmt->stmts.front().strict_as<ast::CompoundStmt>() );
+		auto block = mutate( mutStmt->stmts.front().strict_as<CompoundStmt>() );
 		block->kids = fixBlock( block->kids, true );
 
 		// Add fallthrough label if necessary.
-		assert( !enclosing_control_structures.empty() );
+		assert( ! enclosing_control_structures.empty() );
 		Entry & entry = enclosing_control_structures.back();
 		if ( entry.isFallUsed() ) {
-			mutStmt->stmts.push_back(
-				labelledNullStmt( mutStmt->location, entry.useFallExit() ) );
-		}
-	}
-	assert( !enclosing_control_structures.empty() );
+			mutStmt->stmts.push_back( labelledNullStmt( mutStmt->location, entry.useFallExit() ) );
+		}
+	}
+	assert( ! enclosing_control_structures.empty() );
 	Entry & entry = enclosing_control_structures.back();
-	assertf( dynamic_cast< const ast::SwitchStmt * >( entry.stmt ),
-		"Control structure enclosing a case clause must be a switch, but is: %s",
-		toString( entry.stmt ).c_str() );
+	assertf( dynamic_cast< const SwitchStmt * >( entry.stmt ),
+			 "CFA internal error: control structure enclosing a case clause must be a switch, but is: %s",
+			 toString( entry.stmt ).c_str() );
 	if ( mutStmt->isDefault() ) {
 		if ( entry.isFallDefaultUsed() ) {
 			// Add fallthrough default label if necessary.
-			push_front( mutStmt->stmts, labelledNullStmt(
-				stmt->location, entry.useFallDefaultExit()
-			) );
+			push_front( mutStmt->stmts, labelledNullStmt( stmt->location, entry.useFallDefaultExit() ) );
 		}
 	}
@@ -414,8 +413,8 @@
 }
 
-void MultiLevelExitCore::previsit( const ast::IfStmt * stmt ) {
-	bool labeledBlock = !stmt->labels.empty();
+void MultiLevelExitCore::previsit( const IfStmt * stmt ) {
+	bool labeledBlock = ! stmt->labels.empty();
 	if ( labeledBlock ) {
-		ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt );
+		Label breakLabel = newLabel( "blockBreak", stmt );
 		enclosing_control_structures.emplace_back( stmt, breakLabel );
 		GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
@@ -423,9 +422,9 @@
 }
 
-const ast::IfStmt * MultiLevelExitCore::postvisit( const ast::IfStmt * stmt ) {
-	bool labeledBlock = !stmt->labels.empty();
+const IfStmt * MultiLevelExitCore::postvisit( const IfStmt * stmt ) {
+	bool labeledBlock = ! stmt->labels.empty();
 	if ( labeledBlock ) {
 		auto this_label = enclosing_control_structures.back().useBreakExit();
-		if ( !this_label.empty() ) {
+		if ( ! this_label.empty() ) {
 			break_label = this_label;
 		}
@@ -434,29 +433,26 @@
 }
 
-bool isDefaultCase( const ast::ptr<ast::Stmt> & stmt ) {
-	const ast::CaseStmt * caseStmt = stmt.strict_as<ast::CaseStmt>();
+bool isDefaultCase( const ptr<Stmt> & stmt ) {
+	const CaseStmt * caseStmt = stmt.strict_as<CaseStmt>();
 	return caseStmt->isDefault();
 }
 
-void MultiLevelExitCore::previsit( const ast::SwitchStmt * stmt ) {
-	ast::Label label = LabelGenerator::newLabel( "switchBreak", stmt );
-	auto it = std::find_if( stmt->stmts.rbegin(), stmt->stmts.rend(), isDefaultCase );
-
-	const ast::CaseStmt * defaultCase = it != stmt->stmts.rend()
-		? (it)->strict_as<ast::CaseStmt>() : nullptr;
-	ast::Label defaultLabel = defaultCase
-		? LabelGenerator::newLabel( "fallThroughDefault", defaultCase )
-		: ast::Label( stmt->location, "" );
+void MultiLevelExitCore::previsit( const SwitchStmt * stmt ) {
+	Label label = newLabel( "switchBreak", stmt );
+	auto it = find_if( stmt->stmts.rbegin(), stmt->stmts.rend(), isDefaultCase );
+
+	const CaseStmt * defaultCase = it != stmt->stmts.rend() ? (it)->strict_as<CaseStmt>() : nullptr;
+	Label defaultLabel = defaultCase ? newLabel( "fallThroughDefault", defaultCase ) : Label( stmt->location, "" );
 	enclosing_control_structures.emplace_back( stmt, label, defaultLabel );
 	GuardAction( [this]() { enclosing_control_structures.pop_back(); } );
 
-	// Collect valid labels for fallthrough. It starts with all labels at
-	// this level, then removed as we see them in traversal.
-	for ( const ast::Stmt * stmt : stmt->stmts ) {
-		auto * caseStmt = strict_dynamic_cast< const ast::CaseStmt * >( stmt );
+	// Collect valid labels for fallthrough. It starts with all labels at this level, then remove as each is seen during
+	// traversal.
+	for ( const Stmt * stmt : stmt->stmts ) {
+		auto * caseStmt = strict_dynamic_cast< const CaseStmt * >( stmt );
 		if ( caseStmt->stmts.empty() ) continue;
-		auto block = caseStmt->stmts.front().strict_as<ast::CompoundStmt>();
-		for ( const ast::Stmt * stmt : block->kids ) {
-			for ( const ast::Label & l : stmt->labels ) {
+		auto block = caseStmt->stmts.front().strict_as<CompoundStmt>();
+		for ( const Stmt * stmt : block->kids ) {
+			for ( const Label & l : stmt->labels ) {
 				fallthrough_labels.insert( l );
 			}
@@ -465,26 +461,24 @@
 }
 
-const ast::SwitchStmt * MultiLevelExitCore::postvisit( const ast::SwitchStmt * stmt ) {
-	assert( !enclosing_control_structures.empty() );
+const SwitchStmt * MultiLevelExitCore::postvisit( const SwitchStmt * stmt ) {
+	assert( ! enclosing_control_structures.empty() );
 	Entry & entry = enclosing_control_structures.back();
 	assert( entry.stmt == stmt );
 
-	// Only run if we need to generate the break label.
+	// Only run to generate the break label.
 	if ( entry.isBreakUsed() ) {
-		// To keep the switch statements uniform (all direct children of a
-		// SwitchStmt should be CastStmts), append the exit label and break
-		// to the last case, create a default case is there are no cases.
-		ast::SwitchStmt * mutStmt = ast::mutate( stmt );
+		// To keep the switch statements uniform (all direct children of a SwitchStmt should be CastStmts), append the
+		// exit label and break to the last case, create a default case if no cases.
+		SwitchStmt * mutStmt = mutate( stmt );
 		if ( mutStmt->stmts.empty() ) {
-			mutStmt->stmts.push_back( new ast::CaseStmt(
-				mutStmt->location, nullptr, {} ));
-		}
-
-		auto caseStmt = mutStmt->stmts.back().strict_as<ast::CaseStmt>();
-		auto mutCase = ast::mutate( caseStmt );
+			mutStmt->stmts.push_back( new CaseStmt( mutStmt->location, nullptr, {} ) );
+		}
+
+		auto caseStmt = mutStmt->stmts.back().strict_as<CaseStmt>();
+		auto mutCase = mutate( caseStmt );
 		mutStmt->stmts.back() = mutCase;
 
-		ast::Label label( mutCase->location, "breakLabel" );
-		auto branch = new ast::BranchStmt( mutCase->location, ast::BranchStmt::Break, label );
+		Label label( mutCase->location, "breakLabel" );
+		auto branch = new BranchStmt( mutCase->location, BranchStmt::Break, label );
 		branch->labels.push_back( entry.useBreakExit() );
 		mutCase->stmts.push_back( branch );
@@ -495,5 +489,5 @@
 }
 
-void MultiLevelExitCore::previsit( const ast::ReturnStmt * stmt ) {
+void MultiLevelExitCore::previsit( const ReturnStmt * stmt ) {
 	if ( inFinally ) {
 		SemanticError( stmt->location, "'return' may not appear in a finally clause" );
@@ -501,8 +495,8 @@
 }
 
-void MultiLevelExitCore::previsit( const ast::TryStmt * stmt ) {
-	bool isLabeled = !stmt->labels.empty();
+void MultiLevelExitCore::previsit( const TryStmt * stmt ) {
+	bool isLabeled = ! stmt->labels.empty();
 	if ( isLabeled ) {
-		ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt );
+		Label breakLabel = newLabel( "blockBreak", stmt );
 		enclosing_control_structures.emplace_back( stmt, breakLabel );
 		GuardAction([this](){ enclosing_control_structures.pop_back(); } );
@@ -510,9 +504,9 @@
 }
 
-void MultiLevelExitCore::postvisit( const ast::TryStmt * stmt ) {
-	bool isLabeled = !stmt->labels.empty();
+void MultiLevelExitCore::postvisit( const TryStmt * stmt ) {
+	bool isLabeled = ! stmt->labels.empty();
 	if ( isLabeled ) {
 		auto this_label = enclosing_control_structures.back().useBreakExit();
-		if ( !this_label.empty() ) {
+		if ( ! this_label.empty() ) {
 			break_label = this_label;
 		}
@@ -520,23 +514,30 @@
 }
 
-void MultiLevelExitCore::previsit( const ast::FinallyStmt * ) {
-	GuardAction([this, old = std::move(enclosing_control_structures)](){
-		enclosing_control_structures = std::move(old);
-	});
-	enclosing_control_structures = std::vector<Entry>();
+void MultiLevelExitCore::previsit( const FinallyStmt * ) {
+	GuardAction([this, old = move( enclosing_control_structures)](){ enclosing_control_structures = move(old); });
+	enclosing_control_structures = vector<Entry>();
 	GuardValue( inFinally ) = true;
 }
 
-const ast::Stmt * MultiLevelExitCore::mutateLoop(
-		const ast::Stmt * body, Entry & entry ) {
+const Stmt * MultiLevelExitCore::mutateLoop(
+	const Stmt * body, Entry & entry ) {
 	if ( entry.isBreakUsed() ) {
 		break_label = entry.useBreakExit();
 	}
 
+	// if continue is used insert a continue label into the back of the body of the loop
 	if ( entry.isContUsed() ) {
-		ast::CompoundStmt * new_body = new ast::CompoundStmt( body->location );
+		CompoundStmt * new_body = new CompoundStmt( body->location );
+		// {}
 		new_body->kids.push_back( body );
+		// {
+		//  body
+		// }
 		new_body->kids.push_back(
 			labelledNullStmt( body->location, entry.useContExit() ) );
+		// {
+		//  body
+		//  ContinueLabel: {}
+		// }
 		return new_body;
 	}
@@ -549,7 +550,11 @@
 	// Remember is loop before going onto mutate the body.
 	// The labels will be folded in if they are used.
-	ast::Label breakLabel = LabelGenerator::newLabel( "loopBreak", loopStmt );
-	ast::Label contLabel = LabelGenerator::newLabel( "loopContinue", loopStmt );
+	Label breakLabel = newLabel( "loopBreak", loopStmt );
+	Label contLabel = newLabel( "loopContinue", loopStmt );
 	enclosing_control_structures.emplace_back( loopStmt, breakLabel, contLabel );
+	// labels are added temporarily to see if they are used and then added permanently in postvisit if ther are used
+	// children will tag labels as being used during their traversal which occurs before postvisit
+
+	// GuardAction calls the lambda after the node is done being visited
 	GuardAction( [this](){ enclosing_control_structures.pop_back(); } );
 }
@@ -557,25 +562,27 @@
 template<typename LoopNode>
 const LoopNode * MultiLevelExitCore::posthandleLoopStmt( const LoopNode * loopStmt ) {
-	assert( !enclosing_control_structures.empty() );
+	assert( ! enclosing_control_structures.empty() );
 	Entry & entry = enclosing_control_structures.back();
 	assert( entry.stmt == loopStmt );
 
-	// Now we check if the labels are used and add them if so.
-	return ast::mutate_field(
-		loopStmt, &LoopNode::body, mutateLoop( loopStmt->body, entry ) );
-}
-
-std::list<ast::ptr<ast::Stmt>> MultiLevelExitCore::fixBlock(
-		const std::list<ast::ptr<ast::Stmt>> & kids, bool is_case_clause ) {
-	// Unfortunately we can't use the automatic error collection.
+	// Now check if the labels are used and add them if so.
+	return mutate_field( loopStmt, &LoopNode::body, mutateLoop( loopStmt->body, entry ) );
+	// this call to mutate_field compares loopStmt->body and the result of mutateLoop
+	// 		if they are the same the node isn't mutated, if they differ then the new mutated node is returned
+	// 		the stmts will only differ if a label is used
+}
+
+list<ptr<Stmt>> MultiLevelExitCore::fixBlock(
+	const list<ptr<Stmt>> & kids, bool is_case_clause ) {
+	// Unfortunately cannot use automatic error collection.
 	SemanticErrorException errors;
 
-	std::list<ast::ptr<ast::Stmt>> ret;
+	list<ptr<Stmt>> ret;
 
 	// Manually visit each child.
-	for ( const ast::ptr<ast::Stmt> & kid : kids ) {
+	for ( const ptr<Stmt> & kid : kids ) {
 		if ( is_case_clause ) {
 			// Once a label is seen, it's no longer a valid for fallthrough.
-			for ( const ast::Label & l : kid->labels ) {
+			for ( const Label & l : kid->labels ) {
 				fallthrough_labels.erase( l );
 			}
@@ -588,12 +595,11 @@
 		}
 
-		if ( !break_label.empty() ) {
-			ret.push_back(
-				labelledNullStmt( ret.back()->location, break_label ) );
-			break_label = ast::Label( CodeLocation(), "" );
-		}
-	}
-
-	if ( !errors.isEmpty() ) {
+		if ( ! break_label.empty() ) {
+			ret.push_back( labelledNullStmt( ret.back()->location, break_label ) );
+			break_label = Label( CodeLocation(), "" );
+		}
+	}
+
+	if ( ! errors.isEmpty() ) {
 		throw errors;
 	}
@@ -601,15 +607,12 @@
 }
 
-} // namespace
-
-const ast::CompoundStmt * multiLevelExitUpdate(
-    	const ast::CompoundStmt * stmt,
-		const LabelToStmt & labelTable ) {
+const CompoundStmt * multiLevelExitUpdate(
+	const CompoundStmt * stmt,
+	const LabelToStmt & labelTable ) {
 	// Must start in the body, so FunctionDecls can be a stopping point.
-	ast::Pass<MultiLevelExitCore> visitor( labelTable );
-	const ast::CompoundStmt * ret = stmt->accept( visitor );
+	Pass<MultiLevelExitCore> visitor( labelTable );
+	const CompoundStmt * ret = stmt->accept( visitor );
 	return ret;
 }
-
 } // namespace ControlStruct
 
Index: src/ControlStruct/MultiLevelExit.hpp
===================================================================
--- src/ControlStruct/MultiLevelExit.hpp	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/MultiLevelExit.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Andrew Beach
 // Created On       : Mon Nov  1 13:49:00 2021
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Nov  8 10:53:00 2021
-// Update Count     : 3
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Mon Jan 31 22:34:06 2022
+// Update Count     : 6
 //
 
@@ -19,17 +19,14 @@
 
 namespace ast {
-	class CompoundStmt;
-	class Label;
-	class Stmt;
+class CompoundStmt;
+class Label;
+class Stmt;
 }
 
 namespace ControlStruct {
-
 using LabelToStmt = std::map<ast::Label, const ast::Stmt *>;
 
-/// Mutate a function body to handle multi-level exits.
-const ast::CompoundStmt * multiLevelExitUpdate(
-	const ast::CompoundStmt *, const LabelToStmt & );
-
+// Mutate a function body to handle multi-level exits.
+const ast::CompoundStmt * multiLevelExitUpdate(	const ast::CompoundStmt *, const LabelToStmt & );
 }
 
Index: src/ControlStruct/module.mk
===================================================================
--- src/ControlStruct/module.mk	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ControlStruct/module.mk	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,7 +10,7 @@
 ## Author           : Richard C. Bilson
 ## Created On       : Mon Jun  1 17:49:17 2015
-## Last Modified By : Henry Xue
-## Last Modified On : Tue Jul 20 04:10:50 2021
-## Update Count     : 5
+## Last Modified By : Peter A. Buhr
+## Last Modified On : Sat Jan 29 12:04:19 2022
+## Update Count     : 7
 ###############################################################################
 
@@ -22,8 +22,12 @@
 	ControlStruct/ForExprMutator.cc \
 	ControlStruct/ForExprMutator.h \
+	ControlStruct/HoistControlDecls.cpp \
+	ControlStruct/HoistControlDecls.hpp \
 	ControlStruct/LabelFixer.cc \
 	ControlStruct/LabelFixer.h \
 	ControlStruct/LabelGenerator.cc \
 	ControlStruct/LabelGenerator.h \
+	ControlStruct/LabelGeneratorNew.cpp \
+	ControlStruct/LabelGeneratorNew.hpp \
 	ControlStruct/MLEMutator.cc \
 	ControlStruct/MLEMutator.h \
Index: src/InitTweak/InitTweak.cc
===================================================================
--- src/InitTweak/InitTweak.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/InitTweak/InitTweak.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Fri May 13 11:26:36 2016
 // Last Modified By : Andrew Beach
-// Last Modified On : Fri Nov 19 19:22:00 2021
-// Update Count     : 19
+// Last Modified On : Mon Dec  6 13:21:00 2021
+// Update Count     : 20
 //
 
@@ -1191,14 +1191,30 @@
 	}
 
-	bool isCopyFunction( const ast::FunctionDecl * decl ) {
-		const ast::FunctionType * ftype = decl->type;
-		if ( ftype->params.size() != 2 ) return false;
-
-		const ast::Type * t1 = getPointerBase( ftype->params.front() );
-		if ( ! t1 ) return false;
-		const ast::Type * t2 = ftype->params.back();
-
-		return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} );
-	}
+bool isAssignment( const ast::FunctionDecl * decl ) {
+	return isAssignment( decl->name ) && isCopyFunction( decl );
+}
+
+bool isDestructor( const ast::FunctionDecl * decl ) {
+	return isDestructor( decl->name );
+}
+
+bool isDefaultConstructor( const ast::FunctionDecl * decl ) {
+	return isConstructor( decl->name ) && 1 == decl->params.size();
+}
+
+bool isCopyConstructor( const ast::FunctionDecl * decl ) {
+	return isConstructor( decl->name ) && 2 == decl->params.size();
+}
+
+bool isCopyFunction( const ast::FunctionDecl * decl ) {
+	const ast::FunctionType * ftype = decl->type;
+	if ( ftype->params.size() != 2 ) return false;
+
+	const ast::Type * t1 = getPointerBase( ftype->params.front() );
+	if ( ! t1 ) return false;
+	const ast::Type * t2 = ftype->params.back();
+
+	return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} );
+}
 
 	const FunctionDecl * isAssignment( const Declaration * decl ) {
Index: src/InitTweak/InitTweak.h
===================================================================
--- src/InitTweak/InitTweak.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/InitTweak/InitTweak.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Fri May 13 11:26:36 2016
 // Last Modified By : Andrew Beach
-// Last Modified On : Fri Nov 19 14:18:00 2021
-// Update Count     : 7
+// Last Modified On : Mon Dec  6 13:20:00 2021
+// Update Count     : 8
 //
 
@@ -31,4 +31,8 @@
 	const FunctionDecl * isCopyConstructor( const Declaration * decl );
 	const FunctionDecl * isCopyFunction( const Declaration * decl, const std::string & fname );
+	bool isAssignment( const ast::FunctionDecl * decl );
+	bool isDestructor( const ast::FunctionDecl * decl );
+	bool isDefaultConstructor( const ast::FunctionDecl * decl );
+	bool isCopyConstructor( const ast::FunctionDecl * decl );
 	bool isCopyFunction( const ast::FunctionDecl * decl );
 
Index: src/Parser/ParseNode.h
===================================================================
--- src/Parser/ParseNode.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/Parser/ParseNode.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Sat May 16 13:28:16 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Wed Jul 14 17:28:53 2021
-// Update Count     : 900
+// Last Modified On : Wed Feb  2 09:15:49 2022
+// Update Count     : 905
 //
 
@@ -390,6 +390,6 @@
 Statement * build_expr( ExpressionNode * ctl );
 
-struct IfCtrl {
-	IfCtrl( DeclarationNode * decl, ExpressionNode * condition ) :
+struct CondCtl {
+	CondCtl( DeclarationNode * decl, ExpressionNode * condition ) :
 		init( decl ? new StatementNode( decl ) : nullptr ), condition( condition ) {}
 
@@ -409,12 +409,12 @@
 };
 
-Expression * build_if_control( IfCtrl * ctl, std::list< Statement * > & init );
-Statement * build_if( IfCtrl * ctl, StatementNode * then_stmt, StatementNode * else_stmt );
+Expression * build_if_control( CondCtl * ctl, std::list< Statement * > & init );
+Statement * build_if( CondCtl * ctl, StatementNode * then, StatementNode * else_ );
 Statement * build_switch( bool isSwitch, ExpressionNode * ctl, StatementNode * stmt );
 Statement * build_case( ExpressionNode * ctl );
 Statement * build_default();
-Statement * build_while( IfCtrl * ctl, StatementNode * stmt );
-Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt );
-Statement * build_for( ForCtrl * forctl, StatementNode * stmt );
+Statement * build_while( CondCtl * ctl, StatementNode * stmt, StatementNode * else_ = nullptr );
+Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ = nullptr );
+Statement * build_for( ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ = nullptr );
 Statement * build_branch( BranchStmt::Type kind );
 Statement * build_branch( std::string * identifier, BranchStmt::Type kind );
@@ -424,6 +424,6 @@
 Statement * build_resume( ExpressionNode * ctl );
 Statement * build_resume_at( ExpressionNode * ctl , ExpressionNode * target );
-Statement * build_try( StatementNode * try_stmt, StatementNode * catch_stmt, StatementNode * finally_stmt );
-Statement * build_catch( CatchStmt::Kind kind, DeclarationNode *decl, ExpressionNode *cond, StatementNode *body );
+Statement * build_try( StatementNode * try_, StatementNode * catch_, StatementNode * finally_ );
+Statement * build_catch( CatchStmt::Kind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body );
 Statement * build_finally( StatementNode * stmt );
 Statement * build_compound( StatementNode * first );
Index: src/Parser/StatementNode.cc
===================================================================
--- src/Parser/StatementNode.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/Parser/StatementNode.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -5,11 +5,12 @@
 // file "LICENCE" distributed with Cforall.
 //
-// StatementNode.cc --
+// StatementNode.cc -- Transform from parse data-structures to AST data-structures, usually deleting the parse
+//     data-structure after the transformation.
 //
 // Author           : Rodolfo G. Esteves
 // Created On       : Sat May 16 14:59:41 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Oct 24 04:20:55 2020
-// Update Count     : 383
+// Last Modified On : Wed Feb  2 20:29:30 2022
+// Update Count     : 425
 //
 
@@ -63,5 +64,5 @@
 	// convert from StatementNode list to Statement list
 	StatementNode * node = dynamic_cast< StatementNode * >(prev);
-	std::list< Statement * > stmts;
+	list< Statement * > stmts;
 	buildMoveList( stmt, stmts );
 	// splice any new Statements to end of current Statements
@@ -78,5 +79,5 @@
 } // build_expr
 
-Expression * build_if_control( IfCtrl * ctl, std::list< Statement * > & init ) {
+Expression * build_if_control( CondCtl * ctl, list< Statement * > & init ) {
 	if ( ctl->init != 0 ) {
 		buildMoveList( ctl->init, init );
@@ -100,28 +101,29 @@
 } // build_if_control
 
-Statement * build_if( IfCtrl * ctl, StatementNode * then_stmt, StatementNode * else_stmt ) {
-	Statement * thenb, * elseb = nullptr;
-	std::list< Statement * > branches;
-	buildMoveList< Statement, StatementNode >( then_stmt, branches );
-	assert( branches.size() == 1 );
-	thenb = branches.front();
-
-	if ( else_stmt ) {
-		std::list< Statement * > branches;
-		buildMoveList< Statement, StatementNode >( else_stmt, branches );
-		assert( branches.size() == 1 );
-		elseb = branches.front();
-	} // if
-
-	std::list< Statement * > init;
-	Expression * cond = build_if_control( ctl, init );
-	return new IfStmt( cond, thenb, elseb, init );
+Statement * build_if( CondCtl * ctl, StatementNode * then, StatementNode * else_ ) {
+	list< Statement * > astinit;						// maybe empty
+	Expression * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
+
+	Statement * astthen, * astelse = nullptr;
+	list< Statement * > aststmt;
+	buildMoveList< Statement, StatementNode >( then, aststmt );
+	assert( aststmt.size() == 1 );
+	astthen = aststmt.front();
+
+	if ( else_ ) {
+		list< Statement * > aststmt;
+		buildMoveList< Statement, StatementNode >( else_, aststmt );
+		assert( aststmt.size() == 1 );
+		astelse = aststmt.front();
+	} // if
+
+	return new IfStmt( astcond, astthen, astelse, astinit );
 } // build_if
 
 Statement * build_switch( bool isSwitch, ExpressionNode * ctl, StatementNode * stmt ) {
-	std::list< Statement * > branches;
-	buildMoveList< Statement, StatementNode >( stmt, branches );
-	if ( ! isSwitch ) {										// choose statement
-		for ( Statement * stmt : branches ) {
+	list< Statement * > aststmt;
+	buildMoveList< Statement, StatementNode >( stmt, aststmt );
+	if ( ! isSwitch ) {									// choose statement
+		for ( Statement * stmt : aststmt ) {
 			CaseStmt * caseStmt = strict_dynamic_cast< CaseStmt * >( stmt );
 			if ( ! caseStmt->stmts.empty() ) {			// code after "case" => end of case list
@@ -131,57 +133,61 @@
 		} // for
 	} // if
-	// branches.size() == 0 for switch (...) {}, i.e., no declaration or statements
-	return new SwitchStmt( maybeMoveBuild< Expression >(ctl), branches );
+	// aststmt.size() == 0 for switch (...) {}, i.e., no declaration or statements
+	return new SwitchStmt( maybeMoveBuild< Expression >(ctl), aststmt );
 } // build_switch
 
 Statement * build_case( ExpressionNode * ctl ) {
-	std::list< Statement * > branches;
-	return new CaseStmt( maybeMoveBuild< Expression >(ctl), branches );
+	return new CaseStmt( maybeMoveBuild< Expression >(ctl), {} ); // stmt starts empty and then added to
 } // build_case
 
 Statement * build_default() {
-	std::list< Statement * > branches;
-	return new CaseStmt( nullptr, branches, true );
+	return new CaseStmt( nullptr, {}, true );			// stmt starts empty and then added to
 } // build_default
 
-Statement * build_while( IfCtrl * ctl, StatementNode * stmt ) {
-	std::list< Statement * > branches;
-	buildMoveList< Statement, StatementNode >( stmt, branches );
-	assert( branches.size() == 1 );
-
-	std::list< Statement * > init;
-	Expression * cond = build_if_control( ctl, init );
-	return new WhileStmt( cond, branches.front(), init, false );
+Statement * build_while( CondCtl * ctl, StatementNode * stmt, StatementNode * else_ ) {
+	list< Statement * > astinit;						// maybe empty
+	Expression * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set
+
+	list< Statement * > aststmt;						// loop body, compound created if empty
+	buildMoveList< Statement, StatementNode >( stmt, aststmt );
+	assert( aststmt.size() == 1 );
+
+	list< Statement * > astelse;						// else clause, maybe empty
+	buildMoveList< Statement, StatementNode >( else_, astelse );
+
+	return new WhileDoStmt( astcond, aststmt.front(), astelse.front(), astinit, false );
 } // build_while
 
-Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt ) {
-	std::list< Statement * > branches;
-	buildMoveList< Statement, StatementNode >( stmt, branches );
-	assert( branches.size() == 1 );
-
-	std::list< Statement * > init;
-	return new WhileStmt( notZeroExpr( maybeMoveBuild< Expression >(ctl) ), branches.front(), init, true );
+Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ ) {
+	list< Statement * > aststmt;						// loop body, compound created if empty
+	buildMoveList< Statement, StatementNode >( stmt, aststmt );
+	assert( aststmt.size() == 1 );						// compound created if empty
+
+	list< Statement * > astelse;						// else clause, maybe empty
+	buildMoveList< Statement, StatementNode >( else_, astelse );
+
+	// do-while cannot have declarations in the contitional, so init is always empty
+	return new WhileDoStmt( notZeroExpr( maybeMoveBuild< Expression >(ctl) ), aststmt.front(), astelse.front(), {}, true );
 } // build_do_while
 
-Statement * build_for( ForCtrl * forctl, StatementNode * stmt ) {
-	std::list< Statement * > branches;
-	buildMoveList< Statement, StatementNode >( stmt, branches );
-	assert( branches.size() == 1 );
-
-	std::list< Statement * > init;
-	if ( forctl->init != 0 ) {
-		buildMoveList( forctl->init, init );
-	} // if
-
-	Expression * cond = 0;
-	if ( forctl->condition != 0 )
-		cond = notZeroExpr( maybeMoveBuild< Expression >(forctl->condition) );
-
-	Expression * incr = 0;
-	if ( forctl->change != 0 )
-		incr = maybeMoveBuild< Expression >(forctl->change);
-
+Statement * build_for( ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ ) {
+	list< Statement * > astinit;						// maybe empty
+	buildMoveList( forctl->init, astinit );
+
+	Expression * astcond = nullptr;						// maybe empty
+	astcond = notZeroExpr( maybeMoveBuild< Expression >(forctl->condition) );
+
+	Expression * astincr = nullptr;						// maybe empty
+	astincr = maybeMoveBuild< Expression >(forctl->change);
 	delete forctl;
-	return new ForStmt( init, cond, incr, branches.front() );
+
+	list< Statement * > aststmt;						// loop body, compound created if empty
+	buildMoveList< Statement, StatementNode >( stmt, aststmt );
+	assert( aststmt.size() == 1 );
+
+	list< Statement * > astelse;						// else clause, maybe empty
+	buildMoveList< Statement, StatementNode >( else_, astelse );
+
+	return new ForStmt( astinit, astcond, astincr, aststmt.front(), astelse.front() );
 } // build_for
 
@@ -191,5 +197,5 @@
 } // build_branch
 
-Statement * build_branch( std::string * identifier, BranchStmt::Type kind ) {
+Statement * build_branch( string * identifier, BranchStmt::Type kind ) {
 	Statement * ret = new BranchStmt( * identifier, kind );
 	delete identifier; 									// allocated by lexer
@@ -202,5 +208,5 @@
 
 Statement * build_return( ExpressionNode * ctl ) {
-	std::list< Expression * > exps;
+	list< Expression * > exps;
 	buildMoveList( ctl, exps );
 	return new ReturnStmt( exps.size() > 0 ? exps.back() : nullptr );
@@ -208,14 +214,14 @@
 
 Statement * build_throw( ExpressionNode * ctl ) {
-	std::list< Expression * > exps;
+	list< Expression * > exps;
 	buildMoveList( ctl, exps );
-	assertf( exps.size() < 2, "This means we are leaking memory");
+	assertf( exps.size() < 2, "CFA internal error: leaking memory" );
 	return new ThrowStmt( ThrowStmt::Terminate, !exps.empty() ? exps.back() : nullptr );
 } // build_throw
 
 Statement * build_resume( ExpressionNode * ctl ) {
-	std::list< Expression * > exps;
+	list< Expression * > exps;
 	buildMoveList( ctl, exps );
-	assertf( exps.size() < 2, "This means we are leaking memory");
+	assertf( exps.size() < 2, "CFA internal error: leaking memory" );
 	return new ThrowStmt( ThrowStmt::Resume, !exps.empty() ? exps.back() : nullptr );
 } // build_resume
@@ -227,24 +233,24 @@
 } // build_resume_at
 
-Statement * build_try( StatementNode * try_stmt, StatementNode * catch_stmt, StatementNode * finally_stmt ) {
-	std::list< CatchStmt * > branches;
-	buildMoveList< CatchStmt, StatementNode >( catch_stmt, branches );
-	CompoundStmt * tryBlock = strict_dynamic_cast< CompoundStmt * >(maybeMoveBuild< Statement >(try_stmt));
-	FinallyStmt * finallyBlock = dynamic_cast< FinallyStmt * >(maybeMoveBuild< Statement >(finally_stmt) );
-	return new TryStmt( tryBlock, branches, finallyBlock );
+Statement * build_try( StatementNode * try_, StatementNode * catch_, StatementNode * finally_ ) {
+	list< CatchStmt * > aststmt;
+	buildMoveList< CatchStmt, StatementNode >( catch_, aststmt );
+	CompoundStmt * tryBlock = strict_dynamic_cast< CompoundStmt * >(maybeMoveBuild< Statement >(try_));
+	FinallyStmt * finallyBlock = dynamic_cast< FinallyStmt * >(maybeMoveBuild< Statement >(finally_) );
+	return new TryStmt( tryBlock, aststmt, finallyBlock );
 } // build_try
 
 Statement * build_catch( CatchStmt::Kind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body ) {
-	std::list< Statement * > branches;
-	buildMoveList< Statement, StatementNode >( body, branches );
-	assert( branches.size() == 1 );
-	return new CatchStmt( kind, maybeMoveBuild< Declaration >(decl), maybeMoveBuild< Expression >(cond), branches.front() );
+	list< Statement * > aststmt;
+	buildMoveList< Statement, StatementNode >( body, aststmt );
+	assert( aststmt.size() == 1 );
+	return new CatchStmt( kind, maybeMoveBuild< Declaration >(decl), maybeMoveBuild< Expression >(cond), aststmt.front() );
 } // build_catch
 
 Statement * build_finally( StatementNode * stmt ) {
-	std::list< Statement * > branches;
-	buildMoveList< Statement, StatementNode >( stmt, branches );
-	assert( branches.size() == 1 );
-	return new FinallyStmt( dynamic_cast< CompoundStmt * >( branches.front() ) );
+	list< Statement * > aststmt;
+	buildMoveList< Statement, StatementNode >( stmt, aststmt );
+	assert( aststmt.size() == 1 );
+	return new FinallyStmt( dynamic_cast< CompoundStmt * >( aststmt.front() ) );
 } // build_finally
 
@@ -254,5 +260,5 @@
 	node->type = type;
 
-	std::list< Statement * > stmts;
+	list< Statement * > stmts;
 	buildMoveList< Statement, StatementNode >( then, stmts );
 	if(!stmts.empty()) {
@@ -319,5 +325,5 @@
 } // build_waitfor_timeout
 
-WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when,  StatementNode * else_stmt, ExpressionNode * else_when ) {
+WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when,  StatementNode * else_, ExpressionNode * else_when ) {
 	auto node = new WaitForStmt();
 
@@ -326,5 +332,5 @@
 	node->timeout.condition = notZeroExpr( maybeMoveBuild<Expression>( when ) );
 
-	node->orelse.statement  = maybeMoveBuild<Statement >( else_stmt );
+	node->orelse.statement  = maybeMoveBuild<Statement >( else_ );
 	node->orelse.condition  = notZeroExpr( maybeMoveBuild<Expression>( else_when ) );
 
@@ -333,5 +339,5 @@
 
 Statement * build_with( ExpressionNode * exprs, StatementNode * stmt ) {
-	std::list< Expression * > e;
+	list< Expression * > e;
 	buildMoveList( exprs, e );
 	Statement * s = maybeMoveBuild<Statement>( stmt );
@@ -361,6 +367,6 @@
 
 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) {
-	std::list< Expression * > out, in;
-	std::list< ConstantExpr * > clob;
+	list< Expression * > out, in;
+	list< ConstantExpr * > clob;
 
 	buildMoveList( output, out );
@@ -375,5 +381,5 @@
 
 Statement * build_mutex( ExpressionNode * exprs, StatementNode * stmt ) {
-	std::list< Expression * > expList;
+	list< Expression * > expList;
 	buildMoveList( exprs, expList );
 	Statement * body = maybeMoveBuild<Statement>( stmt );
Index: src/Parser/parser.yy
===================================================================
--- src/Parser/parser.yy	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/Parser/parser.yy	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Sat Sep  1 20:22:55 2001
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Oct 15 09:20:17 2021
-// Update Count     : 5163
+// Last Modified On : Tue Feb  1 11:06:13 2022
+// Update Count     : 5167
 //
 
@@ -238,5 +238,5 @@
 	WaitForStmt * wfs;
 	Expression * constant;
-	IfCtrl * ifctl;
+	CondCtl * ifctl;
 	ForCtrl * fctl;
 	enum OperKinds compop;
@@ -327,5 +327,5 @@
 %type<en> comma_expression				comma_expression_opt
 %type<en> argument_expression_list_opt	argument_expression_list	argument_expression			default_initializer_opt
-%type<ifctl> if_control_expression
+%type<ifctl> conditional_declaration
 %type<fctl> for_control_expression		for_control_expression_list
 %type<compop> inclexcl
@@ -1123,20 +1123,20 @@
 
 if_statement:
-	IF '(' if_control_expression ')' statement			%prec THEN
+	IF '(' conditional_declaration ')' statement		%prec THEN
 		// explicitly deal with the shift/reduce conflict on if/else
 		{ $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), nullptr ) ); }
-	| IF '(' if_control_expression ')' statement ELSE statement
+	| IF '(' conditional_declaration ')' statement ELSE statement
 		{ $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), maybe_build_compound( $7 ) ) ); }
 	;
 
-if_control_expression:
+conditional_declaration:
 	comma_expression
-		{ $$ = new IfCtrl( nullptr, $1 ); }
+		{ $$ = new CondCtl( nullptr, $1 ); }
 	| c_declaration										// no semi-colon
-		{ $$ = new IfCtrl( $1, nullptr ); }
+		{ $$ = new CondCtl( $1, nullptr ); }
 	| cfa_declaration									// no semi-colon
-		{ $$ = new IfCtrl( $1, nullptr ); }
+		{ $$ = new CondCtl( $1, nullptr ); }
 	| declaration comma_expression						// semi-colon separated
-		{ $$ = new IfCtrl( $1, $2 ); }
+		{ $$ = new CondCtl( $1, $2 ); }
  	;
 
@@ -1193,9 +1193,10 @@
 iteration_statement:
 	WHILE '(' ')' statement								// CFA => while ( 1 )
-		{ $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
-	| WHILE '(' if_control_expression ')' statement		%prec THEN
+		{ $$ = new StatementNode( build_while( new CondCtl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
+	| WHILE '(' conditional_declaration ')' statement	%prec THEN
 		{ $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ) ) ); }
-	| WHILE '(' if_control_expression ')' statement ELSE statement // CFA
-		{ SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
+	| WHILE '(' conditional_declaration ')' statement ELSE statement // CFA
+		// { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
+		{ $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ), $7 ) ); }
 	| DO statement WHILE '(' ')' ';'					// CFA => do while( 1 )
 		{ $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); }
@@ -1203,5 +1204,6 @@
 		{ $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); }
 	| DO statement WHILE '(' comma_expression ')' ELSE statement // CFA
-		{ SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
+		// { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
+		{ $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ), $8 ) ); }
 	| FOR '(' ')' statement								// CFA => for ( ;; )
 		{ $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); }
@@ -1209,5 +1211,6 @@
 	  	{ $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ) ) ); }
 	| FOR '(' for_control_expression_list ')' statement ELSE statement // CFA
-		{ SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
+		// { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; }
+		{ $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ), $7 ) ); }
 	;
 
Index: src/ResolvExpr/Resolver.cc
===================================================================
--- src/ResolvExpr/Resolver.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/ResolvExpr/Resolver.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Aaron B. Moss
 // Created On       : Sun May 17 12:17:01 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Fri Mar 27 11:58:00 2020
-// Update Count     : 242
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Tue Feb  1 16:27:14 2022
+// Update Count     : 245
 //
 
@@ -80,5 +80,5 @@
 		void previsit( AsmStmt * asmStmt );
 		void previsit( IfStmt * ifStmt );
-		void previsit( WhileStmt * whileStmt );
+		void previsit( WhileDoStmt * whileDoStmt );
 		void previsit( ForStmt * forStmt );
 		void previsit( SwitchStmt * switchStmt );
@@ -502,6 +502,6 @@
 	}
 
-	void Resolver_old::previsit( WhileStmt * whileStmt ) {
-		findIntegralExpression( whileStmt->condition, indexer );
+	void Resolver_old::previsit( WhileDoStmt * whileDoStmt ) {
+		findIntegralExpression( whileDoStmt->condition, indexer );
 	}
 
@@ -572,8 +572,8 @@
 
 	void Resolver_old::previsit( CatchStmt * catchStmt ) {
-		// Until we are very sure this invarent (ifs that move between passes have thenPart)
+		// Until we are very sure this invarent (ifs that move between passes have then)
 		// holds, check it. This allows a check for when to decode the mangling.
 		if ( IfStmt * ifStmt = dynamic_cast<IfStmt *>( catchStmt->body ) ) {
-			assert( ifStmt->thenPart );
+			assert( ifStmt->then );
 		}
 		// Encode the catchStmt so the condition can see the declaration.
@@ -588,11 +588,11 @@
 		// Decode the catchStmt so everything is stored properly.
 		IfStmt * ifStmt = dynamic_cast<IfStmt *>( catchStmt->body );
-		if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) {
+		if ( nullptr != ifStmt && nullptr == ifStmt->then ) {
 			assert( ifStmt->condition );
-			assert( ifStmt->elsePart );
+			assert( ifStmt->else_ );
 			catchStmt->cond = ifStmt->condition;
-			catchStmt->body = ifStmt->elsePart;
+			catchStmt->body = ifStmt->else_;
 			ifStmt->condition = nullptr;
-			ifStmt->elsePart = nullptr;
+			ifStmt->else_ = nullptr;
 			delete ifStmt;
 		}
@@ -1272,5 +1272,5 @@
 		const ast::AsmStmt *         previsit( const ast::AsmStmt * );
 		const ast::IfStmt *          previsit( const ast::IfStmt * );
-		const ast::WhileStmt *       previsit( const ast::WhileStmt * );
+		const ast::WhileDoStmt *       previsit( const ast::WhileDoStmt * );
 		const ast::ForStmt *         previsit( const ast::ForStmt * );
 		const ast::SwitchStmt *      previsit( const ast::SwitchStmt * );
@@ -1581,7 +1581,7 @@
 	}
 
-	const ast::WhileStmt * Resolver_new::previsit( const ast::WhileStmt * whileStmt ) {
+	const ast::WhileDoStmt * Resolver_new::previsit( const ast::WhileDoStmt * whileDoStmt ) {
 		return ast::mutate_field(
-			whileStmt, &ast::WhileStmt::cond, findIntegralExpression( whileStmt->cond, symtab ) );
+			whileDoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, symtab ) );
 	}
 
@@ -1669,8 +1669,8 @@
 
 	const ast::CatchStmt * Resolver_new::previsit( const ast::CatchStmt * catchStmt ) {
-		// Until we are very sure this invarent (ifs that move between passes have thenPart)
+		// Until we are very sure this invarent (ifs that move between passes have then)
 		// holds, check it. This allows a check for when to decode the mangling.
 		if ( auto ifStmt = catchStmt->body.as<ast::IfStmt>() ) {
-			assert( ifStmt->thenPart );
+			assert( ifStmt->then );
 		}
 		// Encode the catchStmt so the condition can see the declaration.
@@ -1687,10 +1687,10 @@
 		// Decode the catchStmt so everything is stored properly.
 		const ast::IfStmt * ifStmt = catchStmt->body.as<ast::IfStmt>();
-		if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) {
+		if ( nullptr != ifStmt && nullptr == ifStmt->then ) {
 			assert( ifStmt->cond );
-			assert( ifStmt->elsePart );
+			assert( ifStmt->else_ );
 			ast::CatchStmt * stmt = ast::mutate( catchStmt );
 			stmt->cond = ifStmt->cond;
-			stmt->body = ifStmt->elsePart;
+			stmt->body = ifStmt->else_;
 			// ifStmt should be implicately deleted here.
 			return stmt;
Index: src/SymTab/Validate.cc
===================================================================
--- src/SymTab/Validate.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/SymTab/Validate.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -453,4 +453,9 @@
 	}
 
+	void decayForallPointers( std::list< Declaration * > & translationUnit ) {
+		PassVisitor<ForallPointerDecay_old> fpd;
+		acceptAll( translationUnit, fpd );
+	}
+
 	void validate( std::list< Declaration * > &translationUnit, __attribute__((unused)) bool doDebug ) {
 		validate_A( translationUnit );
@@ -470,5 +475,4 @@
 		type->accept( fpd );
 	}
-
 
 	void HoistTypeDecls::handleType( Type * type ) {
Index: src/SymTab/Validate.h
===================================================================
--- src/SymTab/Validate.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/SymTab/Validate.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -42,4 +42,5 @@
 	void validate_E( std::list< Declaration * > &translationUnit );
 	void validate_F( std::list< Declaration * > &translationUnit );
+	void decayForallPointers( std::list< Declaration * > & translationUnit );
 
 	const ast::Type * validateType(
Index: src/SynTree/Mutator.h
===================================================================
--- src/SynTree/Mutator.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/SynTree/Mutator.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:35:36 2021
-// Update Count     : 18
+// Last Modified On : Tue Feb  1 09:26:49 2022
+// Update Count     : 20
 //
 #pragma once
@@ -42,5 +42,5 @@
 	virtual Statement * mutate( DirectiveStmt * dirStmt ) = 0;
 	virtual Statement * mutate( IfStmt * ifStmt ) = 0;
-	virtual Statement * mutate( WhileStmt * whileStmt ) = 0;
+	virtual Statement * mutate( WhileDoStmt * whileDoStmt ) = 0;
 	virtual Statement * mutate( ForStmt * forStmt ) = 0;
 	virtual Statement * mutate( SwitchStmt * switchStmt ) = 0;
Index: src/SynTree/Statement.cc
===================================================================
--- src/SynTree/Statement.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/SynTree/Statement.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -9,7 +9,7 @@
 // Author           : Richard C. Bilson
 // Created On       : Mon May 18 07:44:20 2015
-// Last Modified By : Andrew Beach
-// Last Modified On : Mon Jan 20 16:03:00 2020
-// Update Count     : 71
+// Last Modified By : Peter A. Buhr
+// Last Modified On : Wed Feb  2 20:19:33 2022
+// Update Count     : 90
 //
 
@@ -29,10 +29,10 @@
 #include "SynTree/Label.h"         // for Label, operator<<
 
-using std::string;
-using std::endl;
-
-Statement::Statement( const std::list<Label> & labels ) : labels( labels ) {}
-
-void Statement::print( std::ostream & os, Indenter indent ) const {
+using namespace std;
+
+
+Statement::Statement( const list<Label> & labels ) : labels( labels ) {}
+
+void Statement::print( ostream & os, Indenter indent ) const {
 	if ( ! labels.empty() ) {
 		os << indent << "... Labels: {";
@@ -54,11 +54,11 @@
 }
 
-void ExprStmt::print( std::ostream & os, Indenter indent ) const {
-	os << "Expression Statement:" << endl << indent+1;
-	expr->print( os, indent+1 );
-}
-
-
-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 ) {}
+void ExprStmt::print( ostream & os, Indenter indent ) const {
+	os << "Expression Statement:" << endl << indent + 1;
+	expr->print( os, indent + 1 );
+}
+
+
+AsmStmt::AsmStmt( bool voltile, Expression * instruction, const list<Expression *> output, const list<Expression *> input, const list<ConstantExpr *> clobber, const list<Label> gotolabels ) : Statement(), voltile( voltile ), instruction( instruction ), output( output ), input( input ), clobber( clobber ), gotolabels( gotolabels ) {}
 
 AsmStmt::AsmStmt( const AsmStmt & other ) : Statement( other ), voltile( other.voltile ), instruction( maybeClone( other.instruction ) ), gotolabels( other.gotolabels ) {
@@ -75,26 +75,26 @@
 }
 
-void AsmStmt::print( std::ostream & os, Indenter indent ) const {
+void AsmStmt::print( ostream & os, Indenter indent ) const {
 	os << "Assembler Statement:" << endl;
-	os << indent+1 << "instruction: " << endl << indent;
-	instruction->print( os, indent+1 );
+	os << indent + 1 << "instruction: " << endl << indent;
+	instruction->print( os, indent + 1 );
 	if ( ! output.empty() ) {
-		os << endl << indent+1 << "output: " << endl;
-		printAll( output, os, indent+1 );
+		os << endl << indent + 1 << "output: " << endl;
+		printAll( output, os, indent + 1 );
 	} // if
 	if ( ! input.empty() ) {
-		os << indent+1 << "input: " << endl;
-		printAll( input, os, indent+1 );
+		os << indent + 1 << "input: " << endl;
+		printAll( input, os, indent + 1 );
 	} // if
 	if ( ! clobber.empty() ) {
-		os << indent+1 << "clobber: " << endl;
-		printAll( clobber, os, indent+1 );
+		os << indent + 1 << "clobber: " << endl;
+		printAll( clobber, os, indent + 1 );
 	} // if
 }
 
 
-DirectiveStmt::DirectiveStmt( const std::string & directive ) : Statement(), directive( directive ) {}
-
-void DirectiveStmt::print( std::ostream & os, Indenter ) const {
+DirectiveStmt::DirectiveStmt( const string & directive ) : Statement(), directive( directive ) {}
+
+void DirectiveStmt::print( ostream & os, Indenter ) const {
 	os << "GCC Directive:" << directive << endl;
 }
@@ -120,10 +120,10 @@
 }
 
-void BranchStmt::print( std::ostream & os, Indenter indent ) const {
-	assert(type < 5);
+void BranchStmt::print( ostream & os, Indenter indent ) const {
+	assertf(type < BranchStmts, "CFA internal error: invalid branch statement" );
 	os << "Branch (" << brType[type] << ")" << endl ;
-	if ( target != "" ) os << indent+1 << "with target: " << target << endl;
-	if ( originalTarget != "" ) os << indent+1 << "with original target: " << originalTarget << endl;
-	if ( computedTarget != nullptr ) os << indent+1 << "with computed target: " << computedTarget << endl;
+	if ( target != "" ) os << indent + 1 << "with target: " << target << endl;
+	if ( originalTarget != "" ) os << indent + 1 << "with original target: " << originalTarget << endl;
+	if ( computedTarget != nullptr ) os << indent + 1 << "with computed target: " << computedTarget << endl;
 }
 
@@ -136,18 +136,18 @@
 }
 
-void ReturnStmt::print( std::ostream & os, Indenter indent ) const {
+void ReturnStmt::print( ostream & os, Indenter indent ) const {
 	os << "Return Statement, returning: ";
 	if ( expr != nullptr ) {
-		os << endl << indent+1;
-		expr->print( os, indent+1 );
+		os << endl << indent + 1;
+		expr->print( os, indent + 1 );
 	}
 	os << endl;
 }
 
-IfStmt::IfStmt( Expression * condition, Statement * thenPart, Statement * elsePart, std::list<Statement *> initialization ):
-	Statement(), condition( condition ), thenPart( thenPart ), elsePart( elsePart ), initialization( initialization ) {}
+IfStmt::IfStmt( Expression * condition, Statement * then, Statement * else_, const list<Statement *> initialization ):
+	Statement(), condition( condition ), then( then ), else_( else_ ), initialization( initialization ) {}
 
 IfStmt::IfStmt( const IfStmt & other ) :
-	Statement( other ), condition( maybeClone( other.condition ) ), thenPart( maybeClone( other.thenPart ) ), elsePart( maybeClone( other.elsePart ) ) {
+	Statement( other ), condition( maybeClone( other.condition ) ), then( maybeClone( other.then ) ), else_( maybeClone( other.else_ ) ) {
 	cloneAll( other.initialization, initialization );
 }
@@ -156,18 +156,18 @@
 	deleteAll( initialization );
 	delete condition;
-	delete thenPart;
-	delete elsePart;
-}
-
-void IfStmt::print( std::ostream & os, Indenter indent ) const {
+	delete then;
+	delete else_;
+}
+
+void IfStmt::print( ostream & os, Indenter indent ) const {
 	os << "If on condition: " << endl;
-	os << indent+1;
-	condition->print( os, indent+1 );
+	os << indent + 1;
+	condition->print( os, indent + 1 );
 
 	if ( !initialization.empty() ) {
 		os << indent << "... with initialization: \n";
 		for ( const Statement * stmt : initialization ) {
-			os << indent+1;
-			stmt->print( os, indent+1 );
+			os << indent + 1;
+			stmt->print( os, indent + 1 );
 		}
 		os << endl;
@@ -176,15 +176,15 @@
 	os << indent << "... then: " << endl;
 
-	os << indent+1;
-	thenPart->print( os, indent+1 );
-
-	if ( elsePart != nullptr ) {
+	os << indent + 1;
+	then->print( os, indent + 1 );
+
+	if ( else_ != nullptr ) {
 		os << indent << "... else: " << endl;
-		os << indent+1;
-		elsePart->print( os, indent+1 );
+		os << indent + 1;
+		else_->print( os, indent + 1 );
 	} // if
 }
 
-SwitchStmt::SwitchStmt( Expression * condition, const std::list<Statement *> & statements ):
+SwitchStmt::SwitchStmt( Expression * condition, const list<Statement *> & statements ):
 	Statement(), condition( condition ), statements( statements ) {
 }
@@ -201,5 +201,5 @@
 }
 
-void SwitchStmt::print( std::ostream & os, Indenter indent ) const {
+void SwitchStmt::print( ostream & os, Indenter indent ) const {
 	os << "Switch on condition: ";
 	condition->print( os );
@@ -207,15 +207,15 @@
 
 	for ( const Statement * stmt : statements ) {
-		stmt->print( os, indent+1 );
-	}
-}
-
-CaseStmt::CaseStmt( Expression * condition, const std::list<Statement *> & statements, bool deflt ) throw ( SemanticErrorException ) :
-	Statement(), condition( condition ), stmts( statements ), _isDefault( deflt ) {
+		stmt->print( os, indent + 1 );
+	}
+}
+
+CaseStmt::CaseStmt( Expression * condition, const list<Statement *> & statements, bool deflt ) throw ( SemanticErrorException ) :
+		Statement(), condition( condition ), stmts( statements ), _isDefault( deflt ) {
 	if ( isDefault() && condition != nullptr ) SemanticError( condition, "default case with condition: " );
 }
 
 CaseStmt::CaseStmt( const CaseStmt & other ) :
-	Statement( other ), condition( maybeClone(other.condition ) ), _isDefault( other._isDefault ) {
+		Statement( other ), condition( maybeClone(other.condition ) ), _isDefault( other._isDefault ) {
 	cloneAll( other.stmts, stmts );
 }
@@ -226,5 +226,5 @@
 }
 
-CaseStmt * CaseStmt::makeDefault( const std::list<Label> & labels, std::list<Statement *> stmts ) {
+CaseStmt * CaseStmt::makeDefault( const list<Label> & labels, list<Statement *> stmts ) {
 	CaseStmt * stmt = new CaseStmt( nullptr, stmts, true );
 	stmt->labels = labels;
@@ -232,5 +232,5 @@
 }
 
-void CaseStmt::print( std::ostream & os, Indenter indent ) const {
+void CaseStmt::print( ostream & os, Indenter indent ) const {
 	if ( isDefault() ) os << indent << "Default ";
 	else {
@@ -241,37 +241,41 @@
 
 	for ( Statement * stmt : stmts ) {
-		os << indent+1;
-		stmt->print( os, indent+1 );
-	}
-}
-
-WhileStmt::WhileStmt( Expression * condition, Statement * body, std::list< Statement * > & initialization, bool isDoWhile ):
-	Statement(), condition( condition), body( body), initialization( initialization ), isDoWhile( isDoWhile) {
-}
-
-WhileStmt::WhileStmt( const WhileStmt & other ):
+		os << indent + 1;
+		stmt->print( os, indent + 1 );
+	}
+}
+
+WhileDoStmt::WhileDoStmt( Expression * condition, Statement * body, const list< Statement * > & initialization, bool isDoWhile ):
+	Statement(), condition( condition ), body( body ), else_( nullptr ), initialization( initialization ), isDoWhile( isDoWhile) {
+}
+
+WhileDoStmt::WhileDoStmt( Expression * condition, Statement * body, Statement * else_, const list< Statement * > & initialization, bool isDoWhile ):
+	Statement(), condition( condition), body( body ), else_( else_ ), initialization( initialization ), isDoWhile( isDoWhile) {
+}
+
+WhileDoStmt::WhileDoStmt( const WhileDoStmt & other ):
 	Statement( other ), condition( maybeClone( other.condition ) ), body( maybeClone( other.body ) ), isDoWhile( other.isDoWhile ) {
 }
 
-WhileStmt::~WhileStmt() {
+WhileDoStmt::~WhileDoStmt() {
 	delete body;
 	delete condition;
 }
 
-void WhileStmt::print( std::ostream & os, Indenter indent ) const {
+void WhileDoStmt::print( ostream & os, Indenter indent ) const {
 	os << "While on condition: " << endl ;
-	condition->print( os, indent+1 );
+	condition->print( os, indent + 1 );
 
 	os << indent << "... with body: " << endl;
 
-	if ( body != nullptr ) body->print( os, indent+1 );
-}
-
-ForStmt::ForStmt( std::list<Statement *> initialization, Expression * condition, Expression * increment, Statement * body ):
-	Statement(), initialization( initialization ), condition( condition ), increment( increment ), body( body ) {
+	if ( body != nullptr ) body->print( os, indent + 1 );
+}
+
+ForStmt::ForStmt( const list<Statement *> initialization, Expression * condition, Expression * increment, Statement * body, Statement * else_ ):
+	Statement(), initialization( initialization ), condition( condition ), increment( increment ), body( body ), else_( else_ ) {
 }
 
 ForStmt::ForStmt( const ForStmt & other ):
-	Statement( other ), condition( maybeClone( other.condition ) ), increment( maybeClone( other.increment ) ), body( maybeClone( other.body ) ) {
+	Statement( other ), condition( maybeClone( other.condition ) ), increment( maybeClone( other.increment ) ), body( maybeClone( other.body ) ), else_( maybeClone( other.else_ ) ) {
 		cloneAll( other.initialization, initialization );
 
@@ -283,7 +287,8 @@
 	delete increment;
 	delete body;
-}
-
-void ForStmt::print( std::ostream & os, Indenter indent ) const {
+	delete else_;
+}
+
+void ForStmt::print( ostream & os, Indenter indent ) const {
 	Statement::print( os, indent ); // print labels
 
@@ -293,22 +298,27 @@
 		os << indent << "... initialization: \n";
 		for ( Statement * stmt : initialization ) {
-			os << indent+1;
-			stmt->print( os, indent+1 );
+			os << indent + 1;
+			stmt->print( os, indent + 1 );
 		}
 	}
 
 	if ( condition != nullptr ) {
-		os << indent << "... condition: \n" << indent+1;
-		condition->print( os, indent+1 );
+		os << indent << "... condition: \n" << indent + 1;
+		condition->print( os, indent + 1 );
 	}
 
 	if ( increment != nullptr ) {
-		os << "\n" << indent << "... increment: \n" << indent+1;
-		increment->print( os, indent+1 );
+		os << "\n" << indent << "... increment: \n" << indent + 1;
+		increment->print( os, indent + 1 );
 	}
 
 	if ( body != nullptr ) {
-		os << "\n" << indent << "... with body: \n" << indent+1;
-		body->print( os, indent+1 );
+		os << "\n" << indent << "... with body: \n" << indent + 1;
+		body->print( os, indent + 1 );
+	}
+
+	if ( else_ != nullptr ) {
+		os << "\n" << indent << "... with body: \n" << indent + 1;
+		else_->print( os, indent + 1 );
 	}
 	os << endl;
@@ -329,15 +339,15 @@
 }
 
-void ThrowStmt::print( std::ostream & os, Indenter indent) const {
+void ThrowStmt::print( ostream & os, Indenter indent) const {
 	if ( target ) os << "Non-Local ";
 	os << "Throw Statement, raising: ";
-	expr->print(os, indent+1);
+	expr->print(os, indent + 1);
 	if ( target ) {
 		os << "... at: ";
-		target->print(os, indent+1);
-	}
-}
-
-TryStmt::TryStmt( CompoundStmt * tryBlock, std::list<CatchStmt *> & handlers, FinallyStmt * finallyBlock ) :
+		target->print(os, indent + 1);
+	}
+}
+
+TryStmt::TryStmt( CompoundStmt * tryBlock, const list<CatchStmt *> & handlers, FinallyStmt * finallyBlock ) :
 	Statement(), block( tryBlock ),  handlers( handlers ), finallyBlock( finallyBlock ) {
 }
@@ -353,20 +363,20 @@
 }
 
-void TryStmt::print( std::ostream & os, Indenter indent ) const {
+void TryStmt::print( ostream & os, Indenter indent ) const {
 	os << "Try Statement" << endl;
-	os << indent << "... with block:" << endl << indent+1;
-	block->print( os, indent+1 );
+	os << indent << "... with block:" << endl << indent + 1;
+	block->print( os, indent + 1 );
 
 	// handlers
 	os << indent << "... and handlers:" << endl;
 	for ( const CatchStmt * stmt : handlers ) {
-		os << indent+1;
-		stmt->print( os, indent+1 );
+		os << indent + 1;
+		stmt->print( os, indent + 1 );
 	}
 
 	// finally block
 	if ( finallyBlock != nullptr ) {
-		os << indent << "... and finally:" << endl << indent+1;
-		finallyBlock->print( os, indent+1 );
+		os << indent << "... and finally:" << endl << indent + 1;
+		finallyBlock->print( os, indent + 1 );
 	} // if
 }
@@ -386,19 +396,19 @@
 }
 
-void CatchStmt::print( std::ostream & os, Indenter indent ) const {
+void CatchStmt::print( ostream & os, Indenter indent ) const {
 	os << "Catch " << ((Terminate == kind) ? "Terminate" : "Resume") << " Statement" << endl;
 
 	os << indent << "... catching: ";
-	decl->printShort( os, indent+1 );
+	decl->printShort( os, indent + 1 );
 	os << endl;
 
 	if ( cond ) {
-		os << indent << "... with conditional:" << endl << indent+1;
-		cond->print( os, indent+1 );
+		os << indent << "... with conditional:" << endl << indent + 1;
+		cond->print( os, indent + 1 );
 	}
 
 	os << indent << "... with block:" << endl;
-	os << indent+1;
-	body->print( os, indent+1 );
+	os << indent + 1;
+	body->print( os, indent + 1 );
 }
 
@@ -414,8 +424,8 @@
 }
 
-void FinallyStmt::print( std::ostream & os, Indenter indent ) const {
+void FinallyStmt::print( ostream & os, Indenter indent ) const {
 	os << "Finally Statement" << endl;
-	os << indent << "... with block:" << endl << indent+1;
-	block->print( os, indent+1 );
+	os << indent << "... with block:" << endl << indent + 1;
+	block->print( os, indent + 1 );
 }
 
@@ -429,5 +439,5 @@
 }
 
-void SuspendStmt::print( std::ostream & os, Indenter indent ) const {
+void SuspendStmt::print( ostream & os, Indenter indent ) const {
 	os << "Suspend Statement";
 	switch (type) {
@@ -486,5 +496,5 @@
 }
 
-void WaitForStmt::print( std::ostream & os, Indenter indent ) const {
+void WaitForStmt::print( ostream & os, Indenter indent ) const {
 	os << "Waitfor Statement" << endl;
 	indent += 1;
@@ -521,5 +531,5 @@
 
 
-WithStmt::WithStmt( const std::list< Expression * > & exprs, Statement * stmt ) : Declaration("", noStorageClasses, LinkageSpec::Cforall), exprs( exprs ), stmt( stmt ) {}
+WithStmt::WithStmt( const list< Expression * > & exprs, Statement * stmt ) : Declaration("", noStorageClasses, LinkageSpec::Cforall), exprs( exprs ), stmt( stmt ) {}
 WithStmt::WithStmt( const WithStmt & other ) : Declaration( other ), stmt( maybeClone( other.stmt ) ) {
 	cloneAll( other.exprs, exprs );
@@ -530,17 +540,17 @@
 }
 
-void WithStmt::print( std::ostream & os, Indenter indent ) const {
+void WithStmt::print( ostream & os, Indenter indent ) const {
 	os << "With statement" << endl;
 	os << indent << "... with expressions: " << endl;
-	printAll( exprs, os, indent+1 );
-	os << indent << "... with statement:" << endl << indent+1;
-	stmt->print( os, indent+1 );
-}
-
-
-NullStmt::NullStmt( const std::list<Label> & labels ) : Statement( labels ) {
-}
-
-void NullStmt::print( std::ostream & os, Indenter indent ) const {
+	printAll( exprs, os, indent + 1 );
+	os << indent << "... with statement:" << endl << indent + 1;
+	stmt->print( os, indent + 1 );
+}
+
+
+NullStmt::NullStmt( const list<Label> & labels ) : Statement( labels ) {
+}
+
+void NullStmt::print( ostream & os, Indenter indent ) const {
 	os << "Null Statement" << endl;
 	Statement::print( os, indent );
@@ -558,12 +568,12 @@
 }
 
-void ImplicitCtorDtorStmt::print( std::ostream & os, Indenter indent ) const {
+void ImplicitCtorDtorStmt::print( ostream & os, Indenter indent ) const {
 	os << "Implicit Ctor Dtor Statement" << endl;
 	os << indent << "... with Ctor/Dtor: ";
-	callStmt->print( os, indent+1);
+	callStmt->print( os, indent + 1);
 	os << endl;
 }
 
-MutexStmt::MutexStmt( Statement * stmt, std::list<Expression *> mutexObjs ) 
+MutexStmt::MutexStmt( Statement * stmt, const list<Expression *> mutexObjs ) 
 	: Statement(), stmt( stmt ), mutexObjs( mutexObjs ) { }
 
@@ -577,14 +587,14 @@
 }
 
-void MutexStmt::print( std::ostream & os, Indenter indent ) const {
+void MutexStmt::print( ostream & os, Indenter indent ) const {
 	os << "Mutex Statement" << endl;
 	os << indent << "... with Expressions: " << endl;
 	for (auto * obj : mutexObjs) {
-		os << indent+1;
-		obj->print( os, indent+1);
+		os << indent + 1;
+		obj->print( os, indent + 1);
 		os << endl;
 	}
-	os << indent << "... with Statement: " << endl << indent+1;
-	stmt->print( os, indent+1 );
+	os << indent << "... with Statement: " << endl << indent + 1;
+	stmt->print( os, indent + 1 );
 }
 
Index: src/SynTree/Statement.h
===================================================================
--- src/SynTree/Statement.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/SynTree/Statement.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Jan 10 14:13:24 2020
-// Update Count     : 85
+// Last Modified On : Wed Feb  2 20:15:30 2022
+// Update Count     : 98
 //
 
@@ -107,5 +107,5 @@
 	std::list<Label> gotolabels;
 
-	AsmStmt( bool voltile, Expression * instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels );
+	AsmStmt( bool voltile, Expression * instruction, const std::list<Expression *> output, const std::list<Expression *> input, const std::list<ConstantExpr *> clobber, const std::list<Label> gotolabels );
 	AsmStmt( const AsmStmt & other );
 	virtual ~AsmStmt();
@@ -148,10 +148,10 @@
   public:
 	Expression * condition;
-	Statement * thenPart;
-	Statement * elsePart;
+	Statement * then;
+	Statement * else_;
 	std::list<Statement *> initialization;
 
-	IfStmt( Expression * condition, Statement * thenPart, Statement * elsePart,
-			std::list<Statement *> initialization = std::list<Statement *>() );
+	IfStmt( Expression * condition, Statement * then, Statement * else_,
+			const std::list<Statement *> initialization = std::list<Statement *>() );
 	IfStmt( const IfStmt & other );
 	virtual ~IfStmt();
@@ -160,8 +160,8 @@
 	Expression * get_condition() { return condition; }
 	void set_condition( Expression * newValue ) { condition = newValue; }
-	Statement * get_thenPart() { return thenPart; }
-	void set_thenPart( Statement * newValue ) { thenPart = newValue; }
-	Statement * get_elsePart() { return elsePart; }
-	void set_elsePart( Statement * newValue ) { elsePart = newValue; }
+	Statement * get_then() { return then; }
+	void set_then( Statement * newValue ) { then = newValue; }
+	Statement * get_else() { return else_; }
+	void set_else( Statement * newValue ) { else_ = newValue; }
 
 	virtual IfStmt * clone() const override { return new IfStmt( *this ); }
@@ -225,14 +225,16 @@
 };
 
-class WhileStmt : public Statement {
+class WhileDoStmt : public Statement {
   public:
 	Expression * condition;
 	Statement * body;
+	Statement * else_;
 	std::list<Statement *> initialization;
 	bool isDoWhile;
 
-	WhileStmt( Expression * condition, Statement * body, std::list<Statement *> & initialization, bool isDoWhile = false );
-	WhileStmt( const WhileStmt & other );
-	virtual ~WhileStmt();
+	WhileDoStmt( Expression * condition, Statement * body, const std::list<Statement *> & initialization, bool isDoWhile = false );
+	WhileDoStmt( Expression * condition, Statement * body, Statement * else_, const std::list<Statement *> & initialization, bool isDoWhile = false );
+	WhileDoStmt( const WhileDoStmt & other );
+	virtual ~WhileDoStmt();
 
 	Expression * get_condition() { return condition; }
@@ -243,5 +245,5 @@
 	void set_isDoWhile( bool newValue ) { isDoWhile = newValue; }
 
-	virtual WhileStmt * clone() const override { return new WhileStmt( *this ); }
+	virtual WhileDoStmt * clone() const override { return new WhileDoStmt( *this ); }
 	virtual void accept( Visitor & v ) override { v.visit( this ); }
 	virtual void accept( Visitor & v ) const override { v.visit( this ); }
@@ -256,6 +258,7 @@
 	Expression * increment;
 	Statement * body;
-
-	ForStmt( std::list<Statement *> initialization, Expression * condition = nullptr, Expression * increment = nullptr, Statement * body = nullptr );
+	Statement * else_;
+
+	ForStmt( const std::list<Statement *> initialization, Expression * condition = nullptr, Expression * increment = nullptr, Statement * body = nullptr, Statement * else_ = nullptr );
 	ForStmt( const ForStmt & other );
 	virtual ~ForStmt();
@@ -278,5 +281,5 @@
 class BranchStmt : public Statement {
   public:
-	enum Type { Goto = 0, Break, Continue, FallThrough, FallThroughDefault };
+	enum Type { Goto, Break, Continue, FallThrough, FallThroughDefault, BranchStmts };
 
 	// originalTarget kept for error messages.
@@ -357,5 +360,5 @@
 	FinallyStmt * finallyBlock;
 
-	TryStmt( CompoundStmt * tryBlock, std::list<CatchStmt *> & handlers, FinallyStmt * finallyBlock = nullptr );
+	TryStmt( CompoundStmt * tryBlock, const std::list<CatchStmt *> & handlers, FinallyStmt * finallyBlock = nullptr );
 	TryStmt( const TryStmt & other );
 	virtual ~TryStmt();
@@ -540,5 +543,5 @@
 	std::list<Expression *> mutexObjs; // list of mutex objects to acquire
 
-	MutexStmt( Statement * stmt, std::list<Expression *> mutexObjs );
+	MutexStmt( Statement * stmt, const std::list<Expression *> mutexObjs );
 	MutexStmt( const MutexStmt & other );
 	virtual ~MutexStmt();
Index: src/SynTree/SynTree.h
===================================================================
--- src/SynTree/SynTree.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/SynTree/SynTree.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:56:44 2021
-// Update Count     : 13
+// Last Modified On : Tue Feb  1 09:22:33 2022
+// Update Count     : 14
 //
 
@@ -45,5 +45,5 @@
 class DirectiveStmt;
 class IfStmt;
-class WhileStmt;
+class WhileDoStmt;
 class ForStmt;
 class SwitchStmt;
Index: src/SynTree/Visitor.h
===================================================================
--- src/SynTree/Visitor.h	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/SynTree/Visitor.h	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Mon May 18 07:44:20 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Fri Mar 12 18:35:35 2021
-// Update Count     : 15
+// Last Modified On : Tue Feb  1 09:26:57 2022
+// Update Count     : 17
 //
 
@@ -60,6 +60,6 @@
 	virtual void visit( IfStmt * node ) { visit( const_cast<const IfStmt *>(node) ); }
 	virtual void visit( const IfStmt * ifStmt ) = 0;
-	virtual void visit( WhileStmt * node ) { visit( const_cast<const WhileStmt *>(node) ); }
-	virtual void visit( const WhileStmt * whileStmt ) = 0;
+	virtual void visit( WhileDoStmt * node ) { visit( const_cast<const WhileDoStmt *>(node) ); }
+	virtual void visit( const WhileDoStmt * whileDoStmt ) = 0;
 	virtual void visit( ForStmt * node ) { visit( const_cast<const ForStmt *>(node) ); }
 	virtual void visit( const ForStmt * forStmt ) = 0;
Index: src/Validate/Autogen.cpp
===================================================================
--- src/Validate/Autogen.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ src/Validate/Autogen.cpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,778 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Autogen.cpp -- Generate automatic routines for data types.
+//
+// Author           : Andrew Beach
+// Created On       : Thu Dec  2 13:44:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Thr Jan 27  9:29:00 2022
+// Update Count     : 1
+//
+
+#include "Autogen.hpp"
+
+#include <algorithm>               // for count_if
+#include <cassert>                 // for strict_dynamic_cast, assert, assertf
+#include <iterator>                // for back_insert_iterator, back_inserter
+#include <list>                    // for list, _List_iterator, list<>::iter...
+#include <set>                     // for set, _Rb_tree_const_iterator
+#include <utility>                 // for pair
+#include <vector>                  // for vector
+
+#include "AST/Attribute.hpp"
+#include "AST/Decl.hpp"
+#include "AST/DeclReplacer.hpp"
+#include "AST/Expr.hpp"
+#include "AST/Pass.hpp"
+#include "AST/Stmt.hpp"
+#include "AST/SymbolTable.hpp"
+#include "CodeGen/OperatorTable.h" // for isCtorDtor, isCtorDtorAssign
+#include "Common/ScopedMap.h"      // for ScopedMap<>::const_iterator, Scope...
+#include "Common/utility.h"        // for cloneAll, operator+
+#include "GenPoly/ScopedSet.h"     // for ScopedSet, ScopedSet<>::iterator
+#include "InitTweak/GenInit.h"     // for fixReturnStatements
+#include "InitTweak/InitTweak.h"   // for isAssignment, isCopyConstructor
+#include "SymTab/Mangler.h"        // for Mangler
+#include "CompilationState.h"
+
+// TODO: The other new ast function should be moved over to this file.
+#include "SymTab/Autogen.h"
+
+namespace Validate {
+
+namespace {
+
+// --------------------------------------------------------------------------
+struct AutogenerateRoutines_new final :
+		public ast::WithDeclsToAdd<>,
+		public ast::WithShortCircuiting {
+	void previsit( const ast::EnumDecl * enumDecl );
+	void previsit( const ast::StructDecl * structDecl );
+	void previsit( const ast::UnionDecl * structDecl );
+	void previsit( const ast::TypeDecl * typeDecl );
+	void previsit( const ast::TraitDecl * traitDecl );
+	void previsit( const ast::FunctionDecl * functionDecl );
+	void postvisit( const ast::FunctionDecl * functionDecl );
+
+private:
+	// Current level of nested functions.
+	unsigned int functionNesting = 0;
+};
+
+// --------------------------------------------------------------------------
+/// Class used to generate functions for a particular declaration.
+/// Note it isn't really stored, it is just a class for organization and to
+/// help pass around some of the common arguments.
+class FuncGenerator {
+public:
+	std::list<ast::ptr<ast::Decl>> forwards;
+	std::list<ast::ptr<ast::Decl>> definitions;
+
+	FuncGenerator( const ast::Type * type, unsigned int functionNesting ) :
+		type( type ), functionNesting( functionNesting )
+	{}
+
+	/// Generate functions (and forward decls.) and append them to the list.
+	void generateAndAppendFunctions( std::list<ast::ptr<ast::Decl>> & );
+
+	virtual bool shouldAutogen() const = 0;
+protected:
+	const ast::Type * type;
+	unsigned int functionNesting;
+	ast::Linkage::Spec proto_linkage = ast::Linkage::AutoGen;
+
+	// Internal helpers:
+	void genStandardFuncs();
+	void produceDecl( const ast::FunctionDecl * decl );
+	void produceForwardDecl( const ast::FunctionDecl * decl );
+
+	const CodeLocation& getLocation() const { return getDecl()->location; }
+	ast::FunctionDecl * genProto( const std::string& name,
+		std::vector<ast::ptr<ast::DeclWithType>>&& params,
+		std::vector<ast::ptr<ast::DeclWithType>>&& returns ) const;
+
+	ast::ObjectDecl * dstParam() const;
+	ast::ObjectDecl * srcParam() const;
+	ast::FunctionDecl * genCtorProto() const;
+	ast::FunctionDecl * genCopyProto() const;
+	ast::FunctionDecl * genDtorProto() const;
+	ast::FunctionDecl * genAssignProto() const;
+	ast::FunctionDecl * genFieldCtorProto( unsigned int fields ) const;
+
+	// Internal Hooks:
+	virtual void genFuncBody( ast::FunctionDecl * decl ) = 0;
+	virtual void genFieldCtors() = 0;
+	virtual bool isConcurrentType() const { return false; }
+	virtual const ast::Decl * getDecl() const = 0;
+};
+
+class StructFuncGenerator final : public FuncGenerator {
+	const ast::StructDecl * decl;
+public:
+	StructFuncGenerator( const ast::StructDecl * decl,
+			const ast::StructInstType * type,
+			unsigned int functionNesting ) :
+		FuncGenerator( type, functionNesting ), decl( decl )
+	{}
+
+	// Built-ins do not use autogeneration.
+	bool shouldAutogen() const final { return !decl->linkage.is_builtin; }
+private:
+	void genFuncBody( ast::FunctionDecl * decl ) final;
+	void genFieldCtors() final;
+	bool isConcurrentType() const final {
+		return decl->is_thread() || decl->is_monitor();
+	}
+	virtual const ast::Decl * getDecl() const final { return decl; }
+
+	/// Generates a single struct member operation.
+	/// (constructor call, destructor call, assignment call)
+	// This is managed because it uses another helper that returns a ast::ptr.
+	ast::ptr<ast::Stmt> makeMemberOp(
+		const CodeLocation& location,
+		const ast::ObjectDecl * dstParam, const ast::Expr * src,
+		const ast::ObjectDecl * field, ast::FunctionDecl * func,
+		SymTab::LoopDirection direction );
+
+	/// Generates the body of a struct function by iterating the struct members
+	/// (via parameters). Generates default constructor, copy constructor,
+	/// copy assignment, and destructor bodies. No field constructor bodies.
+	template<typename Iterator>
+	void makeFunctionBody( Iterator member, Iterator end,
+			ast::FunctionDecl * func, SymTab::LoopDirection direction );
+
+	/// Generate the body of a constructor which takes parameters that match
+	/// fields. (With arguments for one to all of the fields.)
+	template<typename Iterator>
+	void makeFieldCtorBody( Iterator member, Iterator end,
+			ast::FunctionDecl * func );
+};
+
+class UnionFuncGenerator final : public FuncGenerator {
+	const ast::UnionDecl * decl;
+public:
+	UnionFuncGenerator( const ast::UnionDecl * decl,
+			const ast::UnionInstType * type,
+			unsigned int functionNesting ) :
+		FuncGenerator( type, functionNesting ), decl( decl )
+	{}
+
+	// Built-ins do not use autogeneration.
+	bool shouldAutogen() const final { return !decl->linkage.is_builtin; }
+private:
+	void genFuncBody( ast::FunctionDecl * decl ) final;
+	void genFieldCtors() final;
+	const ast::Decl * getDecl() const final { return decl; }
+
+	/// Generate a single union assignment expression (using memcpy).
+	ast::ExprStmt * makeAssignOp( const CodeLocation& location,
+		const ast::ObjectDecl * dstParam, const ast::ObjectDecl * srcParam );
+};
+
+class EnumFuncGenerator final : public FuncGenerator {
+	const ast::EnumDecl * decl;
+public:
+	EnumFuncGenerator( const ast::EnumDecl * decl,
+			const ast::EnumInstType * type,
+			unsigned int functionNesting ) :
+		FuncGenerator( type, functionNesting ), decl( decl )
+	{
+		// TODO: These functions are somewhere between instrinsic and autogen,
+		// could possibly use a new linkage type. For now we just make them
+		// intrinsic to code-gen them as C assignments.
+		proto_linkage = ast::Linkage::Intrinsic;
+	}
+
+	bool shouldAutogen() const final { return true; }
+private:
+	void genFuncBody( ast::FunctionDecl * decl ) final;
+	void genFieldCtors() final;
+	const ast::Decl * getDecl() const final { return decl; }
+};
+
+class TypeFuncGenerator final : public FuncGenerator {
+	const ast::TypeDecl * decl;
+public:
+	TypeFuncGenerator( const ast::TypeDecl * decl,
+			ast::TypeInstType * type,
+			unsigned int functionNesting ) :
+		FuncGenerator( type, functionNesting ), decl( decl )
+	{}
+
+	bool shouldAutogen() const final { return true; }
+	void genFieldCtors() final;
+private:
+	void genFuncBody( ast::FunctionDecl * decl ) final;
+	const ast::Decl * getDecl() const final { return decl; }
+};
+
+// --------------------------------------------------------------------------
+const std::vector<ast::ptr<ast::TypeDecl>>& getGenericParams( const ast::Type * t ) {
+	if ( auto inst = dynamic_cast< const ast::StructInstType * >( t ) ) {
+		return inst->base->params;
+	} else if ( auto inst = dynamic_cast< const ast::UnionInstType * >( t ) ) {
+		return inst->base->params;
+	}
+	static std::vector<ast::ptr<ast::TypeDecl>> const empty;
+	return empty;
+}
+
+/// Changes the node inside a pointer so that it has the unused attribute.
+void addUnusedAttribute( ast::ptr<ast::DeclWithType> & declPtr ) {
+	ast::DeclWithType * decl = declPtr.get_and_mutate();
+	decl->attributes.push_back( new ast::Attribute( "unused" ) );
+}
+
+// --------------------------------------------------------------------------
+void AutogenerateRoutines_new::previsit( const ast::EnumDecl * enumDecl ) {
+	// Must visit children (enum constants) to add them to the symbol table.
+	if ( !enumDecl->body ) return;
+
+	ast::EnumInstType enumInst( enumDecl->name );
+	enumInst.base = enumDecl;
+	EnumFuncGenerator gen( enumDecl, &enumInst, functionNesting );
+	gen.generateAndAppendFunctions( declsToAddAfter );
+}
+
+void AutogenerateRoutines_new::previsit( const ast::StructDecl * structDecl ) {
+	visit_children = false;
+	if ( !structDecl->body ) return;
+
+	ast::StructInstType structInst( structDecl->name );
+	structInst.base = structDecl;
+	for ( const ast::TypeDecl * typeDecl : structDecl->params ) {
+		structInst.params.push_back( new ast::TypeExpr(
+			typeDecl->location,
+			new ast::TypeInstType( typeDecl->name, typeDecl )
+		) );
+	}
+	StructFuncGenerator gen( structDecl, &structInst, functionNesting );
+	gen.generateAndAppendFunctions( declsToAddAfter );
+}
+
+void AutogenerateRoutines_new::previsit( const ast::UnionDecl * unionDecl ) {
+	visit_children = false;
+	if ( !unionDecl->body ) return;
+
+	ast::UnionInstType unionInst( unionDecl->name );
+	unionInst.base = unionDecl;
+	for ( const ast::TypeDecl * typeDecl : unionDecl->params ) {
+		unionInst.params.push_back( new ast::TypeExpr(
+			unionDecl->location,
+			new ast::TypeInstType( typeDecl->name, typeDecl )
+		) );
+	}
+	UnionFuncGenerator gen( unionDecl, &unionInst, functionNesting );
+	gen.generateAndAppendFunctions( declsToAddAfter );
+}
+
+/// Generate ctor/dtors/assign for typedecls, e.g., otype T = int *;
+void AutogenerateRoutines_new::previsit( const ast::TypeDecl * typeDecl ) {
+	if ( !typeDecl->base ) return;
+
+	ast::TypeInstType refType( typeDecl->name, typeDecl );
+	TypeFuncGenerator gen( typeDecl, &refType, functionNesting );
+	gen.generateAndAppendFunctions( declsToAddAfter );
+}
+
+void AutogenerateRoutines_new::previsit( const ast::TraitDecl * ) {
+	// Ensure that we don't add assignment ops for types defined as part of the trait
+	visit_children = false;
+}
+
+void AutogenerateRoutines_new::previsit( const ast::FunctionDecl * ) {
+	// Track whether we're currently in a function.
+	// Can ignore function type idiosyncrasies, because function type can never
+	// declare a new type.
+	functionNesting += 1;
+}
+
+void AutogenerateRoutines_new::postvisit( const ast::FunctionDecl * ) {
+	functionNesting -= 1;
+}
+
+void FuncGenerator::generateAndAppendFunctions(
+		std::list<ast::ptr<ast::Decl>> & decls ) {
+	if ( !shouldAutogen() ) return;
+
+	// Generate the functions (they go into forwards and definitions).
+	genStandardFuncs();
+	genFieldCtors();
+
+	// Now export the lists contents.
+	decls.splice( decls.end(), forwards );
+	decls.splice( decls.end(), definitions );
+}
+
+void FuncGenerator::produceDecl( const ast::FunctionDecl * decl ) {
+	assert( nullptr != decl->stmts );
+
+	definitions.push_back( decl );
+}
+
+/// Make a forward declaration of the decl and add it to forwards.
+void FuncGenerator::produceForwardDecl( const ast::FunctionDecl * decl ) {
+	if (0 != functionNesting) return;
+	ast::FunctionDecl * fwd = ast::deepCopy( decl );
+	fwd->stmts = nullptr;
+	fwd->fixUniqueId();
+	forwards.push_back( fwd );
+}
+
+/// Generates a basic prototype function declaration.
+ast::FunctionDecl * FuncGenerator::genProto( const std::string& name,
+		std::vector<ast::ptr<ast::DeclWithType>>&& params,
+		std::vector<ast::ptr<ast::DeclWithType>>&& returns ) const {
+
+	// Handle generic prameters and assertions, if any.
+	auto const & old_type_params = getGenericParams( type );
+	std::vector<ast::ptr<ast::TypeDecl>> type_params;
+	std::vector<ast::ptr<ast::DeclWithType>> assertions;
+	for ( auto & old_param : old_type_params ) {
+		ast::TypeDecl * decl = ast::deepCopy( old_param );
+		for ( auto assertion : decl->assertions ) {
+			assertions.push_back( assertion );
+		}
+		decl->assertions.clear();
+		type_params.push_back( decl );
+	}
+	// TODO: The values in params and returns still may point at the old
+	// generic params, that does not appear to be an issue but perhaps it
+	// should be addressed.
+
+	ast::FunctionDecl * decl = new ast::FunctionDecl(
+		// Auto-generated routines use the type declaration's location.
+		getLocation(),
+		name,
+		std::move( type_params ),
+		std::move( params ),
+		std::move( returns ),
+		// Only a prototype, no body.
+		nullptr,
+		// Use static storage if we are at the top level.
+		(0 < functionNesting) ? ast::Storage::Classes() : ast::Storage::Static,
+		proto_linkage,
+		std::vector<ast::ptr<ast::Attribute>>(),
+		// Auto-generated routines are inline to avoid conflicts.
+		ast::Function::Specs( ast::Function::Inline ) );
+	decl->assertions = std::move( assertions );
+	decl->fixUniqueId();
+	return decl;
+}
+
+ast::ObjectDecl * FuncGenerator::dstParam() const {
+	return new ast::ObjectDecl( getLocation(), "_dst",
+		new ast::ReferenceType( ast::deepCopy( type ) ),
+		nullptr, {}, ast::Linkage::Cforall );
+}
+
+ast::ObjectDecl * FuncGenerator::srcParam() const {
+	return new ast::ObjectDecl( getLocation(), "_src",
+		ast::deepCopy( type ),
+		nullptr, {}, ast::Linkage::Cforall );
+}
+
+/// Use the current type T to create `void ?{}(T & _dst)`.
+ast::FunctionDecl * FuncGenerator::genCtorProto() const {
+	return genProto( "?{}", { dstParam() }, {} );
+}
+
+/// Use the current type T to create `void ?{}(T & _dst, T _src)`.
+ast::FunctionDecl * FuncGenerator::genCopyProto() const {
+	return genProto( "?{}", { dstParam(), srcParam() }, {} );
+}
+
+/// Use the current type T to create `void ?{}(T & _dst)`.
+ast::FunctionDecl * FuncGenerator::genDtorProto() const {
+	// The destructor must be mutex on a concurrent type.
+	auto dst = dstParam();
+	if ( isConcurrentType() ) {
+		add_qualifiers( dst->type, ast::CV::Qualifiers( ast::CV::Mutex ) );
+	}
+	return genProto( "^?{}", { dst }, {} );
+}
+
+/// Use the current type T to create `T ?{}(T & _dst, T _src)`.
+ast::FunctionDecl * FuncGenerator::genAssignProto() const {
+	// Only the name is different, so just reuse the generation function.
+	auto retval = srcParam();
+	retval->name = "_ret";
+	return genProto( "?=?", { dstParam(), srcParam() }, { retval } );
+}
+
+// This one can return null if the last field is an unnamed bitfield.
+ast::FunctionDecl * FuncGenerator::genFieldCtorProto(
+		unsigned int fields ) const {
+	assert( 0 < fields );
+	auto aggr = strict_dynamic_cast<const ast::AggregateDecl *>( getDecl() );
+
+	std::vector<ast::ptr<ast::DeclWithType>> params = { dstParam() };
+	for ( unsigned int index = 0 ; index < fields ; ++index ) {
+		auto member = aggr->members[index].strict_as<ast::DeclWithType>();
+		if ( SymTab::isUnnamedBitfield(
+				dynamic_cast<const ast::ObjectDecl *>( member ) ) ) {
+			if ( index == fields - 1 ) {
+				return nullptr;
+			}
+			continue;
+		}
+
+		auto * paramType = ast::deepCopy( member->get_type() );
+		paramType->attributes.clear();
+		ast::ObjectDecl * param = new ast::ObjectDecl(
+			getLocation(), member->name, paramType );
+		param->linkage = ast::Linkage::Cforall;
+		for ( auto & attr : member->attributes ) {
+			if ( attr->isValidOnFuncParam() ) {
+				param->attributes.push_back( attr );
+			}
+		}
+		params.emplace_back( param );
+	}
+	return genProto( "?{}", std::move( params ), {} );
+}
+
+void appendReturnThis( ast::FunctionDecl * decl ) {
+	assert( 1 <= decl->params.size() );
+	assert( 1 == decl->returns.size() );
+	assert( decl->stmts );
+
+	const CodeLocation& location = (decl->stmts->kids.empty())
+		? decl->stmts->location : decl->stmts->kids.back()->location;
+	const ast::DeclWithType * thisParam = decl->params.front();
+	decl->stmts.get_and_mutate()->push_back(
+		new ast::ReturnStmt( location,
+			new ast::VariableExpr( location, thisParam )
+		)
+	);
+}
+
+void FuncGenerator::genStandardFuncs() {
+	// The order here determines the order that these functions are generated.
+	// Assignment should come last since it uses copy constructor in return.
+	ast::FunctionDecl *(FuncGenerator::*standardProtos[4])() const = {
+			&FuncGenerator::genCtorProto, &FuncGenerator::genCopyProto,
+			&FuncGenerator::genDtorProto, &FuncGenerator::genAssignProto };
+	for ( auto & generator : standardProtos ) {
+		ast::FunctionDecl * decl = (this->*generator)();
+		produceForwardDecl( decl );
+		genFuncBody( decl );
+		if ( CodeGen::isAssignment( decl->name ) ) {
+			appendReturnThis( decl );
+		}
+		produceDecl( decl );
+	}
+}
+
+void StructFuncGenerator::genFieldCtors() {
+	// The field constructors are only generated if the default constructor
+	// and copy constructor are both generated, since the need both.
+	unsigned numCtors = std::count_if( definitions.begin(), definitions.end(),
+		[](const ast::Decl * decl){ return CodeGen::isConstructor( decl->name ); }
+	);
+	if ( 2 != numCtors ) return;
+
+	for ( unsigned int num = 1 ; num <= decl->members.size() ; ++num ) {
+		ast::FunctionDecl * ctor = genFieldCtorProto( num );
+		if ( nullptr == ctor ) {
+			continue;
+		}
+		produceForwardDecl( ctor );
+		makeFieldCtorBody( decl->members.begin(), decl->members.end(), ctor );
+		produceDecl( ctor );
+	}
+}
+
+void StructFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
+	// Generate appropriate calls to member constructors and assignment.
+	// Destructor needs to do everything in reverse,
+	// so pass "forward" based on whether the function is a destructor
+	if ( CodeGen::isDestructor( functionDecl->name ) ) {
+		makeFunctionBody( decl->members.rbegin(), decl->members.rend(),
+			functionDecl, SymTab::LoopBackward );
+	} else {
+		makeFunctionBody( decl->members.begin(), decl->members.end(),
+			functionDecl, SymTab::LoopForward );
+	}
+}
+
+ast::ptr<ast::Stmt> StructFuncGenerator::makeMemberOp(
+		const CodeLocation& location, const ast::ObjectDecl * dstParam,
+		const ast::Expr * src, const ast::ObjectDecl * field,
+		ast::FunctionDecl * func, SymTab::LoopDirection direction ) {
+	InitTweak::InitExpander_new srcParam( src );
+	// Assign to destination.
+	ast::Expr * dstSelect = new ast::MemberExpr(
+		location,
+		field,
+		new ast::CastExpr(
+			location,
+			new ast::VariableExpr( location, dstParam ),
+			dstParam->type.strict_as<ast::ReferenceType>()->base
+		)
+	);
+	return genImplicitCall(
+		srcParam, dstSelect, location, func->name,
+		field, direction
+	);
+}
+
+template<typename Iterator>
+void StructFuncGenerator::makeFunctionBody( Iterator current, Iterator end,
+		ast::FunctionDecl * func, SymTab::LoopDirection direction ) {
+	// Trying to get the best code location. Should probably use a helper or
+	// just figure out what that would be given where this is called.
+	assert( nullptr == func->stmts );
+	const CodeLocation& location = func->location;
+
+	ast::CompoundStmt * stmts = new ast::CompoundStmt( location );
+
+	for ( ; current != end ; ++current ) {
+		const ast::ptr<ast::Decl> & member = *current;
+		auto field = member.as<ast::ObjectDecl>();
+		if ( nullptr == field ) {
+			continue;
+		}
+
+		// Don't assign to constant members (but do construct/destruct them).
+		if ( CodeGen::isAssignment( func->name ) ) {
+			// For array types we need to strip off the array layers.
+			const ast::Type * type = field->get_type();
+			while ( auto at = dynamic_cast<const ast::ArrayType *>( type ) ) {
+				type = at->base;
+			}
+			if ( type->is_const() ) {
+				continue;
+			}
+		}
+
+		assert( !func->params.empty() );
+		const ast::ObjectDecl * dstParam =
+			func->params.front().strict_as<ast::ObjectDecl>();
+		const ast::ObjectDecl * srcParam = nullptr;
+		if ( 2 == func->params.size() ) {
+			srcParam = func->params.back().strict_as<ast::ObjectDecl>();
+		}
+
+		ast::Expr * srcSelect = (srcParam) ? new ast::MemberExpr(
+			location, field, new ast::VariableExpr( location, srcParam )
+		) : nullptr;
+		ast::ptr<ast::Stmt> stmt =
+			makeMemberOp( location, dstParam, srcSelect, field, func, direction );
+
+		if ( nullptr != stmt ) {
+			stmts->kids.push_back( stmt );
+		}
+	}
+
+	func->stmts = stmts;
+}
+
+template<typename Iterator>
+void StructFuncGenerator::makeFieldCtorBody( Iterator current, Iterator end,
+		ast::FunctionDecl * func ) {
+	const CodeLocation& location = func->location;
+	auto & params = func->params;
+	// Need at least the constructed parameter and one field parameter.
+	assert( 2 <= params.size() );
+
+	ast::CompoundStmt * stmts = new ast::CompoundStmt( location );
+
+	auto dstParam = params.front().strict_as<ast::ObjectDecl>();
+	// Skip over the 'this' parameter.
+	for ( auto param = params.begin() + 1 ; current != end ; ++current ) {
+		const ast::ptr<ast::Decl> & member = *current;
+		ast::ptr<ast::Stmt> stmt = nullptr;
+		auto field = member.as<ast::ObjectDecl>();
+		// Not sure why it could be null.
+		// Don't make a function for a parameter that is an unnamed bitfield.
+		if ( nullptr == field || SymTab::isUnnamedBitfield( field ) ) {
+			continue;
+		// Matching Parameter: Initialize the field by copy.
+		} else if ( params.end() != param ) {
+			const ast::Expr *srcSelect = new ast::VariableExpr(
+				func->location, param->get() );
+			stmt = makeMemberOp( location, dstParam, srcSelect, field, func, SymTab::LoopForward );
+			++param;
+		// No Matching Parameter: Initialize the field by default constructor.
+		} else {
+			stmt = makeMemberOp( location, dstParam, nullptr, field, func, SymTab::LoopForward );
+		}
+
+		if ( nullptr != stmt ) {
+			stmts->kids.push_back( stmt );
+		}
+	}
+	func->stmts = stmts;
+}
+
+void UnionFuncGenerator::genFieldCtors() {
+	// Field constructors are only generated if default and copy constructor
+	// are generated, since they need access to both
+	unsigned numCtors = std::count_if( definitions.begin(), definitions.end(),
+		[]( const ast::Decl * d ){ return CodeGen::isConstructor( d->name ); }
+	);
+	if ( 2 != numCtors ) {
+		return;
+	}
+
+	// Create a constructor which takes the first member type as a
+	// parameter. For example for `union A { int x; char y; };` generate
+	// a function with signature `void ?{}(A *, int)`. This mimics C's
+	// behaviour which initializes the first member of the union.
+
+	// Still, there must be some members.
+	if ( !decl->members.empty() ) {
+		ast::FunctionDecl * ctor = genFieldCtorProto( 1 );
+		if ( nullptr == ctor ) {
+			return;
+		}
+		produceForwardDecl( ctor );
+		auto params = ctor->params;
+		auto dstParam = params.front().strict_as<ast::ObjectDecl>();
+		auto srcParam = params.back().strict_as<ast::ObjectDecl>();
+		ctor->stmts = new ast::CompoundStmt( getLocation(),
+			{ makeAssignOp( getLocation(), dstParam, srcParam ) }
+		);
+		produceDecl( ctor );
+	}
+}
+
+void UnionFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
+	const CodeLocation& location = functionDecl->location;
+	auto & params = functionDecl->params;
+	if ( InitTweak::isCopyConstructor( functionDecl )
+			|| InitTweak::isAssignment( functionDecl ) ) {
+		assert( 2 == params.size() );
+		auto dstParam = params.front().strict_as<ast::ObjectDecl>();
+		auto srcParam = params.back().strict_as<ast::ObjectDecl>();
+		functionDecl->stmts = new ast::CompoundStmt( location,
+			{ makeAssignOp( location, dstParam, srcParam ) }
+		);
+	} else {
+		assert( 1 == params.size() );
+		// Default constructor and destructor is empty.
+		functionDecl->stmts = new ast::CompoundStmt( location );
+		// Add unused attribute to parameter to silence warnings.
+		addUnusedAttribute( params.front() );
+
+		// Just an extra step to make the forward and declaration match.
+		if ( forwards.empty() ) return;
+		ast::FunctionDecl * fwd = strict_dynamic_cast<ast::FunctionDecl *>(
+			forwards.back().get_and_mutate() );
+		addUnusedAttribute( fwd->params.front() );
+	}
+}
+
+ast::ExprStmt * UnionFuncGenerator::makeAssignOp( const CodeLocation& location,
+		const ast::ObjectDecl * dstParam, const ast::ObjectDecl * srcParam ) {
+	return new ast::ExprStmt( location, new ast::UntypedExpr(
+		location,
+		new ast::NameExpr( location, "__builtin_memcpy" ),
+		{
+			new ast::AddressExpr( location,
+				new ast::VariableExpr( location, dstParam ) ),
+			new ast::AddressExpr( location,
+				new ast::VariableExpr( location, srcParam ) ),
+			new ast::SizeofExpr( location, srcParam->type ),
+		} ) );
+}
+
+void EnumFuncGenerator::genFieldCtors() {
+	// Enumerations to not have field constructors.
+}
+
+void EnumFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
+	const CodeLocation& location = functionDecl->location;
+	auto & params = functionDecl->params;
+	if ( InitTweak::isCopyConstructor( functionDecl )
+			|| InitTweak::isAssignment( functionDecl ) ) {
+		assert( 2 == params.size() );
+		auto dstParam = params.front().strict_as<ast::ObjectDecl>();
+		auto srcParam = params.back().strict_as<ast::ObjectDecl>();
+
+		/* This looks like a recursive call, but code-gen will turn it into
+		 * a C-style assignment.
+		 *
+		 * This is still before function pointer type conversion,
+		 * so this will have to do it manually.
+		 *
+		 * It will also reference the parent function declaration, creating
+		 * a cycle for references. This also means that the ref-counts are
+		 * now non-zero and the declaration will be deleted if it ever
+		 * returns to zero.
+		 */
+		auto callExpr = new ast::ApplicationExpr( location,
+			ast::VariableExpr::functionPointer( location, functionDecl ),
+			{
+				new ast::VariableExpr( location, dstParam ),
+				new ast::VariableExpr( location, srcParam ),
+			}
+		);
+		functionDecl->stmts = new ast::CompoundStmt( location,
+			{ new ast::ExprStmt( location, callExpr ) }
+		);
+	} else {
+		assert( 1 == params.size() );
+		// Default constructor and destructor is empty.
+		functionDecl->stmts = new ast::CompoundStmt( location );
+		// Just add unused attribute to parameter to silence warnings.
+		addUnusedAttribute( params.front() );
+
+		// Just an extra step to make the forward and declaration match.
+		if ( forwards.empty() ) return;
+		ast::FunctionDecl * fwd = strict_dynamic_cast<ast::FunctionDecl *>(
+			forwards.back().get_and_mutate() );
+		addUnusedAttribute( fwd->params.front() );
+	}
+}
+
+void TypeFuncGenerator::genFieldCtors() {
+	// Opaque types do not have field constructors.
+}
+
+void TypeFuncGenerator::genFuncBody( ast::FunctionDecl * functionDecl ) {
+	const CodeLocation& location = functionDecl->location;
+	auto & params = functionDecl->type->params;
+	assertf( 1 == params.size() || 2 == params.size(),
+		"Incorrect number of parameters in autogenerated typedecl function: %zd",
+		params.size() );
+	auto dstParam = params.front().strict_as<ast::ObjectDecl>();
+	auto srcParam = (2 == params.size())
+		? params.back().strict_as<ast::ObjectDecl>() : nullptr;
+	// Generate appropriate calls to member constructor and assignment.
+	ast::UntypedExpr * expr = new ast::UntypedExpr( location,
+		new ast::NameExpr( location, functionDecl->name )
+	);
+	expr->args.push_back( new ast::CastExpr( location,
+		new ast::VariableExpr( location, dstParam ),
+		new ast::ReferenceType( decl->base )
+	) );
+	if ( srcParam ) {
+		expr->args.push_back( new ast::CastExpr( location,
+			new ast::VariableExpr( location, srcParam ),
+			decl->base
+		) );
+	}
+	functionDecl->stmts = new ast::CompoundStmt( location,
+		{ new ast::ExprStmt( location, expr ) }
+	);
+}
+
+} // namespace
+
+void autogenerateRoutines( ast::TranslationUnit & translationUnit ) {
+	ast::Pass<AutogenerateRoutines_new>::run( translationUnit );
+}
+
+} // Validate
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Validate/Autogen.hpp
===================================================================
--- src/Validate/Autogen.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ src/Validate/Autogen.hpp	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,32 @@
+//
+// Cforall Version 1.0.0 Copyright (C) 2018 University of Waterloo
+//
+// The contents of this file are covered under the licence agreement in the
+// file "LICENCE" distributed with Cforall.
+//
+// Autogen.hpp -- Generate automatic routines for data types.
+//
+// Author           : Andrew Beach
+// Created On       : Wed Dec  1 13:42:00 2021
+// Last Modified By : Andrew Beach
+// Last Modified On : Thr Dec  2 13:56:00 2021
+// Update Count     : 0
+//
+
+#pragma once
+
+namespace ast {
+    class TranslationUnit;
+}
+
+namespace Validate {
+
+void autogenerateRoutines( ast::TranslationUnit & translationUnit );
+
+}
+
+// Local Variables: //
+// tab-width: 4 //
+// mode: c++ //
+// compile-command: "make install" //
+// End: //
Index: src/Validate/module.mk
===================================================================
--- src/Validate/module.mk	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/Validate/module.mk	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -16,4 +16,6 @@
 
 SRC_VALIDATE = \
+	Validate/Autogen.cpp \
+	Validate/Autogen.hpp \
 	Validate/CompoundLiteral.cpp \
 	Validate/CompoundLiteral.hpp \
Index: src/main.cc
===================================================================
--- src/main.cc	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ src/main.cc	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Fri May 15 23:12:02 2015
 // Last Modified By : Andrew Beach
-// Last Modified On : Tue Nov 30 10:25:00 2021
-// Update Count     : 659
+// Last Modified On : Wed Jan 26 14:09:00 2022
+// Update Count     : 670
 //
 
@@ -55,4 +55,5 @@
 #include "ControlStruct/ExceptTranslate.h"  // for translateEHM
 #include "ControlStruct/FixLabels.hpp"      // for fixLabels
+#include "ControlStruct/HoistControlDecls.hpp" //  hoistControlDecls
 #include "ControlStruct/Mutate.h"           // for mutate
 #include "GenPoly/Box.h"                    // for box
@@ -73,4 +74,5 @@
 #include "SynTree/Visitor.h"                // for acceptAll
 #include "Tuples/Tuples.h"                  // for expandMemberTuples, expan...
+#include "Validate/Autogen.hpp"             // for autogenerateRoutines
 #include "Validate/FindSpecialDecls.h"      // for findGlobalDecls
 #include "Validate/CompoundLiteral.hpp"     // for handleCompoundLiterals
@@ -78,5 +80,4 @@
 #include "Validate/LabelAddressFixer.hpp"   // for fixLabelAddresses
 #include "Virtual/ExpandCasts.h"            // for expandCasts
-
 
 static void NewPass( const char * const name ) {
@@ -326,9 +327,12 @@
 		PASS( "Validate-B", SymTab::validate_B( translationUnit ) );
 		PASS( "Validate-C", SymTab::validate_C( translationUnit ) );
-		PASS( "Validate-D", SymTab::validate_D( translationUnit ) );
 
 		CodeTools::fillLocations( translationUnit );
 
 		if( useNewAST ) {
+			PASS( "Apply Concurrent Keywords", Concurrency::applyKeywords( translationUnit ) );
+			PASS( "Forall Pointer Decay", SymTab::decayForallPointers( translationUnit ) );
+			CodeTools::fillLocations( translationUnit );
+
 			if (Stats::Counters::enabled) {
 				ast::pass_visitor_stats.avg = Stats::Counters::build<Stats::Counters::AverageCounter<double>>("Average Depth - New");
@@ -338,4 +342,11 @@
 
 			forceFillCodeLocations( transUnit );
+
+			// Must happen before autogen routines are added.
+			PASS( "Hoist Control Declarations", ControlStruct::hoistControlDecls( transUnit ) );
+
+			// Must be after enum and pointer decay.
+			// Must be before compound literals.
+			PASS( "Generate Autogen Routines", Validate::autogenerateRoutines( transUnit ) );
 
 			PASS( "Implement Mutex", Concurrency::implementMutex( transUnit ) );
@@ -409,4 +420,5 @@
 			translationUnit = convert( move( transUnit ) );
 		} else {
+			PASS( "Validate-D", SymTab::validate_D( translationUnit ) );
 			PASS( "Validate-E", SymTab::validate_E( translationUnit ) );
 			PASS( "Validate-F", SymTab::validate_F( translationUnit ) );
Index: tests/concurrent/.expect/ctor-check.txt
===================================================================
--- tests/concurrent/.expect/ctor-check.txt	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ tests/concurrent/.expect/ctor-check.txt	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -2,5 +2,5 @@
 ?{}: function
 ... with parameters
-  lvalue reference to instance of struct Empty with body
+  this: lvalue reference to instance of struct Empty with body
 ... returning nothing
  with body
Index: tests/concurrent/.expect/preempt2.txt
===================================================================
--- tests/concurrent/.expect/preempt2.txt	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ tests/concurrent/.expect/preempt2.txt	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,1 @@
+Done
Index: tests/concurrent/mutexstmt/.expect/locks.txt
===================================================================
--- tests/concurrent/mutexstmt/.expect/locks.txt	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ tests/concurrent/mutexstmt/.expect/locks.txt	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -3,2 +3,6 @@
 Start Test: multi lock deadlock/mutual exclusion
 End Test: multi lock deadlock/mutual exclusion
+Start Test: single scoped lock mutual exclusion
+End Test: single scoped lock mutual exclusion
+Start Test: multi scoped lock deadlock/mutual exclusion
+End Test: multi scoped lock deadlock/mutual exclusion
Index: tests/concurrent/mutexstmt/locks.cfa
===================================================================
--- tests/concurrent/mutexstmt/locks.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ tests/concurrent/mutexstmt/locks.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -59,5 +59,75 @@
 }
 
+thread T_Mutex_Scoped {};
 
+void main( T_Mutex_Scoped & this ) {
+	for (unsigned int i = 0; i < num_times; i++) {
+		{
+			scoped_lock(single_acquisition_lock) s{m1};
+			count++;
+		}
+		{
+			scoped_lock(single_acquisition_lock) s{m1};
+			assert(!insideFlag);
+			insideFlag = true;
+			assert(insideFlag);
+			insideFlag = false;
+		}
+	}
+}
+
+thread T_Multi_Scoped {};
+
+void main( T_Multi_Scoped & this ) {
+	for (unsigned int i = 0; i < num_times; i++) {
+		{
+			scoped_lock(single_acquisition_lock) s{m1};
+			assert(!insideFlag);
+			insideFlag = true;
+			assert(insideFlag);
+			insideFlag = false;
+		}
+		{
+			scoped_lock(single_acquisition_lock) s1{m1};
+			scoped_lock(single_acquisition_lock) s2{m2};
+			scoped_lock(single_acquisition_lock) s3{m3};
+			scoped_lock(single_acquisition_lock) s4{m4};
+			scoped_lock(single_acquisition_lock) s5{m5};
+			assert(!insideFlag);
+			insideFlag = true;
+			assert(insideFlag);
+			insideFlag = false;
+		}
+		{
+			scoped_lock(single_acquisition_lock) s1{m1};
+			scoped_lock(single_acquisition_lock) s3{m3};
+			assert(!insideFlag);
+			insideFlag = true;
+			assert(insideFlag);
+			insideFlag = false;
+		}
+		{
+			scoped_lock(single_acquisition_lock) s1{m1};
+			scoped_lock(single_acquisition_lock) s2{m2};
+			scoped_lock(single_acquisition_lock) s4{m4};
+			assert(!insideFlag);
+			insideFlag = true;
+			assert(insideFlag);
+			insideFlag = false;
+		}
+		{
+			scoped_lock(single_acquisition_lock) s1{m1};
+			scoped_lock(single_acquisition_lock) s3{m3};
+			scoped_lock(single_acquisition_lock) s4{m4};
+			scoped_lock(single_acquisition_lock) s5{m5};
+			assert(!insideFlag);
+			insideFlag = true;
+			assert(insideFlag);
+			insideFlag = false;
+		}
+	}
+}
+
+int num_tasks = 10;
 int main() {
 	processor p[10];
@@ -67,4 +137,5 @@
 		T_Mutex t[10];
 	}
+	assert(count == num_tasks * num_times);
 	printf("End Test: single lock mutual exclusion\n");
 	printf("Start Test: multi lock deadlock/mutual exclusion\n");
@@ -73,3 +144,16 @@
 	}
 	printf("End Test: multi lock deadlock/mutual exclusion\n");
+	
+	count = 0;
+	printf("Start Test: single scoped lock mutual exclusion\n");
+	{
+		T_Mutex_Scoped t[10];
+	}
+	assert(count == num_tasks * num_times);
+	printf("End Test: single scoped lock mutual exclusion\n");
+	printf("Start Test: multi scoped lock deadlock/mutual exclusion\n");
+	{
+		T_Multi_Scoped t[10];
+	}
+	printf("End Test: multi scoped lock deadlock/mutual exclusion\n");	
 }
Index: tests/concurrent/preempt.cfa
===================================================================
--- tests/concurrent/preempt.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ tests/concurrent/preempt.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -1,2 +1,3 @@
+#include <clock.hfa>
 #include <fstream.hfa>
 #include <kernel.hfa>
@@ -22,9 +23,13 @@
 extern void __cfaabi_check_preemption();
 
-static volatile int counter = 0;
+static struct {
+	volatile int counter;
+	volatile Time prev;
+	Duration durations[6];
+} globals;
 
 thread worker_t {
 	int value;
-	unsigned spin;
+	unsigned long long spin;
 };
 
@@ -35,12 +40,26 @@
 
 void main(worker_t & this) {
-	while(TEST(counter < N)) {
-		if(this.spin > 100_000_000) abort | "Worker" | this.value | "has been spinning too long! (" | this.spin | ")";
+	while(TEST(globals.counter < N)) {
+		if(this.spin > 50_000_000_000) abort | "Worker" | this.value | "has been spinning too long! (" | this.spin | ")";
 		__cfaabi_check_preemption();
-		if( (counter % 7) == this.value ) {
+		if( (globals.counter % 7) == this.value ) {
 			__cfaabi_check_preemption();
-			int next = __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST );
+			#if !defined(TEST_LONG)
+				Time now = timeHiRes();
+				Duration diff = now - globals.prev;
+				globals.prev = now;
+			#endif
+			int next = __atomic_add_fetch( &globals.counter, 1, __ATOMIC_SEQ_CST );
 			__cfaabi_check_preemption();
-			if( (next % 100) == 0 ) printf("%d\n", (int)next);
+			if( (next % 100) == 0 ) {
+				#if !defined(TEST_LONG)
+					unsigned idx = next / 100;
+					if (idx >= 6) abort | "Idx from next is invalid: " | idx | "vs" | next;
+					globals.durations[idx] = diff;
+					if(diff > 12`s) serr | "Duration suspiciously large:" | diff;
+				#endif
+				printf("%d\n", (int)next);
+
+			}
 			__cfaabi_check_preemption();
 			this.spin = 0;
@@ -54,5 +73,13 @@
 int main(int argc, char* argv[]) {
 	processor p;
+	globals.counter = 0;
+	globals.durations[0] = 0;
+	globals.durations[1] = 0;
+	globals.durations[2] = 0;
+	globals.durations[3] = 0;
+	globals.durations[4] = 0;
+	globals.durations[5] = 0;
 	{
+		globals.prev = timeHiRes();
 		worker_t w0 = 0;
 		worker_t w1 = 1;
Index: tests/concurrent/preempt2.cfa
===================================================================
--- tests/concurrent/preempt2.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
+++ tests/concurrent/preempt2.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -0,0 +1,50 @@
+#include <fstream.hfa>
+#include <thread.hfa>
+#include <time.hfa>
+
+#include "long_tests.hfa"
+
+#ifndef PREEMPTION_RATE
+#define PREEMPTION_RATE 10`ms
+#endif
+
+Duration default_preemption() {
+	return PREEMPTION_RATE;
+}
+
+#ifdef TEST_LONG
+static const unsigned long N = 30_000ul;
+#else
+static const unsigned long N = 500ul;
+#endif
+
+extern void __cfaabi_check_preemption();
+
+static struct {
+	volatile int x;
+	volatile int y;
+} globals;
+
+thread T1 {};
+void main(T1 &) {
+	for() {
+		if ( globals.x == N ) break;
+		if ( globals.x < globals.y ) globals.x += 1;
+	}
+}
+
+thread T2 {};
+void main(T2 &) {
+	for() {
+		if ( globals.y == N ) break;
+		if ( globals.y == globals.x ) globals.y += 1;
+	}
+}
+
+int main() {
+	{
+		T1 t1;
+		T2 t2;
+	}
+	sout | "Done";
+} // main
Index: tests/include/.expect/includes.nast.txt
===================================================================
--- tests/include/.expect/includes.nast.txt	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ tests/include/.expect/includes.nast.txt	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -1,1 +1,1 @@
-include/includes.cfa:154:25: warning: Compiled
+include/includes.cfa:153:25: warning: Compiled
Index: tests/include/includes.cfa
===================================================================
--- tests/include/includes.cfa	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ tests/include/includes.cfa	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -10,6 +10,6 @@
 // Created On       : Wed May 27 17:56:53 2015
 // Last Modified By : Peter A. Buhr
-// Last Modified On : Sat Jun  5 10:06:46 2021
-// Update Count     : 751
+// Last Modified On : Thu Feb  3 22:06:07 2022
+// Update Count     : 774
 //
 
@@ -18,4 +18,5 @@
 #endif // __CFA__
 
+#if 1
 //#define _GNU_SOURCE
 #include <aio.h>
@@ -40,5 +41,5 @@
 #include <errno.h>
 #include <error.h>
-//#include <eti.h>                                                                              // may not be installed, comes with ncurses
+//#include <eti.h>										// may not be installed, comes with ncurses
 #include <execinfo.h>
 #include <expat.h>
@@ -49,5 +50,5 @@
 #include <fmtmsg.h>
 #include <fnmatch.h>
-//#include <form.h>                                                                             // may not be installed, comes with ncurses
+//#include <form.h>										// may not be installed, comes with ncurses
 #include <fstab.h>
 #include <fts.h>
@@ -77,14 +78,14 @@
 #include <mcheck.h>
 #include <memory.h>
-//#include <menu.h>                                                                             // may not be installed, comes with ncurses
+//#include <menu.h>										// may not be installed, comes with ncurses
 #include <mntent.h>
 #include <monetary.h>
 #include <mqueue.h>
-//#include <ncurses_dll.h>                                                                      // may not be installed, comes with ncurses
+//#include <ncurses_dll.h>								// may not be installed, comes with ncurses
 #include <netdb.h>
 #include <nl_types.h>
 #include <nss.h>
 #include <obstack.h>
-//#include <panel.h>                                                                            // may not be installed, comes with ncurses
+//#include <panel.h>										// may not be installed, comes with ncurses
 #include <paths.h>
 #include <poll.h>
@@ -117,6 +118,6 @@
 #include <syslog.h>
 #include <tar.h>
-//#include <term.h>                                                                             // may not be installed, comes with ncurses
-//#include <termcap.h>                                                                          // may not be installed, comes with ncurses
+//#include <term.h>										// may not be installed, comes with ncurses
+//#include <termcap.h>									// may not be installed, comes with ncurses
 #include <termio.h>
 #include <termios.h>
@@ -130,5 +131,5 @@
 #include <ucontext.h>
 #include <ulimit.h>
-//#include <unctrl.h>                                                                           // may not be installed, comes with ncurses
+//#include <unctrl.h>										// may not be installed, comes with ncurses
 #include <unistd.h>
 #include <utime.h>
@@ -143,6 +144,4 @@
 #include <wctype.h>
 #include <wordexp.h>
-
-#if 0
 #endif // 0
 
@@ -151,6 +150,6 @@
 #endif // __CFA__
 
-int main( int argc, char const *argv[] ) {
-    #pragma GCC warning "Compiled"                      // force non-empty .expect file, NO TABS!!!
+int main( int argc, char const * argv[] ) {
+    #pragma GCC warning "Compiled"							// force non-empty .expect file, NO TABS!!!
 }
 
Index: tools/auto-complete.md
===================================================================
--- tools/auto-complete.md	(revision 5f3ba117d89cd71b0b82121f2363d27a6752b378)
+++ tools/auto-complete.md	(revision b56ad5e7e1abbbd650e2765fd6b0429712b6cfe6)
@@ -32,2 +32,23 @@
 
 ### Zsh
+
+1 - Add the following somewhere:
+    #compdef test.py
+
+    _test_py() {
+        local -a options
+        options=$($words[1] --list-comp)
+        _alternative "files:filenames:($options)"
+    }
+
+    _test_py "$@"
+
+2 - Add the path to that file to the "fpath" environment variable.
+
+3 - In ~/.zshrc add
+    autoload -U compinit
+    compinit
+
+*How it works:* I don't know ;P
+
+
