Jeremy Siek 4 年之前
父节点
当前提交
d724e8adef
共有 1 个文件被更改,包括 140 次插入138 次删除
  1. 140 138
      book.tex

+ 140 - 138
book.tex

@@ -1508,11 +1508,9 @@ writes the result back to the destination $d$.
 The move instruction $\key{movq}\,s\key{,}\,d$ reads from $s$ and
 stores the result in $d$.
 %
-The $\key{callq}\,\itm{label}$ instruction executes the procedure
+The $\key{callq}\,\itm{label}$ instruction jumps to the procedure
 specified by the label and $\key{retq}$ returns from a procedure to
-its caller. The abstract syntax for \code{callq} includes an extra
-integer field that represents the arity (number of parameters) of the
-function being called.
+its caller. 
 %
 We discuss procedure calls in more detail later in this
 chapter and in Chapter~\ref{ch:functions}. The
@@ -1572,13 +1570,14 @@ increase the size of the stack by subtracting from the stack pointer.
 In the context of a procedure call, the \emph{return
   address}\index{return address} is the instruction after the call
 instruction on the caller side. The function call instruction,
-\code{callq}, pushes the return address onto the stack.  The register
-\key{rbp} is the \emph{base pointer}\index{base pointer} and is used
-to access variables associated with the current procedure call.  The
-base pointer of the caller is pushed onto the stack after the return
-address. We number the variables from $1$ to $n$. Variable $1$ is
-stored at address $-8\key{(\%rbp)}$, variable $2$ at
-$-16\key{(\%rbp)}$, etc.
+\code{callq}, pushes the return address onto the stack prior to
+jumping to the procedure.  The register \key{rbp} is the \emph{base
+  pointer}\index{base pointer} and is used to access variables that
+are stored in the frame of the current procedure call.  The base
+pointer of the caller is pushed onto the stack after the return
+address. In Figure~\ref{fig:frame} we number the variables from $1$ to
+$n$. Variable $1$ is stored at address $-8\key{(\%rbp)}$, variable $2$
+at $-16\key{(\%rbp)}$, etc.
 
 \begin{figure}[tbp]
 \begin{lstlisting}
@@ -1678,16 +1677,17 @@ labeled instructions to appear anywhere, but instead organizes
 instructions into a group called a
 \emph{block}\index{block}\index{basic block} and associates a label
 with every block, which is why the \key{CFG} struct (for control-flow
-graph) includes an alist mapping labels to blocks. The reason for this
-organization becomes apparent in Chapter~\ref{ch:bool-types} when we
-introduce conditional branching. The \code{Block} structure includes
-an $\itm{info}$ field that is not needed for this chapter, but will
-become useful in Chapter~\ref{ch:register-allocation-r1}.  For now,
-the $\itm{info}$ field should just contain an empty list. Also,
-regarding the abstract syntax for \code{callq}, the \code{Callq}
-struct includes an integer for representing the arity of the function,
-i.e., the number of arguments, which is helpful to know during
-register allocation (Chapter~\ref{ch:register-allocation-r1}).
+graph) includes an alist mapping labels to blocks. The reason for
+using blocks and a control-flow graph becomes apparent in
+Chapter~\ref{ch:bool-types} when we introduce conditional
+branching. The \code{Block} structure includes an $\itm{info}$ field
+that is not needed for this chapter, but will become useful in
+Chapter~\ref{ch:register-allocation-r1}.  For now, the $\itm{info}$
+field should just contain an empty list. Also, regarding the abstract
+syntax for \code{callq}, the \code{Callq} struct includes an integer
+for representing the arity of the function, i.e., the number of
+arguments, which is helpful to know during register allocation
+(Chapter~\ref{ch:register-allocation-r1}).
 
 \begin{figure}[tp]
 \fbox{
@@ -1730,9 +1730,9 @@ and x86 assembly? Here are some of the most important ones:
   Furthermore, some instructions place special restrictions on their
   arguments.
 
-\item[(b)] An argument of an $R_1$ operator can be any expression,
-  whereas x86 instructions restrict their arguments to be integers
-  constants, registers, and memory locations.
+\item[(b)] An argument of an $R_1$ operator can be a deeply-nested
+  expression, whereas x86 instructions restrict their arguments to be
+  integers constants, registers, and memory locations.
 
 \item[(c)] The order of execution in x86 is explicit in the syntax: a
   sequence of instructions and jumps to labeled positions, whereas in
@@ -1743,8 +1743,8 @@ and x86 assembly? Here are some of the most important ones:
   x86 has 16 registers and the procedure calls stack.
 
 \item[(e)] Variables in $R_1$ can overshadow other variables with the
-  same name. The registers and memory locations of x86 all have unique
-  names or addresses.
+  same name. In x86, registers have unique names and memory locations
+  have unique addresses.
 \end{enumerate}
 
 We ease the challenge of compiling from $R_1$ to x86 by breaking down
@@ -1752,8 +1752,8 @@ the problem into several steps, dealing with the above differences one
 at a time.  Each of these steps is called a \emph{pass} of the
 compiler.\index{pass}\index{compiler pass}
 %
-This terminology comes from each step traverses (i.e. passes over) the
-AST of the program.
+This terminology comes from each step passing over the AST of the
+program.
 %
 We begin by sketching how we might implement each pass, and give them
 names.  We then figure out an ordering of the passes and the
@@ -1767,30 +1767,28 @@ non-terminal in the grammar of the input language of the pass.
 \index{intermediate language}
 
 \begin{description}
-\item[Pass \key{select-instructions}] To handle the difference between
+\item[Pass \key{select-instructions}] handles the difference between
   $R_1$ operations and x86 instructions we convert each $R_1$
   operation to a short sequence of instructions that accomplishes the
   same task.
 
-\item[Pass \key{remove-complex-opera*}] To ensure that each
+\item[Pass \key{remove-complex-opera*}] ensures that each
   subexpression (i.e. operator and operand, and hence the name
   \key{opera*}) is an \emph{atomic} expression (a variable or
   integer), we introduce temporary variables to hold the results
   of subexpressions.\index{atomic expression}
   
-\item[Pass \key{explicate-control}] To make the execution order of the
+\item[Pass \key{explicate-control}] makes the execution order of the
   program explicit, we convert from the abstract syntax tree
-  representation into a control-flow graph in which each node
-  contains a sequence of statements and the edges between nodes say
-  where to go at the end of the sequence.
+  representation into a control-flow graph in which each node contains
+  a sequence of statements and the edges between nodes say which nodes
+  contain jumps to other nodes.
 
-\item[Pass \key{assign-homes}] To handle the difference between the
-  variables in $R_1$ versus the registers and stack locations in x86,
-  we map each variable to a register or stack location.
+\item[Pass \key{assign-homes}] assigns the variables in $R_1$ to
+  registers or stack locations in x86.
 
-\item[Pass \key{uniquify}] This pass deals with the shadowing of variables
-  by renaming every variable to a unique name, so that shadowing no
-  longer occurs.
+\item[Pass \key{uniquify}] deals with the shadowing of variables by
+  renaming every variable to a unique name.
 \end{description}
 
 The next question is: in what order should we apply these passes? This
@@ -1800,14 +1798,14 @@ efficient code, etc.) so oftentimes trial-and-error is
 involved. Nevertheless, we can try to plan ahead and make educated
 choices regarding the ordering.
 
-Let us consider the ordering of \key{uniquify} and
-\key{remove-complex-opera*}. The assignment of subexpressions to
-temporary variables involves introducing new variables and moving
-subexpressions, which might change the shadowing of variables and
-inadvertently change the behavior of the program.  But if we apply
-\key{uniquify} first, this will not be an issue. Of course, this means
-that in \key{remove-complex-opera*}, we need to ensure that the
-temporary variables that it creates are unique.
+%% Let us consider the ordering of \key{uniquify} and
+%% \key{remove-complex-opera*}. The assignment of subexpressions to
+%% temporary variables involves introducing new variables and moving
+%% subexpressions, which might change the shadowing of variables and
+%% inadvertently change the behavior of the program.  But if we apply
+%% \key{uniquify} first, this will not be an issue. Of course, this means
+%% that in \key{remove-complex-opera*}, we need to ensure that the
+%% temporary variables that it creates are unique.
 
 What should be the ordering of \key{explicate-control} with respect to
 \key{uniquify}? The \key{uniquify} pass should come first because
@@ -1815,36 +1813,41 @@ What should be the ordering of \key{explicate-control} with respect to
 become local variables whose scope is the entire program, which would
 confuse variables with the same name.
 %
-Likewise, we place \key{explicate-control} after
-\key{remove-complex-opera*} because \key{explicate-control} removes
-the \key{let} form, but it is convenient to use \key{let} in the
-output of \key{remove-complex-opera*}.
+Likewise, we place \key{remove-complex-opera*} before
+\key{explicate-control} because \key{explicate-control} removes the
+\key{let} form, but it is convenient to use \key{let} in the output of
+\key{remove-complex-opera*}.
 %
-Regarding \key{assign-homes}, it is helpful to place
-\key{explicate-control} first because \key{explicate-control} changes
-\key{let}-bound variables into program-scope variables.  This means
-that the \key{assign-homes} pass can read off the variables from the
-$\itm{info}$ of the \key{Program} AST node instead of traversing the
-entire program in search of \key{let}-bound variables.
-
-Last, we need to decide on the ordering of \key{select-instructions}
-and \key{assign-homes}.  These two passes are intertwined, creating a
-Gordian Knot. To do a good job of assigning homes, it is helpful to
-have already determined which instructions will be used, because x86
-instructions have restrictions about which of their arguments can be
-registers versus stack locations. One might want to give preferential
-treatment to variables that occur in register-argument positions. On
-the other hand, it may turn out to be impossible to make sure that all
-such variables are assigned to registers, and then one must redo the
-selection of instructions. Some compilers handle this problem by
-iteratively repeating these two passes until a good solution is found.
-We use a simpler approach in which \key{select-instructions}
-comes first, followed by the \key{assign-homes}, then a third
-pass named \key{patch-instructions} that uses a reserved register to
-patch-up outstanding problems regarding instructions with too many
-memory accesses. The disadvantage of this approach is some programs
-may not execute as efficiently as they would if we used the iterative
-approach and used all of the registers for variables.
+The ordering of \key{uniquify} and \key{remove-complex-opera*} does
+not matter, so we arbitrarily choose \key{uniquify} to come first.
+
+%% Regarding \key{assign-homes}, it is helpful to place
+%% \key{explicate-control} first because \key{explicate-control} changes
+%% \key{let}-bound variables into program-scope variables.  This means
+%% that the \key{assign-homes} pass can read off the variables from the
+%% $\itm{info}$ of the \key{Program} AST node instead of traversing the
+%% entire program in search of \key{let}-bound variables.
+
+Last, we consider \key{select-instructions} and \key{assign-homes}.
+These two passes are intertwined, creating a Gordian Knot. To do a
+good job of assigning homes, it is helpful to have already determined
+which instructions will be used, because x86 instructions have
+restrictions about which of their arguments can be registers versus
+stack locations. One might want to give preferential treatment to
+variables that occur in register-argument positions. On the other
+hand, it may turn out to be impossible to make sure that all such
+variables are assigned to registers, and then one must redo the
+selection of instructions. A sophisticated solution to this problem is
+to iteratively repeat the two passes until a good solution is found.
+To reduce implementation complexity, we recommend a simpler approach
+in which \key{select-instructions} comes first, followed by the
+\key{assign-homes}, then a third pass named \key{patch-instructions}
+that uses a reserved register to patch-up outstanding problems
+regarding instructions with too many memory accesses.
+
+%% The disadvantage of this approach is some programs may not execute
+%% as efficiently as they would if we used the iterative approach and
+%% used all of the registers for variables.
 
 
 \begin{figure}[tbp]
@@ -1877,12 +1880,15 @@ Figure~\ref{fig:R1-passes} presents the ordering of the compiler
 passes in the form of a graph. Each pass is an edge and the
 input/output language of each pass is a node in the graph.  The output
 of \key{uniquify} and \key{remove-complex-opera*} are programs that
-are still in the $R_1$ language, but the output of the pass
-\key{explicate-control} is in a different language $C_0$ that is
-designed to make the order of evaluation explicit in its syntax, which
-we introduce in the next section. The \key{select-instruction} pass
-translates from $C_0$ to a variant of x86. The \key{assign-homes} and
-\key{patch-instructions} passes input and output variants of x86
+are still in the $R_1$ language, through the output of the later is a
+subset of $R_1$, a language we name $R^\dagger_1$ and describe in
+Section~\ref{sec:remove-complex-opera-R1}.
+%
+The output of \key{explicate-control} is in an intermediate language
+$C_0$ designed to make the order of evaluation explicit in its syntax,
+which we introduce in the next section. The \key{select-instruction}
+pass translates from $C_0$ to a variant of x86. The \key{assign-homes}
+and \key{patch-instructions} passes input and output variants of x86
 assembly. The last pass in Figure~\ref{fig:R1-passes} is
 \key{print-x86}, which converts from the abstract syntax of
 $\text{x86}_0$ to the concrete syntax of x86.
@@ -1954,18 +1960,19 @@ C_0 & ::= & \PROGRAM{\itm{info}}{\CFG{\key{(}\itm{label}\,\key{.}\,\Tail\key{)}\
 
 The x86$^{*}_0$ language, pronounced ``pseudo x86'', is the output of
 the pass \key{select-instructions}. It extends x86$_0$ with an
-unbounded number of program-scope variables and has looser rules
-regarding instruction arguments. The x86$^{\dagger}$ language, the
-output of \key{print-x86}, is the concrete syntax for x86.
+unbounded number of program-scope variables and it does not have
+special-case rules regarding instruction arguments. The
+x86$^{\dagger}$ language, the output of \key{print-x86}, is the
+concrete syntax for x86.
 
 
 \section{Uniquify Variables}
 \label{sec:uniquify-s0}
 
-The \code{uniquify} pass compiles arbitrary $R_1$ programs into $R_1$
-programs in which every \key{let} uses a unique variable name. For
-example, the \code{uniquify} pass should translate the program on the
-left into the program on the right. \\
+The \code{uniquify} pass compiles $R_1$ programs into $R_1$ programs
+in which every \key{let} uses a unique variable name. For example, the
+\code{uniquify} pass should translate the program on the left into the
+program on the right. \\
 \begin{tabular}{lll}
 \begin{minipage}{0.4\textwidth}
 \begin{lstlisting}
@@ -2007,25 +2014,23 @@ $\Rightarrow$
 \end{minipage}
 \end{tabular}
 
-We recommend implementing \code{uniquify} by creating a function named
-\code{uniquify-exp} that is structurally recursive function and mostly
-just copies the input program. However, when encountering a \key{let},
-it should generate a unique name for the variable (the Racket function
+We recommend implementing \code{uniquify} by creating a structurally
+recursive function function named \code{uniquify-exp} that mostly just
+copies the input program. However, when encountering a \key{let}, it
+should generate a unique name for the variable (the Racket function
 \code{gensym} is handy for this) and associate the old name with the
-new unique name in an alist. The \code{uniquify-exp}
-function will need to access this alist when it gets to a
-variable reference, so we add another parameter to \code{uniquify-exp}
-for the alist.
-
+new unique name in an alist. The \code{uniquify-exp} function will
+need to access this alist when it gets to a variable reference, so we
+add another parameter to \code{uniquify-exp} for the alist.
 
 The skeleton of the \code{uniquify-exp} function is shown in
 Figure~\ref{fig:uniquify-s0}.  The function is curried so that it is
-convenient to partially apply it to a symbol table and then apply it
-to different expressions, as in the last clause for primitive
-operations in Figure~\ref{fig:uniquify-s0}.  The \href{https://docs.racket-lang.org/reference/for.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._for%2Flist%29%29}{\key{for/list}}
-form is useful for applying a function to each element of a list to produce
-a new list.
-\index{for/list}
+convenient to partially apply it to an alist and then apply it to
+different expressions, as in the last clause for primitive operations
+in Figure~\ref{fig:uniquify-s0}.  The
+\href{https://docs.racket-lang.org/reference/for.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._for%2Flist%29%29}{\key{for/list}}
+  form is useful for applying a function to each element of a list to
+  produce a new list.  \index{for/list}
 
 \begin{exercise}
 \normalfont % I don't like the italics for exercises. -Jeremy
@@ -2036,21 +2041,19 @@ implement the clauses for variables and for the \key{let} form.
 
 \begin{figure}[tbp]
 \begin{lstlisting}
-   (define (uniquify-exp symtab)
+   (define (uniquify-exp env)
      (lambda (e)
        (match e
          [(Var x) ___]
          [(Int n) (Int n)]
          [(Let x e body) ___]
          [(Prim op es)
-          (Prim op (for/list ([e es]) ((uniquify-exp symtab) e)))]
+          (Prim op (for/list ([e es]) ((uniquify-exp env) e)))]
          )))
 
    (define (uniquify p)
      (match p
-       [(Program '() e)
-        (Program '() ((uniquify-exp '()) e))]
-       )))
+       [(Program '() e) (Program '() ((uniquify-exp '()) e))]))
 \end{lstlisting}
 \caption{Skeleton for the \key{uniquify} pass.}
 \label{fig:uniquify-s0}
@@ -2059,11 +2062,11 @@ implement the clauses for variables and for the \key{let} form.
 \begin{exercise}
 \normalfont % I don't like the italics for exercises. -Jeremy
 
-Test your \key{uniquify} pass by creating five example $R_1$ programs
-and checking whether the output programs produce the same result as
-the input programs. The $R_1$ programs should be designed to test the
-most interesting parts of the \key{uniquify} pass, that is, the
-programs should include \key{let} forms, variables, and variables that
+Test your \key{uniquify} pass by creating five example $R_1$ programs.
+Check whether the output programs produce the same result as the input
+programs. The $R_1$ programs should be designed to test the most
+interesting parts of the \key{uniquify} pass, that is, the programs
+should include \key{let} forms, variables, and variables that
 overshadow each other.  The five programs should be in a subdirectory
 named \key{tests} and they should have the same file name except for a
 different integer at the end of the name, followed by the ending
@@ -2129,12 +2132,11 @@ R^{\dagger}_1  &::=& \PROGRAM{\code{'()}}{\Exp}
 \end{figure}
 
 Figure~\ref{fig:r1-anf-syntax} presents the grammar for the output of
-this pass, language $R_1^{\dagger}$. The main difference is that
+this pass, the language $R_1^{\dagger}$. The main difference is that
 operator arguments are required to be atomic expressions.  In the
 literature, this is called \emph{administrative normal form}, or ANF
-for short~\citep{Danvy:1991fk,Flanagan:1993cg}.
-\index{administrative normal form}
-\index{ANF}
+for short~\citep{Danvy:1991fk,Flanagan:1993cg}.  \index{administrative
+  normal form} \index{ANF}
 
 We recommend implementing this pass with two mutually recursive
 functions, \code{rco-atom} and \code{rco-exp}. The idea is to apply
@@ -13154,22 +13156,22 @@ C_3 & ::= & \Def\ldots
 %%  LocalWords:  deallocating Ungar Detlefs Tene kx FromSpace ToSpace
 %%  LocalWords:  Appel Diwan Siebert ptr  fromspace rootstack typedef
 %%  LocalWords:  len prev rootlen heaplen setl lt Kohlbecker dk multi
-% LocalWords:  Bloomington Wollowski definitional whitespace deref JM
-% LocalWords:  subexpression subexpressions iteratively ANF Danvy rco
-% LocalWords:  goto stmt JS ly cmp ty le ge jle goto's EFLAG CFG pred
-% LocalWords:  acyclic worklist Aho qf tsort implementer's hj Shidal
-% LocalWords:  nonnegative Shahriyar endian salq sarq uint cheney ior
-% LocalWords:  tospace vecinit collectret alloc initret decrement jl
-% LocalWords:  dereferencing GC di vals ps mcons ds mcdr callee's th
-% LocalWords:  mainDef tailcall prepending mainstart num params rT qb
-% LocalWords:  mainconclusion Cardelli bodyT fvs clos fvts subtype uj
-% LocalWords:  polymorphism untyped elts tys tagof Vectorof tyeq orq
-% LocalWords:  andq untagged Shao inlining ebp jge setle setg setge
-% LocalWords:  struct symtab Friedman's MacOS Nystrom alist sam kate
-% LocalWords:  alists arity github unordered pqueue exprs ret param
-% LocalWords:  tyerr bytereg dh dl JmpIf HasType Osterlund Jacek TODO
-% LocalWords:  Gamari GlobalValue ProgramDefsExp prm ProgramDefs vn
-% LocalWords:  FunRef TailCall tailjmp IndirectCallq TailJmp Gilray
-% LocalWords:  dereference unbox Dataflow versa dataflow Kildall rhs
-% LocalWords:  Kleene enqueue dequeue AssignedFree FV cnvt SetBang tg
-% LocalWords:  ValueOf typechecker
+% LocalWords: Bloomington Wollowski definitional whitespace deref JM
+% LocalWords: subexpression subexpressions iteratively ANF Danvy rco
+% LocalWords: goto stmt JS ly cmp ty le ge jle goto's EFLAG CFG pred
+% LocalWords: acyclic worklist Aho qf tsort implementer's hj Shidal
+% LocalWords: nonnegative Shahriyar endian salq sarq uint cheney ior
+% LocalWords: tospace vecinit collectret alloc initret decrement jl
+% LocalWords: dereferencing GC di vals ps mcons ds mcdr callee's th
+% LocalWords: mainDef tailcall prepending mainstart num params rT qb
+% LocalWords: mainconclusion Cardelli bodyT fvs clos fvts subtype uj
+% LocalWords: polymorphism untyped elts tys tagof Vectorof tyeq orq
+% LocalWords: andq untagged Shao inlining ebp jge setle setg setge
+% LocalWords: struct Friedman's MacOS Nystrom alist sam kate
+% LocalWords: alists arity github unordered pqueue exprs ret param
+% LocalWords: tyerr bytereg dh dl JmpIf HasType Osterlund Jacek TODO
+% LocalWords: Gamari GlobalValue ProgramDefsExp prm ProgramDefs vn
+% LocalWords: FunRef TailCall tailjmp IndirectCallq TailJmp Gilray
+% LocalWords: dereference unbox Dataflow versa dataflow Kildall rhs
+% LocalWords: Kleene enqueue dequeue AssignedFree FV cnvt SetBang tg
+% LocalWords: ValueOf typechecker