Changes in / [f5a51db:97c215f]
- Files:
-
- 8 deleted
- 75 edited
Legend:
- Unmodified
- Added
- Removed
-
benchmark/io/http/Makefile.am
rf5a51db r97c215f 21 21 include $(top_srcdir)/tools/build/cfa.make 22 22 23 AM_CFLAGS = -O 3-Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror23 AM_CFLAGS = -O2 -Wall -Wextra -I$(srcdir) -lrt -pthread # -Werror 24 24 AM_CFAFLAGS = -quiet -nodebug 25 25 AM_LDFLAGS = -quiet -nodebug -
benchmark/io/http/main.cfa
rf5a51db r97c215f 1 #define _ GNU_SOURCE1 #define __USE_GNU 2 2 3 3 #include <errno.h> … … 6 6 #include <unistd.h> 7 7 extern "C" { 8 #include <sched.h>9 8 #include <signal.h> 10 9 #include <sys/socket.h> … … 68 67 (this.self){ "Server Cluster", options.clopts.params }; 69 68 70 cpu_set_t fullset;71 CPU_ZERO(&fullset);72 int ret = sched_getaffinity(getpid(), sizeof(fullset), &fullset);73 if( ret != 0 ) abort | "sched_getaffinity failed with" | errno | strerror( errno );74 int cnt = CPU_COUNT(&fullset);75 76 69 this.procs = alloc(options.clopts.nprocs); 77 70 for(i; options.clopts.nprocs) { 78 71 (this.procs[i]){ "Benchmark Processor", this.self }; 79 80 int c = 0;81 int n = 1 + (i % cnt);82 for(int j = 0; j < CPU_SETSIZE; j++) {83 if(CPU_ISSET(j, &fullset)) n--;84 if(n == 0) {85 c = j;86 break;87 }88 }89 cpu_set_t localset;90 CPU_ZERO(&localset);91 CPU_SET(c, &localset);92 ret = pthread_setaffinity_np(this.procs[i].kernel_thread, sizeof(localset), &localset);93 if( ret != 0 ) abort | "sched_getaffinity failed with" | ret | strerror( ret );94 72 95 73 #if !defined(__CFA_NO_STATISTICS__) … … 168 146 int waited = 0; 169 147 for() { 170 int sockfd = server_fd; 171 __CONST_SOCKADDR_ARG addr; 172 addr.__sockaddr__ = (struct sockaddr *)&address; 173 socklen_t addrlen = sizeof(address); 174 ret = bind( sockfd, addr, addrlen ); 148 ret = bind( server_fd, (struct sockaddr *)&address, sizeof(address) ); 175 149 if(ret < 0) { 176 150 if(errno == EADDRINUSE) { -
doc/theses/thierry_delisle_PhD/thesis/Makefile
rf5a51db r97c215f 48 48 ## Define the documents that need to be made. 49 49 all: thesis.pdf 50 thesis.pdf: ${TEXTS} ${FIGURES} ${PICTURES} thesis.tex glossary.tex local.bib ../../../LaTeXmacros/common.tex ../../../LaTeXmacros/common.sty50 thesis.pdf: ${TEXTS} ${FIGURES} ${PICTURES} thesis.tex glossary.tex local.bib 51 51 52 52 DOCUMENT = thesis.pdf -
doc/theses/thierry_delisle_PhD/thesis/fig/base.fig
rf5a51db r97c215f 12 12 1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6900 4200 20 20 6900 4200 6920 4200 13 13 1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6975 4200 20 20 6975 4200 6995 4200 14 -615 6 6375 5100 6675 525016 1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6450 5175 20 20 6450 5175 6470 517517 1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6525 5175 20 20 6525 5175 6545 517518 1 3 0 1 0 0 50 -1 20 0.000 1 0.0000 6600 5175 20 20 6600 5175 6620 517519 14 -6 20 15 1 3 0 1 0 7 50 -1 -1 0.000 1 0.0000 3900 2400 300 300 3900 2400 4200 2400 … … 80 75 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 81 76 2400 2475 3000 2475 82 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 783 3300 5210 3150 4950 2850 4950 2700 5210 2850 5470 3150 547084 3300 521085 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 786 4500 5210 4350 4950 4050 4950 3900 5210 4050 5470 4350 547087 4500 521088 2 3 0 1 0 7 50 -1 -1 0.000 0 0 0 0 0 789 5700 5210 5550 4950 5250 4950 5100 5210 5250 5470 5550 547090 5700 521091 77 4 2 -1 50 -1 0 12 0.0000 2 135 630 2100 3075 Threads\001 92 78 4 2 -1 50 -1 0 12 0.0000 2 165 450 2100 2850 Ready\001 … … 96 82 4 1 -1 50 -1 0 11 0.0000 2 135 180 2700 3550 TS\001 97 83 4 1 -1 50 -1 0 11 0.0000 2 135 180 2700 2650 TS\001 98 4 2 -1 50 -1 0 12 0.0000 2 135 900 2100 5175 Processors\001 -
doc/theses/thierry_delisle_PhD/thesis/glossary.tex
rf5a51db r97c215f 40 40 41 41 \textit{Synonyms : User threads, Lightweight threads, Green threads, Virtual threads, Tasks.} 42 }43 44 \longnewglossaryentry{rmr}45 {name={remote memory reference}}46 {47 48 42 } 49 43 -
doc/theses/thierry_delisle_PhD/thesis/text/core.tex
rf5a51db r97c215f 49 49 50 50 \section{Design} 51 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.51 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. 52 52 53 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. 53 \subsection{Sharding} \label{sec:sharding} 54 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. 54 55 55 \subsection{Work-Stealing}56 57 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.58 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.59 On the other hand, work-stealing schedulers only attempt to do load-balancing when a \gls{proc} runs out of work.60 This means that the scheduler may never balance unfairness that does not result in a \gls{proc} running out of work.61 Chapter~\ref{microbench} shows that in pathological cases this problem can lead to indefinite starvation.62 63 64 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.65 66 \subsection{Relaxed-Fifo}67 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.68 \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.69 The result is a queue that has both decent scalability and sufficient fairness.70 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.71 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.72 73 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.74 However, another major aspect is that \glspl{proc} will eagerly search for these older elements instead of focusing on specific queues.75 76 While the fairness, of this scheme is good, it does suffer in terms of performance.77 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.78 79 \section{\CFA}80 The \CFA is effectively attempting to merge these two approaches, keeping the best of both.81 It is based on the82 56 \begin{figure} 83 57 \centering 84 58 \input{base.pstex_t} 85 \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.}59 \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.} 86 60 \label{fig:base} 87 61 \end{figure} 88 62 63 \subsection{Finding threads} 64 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. 89 65 66 \begin{figure} 67 \centering 68 \input{empty.pstex_t} 69 \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.} 70 \label{fig:empty} 71 \end{figure} 90 72 91 % 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. 73 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: 92 74 93 % \subsection{Sharding} \label{sec:sharding} 94 % 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. 75 \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. 95 76 96 % \begin{figure} 97 % \centering 98 % \input{base.pstex_t} 99 % \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.} 100 % \label{fig:base} 101 % \end{figure} 77 \begin{figure} 78 \centering 79 \vspace*{-5pt} 80 {\resizebox{0.75\textwidth}{!}{\input{emptybit.pstex_t}}} 81 \vspace*{-5pt} 82 \caption[Underloaded queue with bitmask]{Underloaded queue with bitmask indicating array cells with items.} 83 \label{fig:emptybit} 102 84 103 % \subsection{Finding threads} 104 % 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. 85 \vspace*{10pt} 86 {\resizebox{0.75\textwidth}{!}{\input{emptytree.pstex_t}}} 87 \vspace*{-5pt} 88 \caption[Underloaded queue with binary search-tree]{Underloaded queue with binary search-tree indicating array cells with items.} 89 \label{fig:emptytree} 105 90 106 % \begin{figure}107 % \centering 108 % \input{empty.pstex_t}109 % \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.}110 % \label{fig:empty}111 %\end{figure}91 \vspace*{10pt} 92 {\resizebox{0.95\textwidth}{!}{\input{emptytls.pstex_t}}} 93 \vspace*{-5pt} 94 \caption[Underloaded queue with per processor bitmask]{Underloaded queue with per processor bitmask indicating array cells with items.} 95 \label{fig:emptytls} 96 \end{figure} 112 97 113 % 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: 98 \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. 114 99 115 % \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.100 \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. 116 101 117 % \begin{figure} 118 % \centering 119 % \vspace*{-5pt} 120 % {\resizebox{0.75\textwidth}{!}{\input{emptybit.pstex_t}}} 121 % \vspace*{-5pt} 122 % \caption[Underloaded queue with bitmask]{Underloaded queue with bitmask indicating array cells with items.} 123 % \label{fig:emptybit} 102 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. 124 103 125 % \vspace*{10pt} 126 % {\resizebox{0.75\textwidth}{!}{\input{emptytree.pstex_t}}} 127 % \vspace*{-5pt} 128 % \caption[Underloaded queue with binary search-tree]{Underloaded queue with binary search-tree indicating array cells with items.} 129 % \label{fig:emptytree} 104 \subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/} 105 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. 130 106 131 % \vspace*{10pt} 132 % {\resizebox{0.95\textwidth}{!}{\input{emptytls.pstex_t}}} 133 % \vspace*{-5pt} 134 % \caption[Underloaded queue with per processor bitmask]{Underloaded queue with per processor bitmask indicating array cells with items.} 135 % \label{fig:emptytls} 136 % \end{figure} 107 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. 137 108 138 % \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. 109 The algorithm works as follows: 110 \begin{itemize} 111 \item Each \gls{proc} has two \glsxtrshort{prng} instances, $F$ and $B$. 112 \item Push and Pop operations occur as discussed in Section~\ref{sec:sharding} with the following exceptions: 113 \begin{itemize} 114 \item Push operations use $F$ going forward on each try and on success $F$ is copied into $B$. 115 \item Pop operations use $B$ going backwards on each try. 116 \end{itemize} 117 \end{itemize} 139 118 140 % \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.119 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. 141 120 142 % 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. 143 144 % \subsection{Dynamic Entropy}\cit{https://xkcd.com/2318/} 145 % 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. 146 147 % 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. 148 149 % The algorithm works as follows: 150 % \begin{itemize} 151 % \item Each \gls{proc} has two \glsxtrshort{prng} instances, $F$ and $B$. 152 % \item Push and Pop operations occur as discussed in Section~\ref{sec:sharding} with the following exceptions: 153 % \begin{itemize} 154 % \item Push operations use $F$ going forward on each try and on success $F$ is copied into $B$. 155 % \item Pop operations use $B$ going backwards on each try. 156 % \end{itemize} 157 % \end{itemize} 158 159 % 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. 160 161 % \section{Details} 121 \section{Details} -
doc/theses/thierry_delisle_PhD/thesis/text/eval_macro.tex
rf5a51db r97c215f 4 4 5 5 In Memory Plain Text 6 7 Networked Plain Text 6 8 7 9 Networked ZIPF -
doc/theses/thierry_delisle_PhD/thesis/text/eval_micro.tex
rf5a51db r97c215f 2 2 3 3 The first step of evaluation is always to test-out small controlled cases, to ensure that the basics are working properly. 4 This sections presents f ivedifferent experimental setup, evaluating some of the basic features of \CFA's scheduler.4 This sections presents four different experimental setup, evaluating some of the basic features of \CFA's scheduler. 5 5 6 6 \section{Cycling latency} 7 7 The most basic evaluation of any ready queue is to evaluate the latency needed to push and pop one element from the ready-queue. 8 Since these two operation also describe a \texttt{yield} operation, many systems use this as the most basic benchmark.9 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.8 While these two operation also describe a \texttt{yield} operation, many systems use this as the most basic benchmark. 9 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. 10 10 Not all systems use this information, but those which do may appear to have better performance than they would for disconnected push/pop pairs. 11 11 For this reason, I chose a different first benchmark, which I call the Cycle Benchmark. 12 This benchmark arranges many \glspl{at} into multiple rings of \glspl{at}.12 This benchmark arranges many threads into multiple rings of threads. 13 13 Each ring is effectively a circular singly-linked list. 14 At runtime, each \gls{at} unparks the next \gls{at}before parking itself.14 At runtime, each thread unparks the next thread before parking itself. 15 15 This corresponds to the desired pair of ready queue operations. 16 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.16 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. 17 17 Figure~\ref{fig:cycle} shows a visual representation of this arrangement. 18 18 19 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. 20 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. 21 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. 19 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. 20 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. 21 The size of the cycle is also decided based on this race: cycles that are too small may see the 22 chain of unparks go full circle before the first thread can park. 22 23 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. 23 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. 24 Since silently omitting ready-queue operations would throw off the measuring of these operations. 25 Therefore the ring of threads must be big enough so the threads have the time to fully park before they are unparked. 24 26 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. 25 27 … … 27 29 \centering 28 30 \input{cycle.pstex_t} 29 \caption[Cycle benchmark]{Cycle benchmark\smallskip\newline Each \gls{at} unparks the next \gls{at}in the cycle before parking itself.}31 \caption[Cycle benchmark]{Cycle benchmark\smallskip\newline Each thread unparks the next thread in the cycle before parking itself.} 30 32 \label{fig:cycle} 31 33 \end{figure} 32 34 33 35 \todo{check term ``idle sleep handling''} 34 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.36 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. 35 37 Beyond this point, adding more rings serves to mitigate even more the idle sleep handling. 36 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.38 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. 37 39 38 40 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. 39 41 40 \todo{code, setup, results} 41 \begin{lstlisting} 42 Thread.main() { 43 count := 0 44 for { 45 wait() 46 this.next.wake() 47 count ++ 48 if must_stop() { break } 49 } 50 global.count += count 51 } 52 \end{lstlisting} 53 42 \todo{mention where to get the code.} 54 43 55 44 \section{Yield} 56 45 For completion, I also include the yield benchmark. 57 This benchmark is much simpler than the cycle tests, it simply creates many \glspl{at} that call \texttt{yield}. 58 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. 59 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. 60 This sometimes puts more strain on the idle sleep handling, compared to scenarios where there is clearly plenty of work to be done. 61 62 \todo{code, setup, results} 63 64 \begin{lstlisting} 65 Thread.main() { 66 count := 0 67 while !stop { 68 yield() 69 count ++ 70 } 71 global.count += count 72 } 73 \end{lstlisting} 74 75 76 \section{Churn} 77 The Cycle and Yield benchmark represents an ``easy'' scenario for a scheduler, \eg, an embarrassingly parallel application. 78 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. 79 80 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. 81 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. 82 This results can result in either contention on the remote queue or \glspl{rmr} on \gls{at} data structure. 83 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. 84 85 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}. 86 When a \gls{at} attempts to block on the chair, it must first unblocked the \gls{at} currently blocked on said chair, if any. 87 This creates a flow where \glspl{at} push each other out of the chairs before being pushed out themselves. 88 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}. 89 90 \todo{code, setup, results} 91 \begin{lstlisting} 92 Thread.main() { 93 count := 0 94 for { 95 r := random() % len(spots) 96 next := xchg(spots[r], this) 97 if next { next.wake() } 98 wait() 99 count ++ 100 if must_stop() { break } 101 } 102 global.count += count 103 } 104 \end{lstlisting} 46 This benchmark is much simpler than the cycle tests, it simply creates many threads that call \texttt{yield}. 105 47 106 48 \section{Locality} 107 49 108 \todo{code, setup, results}109 110 50 \section{Transfer} 111 The last benchmark is more exactly characterize as an experiment than a benchmark.112 It tests the behavior of the schedulers for a particularly misbehaved workload.113 In this workload, one of the \gls{at} is selected at random to be the leader.114 The leader then spins in a tight loop until it has observed that all other \glspl{at} have acknowledged its leadership.115 The leader \gls{at} then picks a new \gls{at} to be the ``spinner'' and the cycle repeats.116 117 The benchmark comes in two flavours for the behavior of the non-leader \glspl{at}:118 once they acknowledged the leader, they either block on a semaphore or yield repeatadly.119 120 This experiment is designed to evaluate the short term load balancing of the scheduler.121 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.122 This is because the spinning \gls{at} is effectively preventing the \gls{proc} from runnning any other \glspl{thrd}.123 In the semaphore flavour, the number of runnable \glspl{at} will eventually dwindle down to only the leader.124 This is a simpler case to handle for schedulers since \glspl{proc} eventually run out of work.125 In the yielding flavour, the number of runnable \glspl{at} stays constant.126 This is a harder case to handle because corrective measures must be taken even if work is still available.127 Note that languages that have mandatory preemption do circumvent this problem by forcing the spinner to yield.128 129 \todo{code, setup, results}130 \begin{lstlisting}131 Thread.lead() {132 this.idx_seen = ++lead_idx133 if lead_idx > stop_idx {134 done := true135 return136 }137 138 // Wait for everyone to acknowledge my leadership139 start: = timeNow()140 for t in threads {141 while t.idx_seen != lead_idx {142 asm pause143 if (timeNow() - start) > 5 seconds { error() }144 }145 }146 147 // pick next leader148 leader := threads[ prng() % len(threads) ]149 150 // wake every one151 if !exhaust {152 for t in threads {153 if t != me { t.wake() }154 }155 }156 }157 158 Thread.wait() {159 this.idx_seen := lead_idx160 if exhaust { wait() }161 else { yield() }162 }163 164 Thread.main() {165 while !done {166 if leader == me { this.lead() }167 else { this.wait() }168 }169 }170 \end{lstlisting} -
doc/theses/thierry_delisle_PhD/thesis/text/existing.tex
rf5a51db r97c215f 33 33 34 34 35 \section{Work Stealing} \label{existing:workstealing}35 \section{Work Stealing} 36 36 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. 37 37 -
libcfa/src/concurrency/io.cfa
rf5a51db r97c215f 306 306 ctx->proc->io.pending = true; 307 307 ctx->proc->io.dirty = true; 308 if(sq.to_submit > 30) { 309 __tls_stats()->io.flush.full++; 310 __cfa_io_flush( ctx->proc, 0 ); 311 } 312 if(!lazy) { 313 __tls_stats()->io.flush.eager++; 308 if(sq.to_submit > 30 || !lazy) { 314 309 __cfa_io_flush( ctx->proc, 0 ); 315 310 } -
libcfa/src/concurrency/kernel.cfa
rf5a51db r97c215f 42 42 43 43 #if !defined(__CFA_NO_STATISTICS__) 44 #define __STATS _DEF( ...) __VA_ARGS__44 #define __STATS( ...) __VA_ARGS__ 45 45 #else 46 #define __STATS _DEF( ...)46 #define __STATS( ...) 47 47 #endif 48 48 … … 122 122 static thread$ * __next_thread(cluster * this); 123 123 static thread$ * __next_thread_slow(cluster * this); 124 static thread$ * __next_thread_search(cluster * this);125 124 static inline bool __must_unpark( thread$ * thrd ) __attribute((nonnull(1))); 126 125 static void __run_thread(processor * this, thread$ * dst); … … 188 187 MAIN_LOOP: 189 188 for() { 189 #define OLD_MAIN 1 190 #if OLD_MAIN 190 191 // Check if there is pending io 191 192 __maybe_io_drain( this ); … … 195 196 196 197 if( !readyThread ) { 197 __IO_STATS__(true, io.flush.idle++; )198 198 __cfa_io_flush( this, 0 ); 199 199 200 readyThread = __next_thread( this->cltr );201 }202 203 if( !readyThread ) for(5) {204 __IO_STATS__(true, io.flush.idle++; )205 206 200 readyThread = __next_thread_slow( this->cltr ); 207 208 if( readyThread ) break;209 210 __cfa_io_flush( this, 0 );211 201 } 212 202 … … 216 206 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 217 207 208 #if !defined(__CFA_NO_STATISTICS__) 209 __tls_stats()->ready.sleep.halts++; 210 #endif 211 218 212 // Push self to idle stack 219 213 if(!mark_idle(this->cltr->procs, * this)) continue MAIN_LOOP; 220 214 221 215 // Confirm the ready-queue is empty 222 readyThread = __next_thread_s earch( this->cltr );216 readyThread = __next_thread_slow( this->cltr ); 223 217 if( readyThread ) { 224 218 // A thread was found, cancel the halt 225 219 mark_awake(this->cltr->procs, * this); 226 220 227 __STATS__(true, ready.sleep.cancels++; ) 221 #if !defined(__CFA_NO_STATISTICS__) 222 __tls_stats()->ready.sleep.cancels++; 223 #endif 228 224 229 225 // continue the mai loop … … 252 248 253 249 if(this->io.pending && !this->io.dirty) { 254 __IO_STATS__(true, io.flush.dirty++; )255 250 __cfa_io_flush( this, 0 ); 256 251 } 252 253 #else 254 #warning new kernel loop 255 SEARCH: { 256 /* paranoid */ verify( ! __preemption_enabled() ); 257 258 // First, lock the scheduler since we are searching for a thread 259 ready_schedule_lock(); 260 261 // Try to get the next thread 262 readyThread = pop_fast( this->cltr ); 263 if(readyThread) { ready_schedule_unlock(); break SEARCH; } 264 265 // If we can't find a thread, might as well flush any outstanding I/O 266 if(this->io.pending) { __cfa_io_flush( this, 0 ); } 267 268 // Spin a little on I/O, just in case 269 for(5) { 270 __maybe_io_drain( this ); 271 readyThread = pop_fast( this->cltr ); 272 if(readyThread) { ready_schedule_unlock(); break SEARCH; } 273 } 274 275 // no luck, try stealing a few times 276 for(5) { 277 if( __maybe_io_drain( this ) ) { 278 readyThread = pop_fast( this->cltr ); 279 } else { 280 readyThread = pop_slow( this->cltr ); 281 } 282 if(readyThread) { ready_schedule_unlock(); break SEARCH; } 283 } 284 285 // still no luck, search for a thread 286 readyThread = pop_search( this->cltr ); 287 if(readyThread) { ready_schedule_unlock(); break SEARCH; } 288 289 // Don't block if we are done 290 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) { 291 ready_schedule_unlock(); 292 break MAIN_LOOP; 293 } 294 295 __STATS( __tls_stats()->ready.sleep.halts++; ) 296 297 // Push self to idle stack 298 ready_schedule_unlock(); 299 if(!mark_idle(this->cltr->procs, * this)) goto SEARCH; 300 ready_schedule_lock(); 301 302 // Confirm the ready-queue is empty 303 __maybe_io_drain( this ); 304 readyThread = pop_search( this->cltr ); 305 ready_schedule_unlock(); 306 307 if( readyThread ) { 308 // A thread was found, cancel the halt 309 mark_awake(this->cltr->procs, * this); 310 311 __STATS( __tls_stats()->ready.sleep.cancels++; ) 312 313 // continue the main loop 314 break SEARCH; 315 } 316 317 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 0\n", this->unique_id, rdtscl()); ) 318 __cfadbg_print_safe(runtime_core, "Kernel : core %p waiting on eventfd %d\n", this, this->idle_fd); 319 320 { 321 eventfd_t val; 322 ssize_t ret = read( this->idle_fd, &val, sizeof(val) ); 323 if(ret < 0) { 324 switch((int)errno) { 325 case EAGAIN: 326 #if EAGAIN != EWOULDBLOCK 327 case EWOULDBLOCK: 328 #endif 329 case EINTR: 330 // No need to do anything special here, just assume it's a legitimate wake-up 331 break; 332 default: 333 abort( "KERNEL : internal error, read failure on idle eventfd, error(%d) %s.", (int)errno, strerror( (int)errno ) ); 334 } 335 } 336 } 337 338 __STATS( if(this->print_halts) __cfaabi_bits_print_safe( STDOUT_FILENO, "PH:%d - %lld 1\n", this->unique_id, rdtscl()); ) 339 340 // We were woken up, remove self from idle 341 mark_awake(this->cltr->procs, * this); 342 343 // DON'T just proceed, start looking again 344 continue MAIN_LOOP; 345 } 346 347 RUN_THREAD: 348 /* paranoid */ verify( ! __preemption_enabled() ); 349 /* paranoid */ verify( readyThread ); 350 351 // Reset io dirty bit 352 this->io.dirty = false; 353 354 // We found a thread run it 355 __run_thread(this, readyThread); 356 357 // Are we done? 358 if( __atomic_load_n(&this->do_terminate, __ATOMIC_SEQ_CST) ) break MAIN_LOOP; 359 360 if(this->io.pending && !this->io.dirty) { 361 __cfa_io_flush( this, 0 ); 362 } 363 364 ready_schedule_lock(); 365 __maybe_io_drain( this ); 366 ready_schedule_unlock(); 367 #endif 257 368 } 258 369 … … 365 476 break RUNNING; 366 477 case TICKET_UNBLOCK: 367 __STATS__(true, ready.threads.threads++; ) 478 #if !defined(__CFA_NO_STATISTICS__) 479 __tls_stats()->ready.threads.threads++; 480 #endif 368 481 // This is case 2, the racy case, someone tried to run this thread before it finished blocking 369 482 // In this case, just run it again. … … 380 493 __cfadbg_print_safe(runtime_core, "Kernel : core %p finished running thread %p\n", this, thrd_dst); 381 494 382 __STATS__(true, ready.threads.threads--; ) 495 #if !defined(__CFA_NO_STATISTICS__) 496 __tls_stats()->ready.threads.threads--; 497 #endif 383 498 384 499 /* paranoid */ verify( ! __preemption_enabled() ); … … 391 506 thread$ * thrd_src = kernelTLS().this_thread; 392 507 393 __STATS _DEF( thrd_src->last_proc = kernelTLS().this_processor; )508 __STATS( thrd_src->last_proc = kernelTLS().this_processor; ) 394 509 395 510 // Run the thread on this processor … … 443 558 // Dereference the thread now because once we push it, there is not guaranteed it's still valid. 444 559 struct cluster * cl = thrd->curr_cluster; 445 __STATS _DEF(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; )560 __STATS(bool outside = hint == UNPARK_LOCAL && thrd->last_proc && thrd->last_proc != kernelTLS().this_processor; ) 446 561 447 562 // push the thread to the cluster ready-queue … … 494 609 495 610 ready_schedule_lock(); 496 thread$ * thrd = pop_slow( this ); 497 ready_schedule_unlock(); 498 499 /* paranoid */ verify( ! __preemption_enabled() ); 500 return thrd; 501 } 502 503 // KERNEL ONLY 504 static inline thread$ * __next_thread_search(cluster * this) with( *this ) { 505 /* paranoid */ verify( ! __preemption_enabled() ); 506 507 ready_schedule_lock(); 508 thread$ * thrd = pop_search( this ); 611 thread$ * thrd; 612 for(25) { 613 thrd = pop_slow( this ); 614 if(thrd) goto RET; 615 } 616 thrd = pop_search( this ); 617 618 RET: 509 619 ready_schedule_unlock(); 510 620 … … 622 732 // Wake a thread from the front if there are any 623 733 static void __wake_one(cluster * this) { 734 /* paranoid */ verify( ! __preemption_enabled() ); 735 /* paranoid */ verify( ready_schedule_islocked() ); 736 737 // Check if there is a sleeping processor 738 // int fd = __atomic_load_n(&this->procs.fd, __ATOMIC_SEQ_CST); 739 int fd = 0; 740 if( __atomic_load_n(&this->procs.fd, __ATOMIC_SEQ_CST) != 0 ) { 741 fd = __atomic_exchange_n(&this->procs.fd, 0, __ATOMIC_RELAXED); 742 } 743 744 // If no one is sleeping, we are done 745 if( fd == 0 ) return; 746 747 // We found a processor, wake it up 624 748 eventfd_t val; 625 626 /* paranoid */ verify( ! __preemption_enabled() ); 627 /* paranoid */ verify( ready_schedule_islocked() ); 628 629 // Check if there is a sleeping processor 630 struct __fd_waitctx * fdp = __atomic_load_n(&this->procs.fdw, __ATOMIC_SEQ_CST); 631 632 // If no one is sleeping: we are done 633 if( fdp == 0p ) return; 634 635 int fd = 1; 636 if( __atomic_load_n(&fdp->fd, __ATOMIC_SEQ_CST) != 1 ) { 637 fd = __atomic_exchange_n(&fdp->fd, 1, __ATOMIC_RELAXED); 638 } 639 640 switch(fd) { 641 case 0: 642 // If the processor isn't ready to sleep then the exchange will already wake it up 643 #if !defined(__CFA_NO_STATISTICS__) 644 if( kernelTLS().this_stats ) { __tls_stats()->ready.sleep.early++; 645 } else { __atomic_fetch_add(&this->stats->ready.sleep.early, 1, __ATOMIC_RELAXED); } 646 #endif 647 break; 648 case 1: 649 // If someone else already said they will wake them: we are done 650 #if !defined(__CFA_NO_STATISTICS__) 651 if( kernelTLS().this_stats ) { __tls_stats()->ready.sleep.seen++; 652 } else { __atomic_fetch_add(&this->stats->ready.sleep.seen, 1, __ATOMIC_RELAXED); } 653 #endif 654 break; 655 default: 656 // If the processor was ready to sleep, we need to wake it up with an actual write 657 val = 1; 658 eventfd_write( fd, val ); 659 660 #if !defined(__CFA_NO_STATISTICS__) 661 if( kernelTLS().this_stats ) { __tls_stats()->ready.sleep.wakes++; 662 } else { __atomic_fetch_add(&this->stats->ready.sleep.wakes, 1, __ATOMIC_RELAXED); } 663 #endif 664 break; 665 } 749 val = 1; 750 eventfd_write( fd, val ); 751 752 #if !defined(__CFA_NO_STATISTICS__) 753 if( kernelTLS().this_stats ) { 754 __tls_stats()->ready.sleep.wakes++; 755 } 756 else { 757 __atomic_fetch_add(&this->stats->ready.sleep.wakes, 1, __ATOMIC_RELAXED); 758 } 759 #endif 666 760 667 761 /* paranoid */ verify( ready_schedule_islocked() ); … … 676 770 677 771 __cfadbg_print_safe(runtime_core, "Kernel : waking Processor %p\n", this); 678 679 this->idle_wctx.fd = 1;680 772 681 773 eventfd_t val; … … 687 779 688 780 static void idle_sleep(processor * this, io_future_t & future, iovec & iov) { 689 // Tell everyone we are ready to go do sleep690 for() {691 int expected = this->idle_wctx.fd;692 693 // Someone already told us to wake-up! No time for a nap.694 if(expected == 1) { return; }695 696 // Try to mark that we are going to sleep697 if(__atomic_compare_exchange_n(&this->idle_wctx.fd, &expected, this->idle_fd, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ) {698 // Every one agreed, taking a nap699 break;700 }701 }702 703 704 781 #if !defined(CFA_WITH_IO_URING_IDLE) 705 782 #if !defined(__CFA_NO_STATISTICS__) … … 748 825 749 826 static bool mark_idle(__cluster_proc_list & this, processor & proc) { 750 __STATS__(true, ready.sleep.halts++; )751 752 proc.idle_wctx.fd = 0;753 754 827 /* paranoid */ verify( ! __preemption_enabled() ); 755 828 if(!try_lock( this )) return false; … … 759 832 insert_first(this.idles, proc); 760 833 761 __atomic_store_n(&this.fd w, &proc.idle_wctx, __ATOMIC_SEQ_CST);834 __atomic_store_n(&this.fd, proc.idle_fd, __ATOMIC_SEQ_CST); 762 835 unlock( this ); 763 836 /* paranoid */ verify( ! __preemption_enabled() ); … … 775 848 776 849 { 777 struct __fd_waitctx * wctx= 0;778 if(!this.idles`isEmpty) wctx = &this.idles`first.idle_wctx;779 __atomic_store_n(&this.fd w, wctx, __ATOMIC_SEQ_CST);850 int fd = 0; 851 if(!this.idles`isEmpty) fd = this.idles`first.idle_fd; 852 __atomic_store_n(&this.fd, fd, __ATOMIC_SEQ_CST); 780 853 } 781 854 … … 841 914 unsigned tail = *ctx->cq.tail; 842 915 if(head == tail) return false; 843 ready_schedule_lock(); 844 ret = __cfa_io_drain( proc ); 845 ready_schedule_unlock(); 916 #if OLD_MAIN 917 ready_schedule_lock(); 918 ret = __cfa_io_drain( proc ); 919 ready_schedule_unlock(); 920 #else 921 ret = __cfa_io_drain( proc ); 922 #endif 846 923 #endif 847 924 return ret; -
libcfa/src/concurrency/kernel.hfa
rf5a51db r97c215f 53 53 coroutine processorCtx_t { 54 54 struct processor * proc; 55 };56 57 58 struct __fd_waitctx {59 volatile int fd;60 55 }; 61 56 … … 106 101 int idle_fd; 107 102 108 // Idle waitctx109 struct __fd_waitctx idle_wctx;110 111 103 // Termination synchronisation (user semaphore) 112 104 oneshot terminated; … … 215 207 216 208 // FD to use to wake a processor 217 struct __fd_waitctx * volatile fdw;209 volatile int fd; 218 210 219 211 // Total number of processors -
libcfa/src/concurrency/kernel/fwd.hfa
rf5a51db r97c215f 396 396 if( !(in_kernel) ) enable_interrupts(); \ 397 397 } 398 #if defined(CFA_HAVE_LINUX_IO_URING_H)399 #define __IO_STATS__(in_kernel, ...) { \400 if( !(in_kernel) ) disable_interrupts(); \401 with( *__tls_stats() ) { \402 __VA_ARGS__ \403 } \404 if( !(in_kernel) ) enable_interrupts(); \405 }406 #else407 #define __IO_STATS__(in_kernel, ...)408 #endif409 398 #else 410 399 #define __STATS__(in_kernel, ...) 411 #define __IO_STATS__(in_kernel, ...)412 400 #endif 413 401 } -
libcfa/src/concurrency/kernel/startup.cfa
rf5a51db r97c215f 537 537 } 538 538 539 this.idle_wctx.fd = 0;540 541 // I'm assuming these two are reserved for standard input and output542 // so I'm using them as sentinels with idle_wctx.543 /* paranoid */ verify( this.idle_fd != 0 );544 /* paranoid */ verify( this.idle_fd != 1 );545 546 539 #if !defined(__CFA_NO_STATISTICS__) 547 540 print_stats = 0; … … 597 590 // Cluster 598 591 static void ?{}(__cluster_proc_list & this) { 599 this.fd w = 0p;592 this.fd = 0; 600 593 this.idle = 0; 601 594 this.total = 0; -
libcfa/src/concurrency/mutex_stmt.hfa
rf5a51db r97c215f 38 38 } 39 39 40 struct scoped_lock {41 L * internal_lock;42 };43 44 static inline void ?{}( scoped_lock(L) & this, L & internal_lock ) {45 this.internal_lock = &internal_lock;46 lock(internal_lock);47 }48 49 static inline void ^?{}( scoped_lock(L) & this ) with(this) {50 unlock(*internal_lock);51 }52 53 40 static inline L * __get_ptr( L & this ) { 54 41 return &this; -
libcfa/src/concurrency/preemption.cfa
rf5a51db r97c215f 251 251 bool enabled = __cfaabi_tls.preemption_state.enabled; 252 252 253 // Check if there is a pending preemption254 processor * proc = __cfaabi_tls.this_processor;255 bool pending = proc ? proc->pending_preemption : false;256 if( enabled && pending ) proc->pending_preemption = false;257 258 253 // create a assembler label after 259 254 // marked as clobber all to avoid movement 260 255 __cfaasm_label(check, after); 261 262 // If we can preempt and there is a pending one263 // this is a good time to yield264 if( enabled && pending ) {265 force_yield( __POLL_PREEMPTION );266 }267 256 return enabled; 268 257 } … … 293 282 // marked as clobber all to avoid movement 294 283 __cfaasm_label(get, after); 295 296 // This is used everywhere, to avoid cost, we DO NOT poll pending preemption297 284 return val; 298 285 } … … 371 358 if(!ready) { abort("Preemption should be ready"); } 372 359 373 sigset_t oldset; 374 int ret; 375 ret = pthread_sigmask(0, ( const sigset_t * ) 0p, &oldset); // workaround trac#208: cast should be unnecessary 376 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); } 377 378 ret = sigismember(&oldset, SIGUSR1); 379 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 380 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); } 381 382 ret = sigismember(&oldset, SIGALRM); 383 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 384 if(ret == 0) { abort("ERROR SIGALRM is enabled"); } 385 386 ret = sigismember(&oldset, SIGTERM); 387 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 388 if(ret == 1) { abort("ERROR SIGTERM is disabled"); } 360 __cfaasm_label(debug, before); 361 362 sigset_t oldset; 363 int ret; 364 ret = pthread_sigmask(0, ( const sigset_t * ) 0p, &oldset); // workaround trac#208: cast should be unnecessary 365 if(ret != 0) { abort("ERROR sigprocmask returned %d", ret); } 366 367 ret = sigismember(&oldset, SIGUSR1); 368 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 369 if(ret == 1) { abort("ERROR SIGUSR1 is disabled"); } 370 371 ret = sigismember(&oldset, SIGALRM); 372 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 373 if(ret == 0) { abort("ERROR SIGALRM is enabled"); } 374 375 ret = sigismember(&oldset, SIGTERM); 376 if(ret < 0) { abort("ERROR sigismember returned %d", ret); } 377 if(ret == 1) { abort("ERROR SIGTERM is disabled"); } 378 379 __cfaasm_label(debug, after); 389 380 } 390 381 … … 557 548 __cfaasm_label( check ); 558 549 __cfaasm_label( dsable ); 559 //__cfaasm_label( debug );550 __cfaasm_label( debug ); 560 551 561 552 // Check if preemption is safe … … 564 555 if( __cfaasm_in( ip, check ) ) { ready = false; goto EXIT; }; 565 556 if( __cfaasm_in( ip, dsable ) ) { ready = false; goto EXIT; }; 566 //if( __cfaasm_in( ip, debug ) ) { ready = false; goto EXIT; };557 if( __cfaasm_in( ip, debug ) ) { ready = false; goto EXIT; }; 567 558 if( !__cfaabi_tls.preemption_state.enabled) { ready = false; goto EXIT; }; 568 559 if( __cfaabi_tls.preemption_state.in_progress ) { ready = false; goto EXIT; }; … … 670 661 671 662 // Check if it is safe to preempt here 672 if( !preemption_ready( ip ) ) { 673 #if !defined(__CFA_NO_STATISTICS__) 674 __cfaabi_tls.this_stats->ready.threads.preempt.rllfwd++; 675 #endif 676 return; 677 } 663 if( !preemption_ready( ip ) ) { return; } 678 664 679 665 __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) ); … … 694 680 695 681 // Preemption can occur here 696 697 #if !defined(__CFA_NO_STATISTICS__)698 __cfaabi_tls.this_stats->ready.threads.preempt.yield++;699 #endif700 682 701 683 force_yield( __ALARM_PREEMPTION ); // Do the actual __cfactx_switch -
libcfa/src/concurrency/ready_queue.cfa
rf5a51db r97c215f 201 201 uint_fast32_t ready_mutate_lock( void ) with(*__scheduler_lock) { 202 202 /* paranoid */ verify( ! __preemption_enabled() ); 203 /* paranoid */ verify( ! kernelTLS().sched_lock ); 203 204 204 205 // Step 1 : lock global lock … … 206 207 // to simply lock their own lock and enter. 207 208 __atomic_acquire( &write_lock ); 208 209 // Make sure we won't deadlock ourself210 // Checking before acquiring the writer lock isn't safe211 // because someone else could have locked us.212 /* paranoid */ verify( ! kernelTLS().sched_lock );213 209 214 210 // Step 2 : lock per-proc lock -
libcfa/src/concurrency/stats.cfa
rf5a51db r97c215f 29 29 stats->ready.threads.threads = 0; 30 30 stats->ready.threads.cthreads = 0; 31 stats->ready.threads.preempt.yield = 0;32 stats->ready.threads.preempt.rllfwd = 0;33 31 stats->ready.sleep.halts = 0; 34 32 stats->ready.sleep.cancels = 0; 35 stats->ready.sleep.early = 0;36 33 stats->ready.sleep.wakes = 0; 37 stats->ready.sleep.seen = 0;38 34 stats->ready.sleep.exits = 0; 39 35 … … 47 43 stats->io.submit.slow = 0; 48 44 stats->io.flush.external = 0; 49 stats->io.flush.dirty = 0;50 stats->io.flush.full = 0;51 stats->io.flush.idle = 0;52 stats->io.flush.eager = 0;53 45 stats->io.calls.flush = 0; 54 46 stats->io.calls.submitted = 0; … … 79 71 80 72 void __tally_stats( struct __stats_t * cltr, struct __stats_t * proc ) { 81 tally_one( &cltr->ready.push.local.attempt , &proc->ready.push.local.attempt ); 82 tally_one( &cltr->ready.push.local.success , &proc->ready.push.local.success ); 83 tally_one( &cltr->ready.push.share.attempt , &proc->ready.push.share.attempt ); 84 tally_one( &cltr->ready.push.share.success , &proc->ready.push.share.success ); 85 tally_one( &cltr->ready.push.extrn.attempt , &proc->ready.push.extrn.attempt ); 86 tally_one( &cltr->ready.push.extrn.success , &proc->ready.push.extrn.success ); 87 tally_one( &cltr->ready.pop.local .attempt , &proc->ready.pop.local .attempt ); 88 tally_one( &cltr->ready.pop.local .success , &proc->ready.pop.local .success ); 89 tally_one( &cltr->ready.pop.help .attempt , &proc->ready.pop.help .attempt ); 90 tally_one( &cltr->ready.pop.help .success , &proc->ready.pop.help .success ); 91 tally_one( &cltr->ready.pop.steal .attempt , &proc->ready.pop.steal .attempt ); 92 tally_one( &cltr->ready.pop.steal .success , &proc->ready.pop.steal .success ); 93 tally_one( &cltr->ready.pop.search.attempt , &proc->ready.pop.search.attempt ); 94 tally_one( &cltr->ready.pop.search.success , &proc->ready.pop.search.success ); 95 tally_one( &cltr->ready.threads.migration , &proc->ready.threads.migration ); 96 tally_one( &cltr->ready.threads.extunpark , &proc->ready.threads.extunpark ); 97 tally_one( &cltr->ready.threads.threads , &proc->ready.threads.threads ); 98 tally_one( &cltr->ready.threads.cthreads , &proc->ready.threads.cthreads ); 99 tally_one( &cltr->ready.threads.preempt.yield , &proc->ready.threads.preempt.yield ); 100 tally_one( &cltr->ready.threads.preempt.rllfwd, &proc->ready.threads.preempt.rllfwd ); 101 tally_one( &cltr->ready.sleep.halts , &proc->ready.sleep.halts ); 102 tally_one( &cltr->ready.sleep.cancels , &proc->ready.sleep.cancels ); 103 tally_one( &cltr->ready.sleep.early , &proc->ready.sleep.early ); 104 tally_one( &cltr->ready.sleep.wakes , &proc->ready.sleep.wakes ); 105 tally_one( &cltr->ready.sleep.seen , &proc->ready.sleep.wakes ); 106 tally_one( &cltr->ready.sleep.exits , &proc->ready.sleep.exits ); 73 tally_one( &cltr->ready.push.local.attempt, &proc->ready.push.local.attempt ); 74 tally_one( &cltr->ready.push.local.success, &proc->ready.push.local.success ); 75 tally_one( &cltr->ready.push.share.attempt, &proc->ready.push.share.attempt ); 76 tally_one( &cltr->ready.push.share.success, &proc->ready.push.share.success ); 77 tally_one( &cltr->ready.push.extrn.attempt, &proc->ready.push.extrn.attempt ); 78 tally_one( &cltr->ready.push.extrn.success, &proc->ready.push.extrn.success ); 79 tally_one( &cltr->ready.pop.local .attempt, &proc->ready.pop.local .attempt ); 80 tally_one( &cltr->ready.pop.local .success, &proc->ready.pop.local .success ); 81 tally_one( &cltr->ready.pop.help .attempt, &proc->ready.pop.help .attempt ); 82 tally_one( &cltr->ready.pop.help .success, &proc->ready.pop.help .success ); 83 tally_one( &cltr->ready.pop.steal .attempt, &proc->ready.pop.steal .attempt ); 84 tally_one( &cltr->ready.pop.steal .success, &proc->ready.pop.steal .success ); 85 tally_one( &cltr->ready.pop.search.attempt, &proc->ready.pop.search.attempt ); 86 tally_one( &cltr->ready.pop.search.success, &proc->ready.pop.search.success ); 87 tally_one( &cltr->ready.threads.migration , &proc->ready.threads.migration ); 88 tally_one( &cltr->ready.threads.extunpark , &proc->ready.threads.extunpark ); 89 tally_one( &cltr->ready.threads.threads , &proc->ready.threads.threads ); 90 tally_one( &cltr->ready.threads.cthreads , &proc->ready.threads.cthreads ); 91 tally_one( &cltr->ready.sleep.halts , &proc->ready.sleep.halts ); 92 tally_one( &cltr->ready.sleep.cancels , &proc->ready.sleep.cancels ); 93 tally_one( &cltr->ready.sleep.wakes , &proc->ready.sleep.wakes ); 94 tally_one( &cltr->ready.sleep.exits , &proc->ready.sleep.exits ); 107 95 108 96 #if defined(CFA_HAVE_LINUX_IO_URING_H) … … 115 103 tally_one( &cltr->io.submit.slow , &proc->io.submit.slow ); 116 104 tally_one( &cltr->io.flush.external , &proc->io.flush.external ); 117 tally_one( &cltr->io.flush.dirty , &proc->io.flush.dirty );118 tally_one( &cltr->io.flush.full , &proc->io.flush.full );119 tally_one( &cltr->io.flush.idle , &proc->io.flush.idle );120 tally_one( &cltr->io.flush.eager , &proc->io.flush.eager );121 105 tally_one( &cltr->io.calls.flush , &proc->io.calls.flush ); 122 106 tally_one( &cltr->io.calls.submitted , &proc->io.calls.submitted ); … … 169 153 | " (" | eng3(ready.pop.search.attempt) | " try)"; 170 154 171 sstr | "- Idle Slp : " | eng3(ready.sleep.halts) | "halt," | eng3(ready.sleep.cancels) | "cancel," 172 | eng3(ready.sleep.wakes + ready.sleep.early) | '(' | eng3(ready.sleep.early) | ',' | eng3(ready.sleep.seen) | ')' | " wake(early, seen)," 173 | eng3(ready.sleep.exits) | "exit"; 174 sstr | "- Preemption : " | eng3(ready.threads.preempt.yield) | "yields," | eng3(ready.threads.preempt.rllfwd) | "delayed"; 155 sstr | "- Idle Slp : " | eng3(ready.sleep.halts) | "halt," | eng3(ready.sleep.cancels) | "cancel," | eng3(ready.sleep.wakes) | "wake," | eng3(ready.sleep.exits) | "exit"; 175 156 sstr | nl; 176 157 } … … 197 178 if(io.alloc.fail || io.alloc.revoke || io.alloc.block) 198 179 sstr | "- failures : " | eng3(io.alloc.fail) | "oom, " | eng3(io.alloc.revoke) | "rvk, " | eng3(io.alloc.block) | "blk"; 199 //if(io.flush.external)200 //sstr | "- flush external : " | eng3(io.flush.external);180 if(io.flush.external) 181 sstr | "- flush external : " | eng3(io.flush.external); 201 182 202 183 double avgsubs = ((double)io.calls.submitted) / io.calls.flush; 203 184 double avgcomp = ((double)io.calls.completed) / io.calls.drain; 204 185 sstr | "- syscll : " 205 | " sub " | eng3(io.calls. submitted) | "/" | eng3(io.calls.flush) | "(" | ws(3, 3, avgsubs) | "/flush)"206 | " - cmp " | eng3(io.calls. completed) | "/" | eng3(io.calls.drain) | "(" | ws(3, 3, avgcomp) | "/drain)"186 | " sub " | eng3(io.calls.flush) | "/" | eng3(io.calls.submitted) | "(" | ws(3, 3, avgsubs) | "/flush)" 187 | " - cmp " | eng3(io.calls.drain) | "/" | eng3(io.calls.completed) | "(" | ws(3, 3, avgcomp) | "/drain)" 207 188 | " - " | eng3(io.calls.errors.busy) | " EBUSY"; 208 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";209 189 sstr | "- ops blk: " 210 190 | " sk rd: " | eng3(io.ops.sockread) | "epll: " | eng3(io.ops.epllread) -
libcfa/src/concurrency/stats.hfa
rf5a51db r97c215f 65 65 volatile int64_t threads; // number of threads in the system, includes only local change 66 66 volatile int64_t cthreads; // number of threads in the system, includes only local change 67 struct {68 volatile uint64_t yield;69 volatile uint64_t rllfwd;70 } preempt;71 67 } threads; 72 68 struct { 73 69 volatile uint64_t halts; 74 70 volatile uint64_t cancels; 75 volatile uint64_t early;76 71 volatile uint64_t wakes; 77 volatile uint64_t seen;78 72 volatile uint64_t exits; 79 73 } sleep; … … 95 89 struct { 96 90 volatile uint64_t external; 97 volatile uint64_t dirty;98 volatile uint64_t full;99 volatile uint64_t idle;100 volatile uint64_t eager;101 91 } flush; 102 92 struct { -
libcfa/src/stdhdr/pthread.h
rf5a51db r97c215f 10 10 // Created On : Wed Jun 16 13:39:06 2021 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 3 21:53:26 202213 // Update Count : 1 312 // Last Modified On : Wed Jun 16 13:39:42 2021 13 // Update Count : 1 14 14 // 15 15 16 // pthread.h and setjmp.h cannot agree on the type of __sigsetjmp:17 //18 // extern int __sigsetjmp (struct __jmp_buf_tag *__env, int __savemask) __attribute__ ((__nothrow__));19 // extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __attribute__ ((__nothrow__));20 //21 // With -Wall, gcc-11 warns about the disagreement unless the CPP directive22 //23 // # 1 "/usr/include/pthread.h" 1 3 424 //25 // appears, which appears to be witchcraft. Unfortunately, this directive is removed by the CFA preprocessor, so the26 // batchtest fails because of the spurious warning message. Hence, the warning is elided.27 28 16 extern "C" { 29 #if defined(__GNUC__) && __GNUC__ == 1130 #pragma GCC diagnostic push31 #pragma GCC diagnostic ignored "-Warray-parameter"32 #endif // defined(__GNUC__) && __GNUC__ == 1133 34 17 #include_next <pthread.h> // has internal check for multiple expansion 35 36 #if defined(__GNUC__) && __GNUC__ == 1137 #pragma GCC diagnostic pop38 #endif // defined(__GNUC__) && __GNUC__ == 1139 18 } // extern "C" 40 19 41 20 // Local Variables: // 21 // tab-width: 4 // 42 22 // mode: c++ // 23 // compile-command: "make install" // 43 24 // End: // -
libcfa/src/stdhdr/setjmp.h
rf5a51db r97c215f 10 10 // Created On : Mon Jul 4 23:25:26 2016 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T hu Feb 3 21:53:28 202213 // Update Count : 1 812 // Last Modified On : Tue Jul 5 20:38:33 2016 13 // Update Count : 12 14 14 // 15 15 16 // pthread.h and setjmp.h cannot agree on the type of __sigsetjmp:17 //18 // extern int __sigsetjmp (struct __jmp_buf_tag *__env, int __savemask) __attribute__ ((__nothrow__));19 // extern int __sigsetjmp (struct __jmp_buf_tag __env[1], int __savemask) __attribute__ ((__nothrow__));20 //21 // With -Wall, gcc-11 warns about the disagreement unless the CPP directive22 //23 // # 1 "/usr/include/pthread.h" 1 3 424 //25 // appears, which appears to be witchcraft. Unfortunately, this directive is removed by the CFA preprocessor, so the26 // batchtest fails because of the spurious warning message. Hence, the warning is elided.27 28 16 extern "C" { 29 #if defined(__GNUC__) && __GNUC__ == 1130 #pragma GCC diagnostic push31 #pragma GCC diagnostic ignored "-Warray-parameter"32 #endif // defined(__GNUC__) && __GNUC__ == 1133 34 17 #include_next <setjmp.h> // has internal check for multiple expansion 35 36 #if defined(__GNUC__) && __GNUC__ == 1137 #pragma GCC diagnostic pop38 #endif // defined(__GNUC__) && __GNUC__ == 1139 18 } // extern "C" 40 19 41 20 // Local Variables: // 21 // tab-width: 4 // 42 22 // mode: c++ // 23 // compile-command: "make install" // 43 24 // End: // -
src/AST/Convert.cpp
rf5a51db r97c215f 9 9 // Author : Thierry Delisle 10 10 // Created On : Thu May 09 15::37::05 2019 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Feb 2 13:19:22 202213 // Update Count : 4111 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jul 14 16:15:00 2021 13 // Update Count : 37 14 14 // 15 15 … … 393 393 auto stmt = new IfStmt( 394 394 get<Expression>().accept1( node->cond ), 395 get<Statement>().accept1( node->then ),396 get<Statement>().accept1( node->else _),395 get<Statement>().accept1( node->thenPart ), 396 get<Statement>().accept1( node->elsePart ), 397 397 get<Statement>().acceptL( node->inits ) 398 398 ); … … 419 419 } 420 420 421 const ast::Stmt * visit( const ast::While DoStmt * node ) override final {421 const ast::Stmt * visit( const ast::WhileStmt * node ) override final { 422 422 if ( inCache( node ) ) return nullptr; 423 423 auto inits = get<Statement>().acceptL( node->inits ); 424 auto stmt = new While DoStmt(424 auto stmt = new WhileStmt( 425 425 get<Expression>().accept1( node->cond ), 426 426 get<Statement>().accept1( node->body ), 427 get<Statement>().accept1( node->else_ ),428 427 inits, 429 428 node->isDoWhile … … 438 437 get<Expression>().accept1( node->cond ), 439 438 get<Expression>().accept1( node->inc ), 440 get<Statement>().accept1( node->body ), 441 get<Statement>().accept1( node->else_ ) 439 get<Statement>().accept1( node->body ) 442 440 ); 443 441 return stmtPostamble( stmt, node ); … … 1874 1872 old->location, 1875 1873 GET_ACCEPT_1(condition, Expr), 1876 GET_ACCEPT_1(then , Stmt),1877 GET_ACCEPT_1(else _, Stmt),1874 GET_ACCEPT_1(thenPart, Stmt), 1875 GET_ACCEPT_1(elsePart, Stmt), 1878 1876 GET_ACCEPT_V(initialization, Stmt), 1879 1877 GET_LABELS_V(old->labels) … … 1904 1902 } 1905 1903 1906 virtual void visit( const While DoStmt * old ) override final {1904 virtual void visit( const WhileStmt * old ) override final { 1907 1905 if ( inCache( old ) ) return; 1908 this->node = new ast::While DoStmt(1906 this->node = new ast::WhileStmt( 1909 1907 old->location, 1910 1908 GET_ACCEPT_1(condition, Expr), 1911 1909 GET_ACCEPT_1(body, Stmt), 1912 GET_ACCEPT_1(else_, Stmt),1913 1910 GET_ACCEPT_V(initialization, Stmt), 1914 1911 old->isDoWhile, … … 1926 1923 GET_ACCEPT_1(increment, Expr), 1927 1924 GET_ACCEPT_1(body, Stmt), 1928 GET_ACCEPT_1(else_, Stmt),1929 1925 GET_LABELS_V(old->labels) 1930 1926 ); -
src/AST/Copy.hpp
rf5a51db r97c215f 10 10 // Created On : Wed Jul 10 16:13:00 2019 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Dec 15 11:07:00 202113 // Update Count : 312 // Last Modified On : Thr Nov 11 9:22:00 2021 13 // Update Count : 2 14 14 // 15 15 … … 52 52 Node * deepCopy<Node>( const Node * localRoot ); 53 53 54 template<typename node_t, enum Node::ref_type ref_t>55 node_t * shallowCopy( const ptr_base<node_t, ref_t> & localRoot ) {56 return shallowCopy( localRoot.get() );57 }58 59 template<typename node_t, enum Node::ref_type ref_t>60 node_t * deepCopy( const ptr_base<node_t, ref_t> & localRoot ) {61 return deepCopy( localRoot.get() );62 }63 64 54 } 65 55 -
src/AST/Fwd.hpp
rf5a51db r97c215f 10 10 // Created On : Wed May 8 16:05:00 2019 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:08:33 202213 // Update Count : 512 // Last Modified On : Fri Mar 12 18:37:39 2021 13 // Update Count : 4 14 14 // 15 15 … … 44 44 class DirectiveStmt; 45 45 class IfStmt; 46 class While DoStmt;46 class WhileStmt; 47 47 class ForStmt; 48 48 class SwitchStmt; -
src/AST/Node.cpp
rf5a51db r97c215f 10 10 // Created On : Thu May 16 14:16:00 2019 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:09:39 202213 // Update Count : 312 // Last Modified On : Fri Mar 12 18:25:06 2021 13 // Update Count : 2 14 14 // 15 15 … … 146 146 template class ast::ptr_base< ast::IfStmt, ast::Node::ref_type::weak >; 147 147 template class ast::ptr_base< ast::IfStmt, ast::Node::ref_type::strong >; 148 template class ast::ptr_base< ast::While DoStmt, ast::Node::ref_type::weak >;149 template class ast::ptr_base< ast::While DoStmt, ast::Node::ref_type::strong >;148 template class ast::ptr_base< ast::WhileStmt, ast::Node::ref_type::weak >; 149 template class ast::ptr_base< ast::WhileStmt, ast::Node::ref_type::strong >; 150 150 template class ast::ptr_base< ast::ForStmt, ast::Node::ref_type::weak >; 151 151 template class ast::ptr_base< ast::ForStmt, ast::Node::ref_type::strong >; -
src/AST/Node.hpp
rf5a51db r97c215f 188 188 } 189 189 190 ptr_base & operator=( const node_t * node ) {191 assign( node );192 return *this;193 }194 195 190 template<typename o_node_t> 196 191 ptr_base & operator=( const o_node_t * node ) { -
src/AST/Pass.hpp
rf5a51db r97c215f 146 146 const ast::Stmt * visit( const ast::DirectiveStmt * ) override final; 147 147 const ast::Stmt * visit( const ast::IfStmt * ) override final; 148 const ast::Stmt * visit( const ast::While DoStmt* ) override final;148 const ast::Stmt * visit( const ast::WhileStmt * ) override final; 149 149 const ast::Stmt * visit( const ast::ForStmt * ) override final; 150 150 const ast::Stmt * visit( const ast::SwitchStmt * ) override final; … … 238 238 239 239 private: 240 241 // Regular nodes 240 const ast::Stmt * call_accept( const ast::Stmt * ); 241 const ast::Expr * call_accept( const ast::Expr * ); 242 243 // requests WithStmtsToAdd directly add to this statement, as if it is a compound. 244 245 const ast::Stmt * call_accept_as_compound(const ast::Stmt *); 246 242 247 template< typename node_t > 243 struct result1 { 244 bool differs; 245 const node_t * value; 246 247 template< typename object_t, typename super_t, typename field_t > 248 void apply(object_t *, field_t super_t::* field); 249 }; 250 251 result1<ast::Stmt> call_accept( const ast::Stmt * ); 252 result1<ast::Expr> call_accept( const ast::Expr * ); 253 254 template< typename node_t > 255 auto call_accept( const node_t * node ) 256 -> typename std::enable_if< 248 auto call_accept( const node_t * node ) -> typename std::enable_if< 257 249 !std::is_base_of<ast::Expr, node_t>::value && 258 250 !std::is_base_of<ast::Stmt, node_t>::value 259 , result1< 260 typename std::remove_pointer< decltype( node->accept(*this) ) >::type 261 > 251 , decltype( node->accept(*this) ) 262 252 >::type; 263 253 264 // requests WithStmtsToAdd directly add to this statement, as if it is a compound.265 result1<ast::Stmt> call_accept_as_compound(const ast::Stmt *);266 267 template<typename it_t, template <class...> class container_t>268 static inline void take_all_delta( it_t it, container_t<ast::ptr<ast::Decl>> * decls, bool * mutated = nullptr ) {269 if(empty(decls)) return;270 271 std::transform(decls->begin(), decls->end(), it, [](ast::ptr<ast::Decl>&& decl) -> auto {272 auto loc = decl->location;273 auto stmt = new DeclStmt( loc, decl.release() );274 return { {stmt}, -1, false };275 });276 decls->clear();277 if(mutated) *mutated = true;278 }279 280 // Container of statements281 254 template< template <class...> class container_t > 282 struct resultNstmt { 283 struct delta { 284 ptr<Stmt> nval; 285 ssize_t old_idx; 286 bool is_old; 287 288 delta(const Stmt * s, ssize_t i, bool old) : nval{s}, old_idx{i}, is_old{old} {} 289 }; 290 291 bool differs; 292 container_t< delta > values; 293 294 resultNstmt() : differs(false), values{} {} 295 resultNstmt(bool diff, container_t< delta > && vals) : differs(diff), values(vals) {} 296 297 template< typename object_t, typename super_t, typename field_t > 298 void apply(object_t *, field_t super_t::* field); 299 300 template< template <class...> class incontainer_t > 301 void take_all( incontainer_t<ast::ptr<ast::Stmt>> * stmts ) { 302 if(!stmts || stmts->empty()) return; 303 304 std::transform(stmts->begin(), stmts->end(), std::back_inserter( values ), [](ast::ptr<ast::Stmt>& decl) -> delta { 305 return delta( decl.release(), -1, false ); 306 }); 307 stmts->clear(); 308 differs = true; 309 } 310 311 template< template <class...> class incontainer_t > 312 void take_all( incontainer_t<ast::ptr<ast::Decl>> * decls ) { 313 if(!decls || decls->empty()) return; 314 315 std::transform(decls->begin(), decls->end(), std::back_inserter( values ), [](ast::ptr<ast::Decl>& decl) -> auto { 316 auto loc = decl->location; 317 auto stmt = new DeclStmt( loc, decl.release() ); 318 return delta( stmt, -1, false ); 319 }); 320 decls->clear(); 321 differs = true; 322 } 323 }; 324 325 template< template <class...> class container_t > 326 resultNstmt<container_t> call_accept( const container_t< ptr<Stmt> > & ); 327 328 // Container of something 255 container_t< ptr<Stmt> > call_accept( const container_t< ptr<Stmt> > & ); 256 329 257 template< template <class...> class container_t, typename node_t > 330 struct resultN { 331 bool differs; 332 container_t<ptr<node_t>> values; 333 334 template< typename object_t, typename super_t, typename field_t > 335 void apply(object_t *, field_t super_t::* field); 336 }; 337 338 template< template <class...> class container_t, typename node_t > 339 resultN< container_t, node_t > call_accept( const container_t< ptr<node_t> > & container ); 258 container_t< ptr<node_t> > call_accept( const container_t< ptr<node_t> > & container ); 340 259 341 260 public: 342 261 /// Logic to call the accept and mutate the parent if needed, delegates call to accept 343 template<typename node_t, typename parent_t, typename field_t>344 void maybe_accept(const node_t * &, field_t parent_t::* field);345 346 template<typename node_t, typename parent_t, typename field_t>347 void maybe_accept_as_compound(const node_t * &, field_t parent_t::* field);262 template<typename node_t, typename parent_t, typename child_t> 263 void maybe_accept(const node_t * &, child_t parent_t::* child); 264 265 template<typename node_t, typename parent_t, typename child_t> 266 void maybe_accept_as_compound(const node_t * &, child_t parent_t::* child); 348 267 349 268 private: -
src/AST/Pass.impl.hpp
rf5a51db r97c215f 34 34 __pass::previsit( core, node, 0 ); 35 35 36 #define VISIT( code... ) \ 37 /* if this node should visit its children */ \ 38 if ( __visit_children() ) { \ 39 /* visit the children */ \ 40 code \ 41 } 42 36 43 #define VISIT_END( type, node ) \ 37 44 /* call the implementation of the postvisit of this pass */ \ … … 79 86 80 87 template<typename it_t, template <class...> class container_t> 81 static inline void take_all( it_t it, container_t<ast::ptr<ast::Stmt>> * stmts, bool * mutated = nullptr ) {82 if(empty( stmts)) return;83 84 std::move( stmts->begin(), stmts->end(), it);85 stmts->clear();88 static inline void take_all( it_t it, container_t<ast::ptr<ast::Stmt>> * decls, bool * mutated = nullptr ) { 89 if(empty(decls)) return; 90 91 std::move(decls->begin(), decls->end(), it); 92 decls->clear(); 86 93 if(mutated) *mutated = true; 87 94 } … … 123 130 return !new_val.empty(); 124 131 } 125 }126 127 128 template< typename core_t >129 template< typename node_t >130 template< typename object_t, typename super_t, typename field_t >131 void ast::Pass< core_t >::result1< node_t >::apply(object_t * object, field_t super_t::* field) {132 object->*field = value;133 132 } 134 133 … … 139 138 !std::is_base_of<ast::Expr, node_t>::value && 140 139 !std::is_base_of<ast::Stmt, node_t>::value 141 , ast::Pass< core_t >::result1< 142 typename std::remove_pointer< decltype( node->accept(*this) ) >::type 143 > 140 , decltype( node->accept(*this) ) 144 141 >::type 145 142 { … … 150 147 static_assert( !std::is_base_of<ast::Stmt, node_t>::value, "ERROR"); 151 148 152 auto nval = node->accept( *this ); 153 ast::Pass< core_t >::result1< 154 typename std::remove_pointer< decltype( node->accept(*this) ) >::type 155 > res; 156 res.differs = nval != node; 157 res.value = nval; 158 return res; 149 return node->accept( *this ); 159 150 } 160 151 161 152 template< typename core_t > 162 ast::Pass< core_t >::result1<ast::Expr>ast::Pass< core_t >::call_accept( const ast::Expr * expr ) {153 const ast::Expr * ast::Pass< core_t >::call_accept( const ast::Expr * expr ) { 163 154 __pedantic_pass_assert( __visit_children() ); 164 155 __pedantic_pass_assert( expr ); … … 169 160 } 170 161 171 auto nval = expr->accept( *this ); 172 return { nval != expr, nval }; 162 return expr->accept( *this ); 173 163 } 174 164 175 165 template< typename core_t > 176 ast::Pass< core_t >::result1<ast::Stmt>ast::Pass< core_t >::call_accept( const ast::Stmt * stmt ) {166 const ast::Stmt * ast::Pass< core_t >::call_accept( const ast::Stmt * stmt ) { 177 167 __pedantic_pass_assert( __visit_children() ); 178 168 __pedantic_pass_assert( stmt ); 179 169 180 const ast::Stmt * nval = stmt->accept( *this ); 181 return { nval != stmt, nval }; 170 return stmt->accept( *this ); 182 171 } 183 172 184 173 template< typename core_t > 185 ast::Pass< core_t >::result1<ast::Stmt>ast::Pass< core_t >::call_accept_as_compound( const ast::Stmt * stmt ) {174 const ast::Stmt * ast::Pass< core_t >::call_accept_as_compound( const ast::Stmt * stmt ) { 186 175 __pedantic_pass_assert( __visit_children() ); 187 176 __pedantic_pass_assert( stmt ); … … 208 197 // If the pass doesn't want to add anything then we are done 209 198 if( empty(stmts_before) && empty(stmts_after) && empty(decls_before) && empty(decls_after) ) { 210 return { nstmt != stmt, nstmt };199 return nstmt; 211 200 } 212 201 … … 230 219 __pass::take_all( std::back_inserter( compound->kids ), stmts_after ); 231 220 232 return {true, compound};221 return compound; 233 222 } 234 223 235 224 template< typename core_t > 236 225 template< template <class...> class container_t > 237 template< typename object_t, typename super_t, typename field_t > 238 void ast::Pass< core_t >::resultNstmt<container_t>::apply(object_t * object, field_t super_t::* field) { 239 auto & container = object->*field; 240 __pedantic_pass_assert( container.size() <= values.size() ); 241 242 auto cit = enumerate(container).begin(); 243 244 container_t<ptr<Stmt>> nvals; 245 for(delta & d : values) { 246 if( d.is_old ) { 247 __pedantic_pass_assert( cit.idx <= d.old_idx ); 248 std::advance( cit, d.old_idx - cit.idx ); 249 nvals.push_back( std::move( (*cit).val) ); 250 } else { 251 nvals.push_back( std::move(d.nval) ); 252 } 253 } 254 255 object->*field = std::move(nvals); 256 } 257 258 template< typename core_t > 259 template< template <class...> class container_t > 260 ast::Pass< core_t >::resultNstmt<container_t> ast::Pass< core_t >::call_accept( const container_t< ptr<Stmt> > & statements ) { 226 container_t< ptr<Stmt> > ast::Pass< core_t >::call_accept( const container_t< ptr<Stmt> > & statements ) { 261 227 __pedantic_pass_assert( __visit_children() ); 262 228 if( statements.empty() ) return {}; … … 285 251 pass_visitor_stats.avg->push(pass_visitor_stats.depth); 286 252 287 resultNstmt<container_t> new_kids; 288 for( auto value : enumerate( statements ) ) { 253 bool mutated = false; 254 container_t< ptr<Stmt> > new_kids; 255 for( const Stmt * stmt : statements ) { 289 256 try { 290 size_t i = value.idx;291 const Stmt * stmt = value.val;292 257 __pedantic_pass_assert( stmt ); 293 258 const ast::Stmt * new_stmt = stmt->accept( *this ); 294 259 assert( new_stmt ); 295 if(new_stmt != stmt ) { new_kids.differs = true; }260 if(new_stmt != stmt ) mutated = true; 296 261 297 262 // Make sure that it is either adding statements or declartions but not both … … 303 268 304 269 // Take all the statements which should have gone after, N/A for first iteration 305 new_kids.take_all( decls_before);306 new_kids.take_all( stmts_before);270 __pass::take_all( std::back_inserter( new_kids ), decls_before, &mutated ); 271 __pass::take_all( std::back_inserter( new_kids ), stmts_before, &mutated ); 307 272 308 273 // Now add the statement if there is one 309 if(new_stmt != stmt) { 310 new_kids.values.emplace_back( new_stmt, i, false ); 311 } else { 312 new_kids.values.emplace_back( nullptr, i, true ); 313 } 274 new_kids.emplace_back( new_stmt ); 314 275 315 276 // Take all the declarations that go before 316 new_kids.take_all( decls_after);317 new_kids.take_all( stmts_after);277 __pass::take_all( std::back_inserter( new_kids ), decls_after, &mutated ); 278 __pass::take_all( std::back_inserter( new_kids ), stmts_after, &mutated ); 318 279 } 319 280 catch ( SemanticErrorException &e ) { … … 324 285 if ( !errors.isEmpty() ) { throw errors; } 325 286 326 return new_kids;287 return mutated ? new_kids : container_t< ptr<Stmt> >(); 327 288 } 328 289 329 290 template< typename core_t > 330 291 template< template <class...> class container_t, typename node_t > 331 template< typename object_t, typename super_t, typename field_t > 332 void ast::Pass< core_t >::resultN<container_t, node_t>::apply(object_t * object, field_t super_t::* field) { 333 auto & container = object->*field; 334 __pedantic_pass_assert( container.size() == values.size() ); 335 336 for(size_t i = 0; i < container.size(); i++) { 337 // Take all the elements that are different in 'values' 338 // and swap them into 'container' 339 if( values[i] != nullptr ) std::swap(container[i], values[i]); 340 } 341 342 // Now the original containers should still have the unchanged values 343 // but also contain the new values 344 } 345 346 template< typename core_t > 347 template< template <class...> class container_t, typename node_t > 348 ast::Pass< core_t >::resultN<container_t, node_t> ast::Pass< core_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) { 292 container_t< ast::ptr<node_t> > ast::Pass< core_t >::call_accept( const container_t< ast::ptr<node_t> > & container ) { 349 293 __pedantic_pass_assert( __visit_children() ); 350 294 if( container.empty() ) return {}; … … 356 300 357 301 bool mutated = false; 358 container_t< ptr<node_t>> new_kids;302 container_t< ast::ptr<node_t> > new_kids; 359 303 for ( const node_t * node : container ) { 360 304 try { 361 305 __pedantic_pass_assert( node ); 362 306 const node_t * new_stmt = strict_dynamic_cast< const node_t * >( node->accept( *this ) ); 363 if(new_stmt != node ) { 364 mutated = true; 365 new_kids.emplace_back( new_stmt ); 366 } else { 367 new_kids.emplace_back( nullptr ); 368 } 369 307 if(new_stmt != node ) mutated = true; 308 309 new_kids.emplace_back( new_stmt ); 370 310 } 371 311 catch( SemanticErrorException &e ) { … … 373 313 } 374 314 } 375 376 __pedantic_pass_assert( new_kids.size() == container.size() );377 315 pass_visitor_stats.depth--; 378 316 if ( ! errors.isEmpty() ) { throw errors; } 379 317 380 return ast::Pass< core_t >::resultN<container_t, node_t>{ mutated, new_kids };318 return mutated ? new_kids : container_t< ast::ptr<node_t> >(); 381 319 } 382 320 … … 396 334 auto new_val = call_accept( old_val ); 397 335 398 static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value /* || std::is_same<int, decltype(old_val)>::value */, "ERROR");399 400 if( new_val.differs) {336 static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR"); 337 338 if( __pass::differs(old_val, new_val) ) { 401 339 auto new_parent = __pass::mutate<core_t>(parent); 402 new_ val.apply(new_parent, child);340 new_parent->*child = new_val; 403 341 parent = new_parent; 404 342 } … … 422 360 static_assert( !std::is_same<const ast::Node *, decltype(new_val)>::value || std::is_same<int, decltype(old_val)>::value, "ERROR"); 423 361 424 if( new_val.differs) {362 if( __pass::differs(old_val, new_val) ) { 425 363 auto new_parent = __pass::mutate<core_t>(parent); 426 new_ val.apply( new_parent, child );364 new_parent->*child = new_val; 427 365 parent = new_parent; 428 366 } … … 514 452 VISIT_START( node ); 515 453 516 if ( __visit_children() ) {454 VISIT( 517 455 { 518 456 guard_symtab guard { *this }; … … 522 460 maybe_accept( node, &ObjectDecl::bitfieldWidth ); 523 461 maybe_accept( node, &ObjectDecl::attributes ); 524 }462 ) 525 463 526 464 __pass::symtab::addId( core, 0, node ); … … 537 475 __pass::symtab::addId( core, 0, node ); 538 476 539 if ( __visit_children() ) { 540 maybe_accept( node, &FunctionDecl::withExprs ); 541 } 477 VISIT(maybe_accept( node, &FunctionDecl::withExprs );) 542 478 { 543 479 // with clause introduces a level of scope (for the with expression members). … … 557 493 } }; 558 494 __pass::symtab::addId( core, 0, func ); 559 if ( __visit_children() ) {495 VISIT( 560 496 // parameter declarations 561 497 maybe_accept( node, &FunctionDecl::params ); … … 573 509 maybe_accept( node, &FunctionDecl::stmts ); 574 510 maybe_accept( node, &FunctionDecl::attributes ); 575 }511 ) 576 512 } 577 513 } … … 590 526 __pass::symtab::addStructFwd( core, 0, node ); 591 527 592 if ( __visit_children() ){528 VISIT({ 593 529 guard_symtab guard { * this }; 594 530 maybe_accept( node, &StructDecl::params ); 595 531 maybe_accept( node, &StructDecl::members ); 596 532 maybe_accept( node, &StructDecl::attributes ); 597 } 533 }) 598 534 599 535 // this addition replaces the forward declaration … … 612 548 __pass::symtab::addUnionFwd( core, 0, node ); 613 549 614 if ( __visit_children() ){550 VISIT({ 615 551 guard_symtab guard { * this }; 616 552 maybe_accept( node, &UnionDecl::params ); 617 553 maybe_accept( node, &UnionDecl::members ); 618 554 maybe_accept( node, &UnionDecl::attributes ); 619 } 555 }) 620 556 621 557 __pass::symtab::addUnion( core, 0, node ); … … 632 568 __pass::symtab::addEnum( core, 0, node ); 633 569 634 if ( __visit_children() ) {570 VISIT( 635 571 // unlike structs, traits, and unions, enums inject their members into the global scope 636 572 maybe_accept( node, &EnumDecl::params ); 637 573 maybe_accept( node, &EnumDecl::members ); 638 574 maybe_accept( node, &EnumDecl::attributes ); 639 }575 ) 640 576 641 577 VISIT_END( Decl, node ); … … 648 584 VISIT_START( node ); 649 585 650 if ( __visit_children() ){586 VISIT({ 651 587 guard_symtab guard { *this }; 652 588 maybe_accept( node, &TraitDecl::params ); 653 589 maybe_accept( node, &TraitDecl::members ); 654 590 maybe_accept( node, &TraitDecl::attributes ); 655 } 591 }) 656 592 657 593 __pass::symtab::addTrait( core, 0, node ); … … 666 602 VISIT_START( node ); 667 603 668 if ( __visit_children() ){604 VISIT({ 669 605 guard_symtab guard { *this }; 670 606 maybe_accept( node, &TypeDecl::base ); 671 } 607 }) 672 608 673 609 // see A NOTE ON THE ORDER OF TRAVERSAL, above … … 676 612 __pass::symtab::addType( core, 0, node ); 677 613 678 if ( __visit_children() ) {614 VISIT( 679 615 maybe_accept( node, &TypeDecl::assertions ); 680 616 … … 683 619 maybe_accept( node, &TypeDecl::init ); 684 620 } 685 }621 ) 686 622 687 623 VISIT_END( Decl, node ); … … 694 630 VISIT_START( node ); 695 631 696 if ( __visit_children() ){632 VISIT({ 697 633 guard_symtab guard { *this }; 698 634 maybe_accept( node, &TypedefDecl::base ); 699 } 635 }) 700 636 701 637 __pass::symtab::addType( core, 0, node ); 702 638 703 if ( __visit_children() ) { 704 maybe_accept( node, &TypedefDecl::assertions ); 705 } 639 VISIT( maybe_accept( node, &TypedefDecl::assertions ); ) 706 640 707 641 VISIT_END( Decl, node ); … … 714 648 VISIT_START( node ); 715 649 716 if ( __visit_children() ) {650 VISIT( 717 651 maybe_accept( node, &AsmDecl::stmt ); 718 }652 ) 719 653 720 654 VISIT_END( AsmDecl, node ); … … 727 661 VISIT_START( node ); 728 662 729 if ( __visit_children() ) {663 VISIT( 730 664 maybe_accept( node, &DirectiveDecl::stmt ); 731 }665 ) 732 666 733 667 VISIT_END( DirectiveDecl, node ); … … 740 674 VISIT_START( node ); 741 675 742 if ( __visit_children() ) {676 VISIT( 743 677 maybe_accept( node, &StaticAssertDecl::cond ); 744 678 maybe_accept( node, &StaticAssertDecl::msg ); 745 }679 ) 746 680 747 681 VISIT_END( StaticAssertDecl, node ); … … 753 687 const ast::CompoundStmt * ast::Pass< core_t >::visit( const ast::CompoundStmt * node ) { 754 688 VISIT_START( node ); 755 756 if ( __visit_children() ) { 689 VISIT( 757 690 // Do not enter (or leave) a new scope if atFunctionTop. Remember to save the result. 758 691 auto guard1 = makeFuncGuard( [this, enterScope = !this->atFunctionTop]() { … … 771 704 guard_scope guard3 { *this }; 772 705 maybe_accept( node, &CompoundStmt::kids ); 773 } 774 706 ) 775 707 VISIT_END( CompoundStmt, node ); 776 708 } … … 782 714 VISIT_START( node ); 783 715 784 if ( __visit_children() ) {716 VISIT( 785 717 maybe_accept( node, &ExprStmt::expr ); 786 }718 ) 787 719 788 720 VISIT_END( Stmt, node ); … … 795 727 VISIT_START( node ) 796 728 797 if ( __visit_children() ) {729 VISIT( 798 730 maybe_accept( node, &AsmStmt::instruction ); 799 731 maybe_accept( node, &AsmStmt::output ); 800 732 maybe_accept( node, &AsmStmt::input ); 801 733 maybe_accept( node, &AsmStmt::clobber ); 802 }734 ) 803 735 804 736 VISIT_END( Stmt, node ); … … 820 752 VISIT_START( node ); 821 753 822 if ( __visit_children() ){754 VISIT({ 823 755 // if statements introduce a level of scope (for the initialization) 824 756 guard_symtab guard { *this }; 825 757 maybe_accept( node, &IfStmt::inits ); 826 758 maybe_accept( node, &IfStmt::cond ); 827 maybe_accept_as_compound( node, &IfStmt::then );828 maybe_accept_as_compound( node, &IfStmt::else _);829 } 759 maybe_accept_as_compound( node, &IfStmt::thenPart ); 760 maybe_accept_as_compound( node, &IfStmt::elsePart ); 761 }) 830 762 831 763 VISIT_END( Stmt, node ); … … 833 765 834 766 //-------------------------------------------------------------------------- 835 // While DoStmt836 template< typename core_t > 837 const ast::Stmt * ast::Pass< core_t >::visit( const ast::While DoStmt * node ) {838 VISIT_START( node ); 839 840 if ( __visit_children() ){767 // WhileStmt 768 template< typename core_t > 769 const ast::Stmt * ast::Pass< core_t >::visit( const ast::WhileStmt * node ) { 770 VISIT_START( node ); 771 772 VISIT({ 841 773 // while statements introduce a level of scope (for the initialization) 842 774 guard_symtab guard { *this }; 843 maybe_accept( node, &While DoStmt::inits );844 maybe_accept( node, &While DoStmt::cond );845 maybe_accept_as_compound( node, &While DoStmt::body );846 } 775 maybe_accept( node, &WhileStmt::inits ); 776 maybe_accept( node, &WhileStmt::cond ); 777 maybe_accept_as_compound( node, &WhileStmt::body ); 778 }) 847 779 848 780 VISIT_END( Stmt, node ); … … 855 787 VISIT_START( node ); 856 788 857 if ( __visit_children() ){789 VISIT({ 858 790 // for statements introduce a level of scope (for the initialization) 859 791 guard_symtab guard { *this }; … … 863 795 maybe_accept( node, &ForStmt::inc ); 864 796 maybe_accept_as_compound( node, &ForStmt::body ); 865 } 797 }) 866 798 867 799 VISIT_END( Stmt, node ); … … 874 806 VISIT_START( node ); 875 807 876 if ( __visit_children() ) {808 VISIT( 877 809 maybe_accept( node, &SwitchStmt::cond ); 878 810 maybe_accept( node, &SwitchStmt::stmts ); 879 }811 ) 880 812 881 813 VISIT_END( Stmt, node ); … … 888 820 VISIT_START( node ); 889 821 890 if ( __visit_children() ) {822 VISIT( 891 823 maybe_accept( node, &CaseStmt::cond ); 892 824 maybe_accept( node, &CaseStmt::stmts ); 893 }825 ) 894 826 895 827 VISIT_END( Stmt, node ); … … 910 842 VISIT_START( node ); 911 843 912 if ( __visit_children() ) {844 VISIT( 913 845 maybe_accept( node, &ReturnStmt::expr ); 914 }846 ) 915 847 916 848 VISIT_END( Stmt, node ); … … 923 855 VISIT_START( node ); 924 856 925 if ( __visit_children() ) {857 VISIT( 926 858 maybe_accept( node, &ThrowStmt::expr ); 927 859 maybe_accept( node, &ThrowStmt::target ); 928 }860 ) 929 861 930 862 VISIT_END( Stmt, node ); … … 937 869 VISIT_START( node ); 938 870 939 if ( __visit_children() ) {871 VISIT( 940 872 maybe_accept( node, &TryStmt::body ); 941 873 maybe_accept( node, &TryStmt::handlers ); 942 874 maybe_accept( node, &TryStmt::finally ); 943 }875 ) 944 876 945 877 VISIT_END( Stmt, node ); … … 952 884 VISIT_START( node ); 953 885 954 if ( __visit_children() ){886 VISIT({ 955 887 // catch statements introduce a level of scope (for the caught exception) 956 888 guard_symtab guard { *this }; … … 958 890 maybe_accept( node, &CatchStmt::cond ); 959 891 maybe_accept_as_compound( node, &CatchStmt::body ); 960 } 892 }) 961 893 962 894 VISIT_END( Stmt, node ); … … 969 901 VISIT_START( node ); 970 902 971 if ( __visit_children() ) {903 VISIT( 972 904 maybe_accept( node, &FinallyStmt::body ); 973 }905 ) 974 906 975 907 VISIT_END( Stmt, node ); … … 982 914 VISIT_START( node ); 983 915 984 if ( __visit_children() ) {916 VISIT( 985 917 maybe_accept( node, &SuspendStmt::then ); 986 }918 ) 987 919 988 920 VISIT_END( Stmt, node ); … … 1002 934 // } 1003 935 1004 if ( __visit_children() ){936 VISIT({ 1005 937 std::vector<WaitForStmt::Clause> new_clauses; 1006 938 new_clauses.reserve( node->clauses.size() ); … … 1010 942 const Expr * func = clause.target.func ? clause.target.func->accept(*this) : nullptr; 1011 943 if(func != clause.target.func) mutated = true; 1012 else func = nullptr;1013 944 1014 945 std::vector<ptr<Expr>> new_args; … … 1016 947 for( const auto & arg : clause.target.args ) { 1017 948 auto a = arg->accept(*this); 1018 if( a != arg ) { 1019 mutated = true; 1020 new_args.push_back( a ); 1021 } else 1022 new_args.push_back( nullptr ); 949 new_args.push_back( a ); 950 if( a != arg ) mutated = true; 1023 951 } 1024 952 1025 953 const Stmt * stmt = clause.stmt ? clause.stmt->accept(*this) : nullptr; 1026 954 if(stmt != clause.stmt) mutated = true; 1027 else stmt = nullptr;1028 955 1029 956 const Expr * cond = clause.cond ? clause.cond->accept(*this) : nullptr; 1030 957 if(cond != clause.cond) mutated = true; 1031 else cond = nullptr;1032 958 1033 959 new_clauses.push_back( WaitForStmt::Clause{ {func, std::move(new_args) }, stmt, cond } ); … … 1036 962 if(mutated) { 1037 963 auto n = __pass::mutate<core_t>(node); 1038 for(size_t i = 0; i < new_clauses.size(); i++) { 1039 if(new_clauses.at(i).target.func != nullptr) std::swap(n->clauses.at(i).target.func, new_clauses.at(i).target.func); 1040 1041 for(size_t j = 0; j < new_clauses.at(i).target.args.size(); j++) { 1042 if(new_clauses.at(i).target.args.at(j) != nullptr) std::swap(n->clauses.at(i).target.args.at(j), new_clauses.at(i).target.args.at(j)); 1043 } 1044 1045 if(new_clauses.at(i).stmt != nullptr) std::swap(n->clauses.at(i).stmt, new_clauses.at(i).stmt); 1046 if(new_clauses.at(i).cond != nullptr) std::swap(n->clauses.at(i).cond, new_clauses.at(i).cond); 1047 } 964 n->clauses = std::move( new_clauses ); 1048 965 node = n; 1049 966 } 1050 } 967 }) 1051 968 1052 969 #define maybe_accept(field) \ 1053 970 if(node->field) { \ 1054 971 auto nval = call_accept( node->field ); \ 1055 if(nval .differs) { \972 if(nval != node->field ) { \ 1056 973 auto nparent = __pass::mutate<core_t>(node); \ 1057 nparent->field = nval .value; \974 nparent->field = nval; \ 1058 975 node = nparent; \ 1059 976 } \ 1060 977 } 1061 978 1062 if ( __visit_children() ) {979 VISIT( 1063 980 maybe_accept( timeout.time ); 1064 981 maybe_accept( timeout.stmt ); … … 1066 983 maybe_accept( orElse.stmt ); 1067 984 maybe_accept( orElse.cond ); 1068 }985 ) 1069 986 1070 987 #undef maybe_accept … … 1079 996 VISIT_START( node ); 1080 997 1081 if ( __visit_children() ) {998 VISIT( 1082 999 maybe_accept( node, &WithStmt::exprs ); 1083 1000 { … … 1087 1004 maybe_accept( node, &WithStmt::stmt ); 1088 1005 } 1089 } 1090 1006 ) 1091 1007 VISIT_END( Stmt, node ); 1092 1008 } … … 1106 1022 VISIT_START( node ); 1107 1023 1108 if ( __visit_children() ) {1024 VISIT( 1109 1025 maybe_accept( node, &DeclStmt::decl ); 1110 }1026 ) 1111 1027 1112 1028 VISIT_END( Stmt, node ); … … 1121 1037 // For now this isn't visited, it is unclear if this causes problem 1122 1038 // if all tests are known to pass, remove this code 1123 if ( __visit_children() ) {1039 VISIT( 1124 1040 maybe_accept( node, &ImplicitCtorDtorStmt::callStmt ); 1125 }1041 ) 1126 1042 1127 1043 VISIT_END( Stmt, node ); … … 1134 1050 VISIT_START( node ); 1135 1051 1136 if ( __visit_children() ){1052 VISIT({ 1137 1053 // mutex statements introduce a level of scope (for the initialization) 1138 1054 guard_symtab guard { *this }; 1139 1055 maybe_accept( node, &MutexStmt::stmt ); 1140 1056 maybe_accept( node, &MutexStmt::mutexObjs ); 1141 } 1057 }) 1142 1058 1143 1059 VISIT_END( Stmt, node ); … … 1150 1066 VISIT_START( node ); 1151 1067 1152 if ( __visit_children() ) {1068 VISIT( 1153 1069 { 1154 1070 guard_symtab guard { *this }; … … 1157 1073 maybe_accept( node, &ApplicationExpr::func ); 1158 1074 maybe_accept( node, &ApplicationExpr::args ); 1159 }1075 ) 1160 1076 1161 1077 VISIT_END( Expr, node ); … … 1168 1084 VISIT_START( node ); 1169 1085 1170 if ( __visit_children() ) {1086 VISIT( 1171 1087 { 1172 1088 guard_symtab guard { *this }; … … 1175 1091 1176 1092 maybe_accept( node, &UntypedExpr::args ); 1177 }1093 ) 1178 1094 1179 1095 VISIT_END( Expr, node ); … … 1186 1102 VISIT_START( node ); 1187 1103 1188 if ( __visit_children() ){1104 VISIT({ 1189 1105 guard_symtab guard { *this }; 1190 1106 maybe_accept( node, &NameExpr::result ); 1191 } 1107 }) 1192 1108 1193 1109 VISIT_END( Expr, node ); … … 1200 1116 VISIT_START( node ); 1201 1117 1202 if ( __visit_children() ) { 1203 { 1118 VISIT({ 1204 1119 guard_symtab guard { *this }; 1205 1120 maybe_accept( node, &CastExpr::result ); 1206 1121 } 1207 1122 maybe_accept( node, &CastExpr::arg ); 1208 }1123 ) 1209 1124 1210 1125 VISIT_END( Expr, node ); … … 1217 1132 VISIT_START( node ); 1218 1133 1219 if ( __visit_children() ) { 1220 { 1134 VISIT({ 1221 1135 guard_symtab guard { *this }; 1222 1136 maybe_accept( node, &KeywordCastExpr::result ); 1223 1137 } 1224 1138 maybe_accept( node, &KeywordCastExpr::arg ); 1225 }1139 ) 1226 1140 1227 1141 VISIT_END( Expr, node ); … … 1234 1148 VISIT_START( node ); 1235 1149 1236 if ( __visit_children() ) { 1237 { 1150 VISIT({ 1238 1151 guard_symtab guard { *this }; 1239 1152 maybe_accept( node, &VirtualCastExpr::result ); 1240 1153 } 1241 1154 maybe_accept( node, &VirtualCastExpr::arg ); 1242 }1155 ) 1243 1156 1244 1157 VISIT_END( Expr, node ); … … 1251 1164 VISIT_START( node ); 1252 1165 1253 if ( __visit_children() ) { 1254 { 1166 VISIT({ 1255 1167 guard_symtab guard { *this }; 1256 1168 maybe_accept( node, &AddressExpr::result ); 1257 1169 } 1258 1170 maybe_accept( node, &AddressExpr::arg ); 1259 }1171 ) 1260 1172 1261 1173 VISIT_END( Expr, node ); … … 1268 1180 VISIT_START( node ); 1269 1181 1270 if ( __visit_children() ){1182 VISIT({ 1271 1183 guard_symtab guard { *this }; 1272 1184 maybe_accept( node, &LabelAddressExpr::result ); 1273 } 1185 }) 1274 1186 1275 1187 VISIT_END( Expr, node ); … … 1282 1194 VISIT_START( node ); 1283 1195 1284 if ( __visit_children() ) { 1285 { 1196 VISIT({ 1286 1197 guard_symtab guard { *this }; 1287 1198 maybe_accept( node, &UntypedMemberExpr::result ); … … 1289 1200 maybe_accept( node, &UntypedMemberExpr::aggregate ); 1290 1201 maybe_accept( node, &UntypedMemberExpr::member ); 1291 }1202 ) 1292 1203 1293 1204 VISIT_END( Expr, node ); … … 1300 1211 VISIT_START( node ); 1301 1212 1302 if ( __visit_children() ) { 1303 { 1213 VISIT({ 1304 1214 guard_symtab guard { *this }; 1305 1215 maybe_accept( node, &MemberExpr::result ); 1306 1216 } 1307 1217 maybe_accept( node, &MemberExpr::aggregate ); 1308 }1218 ) 1309 1219 1310 1220 VISIT_END( Expr, node ); … … 1317 1227 VISIT_START( node ); 1318 1228 1319 if ( __visit_children() ){1229 VISIT({ 1320 1230 guard_symtab guard { *this }; 1321 1231 maybe_accept( node, &VariableExpr::result ); 1322 } 1232 }) 1323 1233 1324 1234 VISIT_END( Expr, node ); … … 1331 1241 VISIT_START( node ); 1332 1242 1333 if ( __visit_children() ){1243 VISIT({ 1334 1244 guard_symtab guard { *this }; 1335 1245 maybe_accept( node, &ConstantExpr::result ); 1336 } 1246 }) 1337 1247 1338 1248 VISIT_END( Expr, node ); … … 1345 1255 VISIT_START( node ); 1346 1256 1347 if ( __visit_children() ) { 1348 { 1257 VISIT({ 1349 1258 guard_symtab guard { *this }; 1350 1259 maybe_accept( node, &SizeofExpr::result ); … … 1355 1264 maybe_accept( node, &SizeofExpr::expr ); 1356 1265 } 1357 }1266 ) 1358 1267 1359 1268 VISIT_END( Expr, node ); … … 1366 1275 VISIT_START( node ); 1367 1276 1368 if ( __visit_children() ) { 1369 { 1277 VISIT({ 1370 1278 guard_symtab guard { *this }; 1371 1279 maybe_accept( node, &AlignofExpr::result ); … … 1376 1284 maybe_accept( node, &AlignofExpr::expr ); 1377 1285 } 1378 }1286 ) 1379 1287 1380 1288 VISIT_END( Expr, node ); … … 1387 1295 VISIT_START( node ); 1388 1296 1389 if ( __visit_children() ) { 1390 { 1297 VISIT({ 1391 1298 guard_symtab guard { *this }; 1392 1299 maybe_accept( node, &UntypedOffsetofExpr::result ); 1393 1300 } 1394 1301 maybe_accept( node, &UntypedOffsetofExpr::type ); 1395 }1302 ) 1396 1303 1397 1304 VISIT_END( Expr, node ); … … 1404 1311 VISIT_START( node ); 1405 1312 1406 if ( __visit_children() ) { 1407 { 1313 VISIT({ 1408 1314 guard_symtab guard { *this }; 1409 1315 maybe_accept( node, &OffsetofExpr::result ); 1410 1316 } 1411 1317 maybe_accept( node, &OffsetofExpr::type ); 1412 }1318 ) 1413 1319 1414 1320 VISIT_END( Expr, node ); … … 1421 1327 VISIT_START( node ); 1422 1328 1423 if ( __visit_children() ) { 1424 { 1329 VISIT({ 1425 1330 guard_symtab guard { *this }; 1426 1331 maybe_accept( node, &OffsetPackExpr::result ); 1427 1332 } 1428 1333 maybe_accept( node, &OffsetPackExpr::type ); 1429 }1334 ) 1430 1335 1431 1336 VISIT_END( Expr, node ); … … 1438 1343 VISIT_START( node ); 1439 1344 1440 if ( __visit_children() ) { 1441 { 1345 VISIT({ 1442 1346 guard_symtab guard { *this }; 1443 1347 maybe_accept( node, &LogicalExpr::result ); … … 1445 1349 maybe_accept( node, &LogicalExpr::arg1 ); 1446 1350 maybe_accept( node, &LogicalExpr::arg2 ); 1447 }1351 ) 1448 1352 1449 1353 VISIT_END( Expr, node ); … … 1456 1360 VISIT_START( node ); 1457 1361 1458 if ( __visit_children() ) { 1459 { 1362 VISIT({ 1460 1363 guard_symtab guard { *this }; 1461 1364 maybe_accept( node, &ConditionalExpr::result ); … … 1464 1367 maybe_accept( node, &ConditionalExpr::arg2 ); 1465 1368 maybe_accept( node, &ConditionalExpr::arg3 ); 1466 }1369 ) 1467 1370 1468 1371 VISIT_END( Expr, node ); … … 1475 1378 VISIT_START( node ); 1476 1379 1477 if ( __visit_children() ) { 1478 { 1380 VISIT({ 1479 1381 guard_symtab guard { *this }; 1480 1382 maybe_accept( node, &CommaExpr::result ); … … 1482 1384 maybe_accept( node, &CommaExpr::arg1 ); 1483 1385 maybe_accept( node, &CommaExpr::arg2 ); 1484 }1386 ) 1485 1387 1486 1388 VISIT_END( Expr, node ); … … 1493 1395 VISIT_START( node ); 1494 1396 1495 if ( __visit_children() ) { 1496 { 1397 VISIT({ 1497 1398 guard_symtab guard { *this }; 1498 1399 maybe_accept( node, &TypeExpr::result ); 1499 1400 } 1500 1401 maybe_accept( node, &TypeExpr::type ); 1501 }1402 ) 1502 1403 1503 1404 VISIT_END( Expr, node ); … … 1510 1411 VISIT_START( node ); 1511 1412 1512 if ( __visit_children() ) { 1513 { 1413 VISIT({ 1514 1414 guard_symtab guard { *this }; 1515 1415 maybe_accept( node, &AsmExpr::result ); … … 1517 1417 maybe_accept( node, &AsmExpr::constraint ); 1518 1418 maybe_accept( node, &AsmExpr::operand ); 1519 }1419 ) 1520 1420 1521 1421 VISIT_END( Expr, node ); … … 1528 1428 VISIT_START( node ); 1529 1429 1530 if ( __visit_children() ) { 1531 { 1430 VISIT({ 1532 1431 guard_symtab guard { *this }; 1533 1432 maybe_accept( node, &ImplicitCopyCtorExpr::result ); 1534 1433 } 1535 1434 maybe_accept( node, &ImplicitCopyCtorExpr::callExpr ); 1536 }1435 ) 1537 1436 1538 1437 VISIT_END( Expr, node ); … … 1545 1444 VISIT_START( node ); 1546 1445 1547 if ( __visit_children() ) { 1548 { 1446 VISIT({ 1549 1447 guard_symtab guard { *this }; 1550 1448 maybe_accept( node, &ConstructorExpr::result ); 1551 1449 } 1552 1450 maybe_accept( node, &ConstructorExpr::callExpr ); 1553 }1451 ) 1554 1452 1555 1453 VISIT_END( Expr, node ); … … 1562 1460 VISIT_START( node ); 1563 1461 1564 if ( __visit_children() ) { 1565 { 1462 VISIT({ 1566 1463 guard_symtab guard { *this }; 1567 1464 maybe_accept( node, &CompoundLiteralExpr::result ); 1568 1465 } 1569 1466 maybe_accept( node, &CompoundLiteralExpr::init ); 1570 }1467 ) 1571 1468 1572 1469 VISIT_END( Expr, node ); … … 1579 1476 VISIT_START( node ); 1580 1477 1581 if ( __visit_children() ) { 1582 { 1478 VISIT({ 1583 1479 guard_symtab guard { *this }; 1584 1480 maybe_accept( node, &RangeExpr::result ); … … 1586 1482 maybe_accept( node, &RangeExpr::low ); 1587 1483 maybe_accept( node, &RangeExpr::high ); 1588 }1484 ) 1589 1485 1590 1486 VISIT_END( Expr, node ); … … 1597 1493 VISIT_START( node ); 1598 1494 1599 if ( __visit_children() ) { 1600 { 1495 VISIT({ 1601 1496 guard_symtab guard { *this }; 1602 1497 maybe_accept( node, &UntypedTupleExpr::result ); 1603 1498 } 1604 1499 maybe_accept( node, &UntypedTupleExpr::exprs ); 1605 }1500 ) 1606 1501 1607 1502 VISIT_END( Expr, node ); … … 1614 1509 VISIT_START( node ); 1615 1510 1616 if ( __visit_children() ) { 1617 { 1511 VISIT({ 1618 1512 guard_symtab guard { *this }; 1619 1513 maybe_accept( node, &TupleExpr::result ); 1620 1514 } 1621 1515 maybe_accept( node, &TupleExpr::exprs ); 1622 }1516 ) 1623 1517 1624 1518 VISIT_END( Expr, node ); … … 1631 1525 VISIT_START( node ); 1632 1526 1633 if ( __visit_children() ) { 1634 { 1527 VISIT({ 1635 1528 guard_symtab guard { *this }; 1636 1529 maybe_accept( node, &TupleIndexExpr::result ); 1637 1530 } 1638 1531 maybe_accept( node, &TupleIndexExpr::tuple ); 1639 }1532 ) 1640 1533 1641 1534 VISIT_END( Expr, node ); … … 1648 1541 VISIT_START( node ); 1649 1542 1650 if ( __visit_children() ) { 1651 { 1543 VISIT({ 1652 1544 guard_symtab guard { *this }; 1653 1545 maybe_accept( node, &TupleAssignExpr::result ); 1654 1546 } 1655 1547 maybe_accept( node, &TupleAssignExpr::stmtExpr ); 1656 }1548 ) 1657 1549 1658 1550 VISIT_END( Expr, node ); … … 1665 1557 VISIT_START( node ); 1666 1558 1667 if ( __visit_children() ) { 1668 // don't want statements from outer CompoundStmts to be added to this StmtExpr 1559 VISIT(// don't want statements from outer CompoundStmts to be added to this StmtExpr 1669 1560 // get the stmts that will need to be spliced in 1670 1561 auto stmts_before = __pass::stmtsToAddBefore( core, 0); … … 1683 1574 maybe_accept( node, &StmtExpr::returnDecls ); 1684 1575 maybe_accept( node, &StmtExpr::dtors ); 1685 }1576 ) 1686 1577 1687 1578 VISIT_END( Expr, node ); … … 1694 1585 VISIT_START( node ); 1695 1586 1696 if ( __visit_children() ) { 1697 { 1587 VISIT({ 1698 1588 guard_symtab guard { *this }; 1699 1589 maybe_accept( node, &UniqueExpr::result ); 1700 1590 } 1701 1591 maybe_accept( node, &UniqueExpr::expr ); 1702 }1592 ) 1703 1593 1704 1594 VISIT_END( Expr, node ); … … 1711 1601 VISIT_START( node ); 1712 1602 1713 if ( __visit_children() ) { 1714 { 1603 VISIT({ 1715 1604 guard_symtab guard { *this }; 1716 1605 maybe_accept( node, &UntypedInitExpr::result ); … … 1718 1607 maybe_accept( node, &UntypedInitExpr::expr ); 1719 1608 // not currently visiting initAlts, but this doesn't matter since this node is only used in the resolver. 1720 }1609 ) 1721 1610 1722 1611 VISIT_END( Expr, node ); … … 1729 1618 VISIT_START( node ); 1730 1619 1731 if ( __visit_children() ) { 1732 { 1620 VISIT({ 1733 1621 guard_symtab guard { *this }; 1734 1622 maybe_accept( node, &InitExpr::result ); … … 1736 1624 maybe_accept( node, &InitExpr::expr ); 1737 1625 maybe_accept( node, &InitExpr::designation ); 1738 }1626 ) 1739 1627 1740 1628 VISIT_END( Expr, node ); … … 1747 1635 VISIT_START( node ); 1748 1636 1749 if ( __visit_children() ) { 1750 { 1637 VISIT({ 1751 1638 guard_symtab guard { *this }; 1752 1639 maybe_accept( node, &DeletedExpr::result ); … … 1754 1641 maybe_accept( node, &DeletedExpr::expr ); 1755 1642 // don't visit deleteStmt, because it is a pointer to somewhere else in the tree. 1756 }1643 ) 1757 1644 1758 1645 VISIT_END( Expr, node ); … … 1765 1652 VISIT_START( node ); 1766 1653 1767 if ( __visit_children() ) { 1768 { 1654 VISIT({ 1769 1655 guard_symtab guard { *this }; 1770 1656 maybe_accept( node, &DefaultArgExpr::result ); 1771 1657 } 1772 1658 maybe_accept( node, &DefaultArgExpr::expr ); 1773 }1659 ) 1774 1660 1775 1661 VISIT_END( Expr, node ); … … 1782 1668 VISIT_START( node ); 1783 1669 1784 if ( __visit_children() ) { 1785 { 1670 VISIT({ 1786 1671 guard_symtab guard { *this }; 1787 1672 maybe_accept( node, &GenericExpr::result ); … … 1812 1697 node = n; 1813 1698 } 1814 }1699 ) 1815 1700 1816 1701 VISIT_END( Expr, node ); … … 1841 1726 VISIT_START( node ); 1842 1727 1843 if ( __visit_children() ) {1728 VISIT( 1844 1729 // xxx - should PointerType visit/mutate dimension? 1845 1730 maybe_accept( node, &PointerType::base ); 1846 }1731 ) 1847 1732 1848 1733 VISIT_END( Type, node ); … … 1855 1740 VISIT_START( node ); 1856 1741 1857 if ( __visit_children() ) {1742 VISIT( 1858 1743 maybe_accept( node, &ArrayType::dimension ); 1859 1744 maybe_accept( node, &ArrayType::base ); 1860 }1745 ) 1861 1746 1862 1747 VISIT_END( Type, node ); … … 1869 1754 VISIT_START( node ); 1870 1755 1871 if ( __visit_children() ) {1756 VISIT( 1872 1757 maybe_accept( node, &ReferenceType::base ); 1873 }1758 ) 1874 1759 1875 1760 VISIT_END( Type, node ); … … 1882 1767 VISIT_START( node ); 1883 1768 1884 if ( __visit_children() ) {1769 VISIT( 1885 1770 maybe_accept( node, &QualifiedType::parent ); 1886 1771 maybe_accept( node, &QualifiedType::child ); 1887 }1772 ) 1888 1773 1889 1774 VISIT_END( Type, node ); … … 1896 1781 VISIT_START( node ); 1897 1782 1898 if ( __visit_children() ){1783 VISIT({ 1899 1784 // guard_forall_subs forall_guard { *this, node }; 1900 1785 // mutate_forall( node ); … … 1902 1787 maybe_accept( node, &FunctionType::returns ); 1903 1788 maybe_accept( node, &FunctionType::params ); 1904 } 1789 }) 1905 1790 1906 1791 VISIT_END( Type, node ); … … 1915 1800 __pass::symtab::addStruct( core, 0, node->name ); 1916 1801 1917 if ( __visit_children() ){1802 VISIT({ 1918 1803 guard_symtab guard { *this }; 1919 1804 maybe_accept( node, &StructInstType::params ); 1920 } 1805 }) 1921 1806 1922 1807 VISIT_END( Type, node ); … … 1931 1816 __pass::symtab::addUnion( core, 0, node->name ); 1932 1817 1933 if ( __visit_children() ){1818 VISIT({ 1934 1819 guard_symtab guard { *this }; 1935 1820 maybe_accept( node, &UnionInstType::params ); 1936 } 1821 }) 1937 1822 1938 1823 VISIT_END( Type, node ); … … 1945 1830 VISIT_START( node ); 1946 1831 1947 if ( __visit_children() ){1832 VISIT({ 1948 1833 maybe_accept( node, &EnumInstType::params ); 1949 } 1834 }) 1950 1835 1951 1836 VISIT_END( Type, node ); … … 1958 1843 VISIT_START( node ); 1959 1844 1960 if ( __visit_children() ){1845 VISIT({ 1961 1846 maybe_accept( node, &TraitInstType::params ); 1962 } 1847 }) 1963 1848 1964 1849 VISIT_END( Type, node ); … … 1971 1856 VISIT_START( node ); 1972 1857 1973 if ( __visit_children() ) {1858 VISIT( 1974 1859 { 1975 1860 maybe_accept( node, &TypeInstType::params ); … … 1977 1862 // ensure that base re-bound if doing substitution 1978 1863 __pass::forall::replace( core, 0, node ); 1979 }1864 ) 1980 1865 1981 1866 VISIT_END( Type, node ); … … 1988 1873 VISIT_START( node ); 1989 1874 1990 if ( __visit_children() ) {1875 VISIT( 1991 1876 maybe_accept( node, &TupleType::types ); 1992 1877 maybe_accept( node, &TupleType::members ); 1993 }1878 ) 1994 1879 1995 1880 VISIT_END( Type, node ); … … 2002 1887 VISIT_START( node ); 2003 1888 2004 if ( __visit_children() ) {1889 VISIT( 2005 1890 maybe_accept( node, &TypeofType::expr ); 2006 }1891 ) 2007 1892 2008 1893 VISIT_END( Type, node ); … … 2015 1900 VISIT_START( node ); 2016 1901 2017 if ( __visit_children() ) {1902 VISIT( 2018 1903 maybe_accept( node, &VTableType::base ); 2019 }1904 ) 2020 1905 2021 1906 VISIT_END( Type, node ); … … 2065 1950 VISIT_START( node ); 2066 1951 2067 if ( __visit_children() ) { 2068 maybe_accept( node, &Designation::designators ); 2069 } 1952 VISIT( maybe_accept( node, &Designation::designators ); ) 2070 1953 2071 1954 VISIT_END( Designation, node ); … … 2078 1961 VISIT_START( node ); 2079 1962 2080 if ( __visit_children() ) {1963 VISIT( 2081 1964 maybe_accept( node, &SingleInit::value ); 2082 }1965 ) 2083 1966 2084 1967 VISIT_END( Init, node ); … … 2091 1974 VISIT_START( node ); 2092 1975 2093 if ( __visit_children() ) {1976 VISIT( 2094 1977 maybe_accept( node, &ListInit::designations ); 2095 1978 maybe_accept( node, &ListInit::initializers ); 2096 }1979 ) 2097 1980 2098 1981 VISIT_END( Init, node ); … … 2105 1988 VISIT_START( node ); 2106 1989 2107 if ( __visit_children() ) {1990 VISIT( 2108 1991 maybe_accept( node, &ConstructorInit::ctor ); 2109 1992 maybe_accept( node, &ConstructorInit::dtor ); 2110 1993 maybe_accept( node, &ConstructorInit::init ); 2111 }1994 ) 2112 1995 2113 1996 VISIT_END( Init, node ); … … 2120 2003 VISIT_START( node ); 2121 2004 2122 if ( __visit_children() ) {2005 VISIT( 2123 2006 maybe_accept( node, &Attribute::params ); 2124 }2007 ) 2125 2008 2126 2009 VISIT_END( Attribute, node ); … … 2133 2016 VISIT_START( node ); 2134 2017 2135 if ( __visit_children() ) {2018 VISIT( 2136 2019 { 2137 2020 bool mutated = false; … … 2149 2032 } 2150 2033 } 2151 }2034 ) 2152 2035 2153 2036 VISIT_END( TypeSubstitution, node ); … … 2155 2038 2156 2039 #undef VISIT_START 2040 #undef VISIT 2157 2041 #undef VISIT_END -
src/AST/Print.cpp
rf5a51db r97c215f 333 333 print( node->funcSpec ); 334 334 335 336 337 if ( node->type && node->isTypeFixed ) { 335 if ( node->type ) { 338 336 node->type->accept( *this ); 339 337 } else { 340 if (!node->type_params.empty()) { 341 os << "forall" << endl; 342 ++indent; 343 printAll(node->type_params); 344 os << indent; 345 --indent; 346 347 if (!node->assertions.empty()) { 348 os << "with assertions" << endl; 349 ++indent; 350 printAll(node->assertions); 351 os << indent; 352 --indent; 353 } 354 } 355 356 os << "function" << endl; 357 if ( ! node->params.empty() ) { 358 os << indent << "... with parameters" << endl; 359 ++indent; 360 printAll( node->params ); 361 if ( node->type->isVarArgs ) { 362 os << indent << "and a variable number of other arguments" << endl; 363 } 364 --indent; 365 } else if ( node->type->isVarArgs ) { 366 os << indent+1 << "accepting unspecified arguments" << endl; 367 } 368 369 os << indent << "... returning"; 370 if ( node->returns.empty() ) { 371 os << " nothing" << endl; 372 } else { 373 os << endl; 374 ++indent; 375 printAll( node->returns ); 376 --indent; 377 } 338 os << "untyped entity"; 378 339 } 379 340 … … 511 472 ++indent; 512 473 os << indent; 513 safe_print( node->then );514 --indent; 515 516 if ( node->else _!= 0 ) {474 safe_print( node->thenPart ); 475 --indent; 476 477 if ( node->elsePart != 0 ) { 517 478 os << indent << "... else:" << endl; 518 479 ++indent; 519 480 os << indent; 520 node->else _->accept( *this );481 node->elsePart->accept( *this ); 521 482 --indent; 522 483 } // if … … 524 485 } 525 486 526 virtual const ast::Stmt * visit( const ast::While DoStmt * node ) override final {487 virtual const ast::Stmt * visit( const ast::WhileStmt * node ) override final { 527 488 if ( node->isDoWhile ) { os << "Do-"; } 528 489 os << "While on condition:" << endl; -
src/AST/Stmt.cpp
rf5a51db r97c215f 9 9 // Author : Aaron B. Moss 10 10 // Created On : Wed May 8 13:00:00 2019 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Feb 2 19:01:20 202213 // Update Count : 311 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed May 15 15:53:00 2019 13 // Update Count : 2 14 14 // 15 15 … … 56 56 57 57 // --- BranchStmt 58 BranchStmt::BranchStmt( const CodeLocation& loc, Kind kind, Label target, conststd::vector<Label>&& labels )59 58 BranchStmt::BranchStmt( const CodeLocation& loc, Kind kind, Label target, std::vector<Label>&& labels ) 59 : Stmt(loc, std::move(labels)), originalTarget(target), target(target), kind(kind) { 60 60 // Make sure a syntax error hasn't slipped through. 61 61 assert( Goto != kind || !target.empty() ); -
src/AST/Stmt.hpp
rf5a51db r97c215f 9 9 // Author : Aaron B. Moss 10 10 // Created On : Wed May 8 13:00:00 2019 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Feb 2 20:06:41 202213 // Update Count : 3411 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri May 17 12:45:00 2019 13 // Update Count : 5 14 14 // 15 15 … … 17 17 18 18 #include <list> 19 #include <utility> 19 #include <utility> // for move 20 20 #include <vector> 21 21 22 22 #include "Label.hpp" 23 #include "Node.hpp" 23 #include "Node.hpp" // for node, ptr 24 24 #include "ParseNode.hpp" 25 25 #include "Visitor.hpp" … … 27 27 28 28 // Must be included in *all* AST classes; should be #undef'd at the end of the file 29 #define MUTATE_FRIEND 29 #define MUTATE_FRIEND \ 30 30 template<typename node_t> friend node_t * mutate(const node_t * node); \ 31 31 template<typename node_t> friend node_t * shallowCopy(const node_t * node); 32 32 33 33 namespace ast { 34 34 35 class Expr; 35 36 36 // Base statement node37 /// Base statement node 37 38 class Stmt : public ParseNode { 38 39 public: 39 40 std::vector<Label> labels; 40 41 41 Stmt( const CodeLocation & loc, conststd::vector<Label> && labels = {} )42 43 44 Stmt(const Stmt 42 Stmt( const CodeLocation & loc, std::vector<Label> && labels = {} ) 43 : ParseNode(loc), labels(std::move(labels)) {} 44 45 Stmt(const Stmt& o) : ParseNode(o), labels(o.labels) {} 45 46 46 47 const Stmt * accept( Visitor & v ) const override = 0; 47 48 private: 48 49 Stmt * clone() const override = 0; 49 50 MUTATE_FRIEND 50 51 }; 51 52 52 // Compound statement: { ... }53 /// Compound statement `{ ... }` 53 54 class CompoundStmt final : public Stmt { 54 55 public: 55 56 std::list<ptr<Stmt>> kids; 56 57 57 CompoundStmt(const CodeLocation & loc, const std::list<ptr<Stmt>> && ks = {}, const std::vector<Label> && labels = {} ) 58 : Stmt(loc, std::move(labels)), kids(std::move(ks)) {} 59 60 CompoundStmt( const CompoundStmt & o ); 61 CompoundStmt( CompoundStmt && o ) = default; 58 CompoundStmt(const CodeLocation & loc, std::list<ptr<Stmt>> && ks = {}, 59 std::vector<Label>&& labels = {} ) 60 : Stmt(loc, std::move(labels)), kids(std::move(ks)) {} 61 62 CompoundStmt( const CompoundStmt& o ); 63 CompoundStmt( CompoundStmt&& o ) = default; 62 64 63 65 void push_back( const Stmt * s ) { kids.emplace_back( s ); } … … 65 67 66 68 const CompoundStmt * accept( Visitor & v ) const override { return v.visit( this ); } 67 69 private: 68 70 CompoundStmt * clone() const override { return new CompoundStmt{ *this }; } 69 71 MUTATE_FRIEND 70 72 }; 71 73 72 // Empty statment: ;74 /// Empty statment `;` 73 75 class NullStmt final : public Stmt { 74 75 NullStmt( const CodeLocation & loc, conststd::vector<Label> && labels = {} )76 76 public: 77 NullStmt( const CodeLocation & loc, std::vector<Label> && labels = {} ) 78 : Stmt(loc, std::move(labels)) {} 77 79 78 80 const NullStmt * accept( Visitor & v ) const override { return v.visit( this ); } 79 81 private: 80 82 NullStmt * clone() const override { return new NullStmt{ *this }; } 81 83 MUTATE_FRIEND 82 84 }; 83 85 84 // Expression wrapped by statement86 /// Expression wrapped by statement 85 87 class ExprStmt final : public Stmt { 86 88 public: 87 89 ptr<Expr> expr; 88 90 89 ExprStmt( const CodeLocation & loc, const Expr* e, const std::vector<Label>&& labels = {} )90 91 92 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 93 91 ExprStmt( const CodeLocation& loc, const Expr* e, std::vector<Label>&& labels = {} ) 92 : Stmt(loc, std::move(labels)), expr(e) {} 93 94 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 95 private: 94 96 ExprStmt * clone() const override { return new ExprStmt{ *this }; } 95 97 MUTATE_FRIEND 96 98 }; 97 99 98 // Assembly statement: asm ... ( "..." : ... )100 /// Assembly statement `asm ... ( "..." : ... )` 99 101 class AsmStmt final : public Stmt { 100 102 public: 101 103 bool isVolatile; 102 104 ptr<Expr> instruction; … … 106 108 107 109 AsmStmt( const CodeLocation & loc, bool isVolatile, const Expr * instruction, 108 const std::vector<ptr<Expr>> && output, conststd::vector<ptr<Expr>> && input,109 const std::vector<ptr<ConstantExpr>> && clobber, conststd::vector<Label> && gotoLabels,110 conststd::vector<Label> && labels = {})111 112 113 114 115 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 116 110 std::vector<ptr<Expr>> && output, std::vector<ptr<Expr>> && input, 111 std::vector<ptr<ConstantExpr>> && clobber, std::vector<Label> && gotoLabels, 112 std::vector<Label> && labels = {}) 113 : Stmt(loc, std::move(labels)), isVolatile(isVolatile), instruction(instruction), 114 output(std::move(output)), input(std::move(input)), clobber(std::move(clobber)), 115 gotoLabels(std::move(gotoLabels)) {} 116 117 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 118 private: 117 119 AsmStmt * clone() const override { return new AsmStmt{ *this }; } 118 120 MUTATE_FRIEND 119 121 }; 120 122 121 // C-preprocessor directive: #...123 /// C-preprocessor directive `#...` 122 124 class DirectiveStmt final : public Stmt { 123 125 public: 124 126 std::string directive; 125 127 126 128 DirectiveStmt( const CodeLocation & loc, const std::string & directive, 127 128 129 130 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 131 129 std::vector<Label> && labels = {} ) 130 : Stmt(loc, std::move(labels)), directive(directive) {} 131 132 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 133 private: 132 134 DirectiveStmt * clone() const override { return new DirectiveStmt{ *this }; } 133 135 MUTATE_FRIEND 134 136 }; 135 137 136 // If statement: if (...) ... else ...138 /// If conditional statement `if (...) ... else ...` 137 139 class IfStmt final : public Stmt { 138 139 ptr<Expr> cond; 140 ptr<Stmt> then ;141 ptr<Stmt> else _;140 public: 141 ptr<Expr> cond; 142 ptr<Stmt> thenPart; 143 ptr<Stmt> elsePart; 142 144 std::vector<ptr<Stmt>> inits; 143 145 144 IfStmt( const CodeLocation & loc, const Expr * cond, const Stmt * then ,145 const Stmt * else_ = nullptr, conststd::vector<ptr<Stmt>> && inits = {},146 conststd::vector<Label> && labels = {} )147 : Stmt(loc, std::move(labels)), cond(cond), then(then), else_(else_),148 149 150 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 151 146 IfStmt( const CodeLocation & loc, const Expr * cond, const Stmt * thenPart, 147 const Stmt * elsePart = nullptr, std::vector<ptr<Stmt>> && inits = {}, 148 std::vector<Label> && labels = {} ) 149 : Stmt(loc, std::move(labels)), cond(cond), thenPart(thenPart), elsePart(elsePart), 150 inits(std::move(inits)) {} 151 152 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 153 private: 152 154 IfStmt * clone() const override { return new IfStmt{ *this }; } 153 155 MUTATE_FRIEND 154 156 }; 155 157 156 // Switch or choose statement: switch (...) { ... }158 /// Switch or choose conditional statement `switch (...) { ... }` 157 159 class SwitchStmt final : public Stmt { 158 160 public: 159 161 ptr<Expr> cond; 160 162 std::vector<ptr<Stmt>> stmts; 161 163 162 SwitchStmt( const CodeLocation & loc, const Expr * cond, conststd::vector<ptr<Stmt>> && stmts,163 conststd::vector<Label> && labels = {} )164 165 166 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 167 164 SwitchStmt( const CodeLocation & loc, const Expr * cond, std::vector<ptr<Stmt>> && stmts, 165 std::vector<Label> && labels = {} ) 166 : Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {} 167 168 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 169 private: 168 170 SwitchStmt * clone() const override { return new SwitchStmt{ *this }; } 169 171 MUTATE_FRIEND 170 172 }; 171 173 172 // Case label: case ...: or default:174 /// Case label `case ...:` `default:` 173 175 class CaseStmt final : public Stmt { 174 175 // Null for the default label.176 public: 177 /// Null for the default label. 176 178 ptr<Expr> cond; 177 179 std::vector<ptr<Stmt>> stmts; 178 180 179 CaseStmt( const CodeLocation & loc, const Expr * cond, conststd::vector<ptr<Stmt>> && stmts,180 conststd::vector<Label> && labels = {} )181 181 CaseStmt( const CodeLocation & loc, const Expr * cond, std::vector<ptr<Stmt>> && stmts, 182 std::vector<Label> && labels = {} ) 183 : Stmt(loc, std::move(labels)), cond(cond), stmts(std::move(stmts)) {} 182 184 183 185 bool isDefault() const { return !cond; } 184 186 185 187 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 186 188 private: 187 189 CaseStmt * clone() const override { return new CaseStmt{ *this }; } 188 190 MUTATE_FRIEND 189 191 }; 190 192 191 // While loop: while (...) ... else ... or do ... while (...) else ...;192 class While DoStmt final : public Stmt {193 193 /// While loop `while (...) ...` `do ... while (...); 194 class WhileStmt final : public Stmt { 195 public: 194 196 ptr<Expr> cond; 195 197 ptr<Stmt> body; 196 ptr<Stmt> else_;197 198 std::vector<ptr<Stmt>> inits; 198 199 bool isDoWhile; 199 200 200 WhileDoStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body, 201 const std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, const std::vector<Label> && labels = {} ) 202 : Stmt(loc, std::move(labels)), cond(cond), body(body), else_(nullptr), inits(std::move(inits)), isDoWhile(isDoWhile) {} 203 204 WhileDoStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body, const Stmt * else_, 205 const std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, const std::vector<Label> && labels = {} ) 206 : Stmt(loc, std::move(labels)), cond(cond), body(body), else_(else_), inits(std::move(inits)), isDoWhile(isDoWhile) {} 207 208 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 209 private: 210 WhileDoStmt * clone() const override { return new WhileDoStmt{ *this }; } 211 MUTATE_FRIEND 212 }; 213 214 // For loop: for (... ; ... ; ...) ... else ... 201 WhileStmt( const CodeLocation & loc, const Expr * cond, const Stmt * body, 202 std::vector<ptr<Stmt>> && inits, bool isDoWhile = false, std::vector<Label> && labels = {} ) 203 : Stmt(loc, std::move(labels)), cond(cond), body(body), inits(std::move(inits)), 204 isDoWhile(isDoWhile) {} 205 206 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 207 private: 208 WhileStmt * clone() const override { return new WhileStmt{ *this }; } 209 MUTATE_FRIEND 210 }; 211 212 /// For loop `for (... ; ... ; ...) ...` 215 213 class ForStmt final : public Stmt { 216 214 public: 217 215 std::vector<ptr<Stmt>> inits; 218 216 ptr<Expr> cond; 219 217 ptr<Expr> inc; 220 218 ptr<Stmt> body; 221 ptr<Stmt> else_; 222 223 ForStmt( const CodeLocation & loc, const std::vector<ptr<Stmt>> && inits, const Expr * cond, 224 const Expr * inc, const Stmt * body, const std::vector<Label> && label = {} ) 225 : Stmt(loc, std::move(label)), inits(std::move(inits)), cond(cond), inc(inc), body(body), else_(nullptr) {} 226 227 ForStmt( const CodeLocation & loc, const std::vector<ptr<Stmt>> && inits, const Expr * cond, 228 const Expr * inc, const Stmt * body, const Stmt * else_, const std::vector<Label> && labels = {} ) 229 : Stmt(loc, std::move(labels)), inits(std::move(inits)), cond(cond), inc(inc), body(body), else_(else_) {} 230 231 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 232 private: 219 220 ForStmt( const CodeLocation & loc, std::vector<ptr<Stmt>> && inits, const Expr * cond, 221 const Expr * inc, const Stmt * body, std::vector<Label> && labels = {} ) 222 : Stmt(loc, std::move(labels)), inits(std::move(inits)), cond(cond), inc(inc), 223 body(body) {} 224 225 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 226 private: 233 227 ForStmt * clone() const override { return new ForStmt{ *this }; } 234 228 MUTATE_FRIEND 235 229 }; 236 230 237 // Branch control flow statement: goto ... or break or continue or fallthru231 /// Branch control flow statement `goto ...` `break` `continue` `fallthru` 238 232 class BranchStmt final : public Stmt { 239 233 public: 240 234 enum Kind { Goto, Break, Continue, FallThrough, FallThroughDefault }; 241 235 static constexpr size_t kindEnd = 1 + (size_t)FallThroughDefault; … … 246 240 Kind kind; 247 241 248 BranchStmt( const CodeLocation & loc, Kind kind, Label target, const std::vector<Label> && labels = {} ); 249 BranchStmt( const CodeLocation & loc, const Expr * computedTarget, const std::vector<Label> && labels = {} ) 250 : Stmt(loc, std::move(labels)), originalTarget(loc), target(loc), computedTarget(computedTarget), kind(Goto) {} 242 BranchStmt( const CodeLocation & loc, Kind kind, Label target, 243 std::vector<Label> && labels = {} ); 244 BranchStmt( const CodeLocation & loc, const Expr * computedTarget, 245 std::vector<Label> && labels = {} ) 246 : Stmt(loc, std::move(labels)), originalTarget(loc), target(loc), 247 computedTarget(computedTarget), kind(Goto) {} 251 248 252 249 const char * kindName() const { return kindNames[kind]; } 253 250 254 251 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 255 252 private: 256 253 BranchStmt * clone() const override { return new BranchStmt{ *this }; } 257 254 MUTATE_FRIEND … … 260 257 }; 261 258 262 // Return statement: return ...259 /// Return statement `return ...` 263 260 class ReturnStmt final : public Stmt { 264 261 public: 265 262 ptr<Expr> expr; 266 263 267 ReturnStmt( const CodeLocation & loc, const Expr * expr, conststd::vector<Label> && labels = {} )268 269 270 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 271 264 ReturnStmt( const CodeLocation & loc, const Expr * expr, std::vector<Label> && labels = {} ) 265 : Stmt(loc, std::move(labels)), expr(expr) {} 266 267 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 268 private: 272 269 ReturnStmt * clone() const override { return new ReturnStmt{ *this }; } 273 270 MUTATE_FRIEND 274 271 }; 275 272 276 // Kind of exception273 /// Kind of exception 277 274 enum ExceptionKind { Terminate, Resume }; 278 275 279 // Throw statement: throw ...276 /// Throw statement `throw ...` 280 277 class ThrowStmt final : public Stmt { 281 278 public: 282 279 ptr<Expr> expr; 283 280 ptr<Expr> target; 284 281 ExceptionKind kind; 285 282 286 ThrowStmt( const CodeLocation & loc, ExceptionKind kind, const Expr * expr, 287 const Expr * target, const std::vector<Label> && labels = {} ) 288 : Stmt(loc, std::move(labels)), expr(expr), target(target), kind(kind) {} 289 290 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 291 private: 283 ThrowStmt( 284 const CodeLocation & loc, ExceptionKind kind, const Expr * expr, const Expr * target, 285 std::vector<Label> && labels = {} ) 286 : Stmt(loc, std::move(labels)), expr(expr), target(target), kind(kind) {} 287 288 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 289 private: 292 290 ThrowStmt * clone() const override { return new ThrowStmt{ *this }; } 293 291 MUTATE_FRIEND 294 292 }; 295 293 296 // Try statement: try { ... } ...294 /// Try statement `try { ... } ...` 297 295 class TryStmt final : public Stmt { 298 296 public: 299 297 ptr<CompoundStmt> body; 300 298 std::vector<ptr<CatchStmt>> handlers; 301 299 ptr<FinallyStmt> finally; 302 300 303 TryStmt( const CodeLocation & loc, const CompoundStmt * body, 304 const std::vector<ptr<CatchStmt>> && handlers, const FinallyStmt * finally, 305 const std::vector<Label> && labels = {} ) 306 : Stmt(loc, std::move(labels)), body(body), handlers(std::move(handlers)), finally(finally) {} 307 308 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 309 private: 301 TryStmt( 302 const CodeLocation & loc, const CompoundStmt * body, 303 std::vector<ptr<CatchStmt>> && handlers, const FinallyStmt * finally, 304 std::vector<Label> && labels = {} ) 305 : Stmt(loc, std::move(labels)), body(body), handlers(std::move(handlers)), finally(finally) {} 306 307 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 308 private: 310 309 TryStmt * clone() const override { return new TryStmt{ *this }; } 311 310 MUTATE_FRIEND 312 311 }; 313 312 314 // Catch clause of try statement313 /// Catch clause of try statement 315 314 class CatchStmt final : public Stmt { 316 315 public: 317 316 ptr<Decl> decl; 318 317 ptr<Expr> cond; … … 320 319 ExceptionKind kind; 321 320 322 CatchStmt( const CodeLocation & loc, ExceptionKind kind, const Decl * decl, const Expr * cond, 323 const Stmt * body, const std::vector<Label> && labels = {} ) 324 : Stmt(loc, std::move(labels)), decl(decl), cond(cond), body(body), kind(kind) {} 325 326 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 327 private: 321 CatchStmt( 322 const CodeLocation & loc, ExceptionKind kind, const Decl * decl, const Expr * cond, 323 const Stmt * body, std::vector<Label> && labels = {} ) 324 : Stmt(loc, std::move(labels)), decl(decl), cond(cond), body(body), kind(kind) {} 325 326 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 327 private: 328 328 CatchStmt * clone() const override { return new CatchStmt{ *this }; } 329 329 MUTATE_FRIEND 330 330 }; 331 331 332 // Finally clause of try statement332 /// Finally clause of try statement 333 333 class FinallyStmt final : public Stmt { 334 334 public: 335 335 ptr<CompoundStmt> body; 336 336 337 337 FinallyStmt( const CodeLocation & loc, const CompoundStmt * body, 338 339 340 341 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 342 338 std::vector<Label> && labels = {} ) 339 : Stmt(loc, std::move(labels)), body(body) {} 340 341 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 342 private: 343 343 FinallyStmt * clone() const override { return new FinallyStmt{ *this }; } 344 344 MUTATE_FRIEND 345 345 }; 346 346 347 // Suspend statement347 /// Suspend statement 348 348 class SuspendStmt final : public Stmt { 349 349 public: 350 350 ptr<CompoundStmt> then; 351 351 enum Type { None, Coroutine, Generator } type = None; 352 352 353 SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Type type, conststd::vector<Label> && labels = {} )354 355 356 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 357 353 SuspendStmt( const CodeLocation & loc, const CompoundStmt * then, Type type, std::vector<Label> && labels = {} ) 354 : Stmt(loc, std::move(labels)), then(then), type(type) {} 355 356 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 357 private: 358 358 SuspendStmt * clone() const override { return new SuspendStmt{ *this }; } 359 359 MUTATE_FRIEND 360 360 }; 361 361 362 // Waitfor statement: when (...) waitfor (... , ...) ... timeout(...) ... else ...362 /// Wait for concurrency statement `when (...) waitfor (... , ...) ... timeout(...) ... else ...` 363 363 class WaitForStmt final : public Stmt { 364 364 public: 365 365 struct Target { 366 366 ptr<Expr> func; … … 389 389 OrElse orElse; 390 390 391 WaitForStmt( const CodeLocation & loc, conststd::vector<Label> && labels = {} )392 393 394 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 395 391 WaitForStmt( const CodeLocation & loc, std::vector<Label> && labels = {} ) 392 : Stmt(loc, std::move(labels)) {} 393 394 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 395 private: 396 396 WaitForStmt * clone() const override { return new WaitForStmt{ *this }; } 397 397 MUTATE_FRIEND 398 398 }; 399 399 400 // Any declaration in a (compound) statement.400 /// Any declaration in a (compound) statement. 401 401 class DeclStmt final : public Stmt { 402 402 public: 403 403 ptr<Decl> decl; 404 404 405 DeclStmt( const CodeLocation & loc, const Decl * decl, conststd::vector<Label> && labels = {} )406 407 408 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 409 405 DeclStmt( const CodeLocation & loc, const Decl * decl, std::vector<Label> && labels = {} ) 406 : Stmt(loc, std::move(labels)), decl(decl) {} 407 408 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 409 private: 410 410 DeclStmt * clone() const override { return new DeclStmt{ *this }; } 411 411 MUTATE_FRIEND 412 412 }; 413 413 414 // Represents an implicit application of a constructor or destructor.414 /// Represents an implicit application of a constructor or destructor. 415 415 class ImplicitCtorDtorStmt final : public Stmt { 416 416 public: 417 417 ptr<Stmt> callStmt; 418 418 419 419 ImplicitCtorDtorStmt( const CodeLocation & loc, const Stmt * callStmt, 420 421 422 423 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 424 420 std::vector<Label> && labels = {} ) 421 : Stmt(loc, std::move(labels)), callStmt(callStmt) {} 422 423 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 424 private: 425 425 ImplicitCtorDtorStmt * clone() const override { return new ImplicitCtorDtorStmt{ *this }; } 426 426 MUTATE_FRIEND 427 427 }; 428 428 429 // Mutex Statement429 /// Mutex Statement 430 430 class MutexStmt final : public Stmt { 431 431 public: 432 432 ptr<Stmt> stmt; 433 433 std::vector<ptr<Expr>> mutexObjs; 434 434 435 435 MutexStmt( const CodeLocation & loc, const Stmt * stmt, 436 const std::vector<ptr<Expr>> && mutexes, conststd::vector<Label> && labels = {} )437 438 439 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 440 436 std::vector<ptr<Expr>> && mutexes, std::vector<Label> && labels = {} ) 437 : Stmt(loc, std::move(labels)), stmt(stmt), mutexObjs(std::move(mutexes)) {} 438 439 const Stmt * accept( Visitor & v ) const override { return v.visit( this ); } 440 private: 441 441 MutexStmt * clone() const override { return new MutexStmt{ *this }; } 442 442 MUTATE_FRIEND 443 443 }; 444 } // namespace ast 444 445 } 445 446 446 447 #undef MUTATE_FRIEND 447 448 448 449 // Local Variables: // 450 // tab-width: 4 // 449 451 // mode: c++ // 452 // compile-command: "make install" // 450 453 // End: // -
src/AST/Visitor.hpp
rf5a51db r97c215f 10 10 // Created On : Thr May 9 15:28:00 2019 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:09:34 202213 // Update Count : 212 // Last Modified On : Fri Mar 12 18:25:07 2021 13 // Update Count : 1 14 14 // 15 15 … … 38 38 virtual const ast::Stmt * visit( const ast::DirectiveStmt * ) = 0; 39 39 virtual const ast::Stmt * visit( const ast::IfStmt * ) = 0; 40 virtual const ast::Stmt * visit( const ast::While DoStmt* ) = 0;40 virtual const ast::Stmt * visit( const ast::WhileStmt * ) = 0; 41 41 virtual const ast::Stmt * visit( const ast::ForStmt * ) = 0; 42 42 virtual const ast::Stmt * visit( const ast::SwitchStmt * ) = 0; -
src/CodeGen/CodeGenerator.cc
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 2 20:30:30 202213 // Update Count : 5 4112 // Last Modified On : Fri Mar 12 19:00:42 2021 13 // Update Count : 536 14 14 // 15 15 #include "CodeGenerator.h" … … 42 42 bool wantSpacing( Statement * stmt) { 43 43 return dynamic_cast< IfStmt * >( stmt ) || dynamic_cast< CompoundStmt * >( stmt ) || 44 dynamic_cast< While DoStmt * >( stmt ) || dynamic_cast< ForStmt * >( stmt ) || dynamic_cast< SwitchStmt *>( stmt );44 dynamic_cast< WhileStmt * >( stmt ) || dynamic_cast< ForStmt * >( stmt ) || dynamic_cast< SwitchStmt *>( stmt ); 45 45 } 46 46 … … 955 955 output << " ) "; 956 956 957 ifStmt->get_then ()->accept( *visitor );958 959 if ( ifStmt->get_else () != 0) {957 ifStmt->get_thenPart()->accept( *visitor ); 958 959 if ( ifStmt->get_elsePart() != 0) { 960 960 output << " else "; 961 ifStmt->get_else ()->accept( *visitor );961 ifStmt->get_elsePart()->accept( *visitor ); 962 962 } // if 963 963 } … … 1020 1020 output << "fallthru"; 1021 1021 break; 1022 default: ; // prevent warning1023 1022 } // switch 1024 1023 // print branch target for labelled break/continue/fallthru in debug mode … … 1126 1125 } 1127 1126 1128 void CodeGenerator::postvisit( While DoStmt * whileDoStmt ) {1129 if ( while DoStmt->get_isDoWhile() ) {1127 void CodeGenerator::postvisit( WhileStmt * whileStmt ) { 1128 if ( whileStmt->get_isDoWhile() ) { 1130 1129 output << "do"; 1131 1130 } else { 1132 1131 output << "while ("; 1133 while DoStmt->get_condition()->accept( *visitor );1132 whileStmt->get_condition()->accept( *visitor ); 1134 1133 output << ")"; 1135 1134 } // if 1136 1135 output << " "; 1137 1136 1138 output << CodeGenerator::printLabels( while DoStmt->get_body()->get_labels() );1139 while DoStmt->get_body()->accept( *visitor );1137 output << CodeGenerator::printLabels( whileStmt->get_body()->get_labels() ); 1138 whileStmt->get_body()->accept( *visitor ); 1140 1139 1141 1140 output << indent; 1142 1141 1143 if ( while DoStmt->get_isDoWhile() ) {1142 if ( whileStmt->get_isDoWhile() ) { 1144 1143 output << " while ("; 1145 while DoStmt->get_condition()->accept( *visitor );1144 whileStmt->get_condition()->accept( *visitor ); 1146 1145 output << ");"; 1147 1146 } // if -
src/CodeGen/CodeGenerator.h
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:23:21 202213 // Update Count : 6 412 // Last Modified On : Fri Mar 12 18:35:38 2021 13 // Update Count : 63 14 14 // 15 15 … … 116 116 void postvisit( WaitForStmt * ); 117 117 void postvisit( WithStmt * ); 118 void postvisit( While DoStmt * );118 void postvisit( WhileStmt * ); 119 119 void postvisit( ForStmt * ); 120 120 void postvisit( NullStmt * ); -
src/Common/CodeLocationTools.cpp
rf5a51db r97c215f 10 10 // Created On : Fri Dec 4 15:42:00 2020 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:14:39 202213 // Update Count : 312 // Last Modified On : Fri Mar 12 18:35:37 2021 13 // Update Count : 2 14 14 // 15 15 … … 109 109 macro(DirectiveStmt, Stmt) \ 110 110 macro(IfStmt, Stmt) \ 111 macro(While DoStmt, Stmt) \111 macro(WhileStmt, Stmt) \ 112 112 macro(ForStmt, Stmt) \ 113 113 macro(SwitchStmt, Stmt) \ -
src/Common/PassVisitor.h
rf5a51db r97c215f 92 92 virtual void visit( IfStmt * ifStmt ) override final; 93 93 virtual void visit( const IfStmt * ifStmt ) override final; 94 virtual void visit( While DoStmt * whileDoStmt ) override final;95 virtual void visit( const While DoStmt * whileDoStmt ) override final;94 virtual void visit( WhileStmt * whileStmt ) override final; 95 virtual void visit( const WhileStmt * whileStmt ) override final; 96 96 virtual void visit( ForStmt * forStmt ) override final; 97 97 virtual void visit( const ForStmt * forStmt ) override final; … … 277 277 virtual Statement * mutate( DirectiveStmt * dirStmt ) override final; 278 278 virtual Statement * mutate( IfStmt * ifStmt ) override final; 279 virtual Statement * mutate( While DoStmt * whileDoStmt ) override final;279 virtual Statement * mutate( WhileStmt * whileStmt ) override final; 280 280 virtual Statement * mutate( ForStmt * forStmt ) override final; 281 281 virtual Statement * mutate( SwitchStmt * switchStmt ) override final; -
src/Common/PassVisitor.impl.h
rf5a51db r97c215f 1189 1189 maybeAccept_impl( node->initialization, *this ); 1190 1190 visitExpression ( node->condition ); 1191 node->then = visitStatement( node->then);1192 node->else _ = visitStatement( node->else_);1191 node->thenPart = visitStatement( node->thenPart ); 1192 node->elsePart = visitStatement( node->elsePart ); 1193 1193 } 1194 1194 VISIT_END( node ); … … 1203 1203 maybeAccept_impl( node->initialization, *this ); 1204 1204 visitExpression ( node->condition ); 1205 visitStatement ( node->then );1206 visitStatement ( node->else _);1205 visitStatement ( node->thenPart ); 1206 visitStatement ( node->elsePart ); 1207 1207 } 1208 1208 VISIT_END( node ); … … 1217 1217 maybeMutate_impl( node->initialization, *this ); 1218 1218 node->condition = mutateExpression( node->condition ); 1219 node->then = mutateStatement ( node->then);1220 node->else _ = mutateStatement ( node->else_);1219 node->thenPart = mutateStatement ( node->thenPart ); 1220 node->elsePart = mutateStatement ( node->elsePart ); 1221 1221 } 1222 1222 MUTATE_END( Statement, node ); … … 1224 1224 1225 1225 //-------------------------------------------------------------------------- 1226 // While DoStmt1227 template< typename pass_type > 1228 void PassVisitor< pass_type >::visit( While DoStmt * node ) {1226 // WhileStmt 1227 template< typename pass_type > 1228 void PassVisitor< pass_type >::visit( WhileStmt * node ) { 1229 1229 VISIT_START( node ); 1230 1230 … … 1241 1241 1242 1242 template< typename pass_type > 1243 void PassVisitor< pass_type >::visit( const While DoStmt * node ) {1243 void PassVisitor< pass_type >::visit( const WhileStmt * node ) { 1244 1244 VISIT_START( node ); 1245 1245 … … 1256 1256 1257 1257 template< typename pass_type > 1258 Statement * PassVisitor< pass_type >::mutate( While DoStmt * node ) {1258 Statement * PassVisitor< pass_type >::mutate( WhileStmt * node ) { 1259 1259 MUTATE_START( node ); 1260 1260 -
src/Common/utility.h
rf5a51db r97c215f 371 371 } 372 372 373 template< typename T >374 struct enumerate_t {375 template<typename val_t>376 struct value_t {377 val_t & val;378 size_t idx;379 };380 381 template< typename iter_t, typename val_t >382 struct iterator_t {383 iter_t it;384 size_t idx;385 386 iterator_t( iter_t _it, size_t _idx ) : it(_it), idx(_idx) {}387 388 value_t<val_t> operator*() const { return value_t<val_t>{ *it, idx }; }389 390 bool operator==(const iterator_t & o) const { return o.it == it; }391 bool operator!=(const iterator_t & o) const { return o.it != it; }392 393 iterator_t & operator++() {394 it++;395 idx++;396 return *this;397 }398 399 using difference_type = typename std::iterator_traits< iter_t >::difference_type;400 using value_type = value_t<val_t>;401 using pointer = value_t<val_t> *;402 using reference = value_t<val_t> &;403 using iterator_category = std::forward_iterator_tag;404 };405 406 T & ref;407 408 using iterator = iterator_t< typename T::iterator, typename T::value_type >;409 using const_iterator = iterator_t< typename T::const_iterator, const typename T::value_type >;410 411 iterator begin() { return iterator( ref.begin(), 0 ); }412 iterator end() { return iterator( ref.end(), ref.size() ); }413 414 const_iterator begin() const { return const_iterator( ref.cbegin(), 0 ); }415 const_iterator end() const { return const_iterator( ref.cend(), ref.size() ); }416 417 const_iterator cbegin() const { return const_iterator( ref.cbegin(), 0 ); }418 const_iterator cend() const { return const_iterator( ref.cend(), ref.size() ); }419 };420 421 template< typename T >422 enumerate_t<T> enumerate( T & ref ) {423 return enumerate_t< T >{ ref };424 }425 426 template< typename T >427 const enumerate_t< const T > enumerate( const T & ref ) {428 return enumerate_t< const T >{ ref };429 }430 431 373 template< typename OutType, typename Range, typename Functor > 432 374 OutType map_range( const Range& range, Functor&& functor ) { -
src/ControlStruct/ExceptTranslate.h
rf5a51db r97c215f 31 31 32 32 void translateTries( std::list< Declaration *> & translationUnit ); 33 void translateTries( ast::TranslationUnit & transUnit );34 33 /* Replaces all try blocks (and their many clauses) with function definitions and calls. 35 34 * This uses the exception built-ins to produce typed output and should take place after -
src/ControlStruct/ExceptTranslateNew.cpp
rf5a51db r97c215f 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Nov 8 11:53:00 2021 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Jan 31 18:49:58 202213 // Update Count : 111 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 16:50:00 2021 13 // Update Count : 0 14 14 // 15 15 … … 20 20 #include "AST/Stmt.hpp" 21 21 #include "AST/TranslationUnit.hpp" 22 #include "AST/DeclReplacer.hpp"23 22 24 23 namespace ControlStruct { 25 24 26 25 namespace { 27 28 typedef std::list<ast::CatchStmt*> CatchList;29 30 void split( CatchList& allHandlers, CatchList& terHandlers,31 CatchList& resHandlers ) {32 while ( !allHandlers.empty() ) {33 ast::CatchStmt * stmt = allHandlers.front();34 allHandlers.pop_front();35 if (stmt->kind == ast::ExceptionKind::Terminate) {36 terHandlers.push_back(stmt);37 } else {38 resHandlers.push_back(stmt);39 }40 }41 }42 43 void appendDeclStmt( ast::CompoundStmt * block, ast::DeclWithType * item ) {44 block->push_back(new ast::DeclStmt(block->location, item));45 }46 26 47 27 class TranslateThrowsCore : public ast::WithGuards { … … 148 128 } 149 129 150 151 class TryMutatorCore {152 // The built in types used in translation.153 const ast::StructDecl * except_decl;154 const ast::StructDecl * node_decl;155 const ast::StructDecl * hook_decl;156 157 // The many helper functions for code/syntree generation.158 ast::CompoundStmt * take_try_block( ast::TryStmt * tryStmt );159 ast::FunctionDecl * create_try_wrapper( const ast::CompoundStmt * body );160 ast::FunctionDecl * create_terminate_catch( CatchList &handlers );161 ast::CompoundStmt * create_single_matcher(162 const ast::DeclWithType * except_obj, ast::CatchStmt * modded_handler );163 ast::FunctionDecl * create_terminate_match( CatchList &handlers );164 ast::CompoundStmt * create_terminate_caller( CodeLocation loc, ast::FunctionDecl * try_wrapper,165 ast::FunctionDecl * terminate_catch, ast::FunctionDecl * terminate_match );166 ast::FunctionDecl * create_resume_handler( CatchList &handlers );167 ast::CompoundStmt * create_resume_wrapper(168 const ast::Stmt * wraps, const ast::FunctionDecl * resume_handler );169 ast::FunctionDecl * create_finally_wrapper( ast::TryStmt * tryStmt );170 ast::ObjectDecl * create_finally_hook( ast::FunctionDecl * finally_wrapper );171 ast::Stmt * create_resume_rethrow( const ast::ThrowStmt * throwStmt );172 173 // Types used in translation, make sure to use clone.174 // void (*function)();175 ast::FunctionDecl * try_func_t;176 // void (*function)(int, exception);177 ast::FunctionDecl * catch_func_t;178 // int (*function)(exception);179 ast::FunctionDecl * match_func_t;180 // bool (*function)(exception);181 ast::FunctionDecl * handle_func_t;182 // void (*function)(__attribute__((unused)) void *);183 ast::FunctionDecl * finally_func_t;184 185 ast::StructInstType * create_except_type() {186 assert( except_decl );187 return new ast::StructInstType( except_decl );188 }189 void init_func_types();190 191 public:192 TryMutatorCore() :193 except_decl( nullptr ), node_decl( nullptr ), hook_decl( nullptr )194 {}195 196 void previsit( const ast::StructDecl *structDecl );197 ast::Stmt * postvisit( const ast::TryStmt *tryStmt );198 ast::Stmt * postvisit( const ast::ThrowStmt *throwStmt );199 };200 201 void TryMutatorCore::init_func_types() {202 assert( except_decl );203 204 ast::ObjectDecl index_obj(205 {},206 "__handler_index",207 new ast::BasicType(ast::BasicType::SignedInt)208 );209 ast::ObjectDecl exception_obj(210 {},211 "__exception_inst",212 new ast::PointerType(213 new ast::StructInstType( except_decl )214 ),215 NULL216 );217 ast::ObjectDecl bool_obj(218 {},219 "__ret_bool",220 new ast::BasicType( ast::BasicType::Bool ),221 nullptr, //init222 ast::Storage::Classes{},223 ast::Linkage::Cforall,224 nullptr, //width225 std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }226 );227 ast::ObjectDecl voidptr_obj(228 {},229 "__hook",230 new ast::PointerType(231 new ast::VoidType()232 ),233 nullptr, //init234 ast::Storage::Classes{},235 ast::Linkage::Cforall,236 nullptr, //width237 std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }238 );239 240 ast::ObjectDecl unused_index_obj(241 {},242 "__handler_index",243 new ast::BasicType(ast::BasicType::SignedInt),244 nullptr,245 ast::Storage::Classes{},246 ast::Linkage::Cforall,247 nullptr, //width248 std::vector<ast::ptr<ast::Attribute>>{ new ast::Attribute( "unused" ) }249 );250 //unused_index_obj->attributes.push_back( new Attribute( "unused" ) );251 252 try_func_t = new ast::FunctionDecl(253 {},254 "try",255 {}, //forall256 {}, //no param257 {}, //no return258 nullptr,259 ast::Storage::Classes{},260 ast::Linkage::Cforall261 );262 263 catch_func_t = new ast::FunctionDecl(264 {},265 "catch",266 {}, //forall267 {ast::deepCopy(&index_obj), ast::deepCopy(&exception_obj)},//param268 {}, //return void269 nullptr,270 ast::Storage::Classes{},271 ast::Linkage::Cforall272 );273 274 match_func_t = new ast::FunctionDecl(275 {},276 "match",277 {}, //forall278 {ast::deepCopy(&exception_obj)},279 {ast::deepCopy(&unused_index_obj)},280 nullptr,281 ast::Storage::Classes{},282 ast::Linkage::Cforall283 );284 285 handle_func_t = new ast::FunctionDecl(286 {},287 "handle",288 {}, //forall289 {ast::deepCopy(&exception_obj)},290 {ast::deepCopy(&bool_obj)},291 nullptr,292 ast::Storage::Classes{},293 ast::Linkage::Cforall294 );295 296 finally_func_t = new ast::FunctionDecl(297 {},298 "finally",299 {}, //forall300 {ast::deepCopy(&voidptr_obj)},301 {}, //return void302 nullptr,303 ast::Storage::Classes{},304 ast::Linkage::Cforall305 );306 307 //catch_func_t.get_parameters().push_back( index_obj.clone() );308 //catch_func_t.get_parameters().push_back( exception_obj.clone() );309 //match_func_t.get_returnVals().push_back( unused_index_obj );310 //match_func_t.get_parameters().push_back( exception_obj.clone() );311 //handle_func_t.get_returnVals().push_back( bool_obj.clone() );312 //handle_func_t.get_parameters().push_back( exception_obj.clone() );313 //finally_func_t.get_parameters().push_back( voidptr_obj.clone() );314 }315 316 // TryStmt Mutation Helpers317 318 /*319 ast::CompoundStmt * TryMutatorCore::take_try_block( ast::TryStmt *tryStmt ) {320 ast::CompoundStmt * block = tryStmt->body;321 tryStmt->body = nullptr;322 return block;323 }324 */325 326 ast::FunctionDecl * TryMutatorCore::create_try_wrapper(327 const ast::CompoundStmt *body ) {328 329 ast::FunctionDecl * ret = ast::deepCopy(try_func_t);330 ret->stmts = body;331 return ret;332 }333 334 ast::FunctionDecl * TryMutatorCore::create_terminate_catch(335 CatchList &handlers ) {336 std::vector<ast::ptr<ast::Stmt>> handler_wrappers;337 338 assert (!handlers.empty());339 const CodeLocation loc = handlers.front()->location;340 341 ast::FunctionDecl * func_t = ast::deepCopy(catch_func_t);342 const ast::DeclWithType * index_obj = func_t->params.front();343 const ast::DeclWithType * except_obj = func_t->params.back();344 345 // Index 1..{number of handlers}346 int index = 0;347 CatchList::iterator it = handlers.begin();348 for ( ; it != handlers.end() ; ++it ) {349 ++index;350 ast::CatchStmt * handler = *it;351 const CodeLocation loc = handler->location;352 353 // case `index`:354 // {355 // `handler.decl` = { (virtual `decl.type`)`except` };356 // `handler.body`;357 // }358 // return;359 ast::CompoundStmt * block = new ast::CompoundStmt(loc);360 361 // Just copy the exception value. (Post Validation)362 const ast::ObjectDecl * handler_decl =363 handler->decl.strict_as<ast::ObjectDecl>();364 ast::ObjectDecl * local_except = ast::deepCopy(handler_decl);365 ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc,366 new ast::VariableExpr( loc, except_obj ),367 local_except->get_type()368 );369 vcex->location = handler->location;370 local_except->init = new ast::ListInit(loc, { new ast::SingleInit( loc, vcex ) });371 block->push_back( new ast::DeclStmt( loc, local_except ) );372 373 // Add the cleanup attribute.374 local_except->attributes.push_back( new ast::Attribute(375 "cleanup",376 { new ast::NameExpr( loc, "__cfaehm_cleanup_terminate" ) }377 ) );378 379 ast::DeclReplacer::DeclMap mapping;380 mapping[handler_decl] = local_except;381 const ast::Stmt * mutBody = strict_dynamic_cast<const ast::Stmt *>(382 ast::DeclReplacer::replace(handler->body, mapping));383 384 385 block->push_back( mutBody );386 // handler->body = nullptr;387 388 handler_wrappers.push_back( new ast::CaseStmt(loc,389 ast::ConstantExpr::from_int(loc, index) ,390 { block, new ast::ReturnStmt( loc, nullptr ) }391 ));392 }393 // TODO: Some sort of meaningful error on default perhaps?394 395 /*396 std::list<Statement*> stmt_handlers;397 while ( !handler_wrappers.empty() ) {398 stmt_handlers.push_back( handler_wrappers.front() );399 handler_wrappers.pop_front();400 }401 */402 403 ast::SwitchStmt * handler_lookup = new ast::SwitchStmt(loc,404 new ast::VariableExpr( loc, index_obj ),405 std::move(handler_wrappers)406 );407 ast::CompoundStmt * body = new ast::CompoundStmt(loc,408 {handler_lookup});409 410 func_t->stmts = body;411 return func_t;412 }413 414 // Create a single check from a moddified handler.415 // except_obj is referenced, modded_handler will be freed.416 ast::CompoundStmt * TryMutatorCore::create_single_matcher(417 const ast::DeclWithType * except_obj, ast::CatchStmt * modded_handler ) {418 // {419 // `modded_handler.decl`420 // if ( `decl.name = (virtual `decl.type`)`except`421 // [&& `modded_handler.cond`] ) {422 // `modded_handler.body`423 // }424 // }425 426 const CodeLocation loc = modded_handler->location;427 ast::CompoundStmt * block = new ast::CompoundStmt(loc);428 429 // Local Declaration430 const ast::ObjectDecl * local_except =431 modded_handler->decl.strict_as<ast::ObjectDecl>();432 block->push_back( new ast::DeclStmt( loc, local_except ) );433 434 // Check for type match.435 ast::VirtualCastExpr * vcex = new ast::VirtualCastExpr(loc,436 new ast::VariableExpr(loc, except_obj ),437 local_except->get_type()438 );439 ast::Expr * cond = ast::UntypedExpr::createAssign(loc,440 new ast::VariableExpr(loc, local_except ), vcex );441 442 // Add the check on the conditional if it is provided.443 if ( modded_handler->cond ) {444 cond = new ast::LogicalExpr( loc, cond, modded_handler->cond, ast::LogicalFlag::AndExpr );445 }446 // Construct the match condition.447 block->push_back( new ast::IfStmt(loc,448 cond, modded_handler->body, nullptr ) );449 450 // xxx - how does this work in new ast451 //modded_handler->set_decl( nullptr );452 //modded_handler->set_cond( nullptr );453 //modded_handler->set_body( nullptr );454 //delete modded_handler;455 return block;456 }457 458 ast::FunctionDecl * TryMutatorCore::create_terminate_match(459 CatchList &handlers ) {460 // int match(exception * except) {461 // HANDLER WRAPPERS { return `index`; }462 // }463 464 assert (!handlers.empty());465 const CodeLocation loc = handlers.front()->location;466 467 ast::CompoundStmt * body = new ast::CompoundStmt(loc);468 469 ast::FunctionDecl * func_t = ast::deepCopy(match_func_t);470 const ast::DeclWithType * except_obj = func_t->params.back();471 472 // Index 1..{number of handlers}473 int index = 0;474 CatchList::iterator it;475 for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {476 ++index;477 ast::CatchStmt * handler = *it;478 479 // Body should have been taken by create_terminate_catch.480 // xxx - just ignore it?481 // assert( nullptr == handler->get_body() );482 483 // Create new body.484 handler->body = new ast::ReturnStmt( handler->location,485 ast::ConstantExpr::from_int( handler->location, index ) );486 487 // Create the handler.488 body->push_back( create_single_matcher( except_obj, handler ) );489 *it = nullptr;490 }491 492 body->push_back( new ast::ReturnStmt(loc,493 ast::ConstantExpr::from_int( loc, 0 ) ));494 495 func_t->stmts = body;496 497 return func_t;498 }499 500 ast::CompoundStmt * TryMutatorCore::create_terminate_caller(501 CodeLocation loc,502 ast::FunctionDecl * try_wrapper,503 ast::FunctionDecl * terminate_catch,504 ast::FunctionDecl * terminate_match ) {505 // { __cfaehm_try_terminate(`try`, `catch`, `match`); }506 507 ast::UntypedExpr * caller = new ast::UntypedExpr(loc, new ast::NameExpr(loc,508 "__cfaehm_try_terminate" ) );509 caller->args.push_back( new ast::VariableExpr(loc, try_wrapper ) );510 caller->args.push_back( new ast::VariableExpr(loc, terminate_catch ) );511 caller->args.push_back( new ast::VariableExpr(loc, terminate_match ) );512 513 ast::CompoundStmt * callStmt = new ast::CompoundStmt(loc);514 callStmt->push_back( new ast::ExprStmt( loc, caller ) );515 return callStmt;516 }517 518 ast::FunctionDecl * TryMutatorCore::create_resume_handler(519 CatchList &handlers ) {520 // bool handle(exception * except) {521 // HANDLER WRAPPERS { `hander->body`; return true; }522 // }523 assert (!handlers.empty());524 const CodeLocation loc = handlers.front()->location;525 ast::CompoundStmt * body = new ast::CompoundStmt(loc);526 527 ast::FunctionDecl * func_t = ast::deepCopy(handle_func_t);528 const ast::DeclWithType * except_obj = func_t->params.back();529 530 CatchList::iterator it;531 for ( it = handlers.begin() ; it != handlers.end() ; ++it ) {532 ast::CatchStmt * handler = *it;533 const CodeLocation loc = handler->location;534 // Modifiy body.535 ast::CompoundStmt * handling_code;536 if (handler->body.as<ast::CompoundStmt>()) {537 handling_code =538 strict_dynamic_cast<ast::CompoundStmt*>( handler->body.get_and_mutate() );539 } else {540 handling_code = new ast::CompoundStmt(loc);541 handling_code->push_back( handler->body );542 }543 handling_code->push_back( new ast::ReturnStmt(loc,544 ast::ConstantExpr::from_bool(loc, true ) ) );545 handler->body = handling_code;546 547 // Create the handler.548 body->push_back( create_single_matcher( except_obj, handler ) );549 *it = nullptr;550 }551 552 body->push_back( new ast::ReturnStmt(loc,553 ast::ConstantExpr::from_bool(loc, false ) ) );554 func_t->stmts = body;555 556 return func_t;557 }558 559 ast::CompoundStmt * TryMutatorCore::create_resume_wrapper(560 const ast::Stmt * wraps,561 const ast::FunctionDecl * resume_handler ) {562 const CodeLocation loc = wraps->location;563 ast::CompoundStmt * body = new ast::CompoundStmt(loc);564 565 // struct __try_resume_node __resume_node566 // __attribute__((cleanup( __cfaehm_try_resume_cleanup )));567 // ** unwinding of the stack here could cause problems **568 // ** however I don't think that can happen currently **569 // __cfaehm_try_resume_setup( &__resume_node, resume_handler );570 571 ast::ObjectDecl * obj = new ast::ObjectDecl(572 loc,573 "__resume_node",574 new ast::StructInstType(575 node_decl576 ),577 nullptr,578 ast::Storage::Classes{},579 ast::Linkage::Cforall,580 nullptr,581 {new ast::Attribute("cleanup", {new ast::NameExpr(loc, "__cfaehm_try_resume_cleanup")})}582 );583 appendDeclStmt( body, obj );584 585 ast::UntypedExpr *setup = new ast::UntypedExpr(loc, new ast::NameExpr(loc,586 "__cfaehm_try_resume_setup" ) );587 setup->args.push_back( new ast::AddressExpr( loc, new ast::VariableExpr(loc, obj ) ) );588 setup->args.push_back( new ast::VariableExpr( loc, resume_handler ) );589 590 body->push_back( new ast::ExprStmt(loc, setup ) );591 592 body->push_back( wraps );593 return body;594 }595 596 ast::FunctionDecl * TryMutatorCore::create_finally_wrapper(597 ast::TryStmt * tryStmt ) {598 // void finally() { `finally->block` }599 const ast::FinallyStmt * finally = tryStmt->finally;600 const ast::CompoundStmt * body = finally->body;601 602 ast::FunctionDecl * func_t = ast::deepCopy(finally_func_t);603 func_t->stmts = body;604 605 // finally->set_block( nullptr );606 // delete finally;607 tryStmt->finally = nullptr;608 609 610 return func_t;611 }612 613 ast::ObjectDecl * TryMutatorCore::create_finally_hook(614 ast::FunctionDecl * finally_wrapper ) {615 // struct __cfaehm_cleanup_hook __finally_hook616 // __attribute__((cleanup( `finally_wrapper` )));617 618 const CodeLocation loc = finally_wrapper->location;619 // Make Cleanup Attribute.620 /*621 std::list< ast::Attribute * > attributes;622 {623 std::list< > attr_params;624 attr_params.push_back( nameOf( finally_wrapper ) );625 attributes.push_back( new Attribute( "cleanup", attr_params ) );626 }627 */628 629 return new ast::ObjectDecl(630 loc,631 "__finally_hook",632 new ast::StructInstType(633 hook_decl634 ),635 nullptr,636 ast::Storage::Classes{},637 ast::Linkage::Cforall,638 nullptr,639 {new ast::Attribute("cleanup", {new ast::VariableExpr{loc, finally_wrapper}})}640 );641 }642 643 ast::Stmt * TryMutatorCore::create_resume_rethrow( const ast::ThrowStmt *throwStmt ) {644 // return false;645 const CodeLocation loc = throwStmt->location;646 ast::Stmt * result = new ast::ReturnStmt(loc,647 ast::ConstantExpr::from_bool( loc, false )648 );649 result->labels = throwStmt->labels;650 // delete throwStmt; done by postvisit651 return result;652 }653 654 // Visiting/Mutating Functions655 void TryMutatorCore::previsit( const ast::StructDecl *structDecl ) {656 if ( !structDecl->body ) {657 // Skip children?658 return;659 } else if ( structDecl->name == "__cfaehm_base_exception_t" ) {660 assert( nullptr == except_decl );661 except_decl = structDecl;662 init_func_types();663 } else if ( structDecl->name == "__cfaehm_try_resume_node" ) {664 assert( nullptr == node_decl );665 node_decl = structDecl;666 } else if ( structDecl->name == "__cfaehm_cleanup_hook" ) {667 assert( nullptr == hook_decl );668 hook_decl = structDecl;669 }670 }671 672 ast::Stmt * TryMutatorCore::postvisit( const ast::TryStmt *tryStmt ) {673 assert( except_decl );674 assert( node_decl );675 assert( hook_decl );676 677 const CodeLocation loc = tryStmt->location;678 ast::TryStmt * mutStmt = mutate(tryStmt);679 // Generate a prefix for the function names?680 681 ast::CompoundStmt * block = new ast::CompoundStmt( loc );682 // ast::CompoundStmt * inner = take_try_block( mutStmt );683 // this is never mutated so let node deletion do its job?684 const ast::CompoundStmt * inner = mutStmt->body;685 686 if ( mutStmt->finally ) {687 // Define the helper function.688 ast::FunctionDecl * finally_block =689 create_finally_wrapper( mutStmt );690 appendDeclStmt( block, finally_block );691 // Create and add the finally cleanup hook.692 appendDeclStmt( block, create_finally_hook( finally_block ) );693 }694 695 CatchList termination_handlers;696 CatchList resumption_handlers;697 698 for (auto & handler: mutStmt->handlers) {699 // xxx - should always be unique? mutate as safe const-cast700 assert(handler->unique());701 if (handler->kind == ast::ExceptionKind::Resume) {702 resumption_handlers.push_back(handler.get_and_mutate());703 }704 else {705 termination_handlers.push_back(handler.get_and_mutate());706 }707 }708 // split( mutStmt->handlers,709 // termination_handlers, resumption_handlers );710 711 if ( resumption_handlers.size() ) {712 // Define the helper function.713 ast::FunctionDecl * resume_handler =714 create_resume_handler( resumption_handlers );715 appendDeclStmt( block, resume_handler );716 // Prepare hooks717 inner = create_resume_wrapper( inner, resume_handler );718 }719 720 if ( termination_handlers.size() ) {721 // Define the three helper functions.722 ast::FunctionDecl * try_wrapper = create_try_wrapper( inner );723 appendDeclStmt( block, try_wrapper );724 ast::FunctionDecl * terminate_catch =725 create_terminate_catch( termination_handlers );726 appendDeclStmt( block, terminate_catch );727 ast::FunctionDecl * terminate_match =728 create_terminate_match( termination_handlers );729 appendDeclStmt( block, terminate_match );730 // Build the call to the try wrapper.731 inner = create_terminate_caller(inner->location,732 try_wrapper, terminate_catch, terminate_match );733 }734 735 // Embed the try block.736 block->push_back( inner );737 738 return block;739 }740 741 ast::Stmt * TryMutatorCore::postvisit( const ast::ThrowStmt *throwStmt ) {742 // Only valid `throwResume;` statements should remain. (2/3 checks)743 assert( ast::ExceptionKind::Resume == throwStmt->kind && ! throwStmt->expr );744 return create_resume_rethrow( throwStmt );745 }746 747 130 } // namespace 748 131 749 132 void translateThrows( ast::TranslationUnit & transUnit ) { 750 133 ast::Pass<TranslateThrowsCore>::run( transUnit ); 751 }752 753 void translateTries( ast::TranslationUnit & transUnit ) {754 ast::Pass<TryMutatorCore>::run(transUnit);755 134 } 756 135 -
src/ControlStruct/FixLabels.cpp
rf5a51db r97c215f 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Nov 1 09:39:00 2021 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Jan 31 22:19:17 202213 // Update Count : 911 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 10:53:00 2021 13 // Update Count : 3 14 14 // 15 15 … … 20 20 #include "AST/Stmt.hpp" 21 21 #include "ControlStruct/MultiLevelExit.hpp" 22 using namespace ast;23 22 24 23 namespace ControlStruct { 25 class FixLabelsCore final : public WithGuards { 24 25 namespace { 26 27 class FixLabelsCore final : public ast::WithGuards { 26 28 LabelToStmt labelTable; 27 29 public: 28 30 FixLabelsCore() : labelTable() {} 29 31 30 void previsit( const FunctionDecl * );31 const FunctionDecl * postvisit( constFunctionDecl * );32 void previsit( const Stmt * );33 void previsit( const BranchStmt * );34 void previsit( const LabelAddressExpr * );32 void previsit( const ast::FunctionDecl * ); 33 const ast::FunctionDecl * postvisit( const ast::FunctionDecl * ); 34 void previsit( const ast::Stmt * ); 35 void previsit( const ast::BranchStmt * ); 36 void previsit( const ast::LabelAddressExpr * ); 35 37 36 void setLabelsDef( const std::vector< Label> &, constStmt * );37 void setLabelsUsage( const Label & );38 void setLabelsDef( const std::vector<ast::Label> &, const ast::Stmt * ); 39 void setLabelsUsage( const ast::Label & ); 38 40 }; 39 41 40 void FixLabelsCore::previsit( const FunctionDecl * ) {42 void FixLabelsCore::previsit( const ast::FunctionDecl * ) { 41 43 GuardValue( labelTable ).clear(); 42 44 } 43 45 44 const FunctionDecl * FixLabelsCore::postvisit(45 constFunctionDecl * decl ) {46 const ast::FunctionDecl * FixLabelsCore::postvisit( 47 const ast::FunctionDecl * decl ) { 46 48 if ( nullptr == decl->stmts ) return decl; 47 49 for ( auto kvp : labelTable ) { 48 50 if ( nullptr == kvp.second ) { 49 51 SemanticError( kvp.first.location, 50 52 "Use of undefined label: " + kvp.first.name ); 51 53 } 52 54 } 53 return mutate_field( decl, &FunctionDecl::stmts,54 55 return ast::mutate_field( decl, &ast::FunctionDecl::stmts, 56 multiLevelExitUpdate( decl->stmts.get(), labelTable ) ); 55 57 } 56 58 57 void FixLabelsCore::previsit( const Stmt * stmt ) {59 void FixLabelsCore::previsit( const ast::Stmt * stmt ) { 58 60 if ( !stmt->labels.empty() ) { 59 61 setLabelsDef( stmt->labels, stmt ); … … 61 63 } 62 64 63 void FixLabelsCore::previsit( const BranchStmt * stmt ) {65 void FixLabelsCore::previsit( const ast::BranchStmt * stmt ) { 64 66 if ( !stmt->labels.empty() ) { 65 67 setLabelsDef( stmt->labels, stmt ); … … 70 72 } 71 73 72 void FixLabelsCore::previsit( const LabelAddressExpr * expr ) {74 void FixLabelsCore::previsit( const ast::LabelAddressExpr * expr ) { 73 75 assert( !expr->arg.empty() ); 74 76 setLabelsUsage( expr->arg ); … … 76 78 77 79 void FixLabelsCore::setLabelsDef( 78 const std::vector<Label> & labels, constStmt * stmt ) {80 const std::vector<ast::Label> & labels, const ast::Stmt * stmt ) { 79 81 assert( !labels.empty() ); 80 82 assert( stmt ); … … 87 89 // Duplicate definition, this is an error. 88 90 SemanticError( label.location, 89 91 "Duplicate definition of label: " + label.name ); 90 92 } else { 91 93 // Perviously used, but not defined until now. … … 96 98 97 99 // Label was used, if it is new add it to the table. 98 void FixLabelsCore::setLabelsUsage( const Label & label ) {100 void FixLabelsCore::setLabelsUsage( const ast::Label & label ) { 99 101 if ( labelTable.find( label ) == labelTable.end() ) { 100 102 labelTable[ label ] = nullptr; … … 102 104 } 103 105 104 void fixLabels( TranslationUnit & translationUnit ) { 105 Pass<FixLabelsCore>::run( translationUnit ); 106 } // namespace 107 108 void fixLabels( ast::TranslationUnit & translationUnit ) { 109 ast::Pass<FixLabelsCore>::run( translationUnit ); 106 110 } 111 107 112 } // namespace ControlStruct 108 113 -
src/ControlStruct/FixLabels.hpp
rf5a51db r97c215f 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Nov 1 09:36:00 2021 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Jan 31 22:18:43 202213 // Update Count : 211 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 1 09:40:00 2021 13 // Update Count : 0 14 14 // 15 15 … … 17 17 18 18 namespace ast { 19 class TranslationUnit;19 class TranslationUnit; 20 20 } 21 21 22 22 namespace ControlStruct { 23 // normalizes label definitions and generates multi-level exit labels 23 24 /// normalizes label definitions and generates multi-level exit labels 24 25 void fixLabels( ast::TranslationUnit & translationUnit ); 26 25 27 } 26 28 -
src/ControlStruct/ForExprMutator.cc
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:26:12 202213 // Update Count : 1 612 // Last Modified On : Mon Mar 11 22:26:52 2019 13 // Update Count : 14 14 14 // 15 15 … … 45 45 return hoist( forStmt, forStmt->initialization ); 46 46 } 47 Statement * ForExprMutator::postmutate( While DoStmt * whileDoStmt ) {48 return hoist( while DoStmt, whileDoStmt->initialization );47 Statement * ForExprMutator::postmutate( WhileStmt * whileStmt ) { 48 return hoist( whileStmt, whileStmt->initialization ); 49 49 } 50 50 } // namespace ControlStruct -
src/ControlStruct/ForExprMutator.h
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : T ue Feb 1 09:18:50 202213 // Update Count : 712 // Last Modified On : Thu Aug 17 15:32:48 2017 13 // Update Count : 5 14 14 // 15 15 … … 18 18 class IfStmt; 19 19 class ForStmt; 20 class While DoStmt;20 class WhileStmt; 21 21 class Statement; 22 22 … … 24 24 class ForExprMutator { 25 25 public: 26 Statement * 27 Statement * 28 Statement * postmutate( WhileDoStmt * );26 Statement *postmutate( IfStmt * ); 27 Statement *postmutate( ForStmt * ); 28 Statement *postmutate( WhileStmt * ); 29 29 }; 30 30 } // namespace ControlStruct -
src/ControlStruct/LabelFixer.cc
rf5a51db r97c215f 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Tue Feb 1 09:12:09 202213 // Update Count : 16 211 // Last Modified By : Andrew Beach 12 // Last Modified On : Tue Jan 21 10:32:00 2020 13 // Update Count : 160 14 14 // 15 15 … … 27 27 28 28 namespace ControlStruct { 29 bool LabelFixer::Entry::insideLoop() {30 return ( dynamic_cast< ForStmt * > ( definition ) ||31 dynamic_cast< WhileDoStmt * > ( definition ) );32 }29 bool LabelFixer::Entry::insideLoop() { 30 return ( dynamic_cast< ForStmt * > ( definition ) || 31 dynamic_cast< WhileStmt * > ( definition ) ); 32 } 33 33 34 LabelFixer::LabelFixer( LabelGenerator * gen ) : generator ( gen ) {35 if ( generator == 0 )36 generator = LabelGenerator::getGenerator();37 }34 LabelFixer::LabelFixer( LabelGenerator * gen ) : generator ( gen ) { 35 if ( generator == 0 ) 36 generator = LabelGenerator::getGenerator(); 37 } 38 38 39 void LabelFixer::previsit( FunctionDecl * ) {40 // need to go into a nested function in a fresh state41 GuardValue( labelTable );42 labelTable.clear();43 }39 void LabelFixer::previsit( FunctionDecl * ) { 40 // need to go into a nested function in a fresh state 41 GuardValue( labelTable ); 42 labelTable.clear(); 43 } 44 44 45 void LabelFixer::postvisit( FunctionDecl * functionDecl ) {46 PassVisitor<MultiLevelExitMutator> mlem( resolveJumps(), generator );47 // We start in the body so we can stop when we hit another FunctionDecl.48 maybeMutate( functionDecl->statements, mlem );49 }45 void LabelFixer::postvisit( FunctionDecl * functionDecl ) { 46 PassVisitor<MultiLevelExitMutator> mlem( resolveJumps(), generator ); 47 // We start in the body so we can stop when we hit another FunctionDecl. 48 maybeMutate( functionDecl->statements, mlem ); 49 } 50 50 51 // prune to at most one label definition for each statement52 void LabelFixer::previsit( Statement * stmt ) {53 std::list< Label > &labels = stmt->get_labels();51 // prune to at most one label definition for each statement 52 void LabelFixer::previsit( Statement * stmt ) { 53 std::list< Label > &labels = stmt->get_labels(); 54 54 55 if ( ! labels.empty() ) {56 // only remember one label for each statement57 Label current = setLabelsDef( labels, stmt );58 } // if59 }55 if ( ! labels.empty() ) { 56 // only remember one label for each statement 57 Label current = setLabelsDef( labels, stmt ); 58 } // if 59 } 60 60 61 void LabelFixer::previsit( BranchStmt * branchStmt ) {62 previsit( ( Statement *)branchStmt );61 void LabelFixer::previsit( BranchStmt * branchStmt ) { 62 previsit( ( Statement *)branchStmt ); 63 63 64 // for labeled branches, add an entry to the label table 65 Label target = branchStmt->get_target(); 66 if ( target != "" ) { 67 setLabelsUsg( target, branchStmt ); 64 // for labeled branches, add an entry to the label table 65 Label target = branchStmt->get_target(); 66 if ( target != "" ) { 67 setLabelsUsg( target, branchStmt ); 68 } 68 69 } 69 }70 70 71 void LabelFixer::previsit( LabelAddressExpr * addrExpr ) {72 Label & target = addrExpr->arg;73 assert( target != "" );74 setLabelsUsg( target, addrExpr );75 }71 void LabelFixer::previsit( LabelAddressExpr * addrExpr ) { 72 Label & target = addrExpr->arg; 73 assert( target != "" ); 74 setLabelsUsg( target, addrExpr ); 75 } 76 76 77 77 78 // Sets the definition of the labelTable entry to be the provided statement for every label in79 // the list parameter. Happens for every kind of statement.80 Label LabelFixer::setLabelsDef( std::list< Label > & llabel, Statement * definition ) {81 assert( definition != 0 );82 assert( llabel.size() > 0 );78 // Sets the definition of the labelTable entry to be the provided statement for every label in 79 // the list parameter. Happens for every kind of statement. 80 Label LabelFixer::setLabelsDef( std::list< Label > & llabel, Statement * definition ) { 81 assert( definition != 0 ); 82 assert( llabel.size() > 0 ); 83 83 84 for ( std::list< Label >::iterator i = llabel.begin(); i != llabel.end(); i++ ) {85 Label & l = *i;86 l.set_statement( definition ); // attach statement to the label to be used later87 if ( labelTable.find( l ) == labelTable.end() ) {88 // All labels on this statement need to use the same entry,89 // so this should only be created once.90 // undefined and unused until now, add an entry91 labelTable[ l ] = new Entry( definition );92 } else if ( labelTable[ l ]->defined() ) {93 // defined twice, error94 SemanticError( l.get_statement()->location,95 "Duplicate definition of label: " + l.get_name() );96 } else {97 // used previously, but undefined until now -> link with this entry98 // Question: Is changing objects important?99 delete labelTable[ l ];100 labelTable[ l ] = new Entry( definition );101 } // if102 } // for84 for ( std::list< Label >::iterator i = llabel.begin(); i != llabel.end(); i++ ) { 85 Label & l = *i; 86 l.set_statement( definition ); // attach statement to the label to be used later 87 if ( labelTable.find( l ) == labelTable.end() ) { 88 // All labels on this statement need to use the same entry, 89 // so this should only be created once. 90 // undefined and unused until now, add an entry 91 labelTable[ l ] = new Entry( definition ); 92 } else if ( labelTable[ l ]->defined() ) { 93 // defined twice, error 94 SemanticError( l.get_statement()->location, 95 "Duplicate definition of label: " + l.get_name() ); 96 } else { 97 // used previously, but undefined until now -> link with this entry 98 // Question: Is changing objects important? 99 delete labelTable[ l ]; 100 labelTable[ l ] = new Entry( definition ); 101 } // if 102 } // for 103 103 104 // Produce one of the labels attached to this statement to be temporarily used as the 105 // canonical label. 106 return labelTable[ llabel.front() ]->get_label(); 107 } 108 109 // A label was used, add it to the table if it isn't already there 110 template< typename UsageNode > 111 void LabelFixer::setLabelsUsg( Label orgValue, UsageNode *use ) { 112 assert( use != 0 ); 113 114 // add label with an unknown origin 115 if ( labelTable.find( orgValue ) == labelTable.end() ) { 116 labelTable[ orgValue ] = new Entry( 0 ); 117 } 118 } 119 120 // Builds a table that maps a label to its defining statement. 121 std::map<Label, Statement * > * LabelFixer::resolveJumps() throw ( SemanticErrorException ) { 122 std::map< Label, Statement * > *ret = new std::map< Label, Statement * >(); 123 for ( std::map< Label, Entry * >::iterator i = labelTable.begin(); i != labelTable.end(); ++i ) { 124 if ( ! i->second->defined() ) { 125 SemanticError( i->first.get_statement()->location, "Use of undefined label: " + i->first.get_name() ); 126 } 127 (*ret)[ i->first ] = i->second->get_definition(); 104 // Produce one of the labels attached to this statement to be temporarily used as the 105 // canonical label. 106 return labelTable[ llabel.front() ]->get_label(); 128 107 } 129 108 130 return ret; 131 } 109 // A label was used, add it to the table if it isn't already there 110 template< typename UsageNode > 111 void LabelFixer::setLabelsUsg( Label orgValue, UsageNode *use ) { 112 assert( use != 0 ); 113 114 // add label with an unknown origin 115 if ( labelTable.find( orgValue ) == labelTable.end() ) { 116 labelTable[ orgValue ] = new Entry( 0 ); 117 } 118 } 119 120 // Builds a table that maps a label to its defining statement. 121 std::map<Label, Statement * > * LabelFixer::resolveJumps() throw ( SemanticErrorException ) { 122 std::map< Label, Statement * > *ret = new std::map< Label, Statement * >(); 123 for ( std::map< Label, Entry * >::iterator i = labelTable.begin(); i != labelTable.end(); ++i ) { 124 if ( ! i->second->defined() ) { 125 SemanticError( i->first.get_statement()->location, "Use of undefined label: " + i->first.get_name() ); 126 } 127 (*ret)[ i->first ] = i->second->get_definition(); 128 } 129 130 return ret; 131 } 132 132 } // namespace ControlStruct 133 133 -
src/ControlStruct/LabelFixer.h
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Mon Jan 31 22:28:04 202213 // Update Count : 3 512 // Last Modified On : Sat Jul 22 09:17:24 2017 13 // Update Count : 34 14 14 // 15 15 … … 26 26 27 27 namespace ControlStruct { 28 // normalizes label definitions and generates multi-level exit labels29 class LabelGenerator;28 /// normalizes label definitions and generates multi-level exit labels 29 class LabelGenerator; 30 30 31 class LabelFixer final : public WithGuards {32 public:33 LabelFixer( LabelGenerator *gen = 0 );31 class LabelFixer final : public WithGuards { 32 public: 33 LabelFixer( LabelGenerator *gen = 0 ); 34 34 35 std::map < Label, Statement * > *resolveJumps() throw ( SemanticErrorException );35 std::map < Label, Statement * > *resolveJumps() throw ( SemanticErrorException ); 36 36 37 // Declarations38 void previsit( FunctionDecl *functionDecl );39 void postvisit( FunctionDecl *functionDecl );37 // Declarations 38 void previsit( FunctionDecl *functionDecl ); 39 void postvisit( FunctionDecl *functionDecl ); 40 40 41 // Statements42 void previsit( Statement *stmt );43 void previsit( BranchStmt *branchStmt );41 // Statements 42 void previsit( Statement *stmt ); 43 void previsit( BranchStmt *branchStmt ); 44 44 45 // Expressions46 void previsit( LabelAddressExpr *addrExpr );45 // Expressions 46 void previsit( LabelAddressExpr *addrExpr ); 47 47 48 Label setLabelsDef( std::list< Label > &, Statement *definition ); 49 template< typename UsageNode > 50 void setLabelsUsg( Label, UsageNode *usage = 0 ); 51 52 private: 53 class Entry { 54 public: 55 Entry( Statement *to ) : definition( to ) {} 56 bool defined() { return ( definition != 0 ); } 57 bool insideLoop(); 58 59 Label get_label() const { return label; } 60 void set_label( Label lab ) { label = lab; } 61 62 Statement *get_definition() const { return definition; } 63 void set_definition( Statement *def ) { definition = def; } 48 Label setLabelsDef( std::list< Label > &, Statement *definition ); 49 template< typename UsageNode > 50 void setLabelsUsg( Label, UsageNode *usage = 0 ); 64 51 65 52 private: 66 Label label; 67 Statement *definition; 53 class Entry { 54 public: 55 Entry( Statement *to ) : definition( to ) {} 56 bool defined() { return ( definition != 0 ); } 57 bool insideLoop(); 58 59 Label get_label() const { return label; } 60 void set_label( Label lab ) { label = lab; } 61 62 Statement *get_definition() const { return definition; } 63 void set_definition( Statement *def ) { definition = def; } 64 65 private: 66 Label label; 67 Statement *definition; 68 }; 69 70 std::map < Label, Entry *> labelTable; 71 LabelGenerator *generator; 68 72 }; 69 70 std::map < Label, Entry *> labelTable;71 LabelGenerator *generator;72 };73 73 } // namespace ControlStruct 74 74 -
src/ControlStruct/LabelGenerator.cc
rf5a51db r97c215f 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Jan 31 22:30:26 202213 // Update Count : 2811 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 10:18:00 2021 13 // Update Count : 17 14 14 // 15 15 … … 17 17 #include <sstream> // for ostringstream 18 18 #include <list> // for list 19 using namespace std;20 19 21 20 #include "LabelGenerator.h" 22 21 22 #include "AST/Attribute.hpp" 23 #include "AST/Label.hpp" 24 #include "AST/Stmt.hpp" 23 25 #include "SynTree/Attribute.h" // for Attribute 24 26 #include "SynTree/Label.h" // for Label, operator<< … … 26 28 27 29 namespace ControlStruct { 30 28 31 int LabelGenerator::current = 0; 29 32 LabelGenerator * LabelGenerator::labelGenerator = nullptr; 30 33 31 LabelGenerator * LabelGenerator::getGenerator() { 32 if ( LabelGenerator::labelGenerator == 0 ) 33 LabelGenerator::labelGenerator = new LabelGenerator(); 34 return labelGenerator; 34 LabelGenerator * LabelGenerator::getGenerator() { 35 if ( LabelGenerator::labelGenerator == 0 ) 36 LabelGenerator::labelGenerator = new LabelGenerator(); 37 return labelGenerator; 38 } 39 40 Label LabelGenerator::newLabel( std::string suffix, Statement * stmt ) { 41 std::ostringstream os; 42 os << "__L" << current++ << "__" << suffix; 43 if ( stmt && ! stmt->get_labels().empty() ) { 44 os << "_" << stmt->get_labels().front() << "__"; 45 } // if 46 std::string ret = os.str(); 47 Label l( ret ); 48 l.get_attributes().push_back( new Attribute("unused") ); 49 return l; 50 } 51 52 ast::Label LabelGenerator::newLabel( 53 const std::string & suffix, const ast::Stmt * stmt ) { 54 assert( stmt ); 55 56 std::ostringstream os; 57 os << "__L" << current++ << "__" << suffix; 58 if ( stmt && !stmt->labels.empty() ) { 59 os << "_" << stmt->labels.front() << "__"; 60 } 61 ast::Label ret_label( stmt->location, os.str() ); 62 ret_label.attributes.push_back( new ast::Attribute( "unused" ) ); 63 return ret_label; 35 64 } 36 65 37 Label LabelGenerator::newLabel( string suffix, Statement * stmt ) {38 ostringstream os;39 os << "__L_OLD" << current++ << "__" << suffix;40 if ( stmt && ! stmt->get_labels().empty() ) {41 os << "_" << stmt->get_labels().front() << "__";42 } // if43 string ret = os.str();44 Label l( ret );45 l.get_attributes().push_back( new Attribute( "unused" ) );46 return l;47 }48 66 } // namespace ControlStruct 49 67 50 68 // Local Variables: // 69 // tab-width: 4 // 51 70 // mode: c++ // 71 // compile-command: "make install" // 52 72 // End: // -
src/ControlStruct/LabelGenerator.h
rf5a51db r97c215f 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Jan 31 22:30:10 202213 // Update Count : 1611 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 10:16:00 2021 13 // Update Count : 8 14 14 // 15 15 … … 21 21 22 22 class Statement; 23 24 23 namespace ast { 25 class Stmt;26 class Label;24 class Stmt; 25 class Label; 27 26 } 28 27 29 28 namespace ControlStruct { 29 30 30 class LabelGenerator { 31 31 static int current; 32 32 static LabelGenerator *labelGenerator; 33 33 protected: 34 34 LabelGenerator() {} 35 35 public: 36 36 static LabelGenerator *getGenerator(); 37 37 static Label newLabel(std::string suffix, Statement * stmt = nullptr); 38 static ast::Label newLabel( const std::string&, const ast::Stmt * ); 39 static void reset() { current = 0; } 40 static void rewind() { current--; } 38 41 }; 42 39 43 } // namespace ControlStruct 40 44 -
src/ControlStruct/MLEMutator.cc
rf5a51db r97c215f 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Feb 2 20:18:57 202213 // Update Count : 22 711 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jan 22 11:50:00 2020 13 // Update Count : 223 14 14 // 15 15 … … 39 39 namespace { 40 40 bool isLoop( const MultiLevelExitMutator::Entry & e ) { 41 return dynamic_cast< While DoStmt * >( e.get_controlStructure() )41 return dynamic_cast< WhileStmt * >( e.get_controlStructure() ) 42 42 || dynamic_cast< ForStmt * >( e.get_controlStructure() ); 43 43 } … … 136 136 } 137 137 } 138 assertf( false, "C FA internal error: could not find label '%s' on statement %s",138 assertf( false, "Could not find label '%s' on statement %s", 139 139 originalTarget.get_name().c_str(), toString( stmt ).c_str() ); 140 140 } … … 295 295 } 296 296 297 void MultiLevelExitMutator::premutate( While DoStmt * whileDoStmt ) {298 return prehandleLoopStmt( while DoStmt );297 void MultiLevelExitMutator::premutate( WhileStmt * whileStmt ) { 298 return prehandleLoopStmt( whileStmt ); 299 299 } 300 300 … … 303 303 } 304 304 305 Statement * MultiLevelExitMutator::postmutate( While DoStmt * whileDoStmt ) {306 return posthandleLoopStmt( while DoStmt );305 Statement * MultiLevelExitMutator::postmutate( WhileStmt * whileStmt ) { 306 return posthandleLoopStmt( whileStmt ); 307 307 } 308 308 … … 395 395 } 396 396 assert( ! enclosingControlStructures.empty() ); 397 assertf( dynamic_cast<SwitchStmt *>( enclosingControlStructures.back().get_controlStructure() ), 398 "CFA internal error: control structure enclosing a case clause must be a switch, but is: %s", 399 toCString( enclosingControlStructures.back().get_controlStructure() ) ); 397 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() ) ); 400 398 if ( caseStmt->isDefault() ) { 401 399 if ( enclosingControlStructures.back().isFallDefaultUsed() ) { -
src/ControlStruct/MLEMutator.h
rf5a51db r97c215f 9 9 // Author : Rodolfo G. Esteves 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Tue Feb 1 09:27:24 202213 // Update Count : 5011 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jan 22 11:50:00 2020 13 // Update Count : 48 14 14 // 15 15 … … 42 42 void premutate( CompoundStmt *cmpndStmt ); 43 43 Statement * postmutate( BranchStmt *branchStmt ) throw ( SemanticErrorException ); 44 void premutate( While DoStmt *whileDoStmt );45 Statement * postmutate( While DoStmt *whileDoStmt );44 void premutate( WhileStmt *whileStmt ); 45 Statement * postmutate( WhileStmt *whileStmt ); 46 46 void premutate( ForStmt *forStmt ); 47 47 Statement * postmutate( ForStmt *forStmt ); … … 67 67 stmt( stmt ), breakExit( breakExit ), contExit( contExit ) {} 68 68 69 explicit Entry( While DoStmt *stmt, Label breakExit, Label contExit ) :69 explicit Entry( WhileStmt *stmt, Label breakExit, Label contExit ) : 70 70 stmt( stmt ), breakExit( breakExit ), contExit( contExit ) {} 71 71 -
src/ControlStruct/MultiLevelExit.cpp
rf5a51db r97c215f 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Nov 1 13:48:00 2021 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Feb 2 23:07:54 202213 // Update Count : 3311 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 10:56:00 2021 13 // Update Count : 2 14 14 // 15 15 … … 18 18 #include "AST/Pass.hpp" 19 19 #include "AST/Stmt.hpp" 20 #include " LabelGeneratorNew.hpp"20 #include "ControlStruct/LabelGenerator.h" 21 21 22 22 #include <set> 23 using namespace std;24 using namespace ast;25 23 26 24 namespace ControlStruct { 25 26 namespace { 27 27 28 class Entry { 28 29 const Stmt * stmt;30 29 public: 30 const ast::Stmt * stmt; 31 private: 31 32 // Organized like a manual ADT. Avoids creating a bunch of dead data. 32 33 struct Target { 33 Label label;34 ast::Label label; 34 35 bool used = false; 35 Target( const Label & label ) : label( label ) {}36 Target( const ast::Label & label ) : label( label ) {} 36 37 Target() : label( CodeLocation() ) {} 37 38 }; … … 40 41 41 42 enum Kind { 42 ForStmt K, WhileDoStmtK, CompoundStmtK, IfStmtK, CaseStmtK, SwitchStmtK, TryStmtK43 ForStmt, WhileStmt, CompoundStmt, IfStmt, CaseStmt, SwitchStmt, TryStmt 43 44 } kind; 44 45 45 46 bool fallDefaultValid = true; 46 47 47 static Label & useTarget( Target & target ) {48 static ast::Label & useTarget( Target & target ) { 48 49 target.used = true; 49 50 return target.label; 50 51 } 51 public: 52 Entry( const ForStmt * stmt, Label breakExit, Label contExit ) : 53 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( ForStmtK ) {} 54 Entry( const WhileDoStmt * stmt, Label breakExit, Label contExit ) : 55 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( WhileDoStmtK ) {} 56 Entry( const CompoundStmt *stmt, Label breakExit ) : 57 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( CompoundStmtK ) {} 58 Entry( const IfStmt *stmt, Label breakExit ) : 59 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmtK ) {} 60 Entry( const CaseStmt *stmt, Label fallExit ) : 61 stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseStmtK ) {} 62 Entry( const SwitchStmt *stmt, Label breakExit, Label fallDefaultExit ) : 63 stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmtK ) {} 64 Entry( const TryStmt *stmt, Label breakExit ) : 65 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( TryStmtK ) {} 66 67 bool isContTarget() const { return kind <= WhileDoStmtK; } 68 bool isBreakTarget() const { return kind != CaseStmtK; } 69 bool isFallTarget() const { return kind == CaseStmtK; } 70 bool isFallDefaultTarget() const { return kind == SwitchStmtK; } 71 72 // These routines set a target as being "used" by a BranchStmt 73 Label useContExit() { assert( kind <= WhileDoStmtK ); return useTarget(secondTarget); } 74 Label useBreakExit() { assert( kind != CaseStmtK ); return useTarget(firstTarget); } 75 Label useFallExit() { assert( kind == CaseStmtK ); return useTarget(firstTarget); } 76 Label useFallDefaultExit() { assert( kind == SwitchStmtK ); return useTarget(secondTarget); } 77 78 // These routines check if a specific label for a statement is used by a BranchStmt 79 bool isContUsed() const { assert( kind <= WhileDoStmtK ); return secondTarget.used; } 80 bool isBreakUsed() const { assert( kind != CaseStmtK ); return firstTarget.used; } 81 bool isFallUsed() const { assert( kind == CaseStmtK ); return firstTarget.used; } 82 bool isFallDefaultUsed() const { assert( kind == SwitchStmtK ); return secondTarget.used; } 52 53 public: 54 Entry( const ast::ForStmt * stmt, ast::Label breakExit, ast::Label contExit ) : 55 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( ForStmt ) {} 56 Entry( const ast::WhileStmt * stmt, ast::Label breakExit, ast::Label contExit ) : 57 stmt( stmt ), firstTarget( breakExit ), secondTarget( contExit ), kind( WhileStmt ) {} 58 Entry( const ast::CompoundStmt *stmt, ast::Label breakExit ) : 59 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( CompoundStmt ) {} 60 Entry( const ast::IfStmt *stmt, ast::Label breakExit ) : 61 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( IfStmt ) {} 62 Entry( const ast::CaseStmt *stmt, ast::Label fallExit ) : 63 stmt( stmt ), firstTarget( fallExit ), secondTarget(), kind( CaseStmt ) {} 64 Entry( const ast::SwitchStmt *stmt, ast::Label breakExit, ast::Label fallDefaultExit ) : 65 stmt( stmt ), firstTarget( breakExit ), secondTarget( fallDefaultExit ), kind( SwitchStmt ) {} 66 Entry( const ast::TryStmt *stmt, ast::Label breakExit ) : 67 stmt( stmt ), firstTarget( breakExit ), secondTarget(), kind( TryStmt ) {} 68 69 bool isContTarget() const { return kind <= WhileStmt; } 70 bool isBreakTarget() const { return CaseStmt != kind; } 71 bool isFallTarget() const { return CaseStmt == kind; } 72 bool isFallDefaultTarget() const { return SwitchStmt == kind; } 73 74 ast::Label useContExit() { assert( kind <= WhileStmt ); return useTarget(secondTarget); } 75 ast::Label useBreakExit() { assert( CaseStmt != kind ); return useTarget(firstTarget); } 76 ast::Label useFallExit() { assert( CaseStmt == kind ); return useTarget(firstTarget); } 77 ast::Label useFallDefaultExit() { assert( SwitchStmt == kind ); return useTarget(secondTarget); } 78 79 bool isContUsed() const { assert( kind <= WhileStmt ); return secondTarget.used; } 80 bool isBreakUsed() const { assert( CaseStmt != kind ); return firstTarget.used; } 81 bool isFallUsed() const { assert( CaseStmt == kind ); return firstTarget.used; } 82 bool isFallDefaultUsed() const { assert( SwitchStmt == kind ); return secondTarget.used; } 83 83 void seenDefault() { fallDefaultValid = false; } 84 84 bool isFallDefaultValid() const { return fallDefaultValid; } 85 85 }; 86 86 87 // Helper predicates used in find_if calls (it doesn't take methods):87 // Helper predicates used in std::find_if calls (it doesn't take methods): 88 88 bool isBreakTarget( const Entry & entry ) { 89 89 return entry.isBreakTarget(); … … 103 103 104 104 struct MultiLevelExitCore final : 105 publicWithVisitorRef<MultiLevelExitCore>,106 public WithShortCircuiting, publicWithGuards {105 public ast::WithVisitorRef<MultiLevelExitCore>, 106 public ast::WithShortCircuiting, public ast::WithGuards { 107 107 MultiLevelExitCore( const LabelToStmt & lt ); 108 108 109 void previsit( const FunctionDecl * );110 111 const CompoundStmt * previsit( constCompoundStmt * );112 const BranchStmt * postvisit( constBranchStmt * );113 void previsit( const WhileDoStmt * );114 const WhileDoStmt * postvisit( const WhileDoStmt * );115 void previsit( const ForStmt * );116 const ForStmt * postvisit( constForStmt * );117 const CaseStmt * previsit( constCaseStmt * );118 void previsit( const IfStmt * );119 const IfStmt * postvisit( constIfStmt * );120 void previsit( const SwitchStmt * );121 const SwitchStmt * postvisit( constSwitchStmt * );122 void previsit( const ReturnStmt * );123 void previsit( const TryStmt * );124 void postvisit( const TryStmt * );125 void previsit( const FinallyStmt * );126 127 const Stmt * mutateLoop( constStmt * body, Entry& );109 void previsit( const ast::FunctionDecl * ); 110 111 const ast::CompoundStmt * previsit( const ast::CompoundStmt * ); 112 const ast::BranchStmt * postvisit( const ast::BranchStmt * ); 113 void previsit( const ast::WhileStmt * ); 114 const ast::WhileStmt * postvisit( const ast::WhileStmt * ); 115 void previsit( const ast::ForStmt * ); 116 const ast::ForStmt * postvisit( const ast::ForStmt * ); 117 const ast::CaseStmt * previsit( const ast::CaseStmt * ); 118 void previsit( const ast::IfStmt * ); 119 const ast::IfStmt * postvisit( const ast::IfStmt * ); 120 void previsit( const ast::SwitchStmt * ); 121 const ast::SwitchStmt * postvisit( const ast::SwitchStmt * ); 122 void previsit( const ast::ReturnStmt * ); 123 void previsit( const ast::TryStmt * ); 124 void postvisit( const ast::TryStmt * ); 125 void previsit( const ast::FinallyStmt * ); 126 127 const ast::Stmt * mutateLoop( const ast::Stmt * body, Entry& ); 128 128 129 129 const LabelToStmt & target_table; 130 s et<Label> fallthrough_labels;131 vector<Entry> enclosing_control_structures;132 Label break_label;130 std::set<ast::Label> fallthrough_labels; 131 std::vector<Entry> enclosing_control_structures; 132 ast::Label break_label; 133 133 bool inFinally; 134 134 … … 138 138 const LoopNode * posthandleLoopStmt( const LoopNode * loopStmt ); 139 139 140 list<ptr<Stmt>> fixBlock(141 const list<ptr<Stmt>> & kids, bool caseClause );140 std::list<ast::ptr<ast::Stmt>> fixBlock( 141 const std::list<ast::ptr<ast::Stmt>> & kids, bool caseClause ); 142 142 143 143 template<typename UnaryPredicate> 144 144 auto findEnclosingControlStructure( UnaryPredicate pred ) { 145 return find_if( enclosing_control_structures.rbegin(),146 145 return std::find_if( enclosing_control_structures.rbegin(), 146 enclosing_control_structures.rend(), pred ); 147 147 } 148 148 }; 149 149 150 NullStmt * labelledNullStmt(151 const CodeLocation & cl, constLabel & label ) {152 return new NullStmt( cl, vector<Label>{ label } );150 ast::NullStmt * labelledNullStmt( 151 const CodeLocation & cl, const ast::Label & label ) { 152 return new ast::NullStmt( cl, std::vector<ast::Label>{ label } ); 153 153 } 154 154 … … 158 158 {} 159 159 160 void MultiLevelExitCore::previsit( const FunctionDecl * ) {160 void MultiLevelExitCore::previsit( const ast::FunctionDecl * ) { 161 161 visit_children = false; 162 162 } 163 163 164 const CompoundStmt * MultiLevelExitCore::previsit(165 constCompoundStmt * stmt ) {164 const ast::CompoundStmt * MultiLevelExitCore::previsit( 165 const ast::CompoundStmt * stmt ) { 166 166 visit_children = false; 167 168 // if the stmt is labelled then generate a label to check in postvisit if the label is used 169 bool isLabeled = ! stmt->labels.empty(); 167 bool isLabeled = !stmt->labels.empty(); 170 168 if ( isLabeled ) { 171 Label breakLabel =newLabel( "blockBreak", stmt );169 ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt ); 172 170 enclosing_control_structures.emplace_back( stmt, breakLabel ); 173 171 GuardAction( [this]() { enclosing_control_structures.pop_back(); } ); 174 172 } 175 173 176 auto mutStmt = mutate( stmt );174 auto mutStmt = ast::mutate( stmt ); 177 175 // A child statement may set the break label. 178 mutStmt->kids = move( fixBlock( stmt->kids, false ) );176 mutStmt->kids = std::move( fixBlock( stmt->kids, false ) ); 179 177 180 178 if ( isLabeled ) { 181 assert( ! 179 assert( !enclosing_control_structures.empty() ); 182 180 Entry & entry = enclosing_control_structures.back(); 183 if ( ! 181 if ( !entry.useBreakExit().empty() ) { 184 182 break_label = entry.useBreakExit(); 185 183 } … … 189 187 190 188 size_t getUnusedIndex( 191 const Stmt * stmt, constLabel & originalTarget ) {189 const ast::Stmt * stmt, const ast::Label & originalTarget ) { 192 190 const size_t size = stmt->labels.size(); 193 191 194 // If the label is empty, do not add unused attribute.195 192 // If the label is empty, we can skip adding the unused attribute: 193 if ( originalTarget.empty() ) return size; 196 194 197 195 // Search for a label that matches the originalTarget. 198 196 for ( size_t i = 0 ; i < size ; ++i ) { 199 const Label & label = stmt->labels[i];197 const ast::Label & label = stmt->labels[i]; 200 198 if ( label == originalTarget ) { 201 for ( const Attribute * attr : label.attributes ) {199 for ( const ast::Attribute * attr : label.attributes ) { 202 200 if ( attr->name == "unused" ) return size; 203 201 } … … 205 203 } 206 204 } 207 assertf( false, "C FA internal error: could not find label '%s' on statement %s",208 209 } 210 211 const Stmt * addUnused(212 const Stmt * stmt, constLabel & originalTarget ) {205 assertf( false, "Could not find label '%s' on statement %s", 206 originalTarget.name.c_str(), toString( stmt ).c_str() ); 207 } 208 209 const ast::Stmt * addUnused( 210 const ast::Stmt * stmt, const ast::Label & originalTarget ) { 213 211 size_t i = getUnusedIndex( stmt, originalTarget ); 214 212 if ( i == stmt->labels.size() ) { 215 213 return stmt; 216 214 } 217 Stmt * mutStmt =mutate( stmt );218 mutStmt->labels[i].attributes.push_back( new Attribute( "unused" ) );215 ast::Stmt * mutStmt = ast::mutate( stmt ); 216 mutStmt->labels[i].attributes.push_back( new ast::Attribute( "unused" ) ); 219 217 return mutStmt; 220 218 } 221 219 222 // This routine updates targets on enclosing control structures to indicate which 223 // label is used by the BranchStmt that is passed 224 const BranchStmt * MultiLevelExitCore::postvisit( const BranchStmt * stmt ) { 225 vector<Entry>::reverse_iterator targetEntry = 220 const ast::BranchStmt * MultiLevelExitCore::postvisit( const ast::BranchStmt * stmt ) { 221 std::vector<Entry>::reverse_iterator targetEntry = 226 222 enclosing_control_structures.rend(); 227 228 // Labels on different stmts require different approaches to access229 223 switch ( stmt->kind ) { 230 caseBranchStmt::Goto:224 case ast::BranchStmt::Goto: 231 225 return stmt; 232 case BranchStmt::Continue: 233 case BranchStmt::Break: { 234 bool isContinue = stmt->kind == BranchStmt::Continue; 235 // Handle unlabeled break and continue. 236 if ( stmt->target.empty() ) { 237 if ( isContinue ) { 238 targetEntry = findEnclosingControlStructure( isContinueTarget ); 239 } else { 240 if ( enclosing_control_structures.empty() ) { 241 SemanticError( stmt->location, 242 "'break' outside a loop, 'switch', or labelled block" ); 243 } 244 targetEntry = findEnclosingControlStructure( isBreakTarget ); 245 } 246 // Handle labeled break and continue. 247 } else { 248 // Lookup label in table to find attached control structure. 249 targetEntry = findEnclosingControlStructure( 250 [ targetStmt = target_table.at(stmt->target) ](auto entry){ 251 return entry.stmt == targetStmt; 252 } ); 253 } 254 // Ensure that selected target is valid. 255 if ( targetEntry == enclosing_control_structures.rend() || ( isContinue && ! isContinueTarget( *targetEntry ) ) ) { 256 SemanticError( stmt->location, toString( (isContinue ? "'continue'" : "'break'"), 257 " target must be an enclosing ", (isContinue ? "loop: " : "control structure: "), 258 stmt->originalTarget ) ); 259 } 260 break; 261 } 262 // handle fallthrough in case/switch stmts 263 case BranchStmt::FallThrough: { 264 targetEntry = findEnclosingControlStructure( isFallthroughTarget ); 265 // Check that target is valid. 266 if ( targetEntry == enclosing_control_structures.rend() ) { 267 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" ); 268 } 269 if ( ! stmt->target.empty() ) { 270 // Labelled fallthrough: target must be a valid fallthough label. 271 if ( ! fallthrough_labels.count( stmt->target ) ) { 272 SemanticError( stmt->location, toString( "'fallthrough' target must be a later case statement: ", 273 stmt->originalTarget ) ); 274 } 275 return new BranchStmt( stmt->location, BranchStmt::Goto, stmt->originalTarget ); 276 } 277 break; 278 } 279 case BranchStmt::FallThroughDefault: { 280 targetEntry = findEnclosingControlStructure( isFallthroughDefaultTarget ); 281 282 // Check if in switch or choose statement. 283 if ( targetEntry == enclosing_control_structures.rend() ) { 284 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" ); 285 } 286 287 // Check if switch or choose has default clause. 288 auto switchStmt = strict_dynamic_cast< const SwitchStmt * >( targetEntry->stmt ); 289 bool foundDefault = false; 290 for ( auto subStmt : switchStmt->stmts ) { 291 const CaseStmt * caseStmt = subStmt.strict_as<CaseStmt>(); 292 if ( caseStmt->isDefault() ) { 293 foundDefault = true; 294 break; 295 } 296 } 297 if ( ! foundDefault ) { 298 SemanticError( stmt->location, "'fallthrough default' must be enclosed in a 'switch' or 'choose'" 299 "control structure with a 'default' clause" ); 300 } 301 break; 302 } 303 default: 226 case ast::BranchStmt::Continue: 227 case ast::BranchStmt::Break: { 228 bool isContinue = stmt->kind == ast::BranchStmt::Continue; 229 // Handle unlabeled break and continue. 230 if ( stmt->target.empty() ) { 231 if ( isContinue ) { 232 targetEntry = findEnclosingControlStructure( isContinueTarget ); 233 } else { 234 if ( enclosing_control_structures.empty() ) { 235 SemanticError( stmt->location, 236 "'break' outside a loop, 'switch', or labelled block" ); 237 } 238 targetEntry = findEnclosingControlStructure( isBreakTarget ); 239 } 240 // Handle labeled break and continue. 241 } else { 242 // Lookup label in table to find attached control structure. 243 targetEntry = findEnclosingControlStructure( 244 [ targetStmt = target_table.at(stmt->target) ](auto entry){ 245 return entry.stmt == targetStmt; 246 } ); 247 } 248 // Ensure that selected target is valid. 249 if ( targetEntry == enclosing_control_structures.rend() || ( isContinue && !isContinueTarget( *targetEntry ) ) ) { 250 SemanticError( 251 stmt->location, 252 toString( (isContinue ? "'continue'" : "'break'"), 253 " target must be an enclosing ", 254 (isContinue ? "loop: " : "control structure: "), 255 stmt->originalTarget ) ); 256 } 257 break; 258 } 259 case ast::BranchStmt::FallThrough: { 260 targetEntry = findEnclosingControlStructure( isFallthroughTarget ); 261 // Check that target is valid. 262 if ( targetEntry == enclosing_control_structures.rend() ) { 263 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" ); 264 } 265 if ( !stmt->target.empty() ) { 266 // Labelled fallthrough: target must be a valid fallthough label. 267 if ( !fallthrough_labels.count( stmt->target ) ) { 268 SemanticError( stmt->location, toString( "'fallthrough' target must be a later case statement: ", stmt->originalTarget ) ); 269 } 270 return new ast::BranchStmt( 271 stmt->location, ast::BranchStmt::Goto, stmt->originalTarget ); 272 } 273 break; 274 } 275 case ast::BranchStmt::FallThroughDefault: { 276 targetEntry = findEnclosingControlStructure( isFallthroughDefaultTarget ); 277 278 // Check that this is in a switch or choose statement. 279 if ( targetEntry == enclosing_control_structures.rend() ) { 280 SemanticError( stmt->location, "'fallthrough' must be enclosed in a 'switch' or 'choose'" ); 281 } 282 283 // Check that the switch or choose has a default clause. 284 auto switchStmt = strict_dynamic_cast< const ast::SwitchStmt * >( 285 targetEntry->stmt ); 286 bool foundDefault = false; 287 for ( auto subStmt : switchStmt->stmts ) { 288 const ast::CaseStmt * caseStmt = subStmt.strict_as<ast::CaseStmt>(); 289 if ( caseStmt->isDefault() ) { 290 foundDefault = true; 291 break; 292 } 293 } 294 if ( !foundDefault ) { 295 SemanticError( stmt->location, "'fallthrough default' must be enclosed in a 'switch' or 'choose' control structure with a 'default' clause" ); 296 } 297 break; 298 } 299 default: 304 300 assert( false ); 305 301 } 306 302 307 // Branch error checks: get the appropriate label name, which is always replaced. 308 Label exitLabel( CodeLocation(), "" ); 303 // Branch error checks: get the appropriate label name: 304 // (This label will always be replaced.) 305 ast::Label exitLabel( CodeLocation(), "" ); 309 306 switch ( stmt->kind ) { 310 caseBranchStmt::Break:311 assert( ! 307 case ast::BranchStmt::Break: 308 assert( !targetEntry->useBreakExit().empty() ); 312 309 exitLabel = targetEntry->useBreakExit(); 313 310 break; 314 caseBranchStmt::Continue:315 assert( ! 311 case ast::BranchStmt::Continue: 312 assert( !targetEntry->useContExit().empty() ); 316 313 exitLabel = targetEntry->useContExit(); 317 314 break; 318 caseBranchStmt::FallThrough:319 assert( ! 315 case ast::BranchStmt::FallThrough: 316 assert( !targetEntry->useFallExit().empty() ); 320 317 exitLabel = targetEntry->useFallExit(); 321 318 break; 322 caseBranchStmt::FallThroughDefault:323 assert( ! 319 case ast::BranchStmt::FallThroughDefault: 320 assert( !targetEntry->useFallDefaultExit().empty() ); 324 321 exitLabel = targetEntry->useFallDefaultExit(); 325 322 // Check that fallthrough default comes before the default clause. 326 if ( ! targetEntry->isFallDefaultValid() ) { 327 SemanticError( stmt->location, "'fallthrough default' must precede the 'default' clause" ); 323 if ( !targetEntry->isFallDefaultValid() ) { 324 SemanticError( stmt->location, 325 "'fallthrough default' must precede the 'default' clause" ); 328 326 } 329 327 break; 330 328 default: 331 329 assert(0); 332 330 } … … 335 333 targetEntry->stmt = addUnused( targetEntry->stmt, stmt->originalTarget ); 336 334 337 // Replace withgoto to make later passes more uniform.338 return new BranchStmt( stmt->location,BranchStmt::Goto, exitLabel );339 } 340 341 void MultiLevelExitCore::previsit( const WhileDoStmt * stmt ) {335 // Replace this with a goto to make later passes more uniform. 336 return new ast::BranchStmt( stmt->location, ast::BranchStmt::Goto, exitLabel ); 337 } 338 339 void MultiLevelExitCore::previsit( const ast::WhileStmt * stmt ) { 342 340 return prehandleLoopStmt( stmt ); 343 341 } 344 342 345 const WhileDoStmt * MultiLevelExitCore::postvisit( const WhileDoStmt * stmt ) {343 const ast::WhileStmt * MultiLevelExitCore::postvisit( const ast::WhileStmt * stmt ) { 346 344 return posthandleLoopStmt( stmt ); 347 345 } 348 346 349 void MultiLevelExitCore::previsit( const ForStmt * stmt ) {347 void MultiLevelExitCore::previsit( const ast::ForStmt * stmt ) { 350 348 return prehandleLoopStmt( stmt ); 351 349 } 352 350 353 const ForStmt * MultiLevelExitCore::postvisit( constForStmt * stmt ) {351 const ast::ForStmt * MultiLevelExitCore::postvisit( const ast::ForStmt * stmt ) { 354 352 return posthandleLoopStmt( stmt ); 355 353 } … … 357 355 // Mimic what the built-in push_front would do anyways. It is O(n). 358 356 void push_front( 359 vector<ptr<Stmt>> & vec, constStmt * element ) {357 std::vector<ast::ptr<ast::Stmt>> & vec, const ast::Stmt * element ) { 360 358 vec.emplace_back( nullptr ); 361 359 for ( size_t i = vec.size() - 1 ; 0 < i ; --i ) { 362 vec[ i ] = move( vec[ i - 1 ] );360 vec[ i ] = std::move( vec[ i - 1 ] ); 363 361 } 364 362 vec[ 0 ] = element; 365 363 } 366 364 367 const CaseStmt * MultiLevelExitCore::previsit( constCaseStmt * stmt ) {365 const ast::CaseStmt * MultiLevelExitCore::previsit( const ast::CaseStmt * stmt ) { 368 366 visit_children = false; 369 367 370 // If default, markseen.368 // If it is the default, mark the default as seen. 371 369 if ( stmt->isDefault() ) { 372 assert( ! 370 assert( !enclosing_control_structures.empty() ); 373 371 enclosing_control_structures.back().seenDefault(); 374 372 } 375 373 376 374 // The cond may not exist, but if it does update it now. 377 visitor->maybe_accept( stmt, & CaseStmt::cond );375 visitor->maybe_accept( stmt, &ast::CaseStmt::cond ); 378 376 379 377 // Just save the mutated node for simplicity. 380 CaseStmt * mutStmt =mutate( stmt );381 382 Label fallLabel =newLabel( "fallThrough", stmt );383 if ( ! 378 ast::CaseStmt * mutStmt = ast::mutate( stmt ); 379 380 ast::Label fallLabel = LabelGenerator::newLabel( "fallThrough", stmt ); 381 if ( !mutStmt->stmts.empty() ) { 384 382 // Ensure that the stack isn't corrupted by exceptions in fixBlock. 385 383 auto guard = makeFuncGuard( 386 384 [&](){ enclosing_control_structures.emplace_back( mutStmt, fallLabel ); }, 387 385 [this](){ enclosing_control_structures.pop_back(); } 388 386 ); 389 387 390 388 // These should already be in a block. 391 auto block = mutate( mutStmt->stmts.front().strict_as<CompoundStmt>() );389 auto block = ast::mutate( mutStmt->stmts.front().strict_as<ast::CompoundStmt>() ); 392 390 block->kids = fixBlock( block->kids, true ); 393 391 394 392 // Add fallthrough label if necessary. 395 assert( ! 393 assert( !enclosing_control_structures.empty() ); 396 394 Entry & entry = enclosing_control_structures.back(); 397 395 if ( entry.isFallUsed() ) { 398 mutStmt->stmts.push_back( labelledNullStmt( mutStmt->location, entry.useFallExit() ) ); 399 } 400 } 401 assert( ! enclosing_control_structures.empty() ); 396 mutStmt->stmts.push_back( 397 labelledNullStmt( mutStmt->location, entry.useFallExit() ) ); 398 } 399 } 400 assert( !enclosing_control_structures.empty() ); 402 401 Entry & entry = enclosing_control_structures.back(); 403 assertf( dynamic_cast< const SwitchStmt * >( entry.stmt ),404 "CFA internal error: control structure enclosing a case clause must be a switch, but is: %s",405 402 assertf( dynamic_cast< const ast::SwitchStmt * >( entry.stmt ), 403 "Control structure enclosing a case clause must be a switch, but is: %s", 404 toString( entry.stmt ).c_str() ); 406 405 if ( mutStmt->isDefault() ) { 407 406 if ( entry.isFallDefaultUsed() ) { 408 407 // Add fallthrough default label if necessary. 409 push_front( mutStmt->stmts, labelledNullStmt( stmt->location, entry.useFallDefaultExit() ) ); 408 push_front( mutStmt->stmts, labelledNullStmt( 409 stmt->location, entry.useFallDefaultExit() 410 ) ); 410 411 } 411 412 } … … 413 414 } 414 415 415 void MultiLevelExitCore::previsit( const IfStmt * stmt ) {416 bool labeledBlock = ! 416 void MultiLevelExitCore::previsit( const ast::IfStmt * stmt ) { 417 bool labeledBlock = !stmt->labels.empty(); 417 418 if ( labeledBlock ) { 418 Label breakLabel =newLabel( "blockBreak", stmt );419 ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt ); 419 420 enclosing_control_structures.emplace_back( stmt, breakLabel ); 420 421 GuardAction( [this](){ enclosing_control_structures.pop_back(); } ); … … 422 423 } 423 424 424 const IfStmt * MultiLevelExitCore::postvisit( constIfStmt * stmt ) {425 bool labeledBlock = ! 425 const ast::IfStmt * MultiLevelExitCore::postvisit( const ast::IfStmt * stmt ) { 426 bool labeledBlock = !stmt->labels.empty(); 426 427 if ( labeledBlock ) { 427 428 auto this_label = enclosing_control_structures.back().useBreakExit(); 428 if ( ! 429 if ( !this_label.empty() ) { 429 430 break_label = this_label; 430 431 } … … 433 434 } 434 435 435 bool isDefaultCase( const ptr<Stmt> & stmt ) {436 const CaseStmt * caseStmt = stmt.strict_as<CaseStmt>();436 bool isDefaultCase( const ast::ptr<ast::Stmt> & stmt ) { 437 const ast::CaseStmt * caseStmt = stmt.strict_as<ast::CaseStmt>(); 437 438 return caseStmt->isDefault(); 438 439 } 439 440 440 void MultiLevelExitCore::previsit( const SwitchStmt * stmt ) { 441 Label label = newLabel( "switchBreak", stmt ); 442 auto it = find_if( stmt->stmts.rbegin(), stmt->stmts.rend(), isDefaultCase ); 443 444 const CaseStmt * defaultCase = it != stmt->stmts.rend() ? (it)->strict_as<CaseStmt>() : nullptr; 445 Label defaultLabel = defaultCase ? newLabel( "fallThroughDefault", defaultCase ) : Label( stmt->location, "" ); 441 void MultiLevelExitCore::previsit( const ast::SwitchStmt * stmt ) { 442 ast::Label label = LabelGenerator::newLabel( "switchBreak", stmt ); 443 auto it = std::find_if( stmt->stmts.rbegin(), stmt->stmts.rend(), isDefaultCase ); 444 445 const ast::CaseStmt * defaultCase = it != stmt->stmts.rend() 446 ? (it)->strict_as<ast::CaseStmt>() : nullptr; 447 ast::Label defaultLabel = defaultCase 448 ? LabelGenerator::newLabel( "fallThroughDefault", defaultCase ) 449 : ast::Label( stmt->location, "" ); 446 450 enclosing_control_structures.emplace_back( stmt, label, defaultLabel ); 447 451 GuardAction( [this]() { enclosing_control_structures.pop_back(); } ); 448 452 449 // Collect valid labels for fallthrough. It starts with all labels at this level, then remove as each is seen during450 // t raversal.451 for ( const Stmt * stmt : stmt->stmts ) {452 auto * caseStmt = strict_dynamic_cast< const CaseStmt * >( stmt );453 // Collect valid labels for fallthrough. It starts with all labels at 454 // this level, then removed as we see them in traversal. 455 for ( const ast::Stmt * stmt : stmt->stmts ) { 456 auto * caseStmt = strict_dynamic_cast< const ast::CaseStmt * >( stmt ); 453 457 if ( caseStmt->stmts.empty() ) continue; 454 auto block = caseStmt->stmts.front().strict_as< CompoundStmt>();455 for ( const Stmt * stmt : block->kids ) {456 for ( const Label & l : stmt->labels ) {458 auto block = caseStmt->stmts.front().strict_as<ast::CompoundStmt>(); 459 for ( const ast::Stmt * stmt : block->kids ) { 460 for ( const ast::Label & l : stmt->labels ) { 457 461 fallthrough_labels.insert( l ); 458 462 } … … 461 465 } 462 466 463 const SwitchStmt * MultiLevelExitCore::postvisit( constSwitchStmt * stmt ) {464 assert( ! 467 const ast::SwitchStmt * MultiLevelExitCore::postvisit( const ast::SwitchStmt * stmt ) { 468 assert( !enclosing_control_structures.empty() ); 465 469 Entry & entry = enclosing_control_structures.back(); 466 470 assert( entry.stmt == stmt ); 467 471 468 // Only run to generate the break label.472 // Only run if we need to generate the break label. 469 473 if ( entry.isBreakUsed() ) { 470 // To keep the switch statements uniform (all direct children of a SwitchStmt should be CastStmts), append the 471 // exit label and break to the last case, create a default case if no cases. 472 SwitchStmt * mutStmt = mutate( stmt ); 474 // To keep the switch statements uniform (all direct children of a 475 // SwitchStmt should be CastStmts), append the exit label and break 476 // to the last case, create a default case is there are no cases. 477 ast::SwitchStmt * mutStmt = ast::mutate( stmt ); 473 478 if ( mutStmt->stmts.empty() ) { 474 mutStmt->stmts.push_back( new CaseStmt( mutStmt->location, nullptr, {} ) ); 475 } 476 477 auto caseStmt = mutStmt->stmts.back().strict_as<CaseStmt>(); 478 auto mutCase = mutate( caseStmt ); 479 mutStmt->stmts.push_back( new ast::CaseStmt( 480 mutStmt->location, nullptr, {} )); 481 } 482 483 auto caseStmt = mutStmt->stmts.back().strict_as<ast::CaseStmt>(); 484 auto mutCase = ast::mutate( caseStmt ); 479 485 mutStmt->stmts.back() = mutCase; 480 486 481 Label label( mutCase->location, "breakLabel" );482 auto branch = new BranchStmt( mutCase->location,BranchStmt::Break, label );487 ast::Label label( mutCase->location, "breakLabel" ); 488 auto branch = new ast::BranchStmt( mutCase->location, ast::BranchStmt::Break, label ); 483 489 branch->labels.push_back( entry.useBreakExit() ); 484 490 mutCase->stmts.push_back( branch ); … … 489 495 } 490 496 491 void MultiLevelExitCore::previsit( const ReturnStmt * stmt ) {497 void MultiLevelExitCore::previsit( const ast::ReturnStmt * stmt ) { 492 498 if ( inFinally ) { 493 499 SemanticError( stmt->location, "'return' may not appear in a finally clause" ); … … 495 501 } 496 502 497 void MultiLevelExitCore::previsit( const TryStmt * stmt ) {498 bool isLabeled = ! 503 void MultiLevelExitCore::previsit( const ast::TryStmt * stmt ) { 504 bool isLabeled = !stmt->labels.empty(); 499 505 if ( isLabeled ) { 500 Label breakLabel =newLabel( "blockBreak", stmt );506 ast::Label breakLabel = LabelGenerator::newLabel( "blockBreak", stmt ); 501 507 enclosing_control_structures.emplace_back( stmt, breakLabel ); 502 508 GuardAction([this](){ enclosing_control_structures.pop_back(); } ); … … 504 510 } 505 511 506 void MultiLevelExitCore::postvisit( const TryStmt * stmt ) {507 bool isLabeled = ! 512 void MultiLevelExitCore::postvisit( const ast::TryStmt * stmt ) { 513 bool isLabeled = !stmt->labels.empty(); 508 514 if ( isLabeled ) { 509 515 auto this_label = enclosing_control_structures.back().useBreakExit(); 510 if ( ! 516 if ( !this_label.empty() ) { 511 517 break_label = this_label; 512 518 } … … 514 520 } 515 521 516 void MultiLevelExitCore::previsit( const FinallyStmt * ) { 517 GuardAction([this, old = move( enclosing_control_structures)](){ enclosing_control_structures = move(old); }); 518 enclosing_control_structures = vector<Entry>(); 522 void MultiLevelExitCore::previsit( const ast::FinallyStmt * ) { 523 GuardAction([this, old = std::move(enclosing_control_structures)](){ 524 enclosing_control_structures = std::move(old); 525 }); 526 enclosing_control_structures = std::vector<Entry>(); 519 527 GuardValue( inFinally ) = true; 520 528 } 521 529 522 const Stmt * MultiLevelExitCore::mutateLoop(523 constStmt * body, Entry & entry ) {530 const ast::Stmt * MultiLevelExitCore::mutateLoop( 531 const ast::Stmt * body, Entry & entry ) { 524 532 if ( entry.isBreakUsed() ) { 525 533 break_label = entry.useBreakExit(); 526 534 } 527 535 528 // if continue is used insert a continue label into the back of the body of the loop529 536 if ( entry.isContUsed() ) { 530 CompoundStmt * new_body = new CompoundStmt( body->location ); 531 // {} 537 ast::CompoundStmt * new_body = new ast::CompoundStmt( body->location ); 532 538 new_body->kids.push_back( body ); 533 // {534 // body535 // }536 539 new_body->kids.push_back( 537 540 labelledNullStmt( body->location, entry.useContExit() ) ); 538 // {539 // body540 // ContinueLabel: {}541 // }542 541 return new_body; 543 542 } … … 550 549 // Remember is loop before going onto mutate the body. 551 550 // The labels will be folded in if they are used. 552 Label breakLabel =newLabel( "loopBreak", loopStmt );553 Label contLabel =newLabel( "loopContinue", loopStmt );551 ast::Label breakLabel = LabelGenerator::newLabel( "loopBreak", loopStmt ); 552 ast::Label contLabel = LabelGenerator::newLabel( "loopContinue", loopStmt ); 554 553 enclosing_control_structures.emplace_back( loopStmt, breakLabel, contLabel ); 555 // labels are added temporarily to see if they are used and then added permanently in postvisit if ther are used556 // children will tag labels as being used during their traversal which occurs before postvisit557 558 // GuardAction calls the lambda after the node is done being visited559 554 GuardAction( [this](){ enclosing_control_structures.pop_back(); } ); 560 555 } … … 562 557 template<typename LoopNode> 563 558 const LoopNode * MultiLevelExitCore::posthandleLoopStmt( const LoopNode * loopStmt ) { 564 assert( ! 559 assert( !enclosing_control_structures.empty() ); 565 560 Entry & entry = enclosing_control_structures.back(); 566 561 assert( entry.stmt == loopStmt ); 567 562 568 // Now check if the labels are used and add them if so. 569 return mutate_field( loopStmt, &LoopNode::body, mutateLoop( loopStmt->body, entry ) ); 570 // this call to mutate_field compares loopStmt->body and the result of mutateLoop 571 // if they are the same the node isn't mutated, if they differ then the new mutated node is returned 572 // the stmts will only differ if a label is used 573 } 574 575 list<ptr<Stmt>> MultiLevelExitCore::fixBlock( 576 const list<ptr<Stmt>> & kids, bool is_case_clause ) { 577 // Unfortunately cannot use automatic error collection. 563 // Now we check if the labels are used and add them if so. 564 return ast::mutate_field( 565 loopStmt, &LoopNode::body, mutateLoop( loopStmt->body, entry ) ); 566 } 567 568 std::list<ast::ptr<ast::Stmt>> MultiLevelExitCore::fixBlock( 569 const std::list<ast::ptr<ast::Stmt>> & kids, bool is_case_clause ) { 570 // Unfortunately we can't use the automatic error collection. 578 571 SemanticErrorException errors; 579 572 580 list<ptr<Stmt>> ret;573 std::list<ast::ptr<ast::Stmt>> ret; 581 574 582 575 // Manually visit each child. 583 for ( const ptr<Stmt> & kid : kids ) {576 for ( const ast::ptr<ast::Stmt> & kid : kids ) { 584 577 if ( is_case_clause ) { 585 578 // Once a label is seen, it's no longer a valid for fallthrough. 586 for ( const Label & l : kid->labels ) {579 for ( const ast::Label & l : kid->labels ) { 587 580 fallthrough_labels.erase( l ); 588 581 } … … 595 588 } 596 589 597 if ( ! break_label.empty() ) { 598 ret.push_back( labelledNullStmt( ret.back()->location, break_label ) ); 599 break_label = Label( CodeLocation(), "" ); 600 } 601 } 602 603 if ( ! errors.isEmpty() ) { 590 if ( !break_label.empty() ) { 591 ret.push_back( 592 labelledNullStmt( ret.back()->location, break_label ) ); 593 break_label = ast::Label( CodeLocation(), "" ); 594 } 595 } 596 597 if ( !errors.isEmpty() ) { 604 598 throw errors; 605 599 } … … 607 601 } 608 602 609 const CompoundStmt * multiLevelExitUpdate( 610 const CompoundStmt * stmt, 611 const LabelToStmt & labelTable ) { 603 } // namespace 604 605 const ast::CompoundStmt * multiLevelExitUpdate( 606 const ast::CompoundStmt * stmt, 607 const LabelToStmt & labelTable ) { 612 608 // Must start in the body, so FunctionDecls can be a stopping point. 613 Pass<MultiLevelExitCore> visitor( labelTable );614 const CompoundStmt * ret = stmt->accept( visitor );609 ast::Pass<MultiLevelExitCore> visitor( labelTable ); 610 const ast::CompoundStmt * ret = stmt->accept( visitor ); 615 611 return ret; 616 612 } 613 617 614 } // namespace ControlStruct 618 615 -
src/ControlStruct/MultiLevelExit.hpp
rf5a51db r97c215f 9 9 // Author : Andrew Beach 10 10 // Created On : Mon Nov 1 13:49:00 2021 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Mon Jan 31 22:34:06 202213 // Update Count : 611 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Nov 8 10:53:00 2021 13 // Update Count : 3 14 14 // 15 15 … … 19 19 20 20 namespace ast { 21 class CompoundStmt;22 class Label;23 class Stmt;21 class CompoundStmt; 22 class Label; 23 class Stmt; 24 24 } 25 25 26 26 namespace ControlStruct { 27 27 28 using LabelToStmt = std::map<ast::Label, const ast::Stmt *>; 28 29 29 // Mutate a function body to handle multi-level exits. 30 const ast::CompoundStmt * multiLevelExitUpdate( const ast::CompoundStmt *, const LabelToStmt & ); 30 /// Mutate a function body to handle multi-level exits. 31 const ast::CompoundStmt * multiLevelExitUpdate( 32 const ast::CompoundStmt *, const LabelToStmt & ); 33 31 34 } 32 35 -
src/ControlStruct/module.mk
rf5a51db r97c215f 10 10 ## Author : Richard C. Bilson 11 11 ## Created On : Mon Jun 1 17:49:17 2015 12 ## Last Modified By : Peter A. Buhr13 ## Last Modified On : Sat Jan 29 12:04:19 202214 ## Update Count : 712 ## Last Modified By : Henry Xue 13 ## Last Modified On : Tue Jul 20 04:10:50 2021 14 ## Update Count : 5 15 15 ############################################################################### 16 16 … … 22 22 ControlStruct/ForExprMutator.cc \ 23 23 ControlStruct/ForExprMutator.h \ 24 ControlStruct/HoistControlDecls.cpp \25 ControlStruct/HoistControlDecls.hpp \26 24 ControlStruct/LabelFixer.cc \ 27 25 ControlStruct/LabelFixer.h \ 28 26 ControlStruct/LabelGenerator.cc \ 29 27 ControlStruct/LabelGenerator.h \ 30 ControlStruct/LabelGeneratorNew.cpp \31 ControlStruct/LabelGeneratorNew.hpp \32 28 ControlStruct/MLEMutator.cc \ 33 29 ControlStruct/MLEMutator.h \ -
src/InitTweak/InitTweak.cc
rf5a51db r97c215f 10 10 // Created On : Fri May 13 11:26:36 2016 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Dec 6 13:21:00 202113 // Update Count : 2012 // Last Modified On : Fri Nov 19 19:22:00 2021 13 // Update Count : 19 14 14 // 15 15 … … 1191 1191 } 1192 1192 1193 bool isAssignment( const ast::FunctionDecl * decl ) { 1194 return isAssignment( decl->name ) && isCopyFunction( decl ); 1195 } 1196 1197 bool isDestructor( const ast::FunctionDecl * decl ) { 1198 return isDestructor( decl->name ); 1199 } 1200 1201 bool isDefaultConstructor( const ast::FunctionDecl * decl ) { 1202 return isConstructor( decl->name ) && 1 == decl->params.size(); 1203 } 1204 1205 bool isCopyConstructor( const ast::FunctionDecl * decl ) { 1206 return isConstructor( decl->name ) && 2 == decl->params.size(); 1207 } 1208 1209 bool isCopyFunction( const ast::FunctionDecl * decl ) { 1210 const ast::FunctionType * ftype = decl->type; 1211 if ( ftype->params.size() != 2 ) return false; 1212 1213 const ast::Type * t1 = getPointerBase( ftype->params.front() ); 1214 if ( ! t1 ) return false; 1215 const ast::Type * t2 = ftype->params.back(); 1216 1217 return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} ); 1218 } 1193 bool isCopyFunction( const ast::FunctionDecl * decl ) { 1194 const ast::FunctionType * ftype = decl->type; 1195 if ( ftype->params.size() != 2 ) return false; 1196 1197 const ast::Type * t1 = getPointerBase( ftype->params.front() ); 1198 if ( ! t1 ) return false; 1199 const ast::Type * t2 = ftype->params.back(); 1200 1201 return ResolvExpr::typesCompatibleIgnoreQualifiers( t1, t2, ast::SymbolTable{} ); 1202 } 1219 1203 1220 1204 const FunctionDecl * isAssignment( const Declaration * decl ) { -
src/InitTweak/InitTweak.h
rf5a51db r97c215f 10 10 // Created On : Fri May 13 11:26:36 2016 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Dec 6 13:20:00 202113 // Update Count : 812 // Last Modified On : Fri Nov 19 14:18:00 2021 13 // Update Count : 7 14 14 // 15 15 … … 31 31 const FunctionDecl * isCopyConstructor( const Declaration * decl ); 32 32 const FunctionDecl * isCopyFunction( const Declaration * decl, const std::string & fname ); 33 bool isAssignment( const ast::FunctionDecl * decl );34 bool isDestructor( const ast::FunctionDecl * decl );35 bool isDefaultConstructor( const ast::FunctionDecl * decl );36 bool isCopyConstructor( const ast::FunctionDecl * decl );37 33 bool isCopyFunction( const ast::FunctionDecl * decl ); 38 34 -
src/Parser/ParseNode.h
rf5a51db r97c215f 10 10 // Created On : Sat May 16 13:28:16 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 2 09:15:49 202213 // Update Count : 90 512 // Last Modified On : Wed Jul 14 17:28:53 2021 13 // Update Count : 900 14 14 // 15 15 … … 390 390 Statement * build_expr( ExpressionNode * ctl ); 391 391 392 struct CondCtl {393 CondCtl( DeclarationNode * decl, ExpressionNode * condition ) :392 struct IfCtrl { 393 IfCtrl( DeclarationNode * decl, ExpressionNode * condition ) : 394 394 init( decl ? new StatementNode( decl ) : nullptr ), condition( condition ) {} 395 395 … … 409 409 }; 410 410 411 Expression * build_if_control( CondCtl * ctl, std::list< Statement * > & init );412 Statement * build_if( CondCtl * ctl, StatementNode * then, StatementNode * else_);411 Expression * build_if_control( IfCtrl * ctl, std::list< Statement * > & init ); 412 Statement * build_if( IfCtrl * ctl, StatementNode * then_stmt, StatementNode * else_stmt ); 413 413 Statement * build_switch( bool isSwitch, ExpressionNode * ctl, StatementNode * stmt ); 414 414 Statement * build_case( ExpressionNode * ctl ); 415 415 Statement * build_default(); 416 Statement * build_while( CondCtl * ctl, StatementNode * stmt, StatementNode * else_ = nullptr);417 Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt , StatementNode * else_ = nullptr);418 Statement * build_for( ForCtrl * forctl, StatementNode * stmt , StatementNode * else_ = nullptr);416 Statement * build_while( IfCtrl * ctl, StatementNode * stmt ); 417 Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt ); 418 Statement * build_for( ForCtrl * forctl, StatementNode * stmt ); 419 419 Statement * build_branch( BranchStmt::Type kind ); 420 420 Statement * build_branch( std::string * identifier, BranchStmt::Type kind ); … … 424 424 Statement * build_resume( ExpressionNode * ctl ); 425 425 Statement * build_resume_at( ExpressionNode * ctl , ExpressionNode * target ); 426 Statement * build_try( StatementNode * try_ , StatementNode * catch_, StatementNode * finally_);427 Statement * build_catch( CatchStmt::Kind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode *body );426 Statement * build_try( StatementNode * try_stmt, StatementNode * catch_stmt, StatementNode * finally_stmt ); 427 Statement * build_catch( CatchStmt::Kind kind, DeclarationNode *decl, ExpressionNode *cond, StatementNode *body ); 428 428 Statement * build_finally( StatementNode * stmt ); 429 429 Statement * build_compound( StatementNode * first ); -
src/Parser/StatementNode.cc
rf5a51db r97c215f 5 5 // file "LICENCE" distributed with Cforall. 6 6 // 7 // StatementNode.cc -- Transform from parse data-structures to AST data-structures, usually deleting the parse 8 // data-structure after the transformation. 7 // StatementNode.cc -- 9 8 // 10 9 // Author : Rodolfo G. Esteves 11 10 // Created On : Sat May 16 14:59:41 2015 12 11 // Last Modified By : Peter A. Buhr 13 // Last Modified On : Wed Feb 2 20:29:30 202214 // Update Count : 42512 // Last Modified On : Sat Oct 24 04:20:55 2020 13 // Update Count : 383 15 14 // 16 15 … … 64 63 // convert from StatementNode list to Statement list 65 64 StatementNode * node = dynamic_cast< StatementNode * >(prev); 66 list< Statement * > stmts;65 std::list< Statement * > stmts; 67 66 buildMoveList( stmt, stmts ); 68 67 // splice any new Statements to end of current Statements … … 79 78 } // build_expr 80 79 81 Expression * build_if_control( CondCtl * ctl,list< Statement * > & init ) {80 Expression * build_if_control( IfCtrl * ctl, std::list< Statement * > & init ) { 82 81 if ( ctl->init != 0 ) { 83 82 buildMoveList( ctl->init, init ); … … 101 100 } // build_if_control 102 101 103 Statement * build_if( CondCtl * ctl, StatementNode * then, StatementNode * else_ ) { 104 list< Statement * > astinit; // maybe empty 105 Expression * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set 106 107 Statement * astthen, * astelse = nullptr; 108 list< Statement * > aststmt; 109 buildMoveList< Statement, StatementNode >( then, aststmt ); 110 assert( aststmt.size() == 1 ); 111 astthen = aststmt.front(); 112 113 if ( else_ ) { 114 list< Statement * > aststmt; 115 buildMoveList< Statement, StatementNode >( else_, aststmt ); 116 assert( aststmt.size() == 1 ); 117 astelse = aststmt.front(); 118 } // if 119 120 return new IfStmt( astcond, astthen, astelse, astinit ); 102 Statement * build_if( IfCtrl * ctl, StatementNode * then_stmt, StatementNode * else_stmt ) { 103 Statement * thenb, * elseb = nullptr; 104 std::list< Statement * > branches; 105 buildMoveList< Statement, StatementNode >( then_stmt, branches ); 106 assert( branches.size() == 1 ); 107 thenb = branches.front(); 108 109 if ( else_stmt ) { 110 std::list< Statement * > branches; 111 buildMoveList< Statement, StatementNode >( else_stmt, branches ); 112 assert( branches.size() == 1 ); 113 elseb = branches.front(); 114 } // if 115 116 std::list< Statement * > init; 117 Expression * cond = build_if_control( ctl, init ); 118 return new IfStmt( cond, thenb, elseb, init ); 121 119 } // build_if 122 120 123 121 Statement * build_switch( bool isSwitch, ExpressionNode * ctl, StatementNode * stmt ) { 124 list< Statement * > aststmt;125 buildMoveList< Statement, StatementNode >( stmt, aststmt);126 if ( ! isSwitch ) { // choose statement127 for ( Statement * stmt : aststmt) {122 std::list< Statement * > branches; 123 buildMoveList< Statement, StatementNode >( stmt, branches ); 124 if ( ! isSwitch ) { // choose statement 125 for ( Statement * stmt : branches ) { 128 126 CaseStmt * caseStmt = strict_dynamic_cast< CaseStmt * >( stmt ); 129 127 if ( ! caseStmt->stmts.empty() ) { // code after "case" => end of case list … … 133 131 } // for 134 132 } // if 135 // aststmt.size() == 0 for switch (...) {}, i.e., no declaration or statements136 return new SwitchStmt( maybeMoveBuild< Expression >(ctl), aststmt);133 // branches.size() == 0 for switch (...) {}, i.e., no declaration or statements 134 return new SwitchStmt( maybeMoveBuild< Expression >(ctl), branches ); 137 135 } // build_switch 138 136 139 137 Statement * build_case( ExpressionNode * ctl ) { 140 return new CaseStmt( maybeMoveBuild< Expression >(ctl), {} ); // stmt starts empty and then added to 138 std::list< Statement * > branches; 139 return new CaseStmt( maybeMoveBuild< Expression >(ctl), branches ); 141 140 } // build_case 142 141 143 142 Statement * build_default() { 144 return new CaseStmt( nullptr, {}, true ); // stmt starts empty and then added to 143 std::list< Statement * > branches; 144 return new CaseStmt( nullptr, branches, true ); 145 145 } // build_default 146 146 147 Statement * build_while( CondCtl * ctl, StatementNode * stmt, StatementNode * else_ ) { 148 list< Statement * > astinit; // maybe empty 149 Expression * astcond = build_if_control( ctl, astinit ); // ctl deleted, cond/init set 150 151 list< Statement * > aststmt; // loop body, compound created if empty 152 buildMoveList< Statement, StatementNode >( stmt, aststmt ); 153 assert( aststmt.size() == 1 ); 154 155 list< Statement * > astelse; // else clause, maybe empty 156 buildMoveList< Statement, StatementNode >( else_, astelse ); 157 158 return new WhileDoStmt( astcond, aststmt.front(), astelse.front(), astinit, false ); 147 Statement * build_while( IfCtrl * ctl, StatementNode * stmt ) { 148 std::list< Statement * > branches; 149 buildMoveList< Statement, StatementNode >( stmt, branches ); 150 assert( branches.size() == 1 ); 151 152 std::list< Statement * > init; 153 Expression * cond = build_if_control( ctl, init ); 154 return new WhileStmt( cond, branches.front(), init, false ); 159 155 } // build_while 160 156 161 Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt, StatementNode * else_ ) { 162 list< Statement * > aststmt; // loop body, compound created if empty 163 buildMoveList< Statement, StatementNode >( stmt, aststmt ); 164 assert( aststmt.size() == 1 ); // compound created if empty 165 166 list< Statement * > astelse; // else clause, maybe empty 167 buildMoveList< Statement, StatementNode >( else_, astelse ); 168 169 // do-while cannot have declarations in the contitional, so init is always empty 170 return new WhileDoStmt( notZeroExpr( maybeMoveBuild< Expression >(ctl) ), aststmt.front(), astelse.front(), {}, true ); 157 Statement * build_do_while( ExpressionNode * ctl, StatementNode * stmt ) { 158 std::list< Statement * > branches; 159 buildMoveList< Statement, StatementNode >( stmt, branches ); 160 assert( branches.size() == 1 ); 161 162 std::list< Statement * > init; 163 return new WhileStmt( notZeroExpr( maybeMoveBuild< Expression >(ctl) ), branches.front(), init, true ); 171 164 } // build_do_while 172 165 173 Statement * build_for( ForCtrl * forctl, StatementNode * stmt, StatementNode * else_ ) { 174 list< Statement * > astinit; // maybe empty 175 buildMoveList( forctl->init, astinit ); 176 177 Expression * astcond = nullptr; // maybe empty 178 astcond = notZeroExpr( maybeMoveBuild< Expression >(forctl->condition) ); 179 180 Expression * astincr = nullptr; // maybe empty 181 astincr = maybeMoveBuild< Expression >(forctl->change); 166 Statement * build_for( ForCtrl * forctl, StatementNode * stmt ) { 167 std::list< Statement * > branches; 168 buildMoveList< Statement, StatementNode >( stmt, branches ); 169 assert( branches.size() == 1 ); 170 171 std::list< Statement * > init; 172 if ( forctl->init != 0 ) { 173 buildMoveList( forctl->init, init ); 174 } // if 175 176 Expression * cond = 0; 177 if ( forctl->condition != 0 ) 178 cond = notZeroExpr( maybeMoveBuild< Expression >(forctl->condition) ); 179 180 Expression * incr = 0; 181 if ( forctl->change != 0 ) 182 incr = maybeMoveBuild< Expression >(forctl->change); 183 182 184 delete forctl; 183 184 list< Statement * > aststmt; // loop body, compound created if empty 185 buildMoveList< Statement, StatementNode >( stmt, aststmt ); 186 assert( aststmt.size() == 1 ); 187 188 list< Statement * > astelse; // else clause, maybe empty 189 buildMoveList< Statement, StatementNode >( else_, astelse ); 190 191 return new ForStmt( astinit, astcond, astincr, aststmt.front(), astelse.front() ); 185 return new ForStmt( init, cond, incr, branches.front() ); 192 186 } // build_for 193 187 … … 197 191 } // build_branch 198 192 199 Statement * build_branch( st ring * identifier, BranchStmt::Type kind ) {193 Statement * build_branch( std::string * identifier, BranchStmt::Type kind ) { 200 194 Statement * ret = new BranchStmt( * identifier, kind ); 201 195 delete identifier; // allocated by lexer … … 208 202 209 203 Statement * build_return( ExpressionNode * ctl ) { 210 list< Expression * > exps;204 std::list< Expression * > exps; 211 205 buildMoveList( ctl, exps ); 212 206 return new ReturnStmt( exps.size() > 0 ? exps.back() : nullptr ); … … 214 208 215 209 Statement * build_throw( ExpressionNode * ctl ) { 216 list< Expression * > exps;210 std::list< Expression * > exps; 217 211 buildMoveList( ctl, exps ); 218 assertf( exps.size() < 2, " CFA internal error: leaking memory");212 assertf( exps.size() < 2, "This means we are leaking memory"); 219 213 return new ThrowStmt( ThrowStmt::Terminate, !exps.empty() ? exps.back() : nullptr ); 220 214 } // build_throw 221 215 222 216 Statement * build_resume( ExpressionNode * ctl ) { 223 list< Expression * > exps;217 std::list< Expression * > exps; 224 218 buildMoveList( ctl, exps ); 225 assertf( exps.size() < 2, " CFA internal error: leaking memory");219 assertf( exps.size() < 2, "This means we are leaking memory"); 226 220 return new ThrowStmt( ThrowStmt::Resume, !exps.empty() ? exps.back() : nullptr ); 227 221 } // build_resume … … 233 227 } // build_resume_at 234 228 235 Statement * build_try( StatementNode * try_ , StatementNode * catch_, StatementNode * finally_) {236 list< CatchStmt * > aststmt;237 buildMoveList< CatchStmt, StatementNode >( catch_ , aststmt);238 CompoundStmt * tryBlock = strict_dynamic_cast< CompoundStmt * >(maybeMoveBuild< Statement >(try_ ));239 FinallyStmt * finallyBlock = dynamic_cast< FinallyStmt * >(maybeMoveBuild< Statement >(finally_ ) );240 return new TryStmt( tryBlock, aststmt, finallyBlock );229 Statement * build_try( StatementNode * try_stmt, StatementNode * catch_stmt, StatementNode * finally_stmt ) { 230 std::list< CatchStmt * > branches; 231 buildMoveList< CatchStmt, StatementNode >( catch_stmt, branches ); 232 CompoundStmt * tryBlock = strict_dynamic_cast< CompoundStmt * >(maybeMoveBuild< Statement >(try_stmt)); 233 FinallyStmt * finallyBlock = dynamic_cast< FinallyStmt * >(maybeMoveBuild< Statement >(finally_stmt) ); 234 return new TryStmt( tryBlock, branches, finallyBlock ); 241 235 } // build_try 242 236 243 237 Statement * build_catch( CatchStmt::Kind kind, DeclarationNode * decl, ExpressionNode * cond, StatementNode * body ) { 244 list< Statement * > aststmt;245 buildMoveList< Statement, StatementNode >( body, aststmt);246 assert( aststmt.size() == 1 );247 return new CatchStmt( kind, maybeMoveBuild< Declaration >(decl), maybeMoveBuild< Expression >(cond), aststmt.front() );238 std::list< Statement * > branches; 239 buildMoveList< Statement, StatementNode >( body, branches ); 240 assert( branches.size() == 1 ); 241 return new CatchStmt( kind, maybeMoveBuild< Declaration >(decl), maybeMoveBuild< Expression >(cond), branches.front() ); 248 242 } // build_catch 249 243 250 244 Statement * build_finally( StatementNode * stmt ) { 251 list< Statement * > aststmt;252 buildMoveList< Statement, StatementNode >( stmt, aststmt);253 assert( aststmt.size() == 1 );254 return new FinallyStmt( dynamic_cast< CompoundStmt * >( aststmt.front() ) );245 std::list< Statement * > branches; 246 buildMoveList< Statement, StatementNode >( stmt, branches ); 247 assert( branches.size() == 1 ); 248 return new FinallyStmt( dynamic_cast< CompoundStmt * >( branches.front() ) ); 255 249 } // build_finally 256 250 … … 260 254 node->type = type; 261 255 262 list< Statement * > stmts;256 std::list< Statement * > stmts; 263 257 buildMoveList< Statement, StatementNode >( then, stmts ); 264 258 if(!stmts.empty()) { … … 325 319 } // build_waitfor_timeout 326 320 327 WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when, StatementNode * else_ , ExpressionNode * else_when ) {321 WaitForStmt * build_waitfor_timeout( ExpressionNode * timeout, StatementNode * stmt, ExpressionNode * when, StatementNode * else_stmt, ExpressionNode * else_when ) { 328 322 auto node = new WaitForStmt(); 329 323 … … 332 326 node->timeout.condition = notZeroExpr( maybeMoveBuild<Expression>( when ) ); 333 327 334 node->orelse.statement = maybeMoveBuild<Statement >( else_ );328 node->orelse.statement = maybeMoveBuild<Statement >( else_stmt ); 335 329 node->orelse.condition = notZeroExpr( maybeMoveBuild<Expression>( else_when ) ); 336 330 … … 339 333 340 334 Statement * build_with( ExpressionNode * exprs, StatementNode * stmt ) { 341 list< Expression * > e;335 std::list< Expression * > e; 342 336 buildMoveList( exprs, e ); 343 337 Statement * s = maybeMoveBuild<Statement>( stmt ); … … 367 361 368 362 Statement * build_asm( bool voltile, Expression * instruction, ExpressionNode * output, ExpressionNode * input, ExpressionNode * clobber, LabelNode * gotolabels ) { 369 list< Expression * > out, in;370 list< ConstantExpr * > clob;363 std::list< Expression * > out, in; 364 std::list< ConstantExpr * > clob; 371 365 372 366 buildMoveList( output, out ); … … 381 375 382 376 Statement * build_mutex( ExpressionNode * exprs, StatementNode * stmt ) { 383 list< Expression * > expList;377 std::list< Expression * > expList; 384 378 buildMoveList( exprs, expList ); 385 379 Statement * body = maybeMoveBuild<Statement>( stmt ); -
src/Parser/parser.yy
rf5a51db r97c215f 10 10 // Created On : Sat Sep 1 20:22:55 2001 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 11:06:13 202213 // Update Count : 516 712 // Last Modified On : Fri Oct 15 09:20:17 2021 13 // Update Count : 5163 14 14 // 15 15 … … 238 238 WaitForStmt * wfs; 239 239 Expression * constant; 240 CondCtl * ifctl;240 IfCtrl * ifctl; 241 241 ForCtrl * fctl; 242 242 enum OperKinds compop; … … 327 327 %type<en> comma_expression comma_expression_opt 328 328 %type<en> argument_expression_list_opt argument_expression_list argument_expression default_initializer_opt 329 %type<ifctl> conditional_declaration329 %type<ifctl> if_control_expression 330 330 %type<fctl> for_control_expression for_control_expression_list 331 331 %type<compop> inclexcl … … 1123 1123 1124 1124 if_statement: 1125 IF '(' conditional_declaration ')' statement%prec THEN1125 IF '(' if_control_expression ')' statement %prec THEN 1126 1126 // explicitly deal with the shift/reduce conflict on if/else 1127 1127 { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), nullptr ) ); } 1128 | IF '(' conditional_declaration ')' statement ELSE statement1128 | IF '(' if_control_expression ')' statement ELSE statement 1129 1129 { $$ = new StatementNode( build_if( $3, maybe_build_compound( $5 ), maybe_build_compound( $7 ) ) ); } 1130 1130 ; 1131 1131 1132 conditional_declaration:1132 if_control_expression: 1133 1133 comma_expression 1134 { $$ = new CondCtl( nullptr, $1 ); }1134 { $$ = new IfCtrl( nullptr, $1 ); } 1135 1135 | c_declaration // no semi-colon 1136 { $$ = new CondCtl( $1, nullptr ); }1136 { $$ = new IfCtrl( $1, nullptr ); } 1137 1137 | cfa_declaration // no semi-colon 1138 { $$ = new CondCtl( $1, nullptr ); }1138 { $$ = new IfCtrl( $1, nullptr ); } 1139 1139 | declaration comma_expression // semi-colon separated 1140 { $$ = new CondCtl( $1, $2 ); }1140 { $$ = new IfCtrl( $1, $2 ); } 1141 1141 ; 1142 1142 … … 1193 1193 iteration_statement: 1194 1194 WHILE '(' ')' statement // CFA => while ( 1 ) 1195 { $$ = new StatementNode( build_while( new CondCtl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }1196 | WHILE '(' conditional_declaration ')' statement%prec THEN1195 { $$ = new StatementNode( build_while( new IfCtrl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); } 1196 | WHILE '(' if_control_expression ')' statement %prec THEN 1197 1197 { $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ) ) ); } 1198 | WHILE '(' conditional_declaration ')' statement ELSE statement // CFA 1199 // { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1200 { $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ), $7 ) ); } 1198 | WHILE '(' if_control_expression ')' statement ELSE statement // CFA 1199 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1201 1200 | DO statement WHILE '(' ')' ';' // CFA => do while( 1 ) 1202 1201 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); } … … 1204 1203 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ) ) ); } 1205 1204 | DO statement WHILE '(' comma_expression ')' ELSE statement // CFA 1206 // { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1207 { $$ = new StatementNode( build_do_while( $5, maybe_build_compound( $2 ), $8 ) ); } 1205 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1208 1206 | FOR '(' ')' statement // CFA => for ( ;; ) 1209 1207 { $$ = new StatementNode( build_for( new ForCtrl( (ExpressionNode * )nullptr, (ExpressionNode * )nullptr, (ExpressionNode * )nullptr ), maybe_build_compound( $4 ) ) ); } … … 1211 1209 { $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ) ) ); } 1212 1210 | FOR '(' for_control_expression_list ')' statement ELSE statement // CFA 1213 // { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1214 { $$ = new StatementNode( build_for( $3, maybe_build_compound( $5 ), $7 ) ); } 1211 { SemanticError( yylloc, "Loop default block is currently unimplemented." ); $$ = nullptr; } 1215 1212 ; 1216 1213 -
src/ResolvExpr/Resolver.cc
rf5a51db r97c215f 9 9 // Author : Aaron B. Moss 10 10 // Created On : Sun May 17 12:17:01 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Tue Feb 1 16:27:14 202213 // Update Count : 24 511 // Last Modified By : Andrew Beach 12 // Last Modified On : Fri Mar 27 11:58:00 2020 13 // Update Count : 242 14 14 // 15 15 … … 80 80 void previsit( AsmStmt * asmStmt ); 81 81 void previsit( IfStmt * ifStmt ); 82 void previsit( While DoStmt * whileDoStmt );82 void previsit( WhileStmt * whileStmt ); 83 83 void previsit( ForStmt * forStmt ); 84 84 void previsit( SwitchStmt * switchStmt ); … … 502 502 } 503 503 504 void Resolver_old::previsit( While DoStmt * whileDoStmt ) {505 findIntegralExpression( while DoStmt->condition, indexer );504 void Resolver_old::previsit( WhileStmt * whileStmt ) { 505 findIntegralExpression( whileStmt->condition, indexer ); 506 506 } 507 507 … … 572 572 573 573 void Resolver_old::previsit( CatchStmt * catchStmt ) { 574 // Until we are very sure this invarent (ifs that move between passes have then )574 // Until we are very sure this invarent (ifs that move between passes have thenPart) 575 575 // holds, check it. This allows a check for when to decode the mangling. 576 576 if ( IfStmt * ifStmt = dynamic_cast<IfStmt *>( catchStmt->body ) ) { 577 assert( ifStmt->then );577 assert( ifStmt->thenPart ); 578 578 } 579 579 // Encode the catchStmt so the condition can see the declaration. … … 588 588 // Decode the catchStmt so everything is stored properly. 589 589 IfStmt * ifStmt = dynamic_cast<IfStmt *>( catchStmt->body ); 590 if ( nullptr != ifStmt && nullptr == ifStmt->then ) {590 if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) { 591 591 assert( ifStmt->condition ); 592 assert( ifStmt->else _);592 assert( ifStmt->elsePart ); 593 593 catchStmt->cond = ifStmt->condition; 594 catchStmt->body = ifStmt->else _;594 catchStmt->body = ifStmt->elsePart; 595 595 ifStmt->condition = nullptr; 596 ifStmt->else _= nullptr;596 ifStmt->elsePart = nullptr; 597 597 delete ifStmt; 598 598 } … … 1272 1272 const ast::AsmStmt * previsit( const ast::AsmStmt * ); 1273 1273 const ast::IfStmt * previsit( const ast::IfStmt * ); 1274 const ast::While DoStmt * previsit( const ast::WhileDoStmt * );1274 const ast::WhileStmt * previsit( const ast::WhileStmt * ); 1275 1275 const ast::ForStmt * previsit( const ast::ForStmt * ); 1276 1276 const ast::SwitchStmt * previsit( const ast::SwitchStmt * ); … … 1581 1581 } 1582 1582 1583 const ast::While DoStmt * Resolver_new::previsit( const ast::WhileDoStmt * whileDoStmt ) {1583 const ast::WhileStmt * Resolver_new::previsit( const ast::WhileStmt * whileStmt ) { 1584 1584 return ast::mutate_field( 1585 while DoStmt, &ast::WhileDoStmt::cond, findIntegralExpression( whileDoStmt->cond, symtab ) );1585 whileStmt, &ast::WhileStmt::cond, findIntegralExpression( whileStmt->cond, symtab ) ); 1586 1586 } 1587 1587 … … 1669 1669 1670 1670 const ast::CatchStmt * Resolver_new::previsit( const ast::CatchStmt * catchStmt ) { 1671 // Until we are very sure this invarent (ifs that move between passes have then )1671 // Until we are very sure this invarent (ifs that move between passes have thenPart) 1672 1672 // holds, check it. This allows a check for when to decode the mangling. 1673 1673 if ( auto ifStmt = catchStmt->body.as<ast::IfStmt>() ) { 1674 assert( ifStmt->then );1674 assert( ifStmt->thenPart ); 1675 1675 } 1676 1676 // Encode the catchStmt so the condition can see the declaration. … … 1687 1687 // Decode the catchStmt so everything is stored properly. 1688 1688 const ast::IfStmt * ifStmt = catchStmt->body.as<ast::IfStmt>(); 1689 if ( nullptr != ifStmt && nullptr == ifStmt->then ) {1689 if ( nullptr != ifStmt && nullptr == ifStmt->thenPart ) { 1690 1690 assert( ifStmt->cond ); 1691 assert( ifStmt->else _);1691 assert( ifStmt->elsePart ); 1692 1692 ast::CatchStmt * stmt = ast::mutate( catchStmt ); 1693 1693 stmt->cond = ifStmt->cond; 1694 stmt->body = ifStmt->else _;1694 stmt->body = ifStmt->elsePart; 1695 1695 // ifStmt should be implicately deleted here. 1696 1696 return stmt; -
src/SymTab/Validate.cc
rf5a51db r97c215f 453 453 } 454 454 455 void decayForallPointers( std::list< Declaration * > & translationUnit ) {456 PassVisitor<ForallPointerDecay_old> fpd;457 acceptAll( translationUnit, fpd );458 }459 460 455 void validate( std::list< Declaration * > &translationUnit, __attribute__((unused)) bool doDebug ) { 461 456 validate_A( translationUnit ); … … 475 470 type->accept( fpd ); 476 471 } 472 477 473 478 474 void HoistTypeDecls::handleType( Type * type ) { -
src/SymTab/Validate.h
rf5a51db r97c215f 42 42 void validate_E( std::list< Declaration * > &translationUnit ); 43 43 void validate_F( std::list< Declaration * > &translationUnit ); 44 void decayForallPointers( std::list< Declaration * > & translationUnit );45 44 46 45 const ast::Type * validateType( -
src/SynTree/Mutator.h
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:26:49 202213 // Update Count : 2012 // Last Modified On : Fri Mar 12 18:35:36 2021 13 // Update Count : 18 14 14 // 15 15 #pragma once … … 42 42 virtual Statement * mutate( DirectiveStmt * dirStmt ) = 0; 43 43 virtual Statement * mutate( IfStmt * ifStmt ) = 0; 44 virtual Statement * mutate( While DoStmt * whileDoStmt ) = 0;44 virtual Statement * mutate( WhileStmt * whileStmt ) = 0; 45 45 virtual Statement * mutate( ForStmt * forStmt ) = 0; 46 46 virtual Statement * mutate( SwitchStmt * switchStmt ) = 0; -
src/SynTree/Statement.cc
rf5a51db r97c215f 9 9 // Author : Richard C. Bilson 10 10 // Created On : Mon May 18 07:44:20 2015 11 // Last Modified By : Peter A. Buhr12 // Last Modified On : Wed Feb 2 20:19:33 202213 // Update Count : 9011 // Last Modified By : Andrew Beach 12 // Last Modified On : Mon Jan 20 16:03:00 2020 13 // Update Count : 71 14 14 // 15 15 … … 29 29 #include "SynTree/Label.h" // for Label, operator<< 30 30 31 using namespace std;32 33 34 Statement::Statement( const list<Label> & labels ) : labels( labels ) {}35 36 void Statement::print( ostream & os, Indenter indent ) const {31 using std::string; 32 using std::endl; 33 34 Statement::Statement( const std::list<Label> & labels ) : labels( labels ) {} 35 36 void Statement::print( std::ostream & os, Indenter indent ) const { 37 37 if ( ! labels.empty() ) { 38 38 os << indent << "... Labels: {"; … … 54 54 } 55 55 56 void ExprStmt::print( ostream & os, Indenter indent ) const {57 os << "Expression Statement:" << endl << indent +1;58 expr->print( os, indent +1 );59 } 60 61 62 AsmStmt::AsmStmt( bool voltile, Expression * instruction, const list<Expression *> output, const list<Expression *> input, const list<ConstantExpr *> clobber, constlist<Label> gotolabels ) : Statement(), voltile( voltile ), instruction( instruction ), output( output ), input( input ), clobber( clobber ), gotolabels( gotolabels ) {}56 void ExprStmt::print( std::ostream & os, Indenter indent ) const { 57 os << "Expression Statement:" << endl << indent+1; 58 expr->print( os, indent+1 ); 59 } 60 61 62 AsmStmt::AsmStmt( bool voltile, Expression * instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels ) : Statement(), voltile( voltile ), instruction( instruction ), output( output ), input( input ), clobber( clobber ), gotolabels( gotolabels ) {} 63 63 64 64 AsmStmt::AsmStmt( const AsmStmt & other ) : Statement( other ), voltile( other.voltile ), instruction( maybeClone( other.instruction ) ), gotolabels( other.gotolabels ) { … … 75 75 } 76 76 77 void AsmStmt::print( ostream & os, Indenter indent ) const {77 void AsmStmt::print( std::ostream & os, Indenter indent ) const { 78 78 os << "Assembler Statement:" << endl; 79 os << indent +1 << "instruction: " << endl << indent;80 instruction->print( os, indent +1 );79 os << indent+1 << "instruction: " << endl << indent; 80 instruction->print( os, indent+1 ); 81 81 if ( ! output.empty() ) { 82 os << endl << indent +1 << "output: " << endl;83 printAll( output, os, indent +1 );82 os << endl << indent+1 << "output: " << endl; 83 printAll( output, os, indent+1 ); 84 84 } // if 85 85 if ( ! input.empty() ) { 86 os << indent +1 << "input: " << endl;87 printAll( input, os, indent +1 );86 os << indent+1 << "input: " << endl; 87 printAll( input, os, indent+1 ); 88 88 } // if 89 89 if ( ! clobber.empty() ) { 90 os << indent +1 << "clobber: " << endl;91 printAll( clobber, os, indent +1 );90 os << indent+1 << "clobber: " << endl; 91 printAll( clobber, os, indent+1 ); 92 92 } // if 93 93 } 94 94 95 95 96 DirectiveStmt::DirectiveStmt( const st ring & directive ) : Statement(), directive( directive ) {}97 98 void DirectiveStmt::print( ostream & os, Indenter ) const {96 DirectiveStmt::DirectiveStmt( const std::string & directive ) : Statement(), directive( directive ) {} 97 98 void DirectiveStmt::print( std::ostream & os, Indenter ) const { 99 99 os << "GCC Directive:" << directive << endl; 100 100 } … … 120 120 } 121 121 122 void BranchStmt::print( ostream & os, Indenter indent ) const {123 assert f(type < BranchStmts, "CFA internal error: invalid branch statement");122 void BranchStmt::print( std::ostream & os, Indenter indent ) const { 123 assert(type < 5); 124 124 os << "Branch (" << brType[type] << ")" << endl ; 125 if ( target != "" ) os << indent +1 << "with target: " << target << endl;126 if ( originalTarget != "" ) os << indent +1 << "with original target: " << originalTarget << endl;127 if ( computedTarget != nullptr ) os << indent +1 << "with computed target: " << computedTarget << endl;125 if ( target != "" ) os << indent+1 << "with target: " << target << endl; 126 if ( originalTarget != "" ) os << indent+1 << "with original target: " << originalTarget << endl; 127 if ( computedTarget != nullptr ) os << indent+1 << "with computed target: " << computedTarget << endl; 128 128 } 129 129 … … 136 136 } 137 137 138 void ReturnStmt::print( ostream & os, Indenter indent ) const {138 void ReturnStmt::print( std::ostream & os, Indenter indent ) const { 139 139 os << "Return Statement, returning: "; 140 140 if ( expr != nullptr ) { 141 os << endl << indent +1;142 expr->print( os, indent +1 );143 } 144 os << endl; 145 } 146 147 IfStmt::IfStmt( Expression * condition, Statement * then , Statement * else_, constlist<Statement *> initialization ):148 Statement(), condition( condition ), then ( then ), else_( else_), initialization( initialization ) {}141 os << endl << indent+1; 142 expr->print( os, indent+1 ); 143 } 144 os << endl; 145 } 146 147 IfStmt::IfStmt( Expression * condition, Statement * thenPart, Statement * elsePart, std::list<Statement *> initialization ): 148 Statement(), condition( condition ), thenPart( thenPart ), elsePart( elsePart ), initialization( initialization ) {} 149 149 150 150 IfStmt::IfStmt( const IfStmt & other ) : 151 Statement( other ), condition( maybeClone( other.condition ) ), then ( maybeClone( other.then ) ), else_( maybeClone( other.else_) ) {151 Statement( other ), condition( maybeClone( other.condition ) ), thenPart( maybeClone( other.thenPart ) ), elsePart( maybeClone( other.elsePart ) ) { 152 152 cloneAll( other.initialization, initialization ); 153 153 } … … 156 156 deleteAll( initialization ); 157 157 delete condition; 158 delete then ;159 delete else _;160 } 161 162 void IfStmt::print( ostream & os, Indenter indent ) const {158 delete thenPart; 159 delete elsePart; 160 } 161 162 void IfStmt::print( std::ostream & os, Indenter indent ) const { 163 163 os << "If on condition: " << endl; 164 os << indent +1;165 condition->print( os, indent +1 );164 os << indent+1; 165 condition->print( os, indent+1 ); 166 166 167 167 if ( !initialization.empty() ) { 168 168 os << indent << "... with initialization: \n"; 169 169 for ( const Statement * stmt : initialization ) { 170 os << indent +1;171 stmt->print( os, indent +1 );170 os << indent+1; 171 stmt->print( os, indent+1 ); 172 172 } 173 173 os << endl; … … 176 176 os << indent << "... then: " << endl; 177 177 178 os << indent +1;179 then ->print( os, indent +1 );180 181 if ( else _!= nullptr ) {178 os << indent+1; 179 thenPart->print( os, indent+1 ); 180 181 if ( elsePart != nullptr ) { 182 182 os << indent << "... else: " << endl; 183 os << indent +1;184 else _->print( os, indent +1 );183 os << indent+1; 184 elsePart->print( os, indent+1 ); 185 185 } // if 186 186 } 187 187 188 SwitchStmt::SwitchStmt( Expression * condition, const list<Statement *> & statements ):188 SwitchStmt::SwitchStmt( Expression * condition, const std::list<Statement *> & statements ): 189 189 Statement(), condition( condition ), statements( statements ) { 190 190 } … … 201 201 } 202 202 203 void SwitchStmt::print( ostream & os, Indenter indent ) const {203 void SwitchStmt::print( std::ostream & os, Indenter indent ) const { 204 204 os << "Switch on condition: "; 205 205 condition->print( os ); … … 207 207 208 208 for ( const Statement * stmt : statements ) { 209 stmt->print( os, indent +1 );210 } 211 } 212 213 CaseStmt::CaseStmt( Expression * condition, const list<Statement *> & statements, bool deflt ) throw ( SemanticErrorException ) :214 209 stmt->print( os, indent+1 ); 210 } 211 } 212 213 CaseStmt::CaseStmt( Expression * condition, const std::list<Statement *> & statements, bool deflt ) throw ( SemanticErrorException ) : 214 Statement(), condition( condition ), stmts( statements ), _isDefault( deflt ) { 215 215 if ( isDefault() && condition != nullptr ) SemanticError( condition, "default case with condition: " ); 216 216 } 217 217 218 218 CaseStmt::CaseStmt( const CaseStmt & other ) : 219 219 Statement( other ), condition( maybeClone(other.condition ) ), _isDefault( other._isDefault ) { 220 220 cloneAll( other.stmts, stmts ); 221 221 } … … 226 226 } 227 227 228 CaseStmt * CaseStmt::makeDefault( const list<Label> & labels,list<Statement *> stmts ) {228 CaseStmt * CaseStmt::makeDefault( const std::list<Label> & labels, std::list<Statement *> stmts ) { 229 229 CaseStmt * stmt = new CaseStmt( nullptr, stmts, true ); 230 230 stmt->labels = labels; … … 232 232 } 233 233 234 void CaseStmt::print( ostream & os, Indenter indent ) const {234 void CaseStmt::print( std::ostream & os, Indenter indent ) const { 235 235 if ( isDefault() ) os << indent << "Default "; 236 236 else { … … 241 241 242 242 for ( Statement * stmt : stmts ) { 243 os << indent + 1; 244 stmt->print( os, indent + 1 ); 245 } 246 } 247 248 WhileDoStmt::WhileDoStmt( Expression * condition, Statement * body, const list< Statement * > & initialization, bool isDoWhile ): 249 Statement(), condition( condition ), body( body ), else_( nullptr ), initialization( initialization ), isDoWhile( isDoWhile) { 250 } 251 252 WhileDoStmt::WhileDoStmt( Expression * condition, Statement * body, Statement * else_, const list< Statement * > & initialization, bool isDoWhile ): 253 Statement(), condition( condition), body( body ), else_( else_ ), initialization( initialization ), isDoWhile( isDoWhile) { 254 } 255 256 WhileDoStmt::WhileDoStmt( const WhileDoStmt & other ): 243 os << indent+1; 244 stmt->print( os, indent+1 ); 245 } 246 } 247 248 WhileStmt::WhileStmt( Expression * condition, Statement * body, std::list< Statement * > & initialization, bool isDoWhile ): 249 Statement(), condition( condition), body( body), initialization( initialization ), isDoWhile( isDoWhile) { 250 } 251 252 WhileStmt::WhileStmt( const WhileStmt & other ): 257 253 Statement( other ), condition( maybeClone( other.condition ) ), body( maybeClone( other.body ) ), isDoWhile( other.isDoWhile ) { 258 254 } 259 255 260 While DoStmt::~WhileDoStmt() {256 WhileStmt::~WhileStmt() { 261 257 delete body; 262 258 delete condition; 263 259 } 264 260 265 void While DoStmt::print(ostream & os, Indenter indent ) const {261 void WhileStmt::print( std::ostream & os, Indenter indent ) const { 266 262 os << "While on condition: " << endl ; 267 condition->print( os, indent +1 );263 condition->print( os, indent+1 ); 268 264 269 265 os << indent << "... with body: " << endl; 270 266 271 if ( body != nullptr ) body->print( os, indent +1 );272 } 273 274 ForStmt::ForStmt( const list<Statement *> initialization, Expression * condition, Expression * increment, Statement * body, Statement * else_):275 Statement(), initialization( initialization ), condition( condition ), increment( increment ), body( body ) , else_( else_ ){267 if ( body != nullptr ) body->print( os, indent+1 ); 268 } 269 270 ForStmt::ForStmt( std::list<Statement *> initialization, Expression * condition, Expression * increment, Statement * body ): 271 Statement(), initialization( initialization ), condition( condition ), increment( increment ), body( body ) { 276 272 } 277 273 278 274 ForStmt::ForStmt( const ForStmt & other ): 279 Statement( other ), condition( maybeClone( other.condition ) ), increment( maybeClone( other.increment ) ), body( maybeClone( other.body ) ) , else_( maybeClone( other.else_ ) ){275 Statement( other ), condition( maybeClone( other.condition ) ), increment( maybeClone( other.increment ) ), body( maybeClone( other.body ) ) { 280 276 cloneAll( other.initialization, initialization ); 281 277 … … 287 283 delete increment; 288 284 delete body; 289 delete else_; 290 } 291 292 void ForStmt::print( ostream & os, Indenter indent ) const { 285 } 286 287 void ForStmt::print( std::ostream & os, Indenter indent ) const { 293 288 Statement::print( os, indent ); // print labels 294 289 … … 298 293 os << indent << "... initialization: \n"; 299 294 for ( Statement * stmt : initialization ) { 300 os << indent +1;301 stmt->print( os, indent +1 );295 os << indent+1; 296 stmt->print( os, indent+1 ); 302 297 } 303 298 } 304 299 305 300 if ( condition != nullptr ) { 306 os << indent << "... condition: \n" << indent +1;307 condition->print( os, indent +1 );301 os << indent << "... condition: \n" << indent+1; 302 condition->print( os, indent+1 ); 308 303 } 309 304 310 305 if ( increment != nullptr ) { 311 os << "\n" << indent << "... increment: \n" << indent +1;312 increment->print( os, indent +1 );306 os << "\n" << indent << "... increment: \n" << indent+1; 307 increment->print( os, indent+1 ); 313 308 } 314 309 315 310 if ( body != nullptr ) { 316 os << "\n" << indent << "... with body: \n" << indent + 1; 317 body->print( os, indent + 1 ); 318 } 319 320 if ( else_ != nullptr ) { 321 os << "\n" << indent << "... with body: \n" << indent + 1; 322 else_->print( os, indent + 1 ); 311 os << "\n" << indent << "... with body: \n" << indent+1; 312 body->print( os, indent+1 ); 323 313 } 324 314 os << endl; … … 339 329 } 340 330 341 void ThrowStmt::print( ostream & os, Indenter indent) const {331 void ThrowStmt::print( std::ostream & os, Indenter indent) const { 342 332 if ( target ) os << "Non-Local "; 343 333 os << "Throw Statement, raising: "; 344 expr->print(os, indent +1);334 expr->print(os, indent+1); 345 335 if ( target ) { 346 336 os << "... at: "; 347 target->print(os, indent +1);348 } 349 } 350 351 TryStmt::TryStmt( CompoundStmt * tryBlock, constlist<CatchStmt *> & handlers, FinallyStmt * finallyBlock ) :337 target->print(os, indent+1); 338 } 339 } 340 341 TryStmt::TryStmt( CompoundStmt * tryBlock, std::list<CatchStmt *> & handlers, FinallyStmt * finallyBlock ) : 352 342 Statement(), block( tryBlock ), handlers( handlers ), finallyBlock( finallyBlock ) { 353 343 } … … 363 353 } 364 354 365 void TryStmt::print( ostream & os, Indenter indent ) const {355 void TryStmt::print( std::ostream & os, Indenter indent ) const { 366 356 os << "Try Statement" << endl; 367 os << indent << "... with block:" << endl << indent +1;368 block->print( os, indent +1 );357 os << indent << "... with block:" << endl << indent+1; 358 block->print( os, indent+1 ); 369 359 370 360 // handlers 371 361 os << indent << "... and handlers:" << endl; 372 362 for ( const CatchStmt * stmt : handlers ) { 373 os << indent +1;374 stmt->print( os, indent +1 );363 os << indent+1; 364 stmt->print( os, indent+1 ); 375 365 } 376 366 377 367 // finally block 378 368 if ( finallyBlock != nullptr ) { 379 os << indent << "... and finally:" << endl << indent +1;380 finallyBlock->print( os, indent +1 );369 os << indent << "... and finally:" << endl << indent+1; 370 finallyBlock->print( os, indent+1 ); 381 371 } // if 382 372 } … … 396 386 } 397 387 398 void CatchStmt::print( ostream & os, Indenter indent ) const {388 void CatchStmt::print( std::ostream & os, Indenter indent ) const { 399 389 os << "Catch " << ((Terminate == kind) ? "Terminate" : "Resume") << " Statement" << endl; 400 390 401 391 os << indent << "... catching: "; 402 decl->printShort( os, indent +1 );392 decl->printShort( os, indent+1 ); 403 393 os << endl; 404 394 405 395 if ( cond ) { 406 os << indent << "... with conditional:" << endl << indent +1;407 cond->print( os, indent +1 );396 os << indent << "... with conditional:" << endl << indent+1; 397 cond->print( os, indent+1 ); 408 398 } 409 399 410 400 os << indent << "... with block:" << endl; 411 os << indent +1;412 body->print( os, indent +1 );401 os << indent+1; 402 body->print( os, indent+1 ); 413 403 } 414 404 … … 424 414 } 425 415 426 void FinallyStmt::print( ostream & os, Indenter indent ) const {416 void FinallyStmt::print( std::ostream & os, Indenter indent ) const { 427 417 os << "Finally Statement" << endl; 428 os << indent << "... with block:" << endl << indent +1;429 block->print( os, indent +1 );418 os << indent << "... with block:" << endl << indent+1; 419 block->print( os, indent+1 ); 430 420 } 431 421 … … 439 429 } 440 430 441 void SuspendStmt::print( ostream & os, Indenter indent ) const {431 void SuspendStmt::print( std::ostream & os, Indenter indent ) const { 442 432 os << "Suspend Statement"; 443 433 switch (type) { … … 496 486 } 497 487 498 void WaitForStmt::print( ostream & os, Indenter indent ) const {488 void WaitForStmt::print( std::ostream & os, Indenter indent ) const { 499 489 os << "Waitfor Statement" << endl; 500 490 indent += 1; … … 531 521 532 522 533 WithStmt::WithStmt( const list< Expression * > & exprs, Statement * stmt ) : Declaration("", noStorageClasses, LinkageSpec::Cforall), exprs( exprs ), stmt( stmt ) {}523 WithStmt::WithStmt( const std::list< Expression * > & exprs, Statement * stmt ) : Declaration("", noStorageClasses, LinkageSpec::Cforall), exprs( exprs ), stmt( stmt ) {} 534 524 WithStmt::WithStmt( const WithStmt & other ) : Declaration( other ), stmt( maybeClone( other.stmt ) ) { 535 525 cloneAll( other.exprs, exprs ); … … 540 530 } 541 531 542 void WithStmt::print( ostream & os, Indenter indent ) const {532 void WithStmt::print( std::ostream & os, Indenter indent ) const { 543 533 os << "With statement" << endl; 544 534 os << indent << "... with expressions: " << endl; 545 printAll( exprs, os, indent +1 );546 os << indent << "... with statement:" << endl << indent +1;547 stmt->print( os, indent +1 );548 } 549 550 551 NullStmt::NullStmt( const list<Label> & labels ) : Statement( labels ) {552 } 553 554 void NullStmt::print( ostream & os, Indenter indent ) const {535 printAll( exprs, os, indent+1 ); 536 os << indent << "... with statement:" << endl << indent+1; 537 stmt->print( os, indent+1 ); 538 } 539 540 541 NullStmt::NullStmt( const std::list<Label> & labels ) : Statement( labels ) { 542 } 543 544 void NullStmt::print( std::ostream & os, Indenter indent ) const { 555 545 os << "Null Statement" << endl; 556 546 Statement::print( os, indent ); … … 568 558 } 569 559 570 void ImplicitCtorDtorStmt::print( ostream & os, Indenter indent ) const {560 void ImplicitCtorDtorStmt::print( std::ostream & os, Indenter indent ) const { 571 561 os << "Implicit Ctor Dtor Statement" << endl; 572 562 os << indent << "... with Ctor/Dtor: "; 573 callStmt->print( os, indent +1);574 os << endl; 575 } 576 577 MutexStmt::MutexStmt( Statement * stmt, constlist<Expression *> mutexObjs )563 callStmt->print( os, indent+1); 564 os << endl; 565 } 566 567 MutexStmt::MutexStmt( Statement * stmt, std::list<Expression *> mutexObjs ) 578 568 : Statement(), stmt( stmt ), mutexObjs( mutexObjs ) { } 579 569 … … 587 577 } 588 578 589 void MutexStmt::print( ostream & os, Indenter indent ) const {579 void MutexStmt::print( std::ostream & os, Indenter indent ) const { 590 580 os << "Mutex Statement" << endl; 591 581 os << indent << "... with Expressions: " << endl; 592 582 for (auto * obj : mutexObjs) { 593 os << indent +1;594 obj->print( os, indent +1);583 os << indent+1; 584 obj->print( os, indent+1); 595 585 os << endl; 596 586 } 597 os << indent << "... with Statement: " << endl << indent +1;598 stmt->print( os, indent +1 );587 os << indent << "... with Statement: " << endl << indent+1; 588 stmt->print( os, indent+1 ); 599 589 } 600 590 -
src/SynTree/Statement.h
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Wed Feb 2 20:15:30 202213 // Update Count : 9812 // Last Modified On : Fri Jan 10 14:13:24 2020 13 // Update Count : 85 14 14 // 15 15 … … 107 107 std::list<Label> gotolabels; 108 108 109 AsmStmt( bool voltile, Expression * instruction, const std::list<Expression *> output, const std::list<Expression *> input, const std::list<ConstantExpr *> clobber, conststd::list<Label> gotolabels );109 AsmStmt( bool voltile, Expression * instruction, std::list<Expression *> output, std::list<Expression *> input, std::list<ConstantExpr *> clobber, std::list<Label> gotolabels ); 110 110 AsmStmt( const AsmStmt & other ); 111 111 virtual ~AsmStmt(); … … 148 148 public: 149 149 Expression * condition; 150 Statement * then ;151 Statement * else _;150 Statement * thenPart; 151 Statement * elsePart; 152 152 std::list<Statement *> initialization; 153 153 154 IfStmt( Expression * condition, Statement * then , Statement * else_,155 conststd::list<Statement *> initialization = std::list<Statement *>() );154 IfStmt( Expression * condition, Statement * thenPart, Statement * elsePart, 155 std::list<Statement *> initialization = std::list<Statement *>() ); 156 156 IfStmt( const IfStmt & other ); 157 157 virtual ~IfStmt(); … … 160 160 Expression * get_condition() { return condition; } 161 161 void set_condition( Expression * newValue ) { condition = newValue; } 162 Statement * get_then () { return then; }163 void set_then ( Statement * newValue ) { then= newValue; }164 Statement * get_else () { return else_; }165 void set_else ( Statement * newValue ) { else_= newValue; }162 Statement * get_thenPart() { return thenPart; } 163 void set_thenPart( Statement * newValue ) { thenPart = newValue; } 164 Statement * get_elsePart() { return elsePart; } 165 void set_elsePart( Statement * newValue ) { elsePart = newValue; } 166 166 167 167 virtual IfStmt * clone() const override { return new IfStmt( *this ); } … … 225 225 }; 226 226 227 class While DoStmt : public Statement {227 class WhileStmt : public Statement { 228 228 public: 229 229 Expression * condition; 230 230 Statement * body; 231 Statement * else_;232 231 std::list<Statement *> initialization; 233 232 bool isDoWhile; 234 233 235 WhileDoStmt( Expression * condition, Statement * body, const std::list<Statement *> & initialization, bool isDoWhile = false ); 236 WhileDoStmt( Expression * condition, Statement * body, Statement * else_, const std::list<Statement *> & initialization, bool isDoWhile = false ); 237 WhileDoStmt( const WhileDoStmt & other ); 238 virtual ~WhileDoStmt(); 234 WhileStmt( Expression * condition, Statement * body, std::list<Statement *> & initialization, bool isDoWhile = false ); 235 WhileStmt( const WhileStmt & other ); 236 virtual ~WhileStmt(); 239 237 240 238 Expression * get_condition() { return condition; } … … 245 243 void set_isDoWhile( bool newValue ) { isDoWhile = newValue; } 246 244 247 virtual While DoStmt * clone() const override { return new WhileDoStmt( *this ); }245 virtual WhileStmt * clone() const override { return new WhileStmt( *this ); } 248 246 virtual void accept( Visitor & v ) override { v.visit( this ); } 249 247 virtual void accept( Visitor & v ) const override { v.visit( this ); } … … 258 256 Expression * increment; 259 257 Statement * body; 260 Statement * else_; 261 262 ForStmt( const std::list<Statement *> initialization, Expression * condition = nullptr, Expression * increment = nullptr, Statement * body = nullptr, Statement * else_ = nullptr ); 258 259 ForStmt( std::list<Statement *> initialization, Expression * condition = nullptr, Expression * increment = nullptr, Statement * body = nullptr ); 263 260 ForStmt( const ForStmt & other ); 264 261 virtual ~ForStmt(); … … 281 278 class BranchStmt : public Statement { 282 279 public: 283 enum Type { Goto , Break, Continue, FallThrough, FallThroughDefault, BranchStmts};280 enum Type { Goto = 0, Break, Continue, FallThrough, FallThroughDefault }; 284 281 285 282 // originalTarget kept for error messages. … … 360 357 FinallyStmt * finallyBlock; 361 358 362 TryStmt( CompoundStmt * tryBlock, conststd::list<CatchStmt *> & handlers, FinallyStmt * finallyBlock = nullptr );359 TryStmt( CompoundStmt * tryBlock, std::list<CatchStmt *> & handlers, FinallyStmt * finallyBlock = nullptr ); 363 360 TryStmt( const TryStmt & other ); 364 361 virtual ~TryStmt(); … … 543 540 std::list<Expression *> mutexObjs; // list of mutex objects to acquire 544 541 545 MutexStmt( Statement * stmt, conststd::list<Expression *> mutexObjs );542 MutexStmt( Statement * stmt, std::list<Expression *> mutexObjs ); 546 543 MutexStmt( const MutexStmt & other ); 547 544 virtual ~MutexStmt(); -
src/SynTree/SynTree.h
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:22:33 202213 // Update Count : 1 412 // Last Modified On : Fri Mar 12 18:56:44 2021 13 // Update Count : 13 14 14 // 15 15 … … 45 45 class DirectiveStmt; 46 46 class IfStmt; 47 class While DoStmt;47 class WhileStmt; 48 48 class ForStmt; 49 49 class SwitchStmt; -
src/SynTree/Visitor.h
rf5a51db r97c215f 10 10 // Created On : Mon May 18 07:44:20 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Tue Feb 1 09:26:57 202213 // Update Count : 1 712 // Last Modified On : Fri Mar 12 18:35:35 2021 13 // Update Count : 15 14 14 // 15 15 … … 60 60 virtual void visit( IfStmt * node ) { visit( const_cast<const IfStmt *>(node) ); } 61 61 virtual void visit( const IfStmt * ifStmt ) = 0; 62 virtual void visit( While DoStmt * node ) { visit( const_cast<const WhileDoStmt *>(node) ); }63 virtual void visit( const While DoStmt * whileDoStmt ) = 0;62 virtual void visit( WhileStmt * node ) { visit( const_cast<const WhileStmt *>(node) ); } 63 virtual void visit( const WhileStmt * whileStmt ) = 0; 64 64 virtual void visit( ForStmt * node ) { visit( const_cast<const ForStmt *>(node) ); } 65 65 virtual void visit( const ForStmt * forStmt ) = 0; -
src/Validate/module.mk
rf5a51db r97c215f 16 16 17 17 SRC_VALIDATE = \ 18 Validate/Autogen.cpp \19 Validate/Autogen.hpp \20 18 Validate/CompoundLiteral.cpp \ 21 19 Validate/CompoundLiteral.hpp \ -
src/main.cc
rf5a51db r97c215f 10 10 // Created On : Fri May 15 23:12:02 2015 11 11 // Last Modified By : Andrew Beach 12 // Last Modified On : Wed Jan 26 14:09:00 202213 // Update Count : 6 7012 // Last Modified On : Tue Nov 30 10:25:00 2021 13 // Update Count : 659 14 14 // 15 15 … … 55 55 #include "ControlStruct/ExceptTranslate.h" // for translateEHM 56 56 #include "ControlStruct/FixLabels.hpp" // for fixLabels 57 #include "ControlStruct/HoistControlDecls.hpp" // hoistControlDecls58 57 #include "ControlStruct/Mutate.h" // for mutate 59 58 #include "GenPoly/Box.h" // for box … … 74 73 #include "SynTree/Visitor.h" // for acceptAll 75 74 #include "Tuples/Tuples.h" // for expandMemberTuples, expan... 76 #include "Validate/Autogen.hpp" // for autogenerateRoutines77 75 #include "Validate/FindSpecialDecls.h" // for findGlobalDecls 78 76 #include "Validate/CompoundLiteral.hpp" // for handleCompoundLiterals … … 80 78 #include "Validate/LabelAddressFixer.hpp" // for fixLabelAddresses 81 79 #include "Virtual/ExpandCasts.h" // for expandCasts 80 82 81 83 82 static void NewPass( const char * const name ) { … … 327 326 PASS( "Validate-B", SymTab::validate_B( translationUnit ) ); 328 327 PASS( "Validate-C", SymTab::validate_C( translationUnit ) ); 328 PASS( "Validate-D", SymTab::validate_D( translationUnit ) ); 329 329 330 330 CodeTools::fillLocations( translationUnit ); 331 331 332 332 if( useNewAST ) { 333 PASS( "Apply Concurrent Keywords", Concurrency::applyKeywords( translationUnit ) );334 PASS( "Forall Pointer Decay", SymTab::decayForallPointers( translationUnit ) );335 CodeTools::fillLocations( translationUnit );336 337 333 if (Stats::Counters::enabled) { 338 334 ast::pass_visitor_stats.avg = Stats::Counters::build<Stats::Counters::AverageCounter<double>>("Average Depth - New"); … … 342 338 343 339 forceFillCodeLocations( transUnit ); 344 345 // Must happen before autogen routines are added.346 PASS( "Hoist Control Declarations", ControlStruct::hoistControlDecls( transUnit ) );347 348 // Must be after enum and pointer decay.349 // Must be before compound literals.350 PASS( "Generate Autogen Routines", Validate::autogenerateRoutines( transUnit ) );351 340 352 341 PASS( "Implement Mutex", Concurrency::implementMutex( transUnit ) ); … … 415 404 // Currently not working due to unresolved issues with UniqueExpr 416 405 PASS( "Expand Unique Expr", Tuples::expandUniqueExpr( transUnit ) ); // xxx - is this the right place for this? want to expand ASAP so tha, sequent passes don't need to worry about double-visiting a unique expr - needs to go after InitTweak::fix so that copy constructed return declarations are reused 417 418 PASS( "Translate Tries" , ControlStruct::translateTries( transUnit ) );419 420 406 translationUnit = convert( move( transUnit ) ); 421 407 } else { 422 PASS( "Validate-D", SymTab::validate_D( translationUnit ) );423 408 PASS( "Validate-E", SymTab::validate_E( translationUnit ) ); 424 409 PASS( "Validate-F", SymTab::validate_F( translationUnit ) ); … … 484 469 485 470 PASS( "Expand Unique Expr", Tuples::expandUniqueExpr( translationUnit ) ); // xxx - is this the right place for this? want to expand ASAP so tha, sequent passes don't need to worry about double-visiting a unique expr - needs to go after InitTweak::fix so that copy constructed return declarations are reused 486 487 PASS( "Translate Tries" , ControlStruct::translateTries( translationUnit ) );488 471 } 489 472 490 473 PASS( "Translate Tries" , ControlStruct::translateTries( translationUnit ) ); 491 474 492 475 PASS( "Gen Waitfor" , Concurrency::generateWaitFor( translationUnit ) ); -
tests/concurrent/.expect/ctor-check.txt
rf5a51db r97c215f 2 2 ?{}: function 3 3 ... with parameters 4 this:lvalue reference to instance of struct Empty with body4 lvalue reference to instance of struct Empty with body 5 5 ... returning nothing 6 6 with body -
tests/concurrent/mutexstmt/.expect/locks.txt
rf5a51db r97c215f 3 3 Start Test: multi lock deadlock/mutual exclusion 4 4 End Test: multi lock deadlock/mutual exclusion 5 Start Test: single scoped lock mutual exclusion6 End Test: single scoped lock mutual exclusion7 Start Test: multi scoped lock deadlock/mutual exclusion8 End Test: multi scoped lock deadlock/mutual exclusion -
tests/concurrent/mutexstmt/locks.cfa
rf5a51db r97c215f 59 59 } 60 60 61 thread T_Mutex_Scoped {};62 61 63 void main( T_Mutex_Scoped & this ) {64 for (unsigned int i = 0; i < num_times; i++) {65 {66 scoped_lock(single_acquisition_lock) s{m1};67 count++;68 }69 {70 scoped_lock(single_acquisition_lock) s{m1};71 assert(!insideFlag);72 insideFlag = true;73 assert(insideFlag);74 insideFlag = false;75 }76 }77 }78 79 thread T_Multi_Scoped {};80 81 void main( T_Multi_Scoped & this ) {82 for (unsigned int i = 0; i < num_times; i++) {83 {84 scoped_lock(single_acquisition_lock) s{m1};85 assert(!insideFlag);86 insideFlag = true;87 assert(insideFlag);88 insideFlag = false;89 }90 {91 scoped_lock(single_acquisition_lock) s1{m1};92 scoped_lock(single_acquisition_lock) s2{m2};93 scoped_lock(single_acquisition_lock) s3{m3};94 scoped_lock(single_acquisition_lock) s4{m4};95 scoped_lock(single_acquisition_lock) s5{m5};96 assert(!insideFlag);97 insideFlag = true;98 assert(insideFlag);99 insideFlag = false;100 }101 {102 scoped_lock(single_acquisition_lock) s1{m1};103 scoped_lock(single_acquisition_lock) s3{m3};104 assert(!insideFlag);105 insideFlag = true;106 assert(insideFlag);107 insideFlag = false;108 }109 {110 scoped_lock(single_acquisition_lock) s1{m1};111 scoped_lock(single_acquisition_lock) s2{m2};112 scoped_lock(single_acquisition_lock) s4{m4};113 assert(!insideFlag);114 insideFlag = true;115 assert(insideFlag);116 insideFlag = false;117 }118 {119 scoped_lock(single_acquisition_lock) s1{m1};120 scoped_lock(single_acquisition_lock) s3{m3};121 scoped_lock(single_acquisition_lock) s4{m4};122 scoped_lock(single_acquisition_lock) s5{m5};123 assert(!insideFlag);124 insideFlag = true;125 assert(insideFlag);126 insideFlag = false;127 }128 }129 }130 131 int num_tasks = 10;132 62 int main() { 133 63 processor p[10]; … … 137 67 T_Mutex t[10]; 138 68 } 139 assert(count == num_tasks * num_times);140 69 printf("End Test: single lock mutual exclusion\n"); 141 70 printf("Start Test: multi lock deadlock/mutual exclusion\n"); … … 144 73 } 145 74 printf("End Test: multi lock deadlock/mutual exclusion\n"); 146 147 count = 0;148 printf("Start Test: single scoped lock mutual exclusion\n");149 {150 T_Mutex_Scoped t[10];151 }152 assert(count == num_tasks * num_times);153 printf("End Test: single scoped lock mutual exclusion\n");154 printf("Start Test: multi scoped lock deadlock/mutual exclusion\n");155 {156 T_Multi_Scoped t[10];157 }158 printf("End Test: multi scoped lock deadlock/mutual exclusion\n");159 75 } -
tests/concurrent/preempt.cfa
rf5a51db r97c215f 1 #include <clock.hfa>2 #include <fstream.hfa>3 1 #include <kernel.hfa> 4 2 #include <thread.hfa> … … 23 21 extern void __cfaabi_check_preemption(); 24 22 25 static struct { 26 volatile int counter; 27 volatile Time prev; 28 Duration durations[6]; 29 } globals; 23 static volatile int counter = 0; 30 24 31 25 thread worker_t { 32 26 int value; 33 unsigned long long spin;34 27 }; 35 28 36 29 void ?{}( worker_t & this, int value ) { 37 30 this.value = value; 38 this.spin = 0;39 31 } 40 32 41 33 void main(worker_t & this) { 42 while(TEST(globals.counter < N)) { 43 if(this.spin > 50_000_000_000) abort | "Worker" | this.value | "has been spinning too long! (" | this.spin | ")"; 34 while(TEST(counter < N)) { 44 35 __cfaabi_check_preemption(); 45 if( ( globals.counter % 7) == this.value ) {36 if( (counter % 7) == this.value ) { 46 37 __cfaabi_check_preemption(); 47 #if !defined(TEST_LONG) 48 Time now = timeHiRes(); 49 Duration diff = now - globals.prev; 50 globals.prev = now; 51 #endif 52 int next = __atomic_add_fetch( &globals.counter, 1, __ATOMIC_SEQ_CST ); 38 int next = __atomic_add_fetch( &counter, 1, __ATOMIC_SEQ_CST ); 53 39 __cfaabi_check_preemption(); 54 if( (next % 100) == 0 ) { 55 #if !defined(TEST_LONG) 56 unsigned idx = next / 100; 57 if (idx >= 6) abort | "Idx from next is invalid: " | idx | "vs" | next; 58 globals.durations[idx] = diff; 59 if(diff > 12`s) serr | "Duration suspiciously large:" | diff; 60 #endif 61 printf("%d\n", (int)next); 62 63 } 40 if( (next % 100) == 0 ) printf("%d\n", (int)next); 64 41 __cfaabi_check_preemption(); 65 this.spin = 0;66 42 } 67 43 __cfaabi_check_preemption(); 68 44 KICK_WATCHDOG; 69 this.spin++;70 45 } 71 46 } … … 73 48 int main(int argc, char* argv[]) { 74 49 processor p; 75 globals.counter = 0;76 globals.durations[0] = 0;77 globals.durations[1] = 0;78 globals.durations[2] = 0;79 globals.durations[3] = 0;80 globals.durations[4] = 0;81 globals.durations[5] = 0;82 50 { 83 globals.prev = timeHiRes();84 51 worker_t w0 = 0; 85 52 worker_t w1 = 1; -
tests/include/.expect/includes.nast.txt
rf5a51db r97c215f 1 include/includes.cfa:15 3:25: warning: Compiled1 include/includes.cfa:154:25: warning: Compiled -
tests/include/includes.cfa
rf5a51db r97c215f 10 10 // Created On : Wed May 27 17:56:53 2015 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Thu Feb 3 22:06:07 202213 // Update Count : 7 7412 // Last Modified On : Sat Jun 5 10:06:46 2021 13 // Update Count : 751 14 14 // 15 15 … … 18 18 #endif // __CFA__ 19 19 20 #if 121 20 //#define _GNU_SOURCE 22 21 #include <aio.h> … … 41 40 #include <errno.h> 42 41 #include <error.h> 43 //#include <eti.h> 42 //#include <eti.h> // may not be installed, comes with ncurses 44 43 #include <execinfo.h> 45 44 #include <expat.h> … … 50 49 #include <fmtmsg.h> 51 50 #include <fnmatch.h> 52 //#include <form.h> 51 //#include <form.h> // may not be installed, comes with ncurses 53 52 #include <fstab.h> 54 53 #include <fts.h> … … 78 77 #include <mcheck.h> 79 78 #include <memory.h> 80 //#include <menu.h> 79 //#include <menu.h> // may not be installed, comes with ncurses 81 80 #include <mntent.h> 82 81 #include <monetary.h> 83 82 #include <mqueue.h> 84 //#include <ncurses_dll.h> 83 //#include <ncurses_dll.h> // may not be installed, comes with ncurses 85 84 #include <netdb.h> 86 85 #include <nl_types.h> 87 86 #include <nss.h> 88 87 #include <obstack.h> 89 //#include <panel.h> 88 //#include <panel.h> // may not be installed, comes with ncurses 90 89 #include <paths.h> 91 90 #include <poll.h> … … 118 117 #include <syslog.h> 119 118 #include <tar.h> 120 //#include <term.h> 121 //#include <termcap.h> 119 //#include <term.h> // may not be installed, comes with ncurses 120 //#include <termcap.h> // may not be installed, comes with ncurses 122 121 #include <termio.h> 123 122 #include <termios.h> … … 131 130 #include <ucontext.h> 132 131 #include <ulimit.h> 133 //#include <unctrl.h> 132 //#include <unctrl.h> // may not be installed, comes with ncurses 134 133 #include <unistd.h> 135 134 #include <utime.h> … … 144 143 #include <wctype.h> 145 144 #include <wordexp.h> 145 146 #if 0 146 147 #endif // 0 147 148 … … 150 151 #endif // __CFA__ 151 152 152 int main( int argc, char const * 153 #pragma GCC warning "Compiled" 153 int main( int argc, char const *argv[] ) { 154 #pragma GCC warning "Compiled" // force non-empty .expect file, NO TABS!!! 154 155 } 155 156 -
tools/auto-complete.md
rf5a51db r97c215f 32 32 33 33 ### Zsh 34 35 1 - Add the following somewhere:36 #compdef test.py37 38 _test_py() {39 local -a options40 options=$($words[1] --list-comp)41 _alternative "files:filenames:($options)"42 }43 44 _test_py "$@"45 46 2 - Add the path to that file to the "fpath" environment variable.47 48 3 - In ~/.zshrc add49 autoload -U compinit50 compinit51 52 *How it works:* I don't know ;P53 54
Note:
See TracChangeset
for help on using the changeset viewer.