|
@@ -3343,8 +3343,7 @@ shown in Figure~\ref{fig:reg-alloc-passes}.
|
|
|
\node (R1) at (0,2) {\large $R_1$};
|
|
|
\node (R1-2) at (3,2) {\large $R_1$};
|
|
|
\node (R1-3) at (6,2) {\large $R_1$};
|
|
|
-\node (C0-1) at (6,0) {\large $C_0$};
|
|
|
-\node (C0-2) at (3,0) {\large $C_0$};
|
|
|
+\node (C0-1) at (3,0) {\large $C_0$};
|
|
|
|
|
|
\node (x86-2) at (3,-2) {\large $\text{x86}^{*}$};
|
|
|
\node (x86-3) at (6,-2) {\large $\text{x86}^{*}$};
|
|
@@ -3357,8 +3356,7 @@ shown in Figure~\ref{fig:reg-alloc-passes}.
|
|
|
\path[->,bend left=15] (R1) edge [above] node {\ttfamily\footnotesize uniquify} (R1-2);
|
|
|
\path[->,bend left=15] (R1-2) edge [above] node {\ttfamily\footnotesize remove-complex.} (R1-3);
|
|
|
\path[->,bend left=15] (R1-3) edge [right] node {\ttfamily\footnotesize explicate-control} (C0-1);
|
|
|
-\path[->,bend right=15] (C0-1) edge [above] node {\ttfamily\footnotesize uncover-locals} (C0-2);
|
|
|
-\path[->,bend right=15] (C0-2) edge [left] node {\ttfamily\footnotesize select-instr.} (x86-2);
|
|
|
+\path[->,bend right=15] (C0-1) edge [left] node {\ttfamily\footnotesize select-instr.} (x86-2);
|
|
|
\path[->,bend left=15] (x86-2) edge [right] node {\ttfamily\footnotesize\color{red} uncover-live} (x86-2-1);
|
|
|
\path[->,bend right=15] (x86-2-1) edge [below] node {\ttfamily\footnotesize\color{red} build-inter.} (x86-2-2);
|
|
|
\path[->,bend right=15] (x86-2-2) edge [right] node {\ttfamily\footnotesize\color{red} allocate-reg.} (x86-3);
|
|
@@ -3738,7 +3736,6 @@ conclusion:
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
\chapter{Booleans and Control Flow}
|
|
|
\label{ch:bool-types}
|
|
@@ -4816,7 +4813,6 @@ conclusion:
|
|
|
\path[->,bend left=15] (R2-3) edge [above] node {\ttfamily\footnotesize uniquify} (R2-4);
|
|
|
\path[->,bend left=15] (R2-4) edge [above] node {\ttfamily\footnotesize remove-complex.} (R2-5);
|
|
|
\path[->,bend left=15] (R2-5) edge [right] node {\ttfamily\footnotesize\color{red} explicate-control} (C1-1);
|
|
|
-%\path[->,bend right=15] (C1-1) edge [above] node {\ttfamily\footnotesize uncover-locals} (C1-2);
|
|
|
\path[->,bend right=15] (C1-1) edge [left] node {\ttfamily\footnotesize\color{red} select-instructions} (x86-2);
|
|
|
\path[->,bend left=15] (x86-2) edge [right] node {\ttfamily\footnotesize\color{red} uncover-live} (x86-2-1);
|
|
|
\path[->,bend right=15] (x86-2-1) edge [below] node {\ttfamily\footnotesize build-inter.} (x86-2-2);
|
|
@@ -5048,6 +5044,7 @@ copying live objects back and forth between two halves of the
|
|
|
heap. The garbage collector requires coordination with the compiler so
|
|
|
that it can see all of the \emph{root} pointers, that is, pointers in
|
|
|
registers or on the procedure call stack.
|
|
|
+
|
|
|
Sections~\ref{sec:expose-allocation} through \ref{sec:print-x86-gc}
|
|
|
discuss all the necessary changes and additions to the compiler
|
|
|
passes, including a new compiler pass named \code{expose-allocation}.
|
|
@@ -5057,28 +5054,16 @@ passes, including a new compiler pass named \code{expose-allocation}.
|
|
|
|
|
|
Figure~\ref{fig:r3-concrete-syntax} defines the concrete syntax for
|
|
|
$R_3$ and Figure~\ref{fig:r3-syntax} defines the abstract syntax. The
|
|
|
-$R_3$ language includes three new forms for creating a tuple, reading
|
|
|
-an element of a tuple, and writing to an element of a tuple. The
|
|
|
-program in Figure~\ref{fig:vector-eg} shows the usage of tuples in
|
|
|
-Racket. We create a 3-tuple \code{t} and a 1-tuple. The 1-tuple is
|
|
|
-stored at index $2$ of the 3-tuple, demonstrating that tuples are
|
|
|
-first-class values. The element at index $1$ of \code{t} is
|
|
|
-\code{\#t}, so the ``then'' branch of the \key{if} is taken. The
|
|
|
-element at index $0$ of \code{t} is $40$, to which we add $2$, the
|
|
|
-element at index $0$ of the 1-tuple. So the result of the program is
|
|
|
-$42$.
|
|
|
-
|
|
|
-\begin{figure}[tbp]
|
|
|
-\begin{lstlisting}
|
|
|
- (let ([t (vector 40 #t (vector 2))])
|
|
|
- (if (vector-ref t 1)
|
|
|
- (+ (vector-ref t 0)
|
|
|
- (vector-ref (vector-ref t 2) 0))
|
|
|
- 44))
|
|
|
-\end{lstlisting}
|
|
|
-\caption{Example program that creates tuples and reads from them.}
|
|
|
-\label{fig:vector-eg}
|
|
|
-\end{figure}
|
|
|
+$R_3$ language includes three new forms: \code{vector} for creating a
|
|
|
+tuple, \code{vector-ref} for reading an element of a tuple, and
|
|
|
+\code{vector-set!} for writing to an element of a tuple. The program
|
|
|
+in Figure~\ref{fig:vector-eg} shows the usage of tuples in Racket. We
|
|
|
+create a 3-tuple \code{t} and a 1-tuple that is stored at index $2$ of
|
|
|
+the 3-tuple, demonstrating that tuples are first-class values. The
|
|
|
+element at index $1$ of \code{t} is \code{\#t}, so the ``then'' branch
|
|
|
+of the \key{if} is taken. The element at index $0$ of \code{t} is
|
|
|
+\code{40}, to which we add \code{2}, the element at index $0$ of the
|
|
|
+1-tuple. So the result of the program is \code{42}.
|
|
|
|
|
|
\begin{figure}[tbp]
|
|
|
\centering
|
|
@@ -5111,6 +5096,18 @@ $42$.
|
|
|
\label{fig:r3-concrete-syntax}
|
|
|
\end{figure}
|
|
|
|
|
|
+\begin{figure}[tbp]
|
|
|
+\begin{lstlisting}
|
|
|
+ (let ([t (vector 40 #t (vector 2))])
|
|
|
+ (if (vector-ref t 1)
|
|
|
+ (+ (vector-ref t 0)
|
|
|
+ (vector-ref (vector-ref t 2) 0))
|
|
|
+ 44))
|
|
|
+\end{lstlisting}
|
|
|
+\caption{Example program that creates tuples and reads from them.}
|
|
|
+\label{fig:vector-eg}
|
|
|
+\end{figure}
|
|
|
+
|
|
|
\begin{figure}[tp]
|
|
|
\centering
|
|
|
\fbox{
|
|
@@ -5146,11 +5143,11 @@ $42$.
|
|
|
Tuples are our first encounter with heap-allocated data, which raises
|
|
|
several interesting issues. First, variable binding performs a
|
|
|
shallow-copy when dealing with tuples, which means that different
|
|
|
-variables can refer to the same tuple, i.e., different variables can
|
|
|
-be \emph{aliases} for the same thing. Consider the following example
|
|
|
-in which both \code{t1} and \code{t2} refer to the same tuple. Thus,
|
|
|
-the mutation through \code{t2} is visible when referencing the tuple
|
|
|
-from \code{t1}, so the result of this program is \code{42}.
|
|
|
+variables can refer to the same tuple, that is, different variables
|
|
|
+can be \emph{aliases} for the same entity. Consider the following
|
|
|
+example in which both \code{t1} and \code{t2} refer to the same tuple.
|
|
|
+Thus, the mutation through \code{t2} is visible when referencing the
|
|
|
+tuple from \code{t1}, so the result of this program is \code{42}.
|
|
|
\begin{center}
|
|
|
\begin{minipage}{0.96\textwidth}
|
|
|
\begin{lstlisting}
|
|
@@ -5164,11 +5161,11 @@ from \code{t1}, so the result of this program is \code{42}.
|
|
|
|
|
|
The next issue concerns the lifetime of tuples. Of course, they are
|
|
|
created by the \code{vector} form, but when does their lifetime end?
|
|
|
-Notice that the grammar in Figure~\ref{fig:r3-syntax} does not include
|
|
|
-an operation for deleting tuples. Furthermore, the lifetime of a tuple
|
|
|
-is not tied to any notion of static scoping. For example, the
|
|
|
-following program returns \code{3} even though the variable \code{t}
|
|
|
-goes out of scope prior to accessing the vector.
|
|
|
+Notice that $R_3$ does not include an operation for deleting
|
|
|
+tuples. Furthermore, the lifetime of a tuple is not tied to any notion
|
|
|
+of static scoping. For example, the following program returns \code{3}
|
|
|
+even though the variable \code{t} goes out of scope prior to accessing
|
|
|
+the vector.
|
|
|
\begin{center}
|
|
|
\begin{minipage}{0.96\textwidth}
|
|
|
\begin{lstlisting}
|
|
@@ -5183,7 +5180,7 @@ goes out of scope prior to accessing the vector.
|
|
|
From the perspective of programmer-observable behavior, tuples live
|
|
|
forever. Of course, if they really lived forever, then many programs
|
|
|
would run out of memory.\footnote{The $R_3$ language does not have
|
|
|
- looping or recursive function, so it is nigh impossible to write a
|
|
|
+ looping or recursive functions, so it is nigh impossible to write a
|
|
|
program in $R_3$ that will run out of memory. However, we add
|
|
|
recursive functions in the next Chapter!} A Racket implementation
|
|
|
must therefore perform automatic garbage collection.
|
|
@@ -5193,30 +5190,37 @@ $R_3$ language. We define the \code{vector}, \code{vector-ref}, and
|
|
|
\code{vector-set!} operations for $R_3$ in terms of the corresponding
|
|
|
operations in Racket. One subtle point is that the \code{vector-set!}
|
|
|
operation returns the \code{\#<void>} value. The \code{\#<void>} value
|
|
|
-can be passed around just like other values inside an $R_3$ program,
|
|
|
-but there are no operations specific to the the \code{\#<void>} value
|
|
|
-in $R_3$. In contrast, Racket defines the \code{void?} predicate that
|
|
|
-returns \code{\#t} when applied to \code{\#<void>} and \code{\#f}
|
|
|
-otherwise.
|
|
|
+can be passed around just like other values inside an $R_3$ program
|
|
|
+and a \code{\#<void>} value can be compared for equality with another
|
|
|
+\code{\#<void>} value. However, there are no other operations specific
|
|
|
+to the the \code{\#<void>} value in $R_3$. In contrast, Racket defines
|
|
|
+the \code{void?} predicate that returns \code{\#t} when applied to
|
|
|
+\code{\#<void>} and \code{\#f} otherwise.
|
|
|
|
|
|
\begin{figure}[tbp]
|
|
|
\begin{lstlisting}
|
|
|
- (define primitives (set ... 'vector 'vector-ref 'vector-set!))
|
|
|
+(define primitives (set ... 'vector 'vector-ref 'vector-set!))
|
|
|
+
|
|
|
+(define (interp-op op)
|
|
|
+ (match op
|
|
|
+ ...
|
|
|
+ ['vector vector]
|
|
|
+ ['vector-ref vector-ref]
|
|
|
+ ['vector-set! vector-set!]
|
|
|
+ [else (error 'interp-op "unknown operator")]))
|
|
|
|
|
|
- (define (interp-op op)
|
|
|
- (match op
|
|
|
- ...
|
|
|
- ['vector vector]
|
|
|
- ['vector-ref vector-ref]
|
|
|
- ['vector-set! vector-set!]
|
|
|
- [else (error 'interp-op "unknown operator")]))
|
|
|
+(define (interp-exp env)
|
|
|
+ (lambda (e)
|
|
|
+ (define recur (interp-exp env))
|
|
|
+ (match e
|
|
|
+ ...
|
|
|
+ )))
|
|
|
|
|
|
- (define (interp-R3 env)
|
|
|
- (lambda (e)
|
|
|
- (match e
|
|
|
- ...
|
|
|
- [else (error 'interp-R3 "unrecognized expression")]
|
|
|
- )))
|
|
|
+(define (interp-R3 p)
|
|
|
+ (match p
|
|
|
+ [(Program '() e)
|
|
|
+ ((interp-exp '()) e)]
|
|
|
+ ))
|
|
|
\end{lstlisting}
|
|
|
\caption{Interpreter for the $R_3$ language.}
|
|
|
\label{fig:interp-R3}
|
|
@@ -5224,10 +5228,10 @@ otherwise.
|
|
|
|
|
|
Figure~\ref{fig:typecheck-R3} shows the type checker for $R_3$, which
|
|
|
deserves some explanation. As we shall see in Section~\ref{sec:GC}, we
|
|
|
-need to know which variables are pointers into the heap, that is,
|
|
|
-which variables are vectors. Also, when allocating a vector, we need
|
|
|
-to know which elements of the vector are pointers. We can obtain this
|
|
|
-information during type checking. The type checker in
|
|
|
+need to know which variables contain pointers into the heap, that is,
|
|
|
+which variables contain vectors. Also, when allocating a vector, we
|
|
|
+need to know which elements of the vector are pointers. We can obtain
|
|
|
+this information during type checking. The type checker in
|
|
|
Figure~\ref{fig:typecheck-R3} not only computes the type of an
|
|
|
expression, it also wraps every sub-expression $e$ with the form
|
|
|
$(\key{HasType}~e~T)$, where $T$ is $e$'s type.
|
|
@@ -6148,6 +6152,7 @@ _conclusion:
|
|
|
\node (R3-3) at (6,2) {\large $R_3$};
|
|
|
\node (R3-4) at (9,2) {\large $R_3$};
|
|
|
\node (R3-5) at (12,2) {\large $R_3$};
|
|
|
+\node (R3-6) at (12,0) {\large $R_3$};
|
|
|
\node (C2-4) at (3,0) {\large $C_2$};
|
|
|
\node (C2-3) at (6,0) {\large $C_2$};
|
|
|
|
|
@@ -6160,10 +6165,11 @@ _conclusion:
|
|
|
\node (x86-2-2) at (6,-4) {\large $\text{x86}^{*}_2$};
|
|
|
|
|
|
\path[->,bend left=15] (R3) edge [above] node {\ttfamily\footnotesize\color{red} typecheck} (R3-2);
|
|
|
-\path[->,bend left=15] (R3-2) edge [above] node {\ttfamily\footnotesize uniquify} (R3-3);
|
|
|
-\path[->,bend left=15] (R3-3) edge [above] node {\ttfamily\footnotesize\color{red} expose-alloc.} (R3-4);
|
|
|
-\path[->,bend left=15] (R3-4) edge [above] node {\ttfamily\footnotesize remove-complex.} (R3-5);
|
|
|
-\path[->,bend left=20] (R3-5) edge [right] node {\ttfamily\footnotesize explicate-control} (C2-3);
|
|
|
+\path[->,bend left=15] (R3-2) edge [above] node {\ttfamily\footnotesize shrink} (R3-3);
|
|
|
+\path[->,bend left=15] (R3-3) edge [above] node {\ttfamily\footnotesize uniquify} (R3-4);
|
|
|
+\path[->,bend left=15] (R3-4) edge [above] node {\ttfamily\footnotesize\color{red} expose-alloc.} (R3-5);
|
|
|
+\path[->,bend left=15] (R3-5) edge [right] node {\ttfamily\footnotesize remove-complex.} (R3-6);
|
|
|
+\path[->,bend right=20] (R3-6) edge [above] node {\ttfamily\footnotesize explicate-control} (C2-3);
|
|
|
\path[->,bend right=15] (C2-3) edge [above] node {\ttfamily\footnotesize\color{red} uncover-locals} (C2-4);
|
|
|
\path[->,bend right=15] (C2-4) edge [left] node {\ttfamily\footnotesize\color{red} select-instr.} (x86-2);
|
|
|
\path[->,bend left=15] (x86-2) edge [right] node {\ttfamily\footnotesize uncover-live} (x86-2-1);
|