|
@@ -9216,11 +9216,10 @@ conclusion:
|
|
|
\begin{tcolorbox}[colback=white]
|
|
|
{\if\edition\racketEd
|
|
|
\begin{tikzpicture}[baseline=(current bounding box.center),scale=0.90]
|
|
|
-\node (Lif) at (0,2) {\large \LangIf{}};
|
|
|
-\node (Lif-2) at (3,2) {\large \LangIf{}};
|
|
|
-\node (Lif-3) at (6,2) {\large \LangIf{}};
|
|
|
-\node (Lif-4) at (9,2) {\large \LangIf{}};
|
|
|
-\node (Lif-5) at (9,0) {\large \LangIfANF{}};
|
|
|
+\node (Lif-2) at (0,2) {\large \LangIf{}};
|
|
|
+\node (Lif-3) at (3,2) {\large \LangIf{}};
|
|
|
+\node (Lif-4) at (6,2) {\large \LangIf{}};
|
|
|
+\node (Lif-5) at (10,2) {\large \LangIfANF{}};
|
|
|
\node (C1-1) at (0,0) {\large \LangCIf{}};
|
|
|
|
|
|
\node (x86-2) at (0,-2) {\large \LangXIfVar{}};
|
|
@@ -9230,11 +9229,10 @@ conclusion:
|
|
|
\node (x86-4) at (8,-2) {\large \LangXIf{}};
|
|
|
\node (x86-5) at (8,-4) {\large \LangXIf{}};
|
|
|
|
|
|
-\path[->,bend left=15] (Lif) edge [above] node {\ttfamily\footnotesize type\_check} (Lif-2);
|
|
|
\path[->,bend left=15] (Lif-2) edge [above] node {\ttfamily\footnotesize shrink} (Lif-3);
|
|
|
\path[->,bend left=15] (Lif-3) edge [above] node {\ttfamily\footnotesize uniquify} (Lif-4);
|
|
|
-\path[->,bend left=15] (Lif-4) edge [left] node {\ttfamily\footnotesize remove\_complex\_operands} (Lif-5);
|
|
|
-\path[->,bend left=10] (Lif-5) edge [above] node {\ttfamily\footnotesize explicate\_control} (C1-1);
|
|
|
+\path[->,bend left=15] (Lif-4) edge [above] node {\ttfamily\footnotesize remove\_complex\_operands} (Lif-5);
|
|
|
+\path[->,bend left=10] (Lif-5) edge [right] node {\ttfamily\footnotesize explicate\_control} (C1-1);
|
|
|
\path[->,bend right=15] (C1-1) edge [right] node {\ttfamily\footnotesize select\_instructions} (x86-2);
|
|
|
\path[->,bend left=15] (x86-2) edge [right] node {\ttfamily\footnotesize uncover\_live} (x86-2-1);
|
|
|
\path[->,bend right=15] (x86-2-1) edge [below] node {\ttfamily\footnotesize build\_interference} (x86-2-2);
|
|
@@ -9794,7 +9792,7 @@ and the case-of-case transformation~\citep{PeytonJones:1998}.
|
|
|
In this chapter we study two features that are the hallmarks of
|
|
|
imperative programming languages: loops and assignments to local
|
|
|
variables. The following example demonstrates these new features by
|
|
|
-computing the sum of the first five positive integers.
|
|
|
+computing the sum of the first five positive integers:
|
|
|
% similar to loop_test_1.rkt
|
|
|
\begin{lstlisting}
|
|
|
(let ([sum 0])
|
|
@@ -9807,10 +9805,10 @@ computing the sum of the first five positive integers.
|
|
|
sum)))
|
|
|
\end{lstlisting}
|
|
|
The \code{while} loop consists of a condition and a
|
|
|
-body\footnote{The \code{while} loop is not a built-in
|
|
|
+body.\footnote{The \code{while} loop is not a built-in
|
|
|
feature of the Racket language, but Racket includes many looping
|
|
|
constructs and it is straightforward to define \code{while} as a
|
|
|
-macro.}. The body is evaluated repeatedly so long as the condition
|
|
|
+macro.} The body is evaluated repeatedly so long as the condition
|
|
|
remains true.
|
|
|
%
|
|
|
The \code{set!} consists of a variable and a right-hand side
|
|
@@ -9818,26 +9816,27 @@ expression. The \code{set!} updates value of the variable to the
|
|
|
value of the right-hand side.
|
|
|
%
|
|
|
The primary purpose of both the \code{while} loop and \code{set!} is
|
|
|
-to cause side effects, so they do not have a meaningful result
|
|
|
-value. Instead their result is the \code{\#<void>} value. The
|
|
|
+to cause side effects, so they do not give a meaningful result
|
|
|
+value. Instead, their result is the \code{\#<void>} value. The
|
|
|
expression \code{(void)} is an explicit way to create the
|
|
|
-\code{\#<void>} value and it has type \code{Void}. The
|
|
|
+\code{\#<void>} value, and it has type \code{Void}. The
|
|
|
\code{\#<void>} value can be passed around just like other values
|
|
|
-inside an \LangLoop{} program and it can be compared for equality with
|
|
|
+inside an \LangLoop{} program, and it can be compared for equality with
|
|
|
another \code{\#<void>} value. However, there are no other operations
|
|
|
-specific to the the \code{\#<void>} value in \LangLoop{}. In contrast,
|
|
|
+specific to the \code{\#<void>} value in \LangLoop{}. In contrast,
|
|
|
Racket defines the \code{void?} predicate that returns \code{\#t}
|
|
|
when applied to \code{\#<void>} and \code{\#f} otherwise.%
|
|
|
%
|
|
|
\footnote{Racket's \code{Void} type corresponds to what is often
|
|
|
called the \code{Unit} type. Racket's \code{Void} type is inhabited
|
|
|
- by a single value \code{\#<void>} which corresponds to \code{unit}
|
|
|
- or \code{()} in the literature~\citep{Pierce:2002hj}.}.
|
|
|
+ by a single value \code{\#<void>}, which corresponds to \code{unit}
|
|
|
+ or \code{()} in the literature~\citep{Pierce:2002hj}.}
|
|
|
%
|
|
|
-With the addition of side-effecting features such as \code{while} loop
|
|
|
-and \code{set!}, it is helpful to also include in a language feature
|
|
|
-for sequencing side effects: the \code{begin} expression. It consists
|
|
|
-of one or more subexpressions that are evaluated left-to-right.
|
|
|
+With the addition of side effect-producing features such as
|
|
|
+\code{while} loop and \code{set!}, it is helpful to include a language
|
|
|
+feature for sequencing side effects: the \code{begin} expression. It
|
|
|
+consists of one or more subexpressions that are evaluated
|
|
|
+left to right.
|
|
|
%
|
|
|
\fi}
|
|
|
|
|
@@ -9966,9 +9965,9 @@ the condition remains true.
|
|
|
\label{fig:Lwhile-syntax}
|
|
|
\end{figure}
|
|
|
|
|
|
-The concrete syntax of \LangLoop{} is defined in
|
|
|
-figure~\ref{fig:Lwhile-concrete-syntax} and its abstract syntax is defined
|
|
|
-in figure~\ref{fig:Lwhile-syntax}.
|
|
|
+Figure~\ref{fig:Lwhile-concrete-syntax} shows the definition of the
|
|
|
+concrete syntax of \LangLoop{}, and figure~\ref{fig:Lwhile-syntax}
|
|
|
+shows the definition of its abstract syntax.
|
|
|
%
|
|
|
The definitional interpreter for \LangLoop{} is shown in
|
|
|
figure~\ref{fig:interp-Lwhile}.
|
|
@@ -9976,20 +9975,20 @@ figure~\ref{fig:interp-Lwhile}.
|
|
|
{\if\edition\racketEd
|
|
|
%
|
|
|
We add new cases for \code{SetBang}, \code{WhileLoop}, \code{Begin},
|
|
|
-and \code{Void} and we make changes to the cases for \code{Var} and
|
|
|
+and \code{Void}, and we make changes to the cases for \code{Var} and
|
|
|
\code{Let} regarding variables. To support assignment to variables and
|
|
|
to make their lifetimes indefinite (see the second example in
|
|
|
section~\ref{sec:assignment-scoping}), we box the value that is bound
|
|
|
to each variable (in \code{Let}). The case for \code{Var} unboxes the
|
|
|
value.
|
|
|
%
|
|
|
-Now to discuss the new cases. For \code{SetBang}, we find the
|
|
|
-variable in the environment to obtain a boxed value and then we change
|
|
|
+Now we discuss the new cases. For \code{SetBang}, we find the
|
|
|
+variable in the environment to obtain a boxed value, and then we change
|
|
|
it using \code{set-box!} to the result of evaluating the right-hand
|
|
|
side. The result value of a \code{SetBang} is \code{\#<void>}.
|
|
|
%
|
|
|
-For the \code{WhileLoop}, we repeatedly 1) evaluate the condition, and
|
|
|
-if the result is true, 2) evaluate the body.
|
|
|
+For the \code{WhileLoop}, we repeatedly (1) evaluate the condition, and
|
|
|
+if the result is true, (2) evaluate the body.
|
|
|
The result value of a \code{while} loop is also \code{\#<void>}.
|
|
|
%
|
|
|
The $\BEGIN{\itm{es}}{\itm{body}}$ expression evaluates the
|
|
@@ -10062,7 +10061,7 @@ class InterpLwhile(InterpLif):
|
|
|
\label{fig:interp-Lwhile}
|
|
|
\end{figure}
|
|
|
|
|
|
-The type checker for \LangLoop{} is defined in
|
|
|
+The definition of the type checker for \LangLoop{} is shown in
|
|
|
figure~\ref{fig:type-check-Lwhile}.
|
|
|
%
|
|
|
{\if\edition\racketEd
|
|
@@ -10146,9 +10145,9 @@ class TypeCheckLwhile(TypeCheckLif):
|
|
|
%
|
|
|
At first glance, the translation of these language features to x86
|
|
|
seems straightforward because the \LangCIf{} intermediate language
|
|
|
-already supports all of the ingredients that we need: assignment,
|
|
|
+already supports all the ingredients that we need: assignment,
|
|
|
\code{goto}, conditional branching, and sequencing. However, there are
|
|
|
-complications that arise which we discuss in the next section. After
|
|
|
+complications that arise, which we discuss in the next section. After
|
|
|
that we introduce the changes necessary to the existing passes.
|
|
|
%
|
|
|
\fi}
|
|
@@ -10166,16 +10165,16 @@ that we introduce the changes necessary to the existing passes.
|
|
|
\section{Cyclic Control Flow and Dataflow Analysis}
|
|
|
\label{sec:dataflow-analysis}
|
|
|
|
|
|
-Up until this point the programs generated in
|
|
|
+Up until this point, the programs generated in
|
|
|
\code{explicate\_control} were guaranteed to be acyclic. However, each
|
|
|
-\code{while} loop introduces a cycle. But does that matter?
|
|
|
+\code{while} loop introduces a cycle. Does that matter?
|
|
|
%
|
|
|
-Indeed it does. Recall that for register allocation, the compiler
|
|
|
+Indeed, it does. Recall that for register allocation, the compiler
|
|
|
performs liveness analysis to determine which variables can share the
|
|
|
-same register. To accomplish this we analyzed the control-flow graph
|
|
|
+same register. To accomplish this, we analyzed the control-flow graph
|
|
|
in reverse topological order
|
|
|
(section~\ref{sec:liveness-analysis-Lif}), but topological order is
|
|
|
-only well-defined for acyclic graphs.
|
|
|
+well defined only for acyclic graphs.
|
|
|
|
|
|
Let us return to the example of computing the sum of the first five
|
|
|
positive integers. Here is the program after instruction selection but
|
|
@@ -10242,10 +10241,10 @@ block8:
|
|
|
\end{minipage}
|
|
|
\fi}
|
|
|
\end{center}
|
|
|
-Recall that liveness analysis works backwards, starting at the end
|
|
|
+Recall that liveness analysis works backward, starting at the end
|
|
|
of each function. For this example we could start with \code{block8}
|
|
|
-because we know what is live at the beginning of the conclusion,
|
|
|
-just \code{rax} and \code{rsp}. So the live-before set
|
|
|
+because we know what is live at the beginning of the conclusion:
|
|
|
+only \code{rax} and \code{rsp}. So the live-before set
|
|
|
for \code{block8} is \code{\{rsp,sum\}}.
|
|
|
%
|
|
|
Next we might try to analyze \code{block5} or \code{block7}, but
|
|
@@ -10253,26 +10252,26 @@ Next we might try to analyze \code{block5} or \code{block7}, but
|
|
|
we are stuck.
|
|
|
|
|
|
The way out of this impasse is to realize that we can compute an
|
|
|
-under-approximation of each live-before set by starting with empty
|
|
|
-live-after sets. By \emph{under-approximation}, we mean that the set
|
|
|
-only contains variables that are live for some execution of the
|
|
|
+underapproximation of each live-before set by starting with empty
|
|
|
+live-after sets. By \emph{underapproximation}, we mean that the set
|
|
|
+contains only variables that are live for some execution of the
|
|
|
program, but the set may be missing some variables that are live.
|
|
|
-Next, the under-approximations for each block can be improved by 1)
|
|
|
+Next, the underapproximations for each block can be improved by (1)
|
|
|
updating the live-after set for each block using the approximate
|
|
|
-live-before sets from the other blocks and 2) perform liveness
|
|
|
+live-before sets from the other blocks, and (2) performing liveness
|
|
|
analysis again on each block. In fact, by iterating this process, the
|
|
|
-under-approximations eventually become the correct solutions!
|
|
|
+underapproximations eventually become the correct solutions!
|
|
|
%
|
|
|
This approach of iteratively analyzing a control-flow graph is
|
|
|
applicable to many static analysis problems and goes by the name
|
|
|
\emph{dataflow analysis}\index{subject}{dataflow analysis}. It was invented by
|
|
|
-\citet{Kildall:1973vn} in his Ph.D. thesis at the University of
|
|
|
+\citet{Kildall:1973vn} in his PhD thesis at the University of
|
|
|
Washington.
|
|
|
|
|
|
-Let us apply this approach to the above example. We use the empty set
|
|
|
-for the initial live-before set for each block. Let $m_0$ be the
|
|
|
-following mapping from label names to sets of locations (variables and
|
|
|
-registers).
|
|
|
+Let us apply this approach to the previously presented example. We use
|
|
|
+the empty set for the initial live-before set for each block. Let
|
|
|
+$m_0$ be the following mapping from label names to sets of locations
|
|
|
+(variables and registers):
|
|
|
\begin{center}
|
|
|
\begin{lstlisting}
|
|
|
mainstart: {}, block5: {}, block7: {}, block8: {}
|
|
@@ -10289,8 +10288,8 @@ mainstart: {}, block5: {i}, block7: {i, sum}, block8: {rsp, sum}
|
|
|
\end{center}
|
|
|
|
|
|
For the second round, the live-after for \code{mainstart} is the
|
|
|
-current live-before for \code{block5}, which is \code{\{i\}}. So the
|
|
|
-liveness analysis for \code{mainstart} computes the empty set. The
|
|
|
+current live-before for \code{block5}, which is \code{\{i\}}. Therefore
|
|
|
+the liveness analysis for \code{mainstart} computes the empty set. The
|
|
|
live-after for \code{block5} is the union of the live-before sets for
|
|
|
\code{block7} and \code{block8}, which is \code{\{i , rsp, sum\}}.
|
|
|
So the liveness analysis for \code{block5} computes \code{\{i , rsp,
|
|
@@ -10298,7 +10297,7 @@ So the liveness analysis for \code{block5} computes \code{\{i , rsp,
|
|
|
\code{block5} (from the previous iteration), which is \code{\{i\}}.
|
|
|
So the liveness analysis for \code{block7} remains \code{\{i,
|
|
|
sum\}}. Together these yield the following approximation $m_2$ of
|
|
|
-the live-before sets.
|
|
|
+the live-before sets:
|
|
|
\begin{center}
|
|
|
\begin{lstlisting}
|
|
|
mainstart: {}, block5: {i, rsp, sum}, block7: {i, sum}, block8: {rsp, sum}
|
|
@@ -10308,7 +10307,7 @@ In the preceding iteration, only \code{block5} changed, so we can
|
|
|
limit our attention to \code{mainstart} and \code{block7}, the two
|
|
|
blocks that jump to \code{block5}. As a result, the live-before sets
|
|
|
for \code{mainstart} and \code{block7} are updated to include
|
|
|
-\code{rsp}, yielding the following approximation $m_3$.
|
|
|
+\code{rsp}, yielding the following approximation $m_3$:
|
|
|
\begin{center}
|
|
|
\begin{lstlisting}
|
|
|
mainstart: {rsp}, block5: {i,rsp,sum}, block7: {i,rsp,sum}, block8: {rsp,sum}
|
|
@@ -10319,20 +10318,21 @@ its live-before set remains \code{\{i,rsp,sum\}}. At this point
|
|
|
our approximations have converged, so $m_3$ is the solution.
|
|
|
|
|
|
This iteration process is guaranteed to converge to a solution by the
|
|
|
-Kleene Fixed-Point Theorem, a general theorem about functions on
|
|
|
+Kleene fixed-point theorem, a general theorem about functions on
|
|
|
lattices~\citep{Kleene:1952aa}. Roughly speaking, a \emph{lattice} is
|
|
|
any collection that comes with a partial ordering $\sqsubseteq$ on its
|
|
|
-elements, a least element $\bot$ (pronounced bottom), and a join
|
|
|
-operator $\sqcup$.\index{subject}{lattice}\index{subject}{bottom}\index{subject}{partial
|
|
|
- ordering}\index{subject}{join}\footnote{Technically speaking, we will be
|
|
|
- working with join semi-lattices.} When two elements are ordered $m_i
|
|
|
-\sqsubseteq m_j$, it means that $m_j$ contains at least as much
|
|
|
-information as $m_i$, so we can think of $m_j$ as a better-or-equal
|
|
|
-approximation than $m_i$. The bottom element $\bot$ represents the
|
|
|
-complete lack of information, i.e., the worst approximation. The join
|
|
|
-operator takes two lattice elements and combines their information,
|
|
|
-i.e., it produces the least upper bound of the two.\index{subject}{least upper
|
|
|
- bound}
|
|
|
+elements, a least element $\bot$ (pronounced \emph{bottom}), and a
|
|
|
+join operator
|
|
|
+$\sqcup$.\index{subject}{lattice}\index{subject}{bottom}\index{subject}{partial
|
|
|
+ ordering}\index{subject}{join}\footnote{Technically speaking, we
|
|
|
+ will be working with join semilattices.} When two elements are
|
|
|
+ordered $m_i \sqsubseteq m_j$, it means that $m_j$ contains at least
|
|
|
+as much information as $m_i$, so we can think of $m_j$ as a
|
|
|
+better-than-or-equal-to approximation in relation to $m_i$. The
|
|
|
+bottom element $\bot$ represents the complete lack of information,
|
|
|
+that is, the worst approximation. The join operator takes two lattice
|
|
|
+elements and combines their information; that is, it produces the
|
|
|
+least upper bound of the two.\index{subject}{least upper bound}
|
|
|
|
|
|
A dataflow analysis typically involves two lattices: one lattice to
|
|
|
represent abstract states and another lattice that aggregates the
|
|
@@ -10344,11 +10344,11 @@ set, and the join operator to be set union.
|
|
|
%
|
|
|
We form a second lattice $M$ by taking its elements to be mappings
|
|
|
from the block labels to sets of locations (elements of $L$). We
|
|
|
-order the mappings point-wise, using the ordering of $L$. So given any
|
|
|
+order the mappings point-wise, using the ordering of $L$. So, given any
|
|
|
two mappings $m_i$ and $m_j$, $m_i \sqsubseteq_M m_j$ when $m_i(\ell)
|
|
|
\subseteq m_j(\ell)$ for every block label $\ell$ in the program. The
|
|
|
bottom element of $M$ is the mapping $\bot_M$ that sends every label
|
|
|
-to the empty set, i.e., $\bot_M(\ell) = \emptyset$.
|
|
|
+to the empty set; that is, $\bot_M(\ell) = \emptyset$.
|
|
|
|
|
|
We can think of one iteration of liveness analysis applied to the
|
|
|
whole program as being a function $f$ on the lattice $M$. It takes a
|
|
@@ -10363,20 +10363,20 @@ solution should be a \emph{fixed point} of the function $f$.\index{subject}{fixe
|
|
|
\[
|
|
|
f(m_s) = m_s
|
|
|
\]
|
|
|
-Furthermore, the solution should only include locations that are
|
|
|
+Furthermore, the solution should include only locations that are
|
|
|
forced to be there by performing liveness analysis on the program, so
|
|
|
the solution should be the \emph{least} fixed point.\index{subject}{least fixed point}
|
|
|
|
|
|
-The Kleene Fixed-Point Theorem states that if a function $f$ is
|
|
|
+The Kleene fixed-point theorem states that if a function $f$ is
|
|
|
monotone (better inputs produce better outputs), then the least fixed
|
|
|
point of $f$ is the least upper bound of the \emph{ascending Kleene
|
|
|
- chain} obtained by starting at $\bot$ and iterating $f$ as
|
|
|
-follows.\index{subject}{Kleene Fixed-Point Theorem}
|
|
|
+ chain} obtained by starting at $\bot$ and iterating $f$, as
|
|
|
+follows:\index{subject}{Kleene Fixed-Point Theorem}
|
|
|
\[
|
|
|
\bot \sqsubseteq f(\bot) \sqsubseteq f(f(\bot)) \sqsubseteq \cdots
|
|
|
\sqsubseteq f^n(\bot) \sqsubseteq \cdots
|
|
|
\]
|
|
|
-When a lattice contains only finitely-long ascending chains, then
|
|
|
+When a lattice contains only finitely long ascending chains, then
|
|
|
every Kleene chain tops out at some fixed point after some number of
|
|
|
iterations of $f$.
|
|
|
\[
|
|
@@ -10385,7 +10385,7 @@ iterations of $f$.
|
|
|
\]
|
|
|
|
|
|
The liveness analysis is indeed a monotone function and the lattice
|
|
|
-$M$ only has finitely-long ascending chains because there are only a
|
|
|
+$M$ has finitely long ascending chains because there are only a
|
|
|
finite number of variables and blocks in the program. Thus we are
|
|
|
guaranteed that iteratively applying liveness analysis to all blocks
|
|
|
in the program will eventually produce the least fixed point solution.
|
|
@@ -10394,17 +10394,17 @@ Next let us consider dataflow analysis in general and discuss the
|
|
|
generic work list algorithm (figure~\ref{fig:generic-dataflow}).
|
|
|
%
|
|
|
The algorithm has four parameters: the control-flow graph \code{G}, a
|
|
|
-function \code{transfer} that applies the analysis to one block, the
|
|
|
-\code{bottom} and \code{join} operator for the lattice of abstract
|
|
|
+function \code{transfer} that applies the analysis to one block, and the
|
|
|
+\code{bottom} and \code{join} operators for the lattice of abstract
|
|
|
states. The \code{analyze\_dataflow} function is formulated as a
|
|
|
-\emph{forward} dataflow analysis, that is, the inputs to the transfer
|
|
|
+\emph{forward} dataflow analysis; that is, the inputs to the transfer
|
|
|
function come from the predecessor nodes in the control-flow
|
|
|
graph. However, liveness analysis is a \emph{backward} dataflow
|
|
|
analysis, so in that case one must supply the \code{analyze\_dataflow}
|
|
|
function with the transpose of the control-flow graph.
|
|
|
|
|
|
The algorithm begins by creating the bottom mapping, represented by a
|
|
|
-hash table. It then pushes all of the nodes in the control-flow graph
|
|
|
+hash table. It then pushes all the nodes in the control-flow graph
|
|
|
onto the work list (a queue). The algorithm repeats the \code{while}
|
|
|
loop as long as there are items in the work list. In each iteration, a
|
|
|
node is popped from the work list and processed. The \code{input} for
|
|
@@ -10414,7 +10414,6 @@ obtain the \code{output} abstract state. If the output differs from
|
|
|
the previous state for this block, the mapping for this block is
|
|
|
updated and its successor nodes are pushed onto the work list.
|
|
|
|
|
|
-
|
|
|
\begin{figure}[tb]
|
|
|
\begin{tcolorbox}[colback=white]
|
|
|
{\if\edition\racketEd
|
|
@@ -10468,7 +10467,7 @@ def analyze_dataflow(G, transfer, bottom, join):
|
|
|
There is a subtle interaction between the
|
|
|
\code{remove\_complex\_operands} pass, the addition of \code{set!},
|
|
|
and the left-to-right order of evaluation of Racket. Consider the
|
|
|
-following example.
|
|
|
+following example:
|
|
|
\begin{lstlisting}
|
|
|
(let ([x 2])
|
|
|
(+ x (begin (set! x 40) x)))
|
|
@@ -10482,7 +10481,7 @@ example we obtain the following program whose result is \code{80}!
|
|
|
(let ([tmp (begin (set! x 40) x)])
|
|
|
(+ x tmp)))
|
|
|
\end{lstlisting}
|
|
|
-The problem is that, with mutable variables, the ordering between
|
|
|
+The problem is that with mutable variables, the ordering between
|
|
|
reads and writes is important, and the
|
|
|
\code{remove\_complex\_operands} pass moved the \code{set!} to happen
|
|
|
before the first read of \code{x}.
|
|
@@ -10493,16 +10492,16 @@ side of a \code{set!}. We mark each read from a mutable variable with
|
|
|
the form \code{get!} (\code{GetBang} in abstract syntax) to indicate
|
|
|
that the read operation is effectful in that it can produce different
|
|
|
results at different points in time. Let's apply this idea to the
|
|
|
-following variation that also involves a variable that is not mutated.
|
|
|
+following variation that also involves a variable that is not mutated:
|
|
|
% loop_test_24.rkt
|
|
|
\begin{lstlisting}
|
|
|
(let ([x 2])
|
|
|
(let ([y 0])
|
|
|
(+ y (+ x (begin (set! x 40) x)))))
|
|
|
\end{lstlisting}
|
|
|
-We first analyze the above program to discover that variable \code{x}
|
|
|
+We first analyze this program to discover that variable \code{x}
|
|
|
is mutable but \code{y} is not. We then transform the program as
|
|
|
-follows, replacing each occurrence of \code{x} with \code{(get! x)}.
|
|
|
+follows, replacing each occurrence of \code{x} with \code{(get! x)}:
|
|
|
\begin{lstlisting}
|
|
|
(let ([x 2])
|
|
|
(let ([y 0])
|
|
@@ -10513,7 +10512,7 @@ immutable variables, we can apply the \code{remove\_complex\_operands}
|
|
|
pass, where reads from immutable variables are still classified as
|
|
|
atomic expressions but reads from mutable variables are classified as
|
|
|
complex. Thus, \code{remove\_complex\_operands} yields the following
|
|
|
-program.\\
|
|
|
+program:\\
|
|
|
\begin{minipage}{\textwidth}
|
|
|
\begin{lstlisting}
|
|
|
(let ([x 2])
|
|
@@ -10533,7 +10532,7 @@ variables, making it more likely for some of them to be spilled. The
|
|
|
result of this program is \code{42}, the same as the result prior to
|
|
|
\code{remove\_complex\_operands}.
|
|
|
|
|
|
-The approach that we've sketched above requires only a small
|
|
|
+The approach that we've sketched requires only a small
|
|
|
modification to \code{remove\_complex\_operands} to handle
|
|
|
\code{get!}. However, it requires a new pass, called
|
|
|
\code{uncover-get!}, that we discuss in
|
|
@@ -10563,10 +10562,10 @@ passes.
|
|
|
\section{Uncover \texttt{get!}}
|
|
|
\label{sec:uncover-get-bang}
|
|
|
|
|
|
-The goal of this pass it to mark uses of mutable variables so that
|
|
|
+The goal of this pass is to mark uses of mutable variables so that
|
|
|
\code{remove\_complex\_operands} can treat them as complex expressions
|
|
|
-and thereby preserve their ordering relative to the side-effects in
|
|
|
-other operands. So the first step is to collect all the mutable
|
|
|
+and thereby preserve their ordering relative to the side effects in
|
|
|
+other operands. So, the first step is to collect all the mutable
|
|
|
variables. We recommend creating an auxiliary function for this,
|
|
|
named \code{collect-set!}, that recursively traverses expressions,
|
|
|
returning the set of all variables that occur on the left-hand side of a
|
|
@@ -10587,14 +10586,14 @@ returning the set of all variables that occur on the left-hand side of a
|
|
|
\end{minipage}
|
|
|
\end{center}
|
|
|
By placing this pass after \code{uniquify}, we need not worry about
|
|
|
-variable shadowing and our logic for \code{Let} can remain simple, as
|
|
|
-in the excerpt above.
|
|
|
+variable shadowing, and our logic for \code{Let} can remain simple, as
|
|
|
+in this excerpt.
|
|
|
|
|
|
The second step is to mark the occurrences of the mutable variables
|
|
|
with the new \code{GetBang} AST node (\code{get!} in concrete
|
|
|
syntax). The following is an excerpt of the \code{uncover-get!-exp}
|
|
|
function, which takes two parameters: the set of mutable variables
|
|
|
-\code{set!-vars}, and the expression \code{e} to be processed. The
|
|
|
+\code{set!-vars} and the expression \code{e} to be processed. The
|
|
|
case for \code{(Var x)} replaces it with \code{(GetBang x)} if it is a
|
|
|
mutable variable or leaves it alone if not.
|
|
|
\begin{center}
|
|
@@ -10710,7 +10709,7 @@ must introduce a temporary variable and bind it to the complex
|
|
|
expression. This approach applies, unchanged, to handle the new
|
|
|
language forms. For example, in the following code there are two
|
|
|
\code{begin} expressions appearing as arguments to the \code{+}
|
|
|
-operator. The output of \code{rco\_exp} is shown below, in which the
|
|
|
+operator. The output of \code{rco\_exp} is then shown, in which the
|
|
|
\code{begin} expressions have been bound to temporary
|
|
|
variables. Recall that \code{let} expressions in \LangLoopANF{} are
|
|
|
allowed to have arbitrary expressions in their right-hand side
|
|
@@ -10768,7 +10767,7 @@ $\Rightarrow$
|
|
|
|
|
|
Recall that in the \code{explicate\_control} pass we define one helper
|
|
|
function for each kind of position in the program. For the \LangVar{}
|
|
|
-language of integers and variables we needed assignment and tail
|
|
|
+language of integers and variables, we needed assignment and tail
|
|
|
positions. The \code{if} expressions of \LangIf{} introduced predicate
|
|
|
positions. For \LangLoop{}, the \code{begin} expression introduces yet
|
|
|
another kind of position: effect position. Except for the last
|
|
@@ -10778,11 +10777,11 @@ generate better code by taking this fact into account.
|
|
|
|
|
|
The output language of \code{explicate\_control} is \LangCLoop{}
|
|
|
(figure~\ref{fig:c7-syntax}), which is nearly identical to
|
|
|
-\LangCIf{}. The only syntactic difference is the addition of \VOID{}
|
|
|
+\LangCIf{}. The only syntactic differences are the addition of \VOID{}
|
|
|
and that \code{read} may appear as a statement. The most significant
|
|
|
difference between the programs generated by \code{explicate\_control}
|
|
|
in chapter~\ref{ch:Lif} versus \code{explicate\_control} in this
|
|
|
-chapter is that the control-flow graphs of the later may contain
|
|
|
+chapter is that the control-flow graphs of the latter may contain
|
|
|
cycles.
|
|
|
|
|
|
\begin{figure}[tp]
|
|
@@ -10813,7 +10812,7 @@ causes side effects, then the expression can be removed, so the result
|
|
|
is just the continuation.
|
|
|
%
|
|
|
The case for $\WHILE{\itm{cnd}}{\itm{body}}$ expressions is
|
|
|
-interesting; the generated code is depicted in the following diagram.
|
|
|
+interesting; the generated code is depicted in the following diagram:
|
|
|
\begin{center}
|
|
|
\begin{minipage}{0.3\textwidth}
|
|
|
\xymatrix{
|
|
@@ -10828,8 +10827,8 @@ We start by creating a fresh label $\itm{loop}$ for the top of the
|
|
|
loop. Next, recursively process the \itm{body} (in effect position)
|
|
|
with a \code{goto} to $\itm{loop}$ as the continuation, producing
|
|
|
\itm{body'}. Process the \itm{cnd} (in predicate position) with
|
|
|
-\itm{body'} as the then-branch and the continuation block as the
|
|
|
-else-branch. The result should be added to the dictionary of
|
|
|
+\itm{body'} as the \emph{then} branch and the continuation block as the
|
|
|
+\emph{else} branch. The result should be added to the dictionary of
|
|
|
\code{basic-blocks} with the label \itm{loop}. The result for the
|
|
|
whole \code{while} loop is a \code{goto} to the \itm{loop} label.
|
|
|
|
|
@@ -10862,8 +10861,8 @@ the condition expression.
|
|
|
Only two small additions are needed in the \code{select\_instructions}
|
|
|
pass to handle the changes to \LangCLoop{}. First, to handle the
|
|
|
addition of \VOID{} we simply translate it to \code{0}. Second,
|
|
|
-\code{read} may appear as a stand-alone statement instead of only
|
|
|
-appearing on the right-hand side of an assignment statement. The code
|
|
|
+\code{read} may appear as a stand-alone statement instead of
|
|
|
+appearing only on the right-hand side of an assignment statement. The code
|
|
|
generation is nearly identical to the one for assignment; just leave
|
|
|
off the instruction for moving the result into the left-hand side.
|
|
|
|
|
@@ -10883,7 +10882,7 @@ perform liveness analysis, replacing the code in
|
|
|
\code{uncover\_live} that processed the basic blocks in topological
|
|
|
order (section~\ref{sec:liveness-analysis-Lif}).
|
|
|
|
|
|
-The \code{analyze\_dataflow} function has four parameters.
|
|
|
+The \code{analyze\_dataflow} function has the following four parameters.
|
|
|
\begin{enumerate}
|
|
|
\item The first parameter \code{G} should be passed the transpose
|
|
|
of the control-flow graph.
|
|
@@ -10893,7 +10892,7 @@ The \code{analyze\_dataflow} function has four parameters.
|
|
|
set for that block. The transfer function should return the
|
|
|
live-before set for the block.
|
|
|
%
|
|
|
- \racket{Also, as a side-effect, it should update the block's
|
|
|
+ \racket{Also, as a side effect, it should update the block's
|
|
|
$\itm{info}$ with the liveness information for each instruction.}
|
|
|
%
|
|
|
\python{Also, as a side-effect, it should update the live-before and
|
|
@@ -10903,14 +10902,14 @@ The \code{analyze\_dataflow} function has four parameters.
|
|
|
reuse the code you already have for analyzing basic blocks.
|
|
|
\item The third and fourth parameters of \code{analyze\_dataflow} are
|
|
|
\code{bottom} and \code{join} for the lattice of abstract states,
|
|
|
- i.e. sets of locations. For liveness analysis, the bottom of the
|
|
|
- lattice is the empty set and the join operator is set union.
|
|
|
+ that is, sets of locations. For liveness analysis, the bottom of the
|
|
|
+ lattice is the empty set, and the join operator is set union.
|
|
|
\end{enumerate}
|
|
|
|
|
|
|
|
|
\begin{figure}[p]
|
|
|
\begin{tcolorbox}[colback=white]
|
|
|
-\begin{tikzpicture}[baseline=(current bounding box.center)]
|
|
|
+\begin{tikzpicture}[baseline=(current bounding box.center),scale=0.90]
|
|
|
\node (Lfun) at (0,2) {\large \LangLoop{}};
|
|
|
\node (Lfun-2) at (3,2) {\large \LangLoop{}};
|
|
|
%\node (Lfun-3) at (6,2) {\large \LangLoop{}};
|
|
@@ -10921,14 +10920,14 @@ The \code{analyze\_dataflow} function has four parameters.
|
|
|
\node (F1-4) at (6,2) {\large \LangLoop{}};
|
|
|
\node (F1-5) at (9,2) {\large \LangLoop{}};
|
|
|
\node (F1-6) at (9,0) {\large \LangLoopANF{}};
|
|
|
-\node (C3-2) at (3,0) {\large \racket{\LangCLoop{}}\python{\LangCIf{}}};
|
|
|
+\node (C3-2) at (0,0) {\large \racket{\LangCLoop{}}\python{\LangCIf{}}};
|
|
|
|
|
|
-\node (x86-2) at (3,-2) {\large \LangXIfVar{}};
|
|
|
-\node (x86-2-1) at (3,-4) {\large \LangXIfVar{}};
|
|
|
-\node (x86-2-2) at (6,-4) {\large \LangXIfVar{}};
|
|
|
-\node (x86-3) at (6,-2) {\large \LangXIfVar{}};
|
|
|
-\node (x86-4) at (9,-2) {\large \LangXIf{}};
|
|
|
-\node (x86-5) at (9,-4) {\large \LangXIf{}};
|
|
|
+\node (x86-2) at (0,-2) {\large \LangXIfVar{}};
|
|
|
+\node (x86-2-1) at (0,-4) {\large \LangXIfVar{}};
|
|
|
+\node (x86-2-2) at (4,-4) {\large \LangXIfVar{}};
|
|
|
+\node (x86-3) at (4,-2) {\large \LangXIfVar{}};
|
|
|
+\node (x86-4) at (8,-2) {\large \LangXIf{}};
|
|
|
+\node (x86-5) at (8,-4) {\large \LangXIf{}};
|
|
|
|
|
|
|
|
|
%% \path[->,bend left=15] (Lfun) edge [above] node
|
|
@@ -10949,22 +10948,22 @@ The \code{analyze\_dataflow} function has four parameters.
|
|
|
%% {\ttfamily\footnotesize expose-alloc.} (F1-4);
|
|
|
\path[->,bend left=15] (F1-4) edge [above] node
|
|
|
{\ttfamily\footnotesize uncover\_get!} (F1-5);
|
|
|
-\path[->,bend left=15] (F1-5) edge [right] node
|
|
|
- {\ttfamily\footnotesize remove\_complex.} (F1-6);
|
|
|
-\path[->,bend right=15] (F1-6) edge [above] node
|
|
|
+\path[->,bend left=15] (F1-5) edge [left] node
|
|
|
+ {\ttfamily\footnotesize remove\_complex\_operands} (F1-6);
|
|
|
+\path[->,bend left=10] (F1-6) edge [above] node
|
|
|
{\ttfamily\footnotesize explicate\_control} (C3-2);
|
|
|
-\path[->,bend left=15] (C3-2) edge [left] node
|
|
|
- {\ttfamily\footnotesize select\_instr.} (x86-2);
|
|
|
-\path[->,bend right=15] (x86-2) edge [left] node
|
|
|
+\path[->,bend left=15] (C3-2) edge [right] node
|
|
|
+ {\ttfamily\footnotesize select\_instructions} (x86-2);
|
|
|
+\path[->,bend right=15] (x86-2) edge [right] node
|
|
|
{\ttfamily\footnotesize uncover\_live} (x86-2-1);
|
|
|
\path[->,bend right=15] (x86-2-1) edge [below] node
|
|
|
- {\ttfamily\footnotesize build\_inter.} (x86-2-2);
|
|
|
-\path[->,bend right=15] (x86-2-2) edge [left] node
|
|
|
- {\ttfamily\footnotesize allocate\_reg.} (x86-3);
|
|
|
+ {\ttfamily\footnotesize build\_interference} (x86-2-2);
|
|
|
+\path[->,bend right=15] (x86-2-2) edge [right] node
|
|
|
+ {\ttfamily\footnotesize allocate\_registers} (x86-3);
|
|
|
\path[->,bend left=15] (x86-3) edge [above] node
|
|
|
- {\ttfamily\footnotesize patch\_instr.} (x86-4);
|
|
|
+ {\ttfamily\footnotesize patch\_instructions} (x86-4);
|
|
|
\path[->,bend left=15] (x86-4) edge [right] node
|
|
|
- {\ttfamily\footnotesize pre.\_and\_concl.} (x86-5);
|
|
|
+ {\ttfamily\footnotesize prelude\_and\_conclusion} (x86-5);
|
|
|
\end{tikzpicture}
|
|
|
\end{tcolorbox}
|
|
|
|