Forráskód Böngészése

more progress on closure conversion

Jeremy Siek 9 éve
szülő
commit
6d638a89a1
3 módosított fájl, 124 hozzáadás és 7 törlés
  1. BIN
      Closures.graffle/data.plist
  2. 124 7
      book.tex
  3. BIN
      closures.pdf

BIN
Closures.graffle/data.plist


+ 124 - 7
book.tex

@@ -4385,6 +4385,7 @@ representation.)
 (assign |$\itm{lhs}$| (vector-set! |$\itm{vec}$| |$n$| |$\itm{arg}$|))
 |$\Longrightarrow$|
 (movq |$\itm{arg}'$| (offset |$\itm{vec}'$| |$8(n+1)$|))
+(mvoq (int 0) |$\itm{lhs}$|)
 \end{lstlisting}
 The $\itm{vec}'$ and $\itm{arg}'$ are obtained by recursively
 processing $\itm{vec}$ and $\itm{arg}$.
@@ -4964,7 +4965,7 @@ defined using the \key{lambda} form.
 \begin{lstlisting}
    (define (f [x : Integer]) : (Integer -> Integer)
       (let ([y 4])
-         (lambda ([z : Integer]) : Integer
+         (lambda: ([z : Integer]) : Integer
             (+ x (+ y z)))))
 
    (let ([g (f 5)])
@@ -4988,6 +4989,8 @@ they use different values for \code{x}. Finally, we apply \code{g} to
 \code{11} (producing \code{20}) and apply \code{h} to \code{15}
 (producing \code{22}) so the result of this program is \code{42}.
 
+\section{The $R_5$ Language}
+
 The syntax for this language with lexical scoping, $R_5$, is defined
 in Figure~\ref{fig:r5-syntax}. It adds the \key{lambda} form to the
 grammar for $R_4$, which already has syntax for function application.
@@ -5029,11 +5032,11 @@ free with respect to the expression \code{(+ x (+ y z))}.  On the
 other hand, only \code{x} and \code{y} are free with respect to the
 following expression becuase \code{z} is bound by the \code{lambda}.
 \begin{lstlisting}
-   (lambda ([z : Integer]) : Integer
+   (lambda: ([z : Integer]) : Integer
       (+ x (+ y z)))
 \end{lstlisting}
 
-Once we have identified the free variables of a \code{lamda}, we need
+Once we have identified the free variables of a \code{lambda}, we need
 to arrange for some way to transport, at runtime, the values of those
 variables from the point where the \code{lambda} was created to the
 point where the \code{lambda} is applied. Referring again to
@@ -5047,17 +5050,131 @@ already have the appropriate ingredients to make closures,
 Chapter~\ref{ch:tuples} gave us tuples and Chapter~\ref{ch:functions}
 gave us function pointers. The function pointer shall reside at index
 $0$ and the values for free variables will fill in the rest of the
-tuple.
-
+tuple. Figure~\ref{fig:closures} depicts the two closures created by
+the two calls to \code{f} in Figure~\ref{fig:lexical-scoping}.
+Because the two closures came from the same \key{lambda}, they share
+the same code but differ in the values for free variable \code{x}.
 
 \begin{figure}[tbp]
-\centering \includegraphics[width=0.8\textwidth]{closures}
+\centering \includegraphics[width=0.7\textwidth]{closures}
 \caption{Example closure representation for the \key{lambda}'s
   in Figure~\ref{fig:lexical-scoping}.}
-\label{fig:tuple-rep}
+\label{fig:closures}
+\end{figure}
+
+
+\section{Interpreting $R_5$}
+
+Figure~\ref{fig:interp-R5} shows the definitional interpreter for
+$R_5$. There are several things to worth noting. First, and most
+importantly, the match clause for \key{lambda} saves the current
+environment inside the returned \key{lambda}. Then the clause for
+\key{app} uses the environment from the \key{lambda}, the
+\code{lam-env}, when interpreting the body of the \key{lambda}.  Of
+course, the \code{lam-env} environment is extending with the mapping
+parameters to argument values. To enable mutual recursion and allow a
+unified handling of functions created with \key{lambda} and with
+\key{define}, the match clause for \key{program} includes a second
+pass over the top-level functions to set their environments to be the
+top-level environment.
+
+\begin{figure}[tbp]
+\begin{lstlisting}
+(define (interp-R5 env)
+  (lambda (ast)
+    (match ast
+       [`(lambda: ([,xs : ,Ts] ...) : ,rT ,body)
+        `(lambda ,xs ,body ,env)]
+       [`(app ,fun ,args ...)
+        (define arg-vals (map (interp-R5 env) args))
+        (define fun-val ((interp-R5 env) fun))
+        (match fun-val
+           [`(lambda (,xs ...) ,body ,lam-env)
+            (define new-env (append (map cons xs arg-vals) lam-env))
+            ((interp-R5 new-env) body)]
+           [else (error "interp-R5, expected function, not" fun-val)])]
+       [`(define (,f [,xs : ,ps] ...) : ,rt ,body)
+        (mcons f `(lambda ,xs ,body))]
+       [`(program ,defs ... ,body)
+        (let ([top-level (map (interp-R5 '()) defs)])
+          (for/list ([b top-level])
+                    (set-mcdr! b (match (mcdr b)
+                                   [`(lambda ,xs ,body)
+                                    `(lambda ,xs ,body ,top-level)])))
+          ((interp-R5 top-level) body))]
+       ...)))
+\end{lstlisting}
+\caption{Definitional interpreter for $R_5$.}
+\label{fig:interp-R5}
 \end{figure}
 
+\section{Type Checking $R_5$}
+
+Figure~\ref{fig:typecheck-R5} shows how to type check the new
+\key{lambda} form. The body of the \key{lambda} is checked in an
+environment that includes the current environment (because it is
+lexically scoped) and also includes the \key{lambda}'s parameters.  We
+require the body's type to match the declared return type.
+
+\begin{figure}[tbp]
+\begin{lstlisting}
+(define (typecheck-R5 env)
+  (lambda (e)
+    (match e
+      [`(lambda: ([,xs : ,Ts] ...) : ,rT ,body)
+       (define new-env (append (map cons xs Ts) env))
+       (define bodyT ((typecheck-R5 new-env) body))
+       (cond [(equal? rT bodyT)
+              `(,@Ts -> ,rT)]
+             [else
+              (error "mismatch in return type" bodyT rT)])]
+      ...
+      )))
+\end{lstlisting}
+\caption{Type checking the \key{lambda}'s in $R_5$.}
+\label{fig:typecheck-R5}
+\end{figure}
+
+
+\section{Closure Conversion}
+
+The compiling of lexically-scoped functions into $R_4$-style functions
+is accomplished in the pass \code{convert-to-closures} that comes
+after \code{reveal-functions} and before flatten. This pass needs to
+treat regular function calls differently from applying primitive
+operators, and \code{reveal-functions} differentiates those two cases
+for us.
 
+As usual, we shall implement the pass as a recursive function over the
+AST. All of the action is in the clauses for \key{lambda} and
+\key{app} (function application). We transform a \key{lambda}
+expression into an expression that creates a closure, that is, creates
+a vector whose first element is the function pointer and the rest of
+the elements are the values of the free variables.
+
+\begin{tabular}{lll}
+\begin{minipage}{0.4\textwidth}
+\begin{lstlisting}
+(lambda: (|\itm{ps}| ...) : |\itm{rt}| |\itm{body}|)
+\end{lstlisting}
+\end{minipage}
+&
+$\Rightarrow$
+&
+\begin{minipage}{0.4\textwidth}
+\begin{lstlisting}
+(vector |\itm{name}| |\itm{fvs}| ...)
+\end{lstlisting}
+\end{minipage}
+\end{tabular}  \\
+
+\begin{lstlisting}
+(define (|\itm{name}| [clos : _] |\itm{ps}| ...)
+   (let ([|$\itm{fvs}_1$| (vector-ref clos 1)])
+     ...
+     (let ([|$\itm{fvs}_n$| (vector-ref clos $n$)])
+       |\itm{body'}|)...))
+\end{lstlisting}
 
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

BIN
closures.pdf