diff --git a/docs/internals/back.tex b/docs/internals/back.tex index edeff4674c11d0942a52cb924adcbe972e3c5537..9d11f8dc3275fbdfa924276ce44c6f47a50c3ec4 100644 --- a/docs/internals/back.tex +++ b/docs/internals/back.tex @@ -655,7 +655,7 @@ leaf. Kind is a keyword indicating how the constant is computed, and What is some context. Some interesting load-time constants: - +\begin{verbatim} (:label . <label>) Is replaced with the byte offset of the label within the code-vector. @@ -667,7 +667,7 @@ Some interesting load-time constants: Is replaced by the function-entry structure for the specified function. :Entry is how the top-level component gets a handle on the function definitions so that it can set them up. - +\end{verbatim} We also need to remember the starting offset for each entry, although these don't in general appear as explicit constants. diff --git a/docs/internals/compiler-overview.tex b/docs/internals/compiler-overview.tex index 74182cd550d07d7bc187912d94d722f0792c5a7a..036f0802213b76359b2ebbebd3939b019dace225 100644 --- a/docs/internals/compiler-overview.tex +++ b/docs/internals/compiler-overview.tex @@ -228,13 +228,13 @@ by watching for a change in the cleanup between two continuations. We can't emit cleanup code during ICR conversion, since we don't know which exits will be local until after ICR optimizations are done. -Special binding is represented by a call to the funny function %Special-Bind. +Special binding is represented by a call to the funny function \%Special-Bind. The first argument is the Global-Var structure for the variable bound and the second argument is the value to bind it to. Some subprimitives are implemented using a macro-like mechanism for translating -%PRIMITIVE forms into arbitrary lisp code. Subprimitives special-cased by VMR -conversion are represented by a call to the funny function %%Primitive. The +\%PRIMITIVE forms into arbitrary lisp code. Subprimitives special-cased by VMR +conversion are represented by a call to the funny function \%\%Primitive. The corresponding Template structure is passed as the first argument. We check global function calls for syntactic legality with respect to any @@ -341,12 +341,12 @@ where the default is a constant, we don't have to special-bind at all, since the computation of the default is not affected by and cannot affect any special bindings. -Keyword and rest args are both implemented using a LEXPR-like "more args" -convention. The More-Entry takes two arguments in addition to the fixed and -optional arguments: the argument context and count. (ARG <context> <n>) -accesses the N'th additional argument. Keyword args are implemented directly -using this mechanism. Rest args are created by calling %Listify-Rest-Args with -the context and count. +Keyword and rest args are both implemented using a LEXPR-like "more +args" convention. The More-Entry takes two arguments in addition to +the fixed and optional arguments: the argument context and count. +\verb+(ARG <context> <n>)+ accesses the N'th additional argument. Keyword +args are implemented directly using this mechanism. Rest args are +created by calling \%Listify-Rest-Args with the context and count. The More-Entry parses the keyword arguments and passes the values to the main function as positional arguments. If a keyword default is not constant, then @@ -361,8 +361,10 @@ can figure out how to call the main entry. All exits are initially represented by EXIT nodes: How about an Exit node: +\begin{verbatim} (defstruct (exit (:include node)) value) +\end{verbatim} The Exit node uses the continuation that is to receive the thrown Value. During optimization, if we discover that the Cont's home-lambda is the same is the exit node's, then we can delete the Exit node, substituting the Cont for @@ -377,9 +379,10 @@ We ensure that all Exits that aren't for a NLX don't have any Value, so that local exits never require any value massaging. The Entry node marks the beginning of a block or tagbody: +\begin{verbatim} (defstruct (entry (:include node)) (continuations nil :type list)) - +\end{verbatim} It contains a list of all the continuations that the body could exit to. The Entry node is used as a marker for the the place to snapshot state, including the control stack pointer. Each lambda has a list of its Entries so @@ -391,39 +394,42 @@ no-var let). We represent CATCH using the lexical exit mechanism. We do a transformation like this: +\begin{verbatim} (catch 'foo xxx) ==> (block \#:foo (%catch \#'(lambda () (return-from \#:foo (%unknown-values))) 'foo) (%within-cleanup :catch xxx)) +\end{verbatim} -%CATCH just sets up the catch frame which points to the exit function. %Catch +\%CATCH just sets up the catch frame which points to the exit function. %Catch is an ordinary function as far as ICR is concerned. The fact that the catcher needs to be cleaned up is expressed by the Cleanup slots in the continuations -in the body. %UNKNOWN-VALUES is a dummy function call which represents the +in the body. \%UNKNOWN-VALUES is a dummy function call which represents the fact that we don't know what values will be thrown. -%WITHIN-CLEANUP is a special special form that instantiates its first argument +\%WITHIN-CLEANUP is a special special form that instantiates its first argument as the current cleanup when converting the body. In reality, the lambda is -also created by the special special form %ESCAPE-FUNCTION, which gives the +also created by the special special form \%ESCAPE-FUNCTION, which gives the lambda a special :ESCAPE kind so that the back end knows not to generate any code for it. We use a similar hack in Unwind-Protect to represent the fact that the cleanup forms can be invoked at arbitrarily random times. +\begin{verbatim} (unwind-protect p c) ==> (flet ((\#:cleanup () c)) (block \#:return (multiple-value-bind (\#:next \#:start \#:count) (block \#:unwind - (%unwind-protect \#'(lambda (x) (return-from \#:unwind x))) - (%within-cleanup :unwind-protect + (\%unwind-protect \#'(lambda (x) (return-from \#:unwind x))) + (\%within-cleanup :unwind-protect (return-from \#:return p))) (\#:cleanup) - (%continue-unwind \#:next \#:start \#:count)))) - + (\%continue-unwind \#:next \#:start \#:count)))) +\end{verbatim} We use the block \#:unwind to represent the entry to cleanup code in the case where we are non-locally unwound. Calling of the cleanup function in the drop-through case (or any local exit) is handled by cleanup generation. We @@ -473,7 +479,7 @@ Since we need to evaluate potentially arbitrary code in the XEP argument forms (for type checking), we can't leave the arguments in the wired passing locations. Instead, it seems better to give the XEP max-args fixed arguments, with the passing locations being the true passing locations. Instead of using -%XEP-ARG, we reference the appropriate variable. +\%XEP-ARG, we reference the appropriate variable. Also, it might be a good idea to do argument count checking and dispatching with explicit conditional code in the XEP. This would simplify both the code @@ -510,25 +516,27 @@ If an optional argument is unsupplied, the value passed into the XEP is undefined. The XEP is responsible for doing argument count checking and dispatching. -In the case of a fixed-arg lambda, we emit a call to the %VERIFY-ARGUMENT-COUNT +In the case of a fixed-arg lambda, we emit a call to the \%VERIFY-ARGUMENT-COUNT funny function (conditional on policy), then call the real function on the passed arguments. Even in this simple case, we benefit several ways from having a separate XEP: - -- The argument count checking is factored out, and only needs to be done in - full calls. - -- Argument type checking happens automatically as a consequence of passing - the XEP arguments in a local call to the real function. This type checking - is also only done in full calls. - -- The real function may use a non-standard calling convention for the benefit - of recursive or block-compiled calls. The XEP converts arguments/return - values to/from the standard convention. This also requires little - special-casing of XEPs. +\begin{itemize} +\item The argument count checking is factored out, and only needs to + be done in full calls. +\item Argument type checking happens automatically as a consequence of + passing the XEP arguments in a local call to the real function. + This type checking is also only done in full calls. +\item The real function may use a non-standard calling convention for + the benefit of recursive or block-compiled calls. The XEP converts + arguments/return values to/from the standard convention. This also + requires little special-casing of XEPs. +\end{itemize} If the function has variable argument count (represented by an OPTIONAL-DISPATCH), then the XEP contains a COND which dispatches off of the argument count, calling the appropriate entry-point function (which then does defaulting). If there is a more entry (for keyword or rest args), then the XEP -obtains the more arg context and count by calling the %MORE-ARG-CONTEXT funny +obtains the more arg context and count by calling the \%MORE-ARG-CONTEXT funny function. All non-local-call references to functions are replaced with references to the @@ -537,4 +545,4 @@ previously a non-local reference. When we delete the reference to the XEP, we may find that it has no references. In this case, we can delete the XEP, causing the function to no longer be an entry-point. - \ No newline at end of file + diff --git a/docs/internals/compiler.tex b/docs/internals/compiler.tex index 4f8372a52ee4ba4e77fc442c7bd441efb1cb0d9f..cc5c0d7d4a3de0ee76d085a42c96bb0bdbe93154 100644 --- a/docs/internals/compiler.tex +++ b/docs/internals/compiler.tex @@ -1,6 +1,6 @@ \part{Compiler Organization} -\include{compiler-overview} -\include{front} -\include{middle} -\include{back} -\include{interface} +\input{compiler-overview} +\input{front} +\input{middle} +\input{back} +\input{interface} diff --git a/docs/internals/debugger.tex b/docs/internals/debugger.tex index baeeaa46edf2c4e1d948ba1f3e11988c9fbc3718..482baf71f806e7681cc7676d82ba9eaccbb6e0f7 100644 --- a/docs/internals/debugger.tex +++ b/docs/internals/debugger.tex @@ -69,7 +69,7 @@ form appeared in the original source.] Note that form numbers have an interesting property: it is quite efficient to determine whether an arbitrary form is a subform of some other form, since the -form number of B will be > than A's number and < A's next sibling's number iff +form number of B will be \verb+>+ than A's number and \verb+<+ A's next sibling's number iff B is a subform of A. This should be quite useful for doing the source=>pc mapping in the debugger, @@ -85,6 +85,7 @@ like this: Then a compiled debug block is just a sequence of variable-length integers in a particular order, something like this: +\begin{verbatim} number of successors ...offsets of each successor in the function's blocks vector... first PC @@ -92,7 +93,7 @@ particular order, something like this: form number of first source form first live mask (length in bytes determined by number of VARIABLES) ...more <PC, top-level form offset, form-number, live-set> tuples... - +\end{verbatim} We determine the number of locations recorded in a block by the finding the start of the next compiled debug block in the blocks vector. @@ -161,7 +162,7 @@ or letting the user choose. At the primitive level, I guess what this means is that the structure of source locations (i.e. source paths) must be known, and the source=>code operation -should return a list of <source,code> pairs, rather than just a list of code +should return a list of \verb+<source,code>+ pairs, rather than just a list of code locations. This allows the debugger to resolve the ambiguity however it wants. I guess the formal definition of which source paths we would return is: diff --git a/docs/internals/design.tex b/docs/internals/design.tex index 114d7d993ff83144f5704afdd42a7fa48a07d37d..c55dc9473bce566e3a48fbbe6956754261404b90 100644 --- a/docs/internals/design.tex +++ b/docs/internals/design.tex @@ -1,13 +1,65 @@ -\documentstyle[cmu-titlepage]{report} % -*- Dictionary: design -*- +%%\documentstyle[cmu-titlepage]{report} % -*- Dictionary: design -*- +%\documentstyle{report} % -*- Dictionary: design -*- +\documentclass{report} +\usepackage{ifthen} +\usepackage{calc} + \title{Design of CMU Common Lisp} +\date{January 3, 2000} \author{Robert A. MacLachlan (ed)} -\trnumber{CMU-CS-91-???} +%%\trnumber{CMU-CS-91-???} + +%% This code taken from the LaTeX companion. It's meant as a +%% replacement for the description environment. We want one that +%% prints description items in a fixed size box and puts the +%% description itself on the same line or the next depending on the +%% size of the item. +\newcommand{\entrylabel}[1]{\mbox{#1}\hfil} +\newenvironment{entry}{% + \begin{list}{}% + {\renewcommand{\makelabel}{\entrylabel}% + \setlength{\labelwidth}{45pt}% + \setlength{\leftmargin}{\labelwidth+\labelsep}}}% + {\end{list}} + +\newlength{\Mylen} +\newcommand{\Lentrylabel}[1]{% + \settowidth{\Mylen}{#1}% + \ifthenelse{\lengthtest{\Mylen > \labelwidth}}% + {\parbox[b]{\labelwidth}% term > labelwidth + {\makebox[0pt][l]{#1}\\}}% + {#1}% + \hfil\relax} +\newenvironment{Lentry}{% + \renewcommand{\entrylabel}{\Lentrylabel} + \begin{entry}}% + {\end{entry}} + +\setcounter{tocdepth}{2} +\setcounter{secnumdepth}{3} +\def\textfraction{.1} +\def\bottomfraction{.9} % was .3 +\def\topfraction{.9} + +%% Set up margins +\setlength{\oddsidemargin}{-10pt} +\setlength{\evensidemargin}{-10pt} +\setlength{\topmargin}{-40pt} +\setlength{\headheight}{12pt} +\setlength{\headsep}{25pt} +\setlength{\footskip}{30pt} +\setlength{\textheight}{9.25in} +\setlength{\textwidth}{6.75in} +\setlength{\columnsep}{0.375in} +\setlength{\columnseprule}{0pt} + + +\begin{document} +\maketitle \abstract{This report documents internal details of the CMU Common Lisp compiler and run-time system. CMU Common Lisp is a public domain implementation of Common Lisp that runs on various Unix workstations.} -\begin{document} -\maketitle \tableofcontents \include{architecture} \include{compiler} diff --git a/docs/internals/fasl.tex b/docs/internals/fasl.tex index b0ad30512c849dd9a53fdef27cf40a7f6b47bc58..ef743cd439122b7ce9a907cc165d5c28d20c48be 100644 --- a/docs/internals/fasl.tex +++ b/docs/internals/fasl.tex @@ -321,7 +321,7 @@ bits long, and is packed according to the machine's native byte ordering. supported values are 1,2,4,8,16 and 32. \item[44:] \hspace{2em} {\tt FOP-UNIFORM-INT-VECTOR} \hspace{2em} {\it n}(4) \hspace{2em} {\it size}(1) \hspace{2em} -{\it value}(@ceiling<{\it size}/8>) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ +{\it value}(@ceiling$<${\it size}/8$>$) \hspace{2em} $\Rightarrow$ \hspace{2em} stack \\ The four-byte operand {\it n} specifies the length of a vector of unsigned integers to construct. Each integer is {\it size} bits big, and is initialized to the value diff --git a/docs/internals/front.tex b/docs/internals/front.tex index b55d41904aec977e89bd51af3eae93140c4c1d22..1ede8ec5d3702bc30b3027b5779eb0969ffaf4a5 100644 --- a/docs/internals/front.tex +++ b/docs/internals/front.tex @@ -117,12 +117,13 @@ Notinline declaration. This shouldn't be a problem, since everyone should keep their hands off of Notinline calls. Some transformations: - +\begin{verbatim} Endp ==> (NULL (THE LIST ...)) (NOT xxx) or (NULL xxx) => (IF xxx NIL T) (typep x '<simple type>) => (<simple predicate> x) (typep x '<complex type>) => ...composition of simpler operations... +\end{verbatim} TYPEP of AND, OR and NOT types turned into conditionals over multiple TYPEP calls. This makes hairy TYPEP calls more digestible to type constraint propagation, and also means that the TYPEP code generators don't have to deal @@ -151,7 +152,7 @@ All specialized aref functions => (aref (the xxx) ...) Convert (ldb (byte ...) ...) into internal frob that takes size and position as separate args. Other byte functions also... -Change for-value primitive predicates into (if <pred> t nil). This isn't +Change for-value primitive predicates into \verb+(if <pred> t nil)+. This isn't particularly useful during ICR phases, but makes life easy for VMR conversion. This last can't be a source transformation, since a source transform can't tell @@ -646,6 +647,7 @@ If after ICR phases, we have a continuation with check-type set in a context where it seems likely a check will be emitted, and the type is too hairy to be easily checked (i.e. no CHECK-xxx VOP), then we do a transformation on the ICR equivalent to: +\begin{verbatim} (... (the hair <foo>) ...) ==> (... (funcall \#'(lambda (\#:val) @@ -654,6 +656,7 @@ on the ICR equivalent to: (%type-check-error \#:val 'hair))) <foo>) ...) +\end{verbatim} This way, we guarantee that VMR conversion never has to emit type checks for hairy types. @@ -813,7 +816,7 @@ available from two predecessors, then we could infer that we have an (or a b c) constraint, but the above algorithm would come up with none. This probably isn't a big problem. -[\#\#\# Do we want to deal with (if (eq <var> '<foo>) ...) indicating singleton +[\#\#\# Do we want to deal with \verb+(if (eq <var> '<foo>) ...)+ indicating singleton member type?] We detect explicit type tests by looking at type test annotation in the IF diff --git a/docs/internals/interpreter.tex b/docs/internals/interpreter.tex index c3d1c31812fb8842f8d71914d32228a1de7bbb70..0b302dfd77a05326a2519552d0841248a8a9bda2 100644 --- a/docs/internals/interpreter.tex +++ b/docs/internals/interpreter.tex @@ -32,9 +32,11 @@ single cell. The compiler can have some special frobs for making the interpreter efficient, such as a call operation that extracts arguments from the stack slots designated by a continuation list. Perhaps +\begin{verbatim} (values-mapcar fun . lists) <==> (values-list (mapcar fun . lists)) +\end{verbatim} This would be used with MV-CALL. diff --git a/docs/internals/retargeting.tex b/docs/internals/retargeting.tex index 82ab04391196232bbce654994e58b8e9ee7595de..530bd04dd496a03de1f330a492c58815890c2e69 100644 --- a/docs/internals/retargeting.tex +++ b/docs/internals/retargeting.tex @@ -14,18 +14,18 @@ return point anyway. Have a way for template conversion to special-case constant arguments? How about: - If an arg restriction is (:satisfies [<predicate function>]), and the + If an arg restriction is (:satisfies [$<$predicate function$>$]), and the corresponding argument is constant, with the constant value satisfying the predicate, then (if any other restrictions are satisfied), the template will be emitted with the literal value passed as an info argument. If the predicate is omitted, then any constant will do. - We could sugar this up a bit by allowing (:member <object>*) for - (:satisfies (lambda (x) (member x '(<object>*)))) + We could sugar this up a bit by allowing (:member $<$object$>$*) for + (:satisfies (lambda (x) (member x '($<$object$>$*)))) We could allow this to be translated into a Lisp type by adding a new Constant type specifier. This could only appear as an argument to a function type. -To satisfy (Constant <type>), the argument must be a compile-time constant of +To satisfy (Constant $<$type$>$), the argument must be a compile-time constant of the specified type. Just Constant means any constant (i.e. (Constant *)). This would be useful for the type constraints on ICR transforms. @@ -81,7 +81,7 @@ There are basically three levels of target dependence: \chapter{Storage bases and classes} -New interface: instead of CURRENT-FRAME-SIZE, have CURRENT-SB-SIZE <name> which +New interface: instead of CURRENT-FRAME-SIZE, have CURRENT-SB-SIZE \verb+<name>+ which returns the current element size of the named SB. How can we have primitive types that overlap, i.e. (UNSIGNED-BYTE 32), @@ -203,6 +203,7 @@ There are SCs for non-immediate constants and for each significant kind of immediate operand in the architecture. On the RT, 4, 8 and 20 bit integer SCs are probably worth having. +\begin{verbatim} Non-packed SCs: Constant Immediate constant SCs: @@ -211,7 +212,7 @@ Non-packed SCs: String-Char XXX-Float Magic values: T, NIL, 0. - +\end{verbatim} \chapter{Type system parameterization} @@ -299,16 +300,16 @@ The times within a VOP are the following: conflicts are concerned. If load-TNs are allocated, then this is the beginning of their lives. -(:Argument <n>) +(:Argument $<$n$>$) The point at which the N'th argument is read for the last time (by this VOP). If the argument is dead after this VOP, then the argument becomes dead at this time, and may be reused as a temporary or result load-TN. -(:Eval <n>) +(:Eval $<$n$>$) The N'th evaluation step. There may be any number of evaluation steps, but it is unlikely that more than two are needed. -(:Result <n>) +(:Result $<$n$>$) The point at which the N'th result is first written into. This is the point at which that result becomes live. @@ -318,14 +319,15 @@ The times within a VOP are the following: In any of the list-style time specifications, the keyword by itself stands for the first such time, i.e. +\begin{verbatim} :argument <==> (:argument 0) - +\end{verbatim} Note that argument/result read/write times don't actually have to be in the order specified, but they must *appear* to happen in that order as far as conflict analysis is concerned. For example, the arguments can be read in any order as long no TN is written that has a life beginning at or after -(:Argument <n>), where N is the number of an argument whose reading was +(:Argument $<$n$>$), where N is the number of an argument whose reading was postponed. [\#\#\# (???) @@ -392,42 +394,44 @@ emitted. \chapter{Writing Assembly Code} VOP writers expect: - MOVE +\begin{Lentry} +\item[MOVE] You write when you port the assembler.) - EMIT-LABEL +\item[EMIT-LABEL] Assembler interface like INST. Takes a label you made and says "stick it here." - GEN-LABEL + \item[GEN-LABEL] Returns a new label suitable for use with EMIT-LABEL exactly once and for referencing as often as necessary. - INST + \item[INST] Recognizes and dispatches to instructions you defined for assembler. - ALIGN + \item[ALIGN] This takes the number of zero bits you want in the low end of the address of the next instruction. - ASSEMBLE - ASSEMBLE-ELSEWHERE + \item[ASSEMBLE] + \item[ASSEMBLE-ELSEWHERE] Get ready for assembling stuff. Takes a VOP and arbitrary PROGN-style body. Wrap these around instruction emission code announcing the first pass of our assembler. - CURRENT-NFP-TN + \item[CURRENT-NFP-TN] This returns a TN for the NFP if the caller uses the number stack, or nil. - SB-ALLOCATED-SIZE + \item[SB-ALLOCATED-SIZE] This returns the size of some storage based used by the currently compiling component. - ... - + \item[...] +\end{Lentry} ;;; ;;; VOP idioms ;;; -STORE-STACK-TN -LOAD-STACK-TN +\begin{Lentry} +\item[STORE-STACK-TN] +\item[LOAD-STACK-TN] These move a value from a register to the control stack, or from the control stack to a register. They take care of checking the TN types, modifying offsets according to the address units per word, etc. - +\end{Lentry} \chapter{Required VOPS} @@ -463,25 +467,28 @@ beginning of the current frame. Implement more args by BLT'ing the more args *on top* of the current frame. This solves two problems: - -- Any register more arguments can be made uniformly accessibly by copying +\begin{itemize} +\item Any register more arguments can be made uniformly accessibly by copying them into memory. [We can't store the registers in place, since the beginning of the frame gets double use for storing the old-cont, return-pc and env.] - -- It solves the deallocation problem: the arguments will be deallocated when +\item It solves the deallocation problem: the arguments will be deallocated when the frame is returned from or a tail full call is done out of it. So keyword args will be properly tail-recursive without any special mechanism for squeezing out the more arg once the parsing is done. Note that a tail local call won't blast the more arg, since in local call the callee just takes the frame it is given (in this case containing the more arg). +\end{itemize} More args in local call??? Perhaps we should not attempt local call conversion in this case. We already special-case keyword args in local call. It seems that the main importance of more args is primarily related to full call: it is used for defining various kinds of frobs that need to take arbitrary arguments: - -- Keyword arguments - -- Interpreter stubs - -- "Pass through" applications such as dispatch functions - +\begin{itemize} +\item Keyword arguments +\item Interpreter stubs +\item "Pass through" applications such as dispatch functions +\end{itemize} Given the marginal importance of more args in local call, it seems unworth going to any implementation difficulty. In fact, it seems that it would cause complications both at the VMR level and also in the VM definition. This being @@ -593,7 +600,7 @@ Things that need to be done on XEP entry: 5] Argument count checking and dispatching XEP VOPs: - +\begin{verbatim} Allocate-Frame Copy-More-Arg <nargs-tn> 'fixed {in a3} => <context>, <count> Setup-Environment @@ -601,17 +608,20 @@ Setup-Closure-Environment => <closure> Verify-Argument-Count <nargs-tn> 'count {for fixed-arg lambdas} Argument-Count-Error <nargs-tn> {Drop-thru on hairy arg dispatching} Use fast-if-=/fixnum and fast-if-</fixnum for dispatching. +\end{verbatim} Closure vops: +\begin{verbatim} make-closure <fun entry> <slot count> => <closure> closure-init <closure> <values> 'slot - +\end{verbatim} Things that need to be done on all function entry: - -- Move arguments to the variable home (consing value cells as necessary) - -- Move environment values to the local home - -- Move old-cont and return-pc to the save locations - +\begin{itemize} +\item Move arguments to the variable home (consing value cells as necessary) +\item Move environment values to the local home +\item Move old-cont and return-pc to the save locations +\end{itemize} \section{Calls} @@ -700,7 +710,7 @@ A0 ... An These holds the first n+1 values, or NIL if there are less than n+1 values. CSP - Returner stores CSP to hold its CFP + NARGS * <address units per word> + Returner stores CSP to hold its CFP + NARGS * \verb+<address units per word>+ OCFP Returner stores this as its CFP, so the returnee has a handle on either @@ -712,8 +722,10 @@ ALLOCATE FULL CALL FRAME. If the number of call arguments (passed to the VOP as an info argument) indicates that there are stack arguments, then it makes some callee frame for arguments: +\begin{verbatim} VOP-result <- CSP CSP <- CSP + value of VOP info arg times address units per word. +\end{verbatim} In a call sequence, move some arguments to the right places. @@ -722,7 +734,7 @@ There's a variety of MOVE-ARGUMENT VOP's. FULL CALL VOP'S (variations determined by whether it's named, it's a tail call, there is a variable arg count, etc.) - +\begin{verbatim} if variable number of arguments NARGS <- (CSP - value of VOP argument) shift right by address units per word. A0...An <- values off of VOP argument (just fill them all) @@ -756,9 +768,10 @@ is a variable arg count, etc.) <default and move return values OR receive return values> when (current-nfp-tn VOP-self-pointer) NFP <- stack-temp - +\end{verbatim} Callee: +\begin{verbatim} XEP-ALLOCATE-FRAME emit function header (maybe initializes offset back to component start, but other pointers are set up at load-time. Pads @@ -767,13 +780,17 @@ XEP-ALLOCATE-FRAME if the function uses the number stack NFP <- NSP NSP <- NSP + compile-time determined constant (number stack frame size) +\end{verbatim} +\begin{verbatim} SETUP-ENVIRONMENT (either use this or the next one) CODE <- CODE - assembler-time determined offset from function-entry back to the code data-block address. +\end{verbatim} +\begin{verbatim} SETUP-CLOSURE-ENVIRONMENT (either use this or the previous one) After this the CLOSURE-REF VOP can reference closure variables. @@ -781,6 +798,7 @@ After this the CLOSURE-REF VOP can reference closure variables. VOP-result <- LEXENV CODE <- CODE - assembler-time determined offset from function-entry back to the code data-block address. +\end{verbatim} Return VOP's RETURN and RETURN-MULTIPLE are for the unknown-values return convention. @@ -795,6 +813,7 @@ RETURN When compiler invokes VOP, all values are already where they should be; just get back to caller. +\begin{verbatim} when (current-nfp-tn VOP-self-pointer) ;; The number stack grows down in memory. NSP <- NFP + number stack frame size for calls within the currently @@ -815,6 +834,7 @@ else LIP <- calc target addr (CODE + skip over LRA header word) CFP <- value of VOP argument with OCFP jump and run off LIP +\end{verbatim} RETURN-MULTIPLE (unknown number of values, used with the unknown-values convention in @@ -822,6 +842,7 @@ RETURN-MULTIPLE When compiler invokes VOP, it gets TN's representing a pointer to the values on the stack and how many values were computed. +\begin{verbatim} when (current-nfp-tn VOP-self-pointer) ;; The number stack grows down in memory. NSP <- NFP + number stack frame size for calls within the currently @@ -839,7 +860,7 @@ CFP <- value of VOP argument with OCFP CODE <- value of VOP argument with LRA LIP <- calc target addr (CODE + skip over LRA header word) jump and run off LIP - +\end{verbatim} Returnee The call VOP's call DEFAULT-UNKNOWN-VALUES or RECEIVE-UNKNOWN-VALUES after @@ -861,6 +882,7 @@ stack pointer to dump possible stack return values (move OCFP to CSP). Two, if the returnee wants more values than the number of return registers, and it starts at PC+N (got one value), then it sets up returnee state as if an unknown number of values came back: +\begin{verbatim} A0 has the one value A1..An get nil NARGS gets 1 @@ -871,6 +893,7 @@ code which takes care of the following issues: values and move OCFP into CSP If k >= n, move OCFP into CSP This also restores CODE from LRA by subtracting an assemble-time constant. +\end{verbatim} RECEIVE-UKNOWN-VALUES (I want whatever I get.) @@ -900,6 +923,7 @@ ALLOCATE-FRAME If the number of call arguments (passed to the VOP as an info argument) indicates that there are stack arguments, then it makes some callee frame for arguments: +\begin{verbatim} VOP-result1 <- CSP CSP <- CSP + control stack frame size for calls within the currently compiling component @@ -912,7 +936,7 @@ arguments: compiling component times address units per word VOP-result2 <- NSP - +\end{verbatim} KNOWN-CALL-LOCAL, CALL-LOCAL, MULTIPLE-CALL-LOCAL KNOWN-CALL-LOCAL has no need to affect CODE since CODE is the same for the caller/returnee and the returner. This uses KNOWN-RETURN. @@ -924,6 +948,7 @@ will give us. This could happen due to a tail full call to an unknown function, or because the callee had different return points that returned various numbers of values. +\begin{verbatim} when (current-nfp-tn VOP-self-pointer) ;Get VOP self-pointer with ;DEFINE-VOP switch :vop-var. stack-temp <- NFP @@ -942,9 +967,10 @@ jump and run off VOP info arg holding start instruction for callee > when (current-nfp-tn VOP-self-pointer) NFP <- stack-temp +\end{verbatim} KNOWN-RETURN - +\begin{verbatim} CSP <- CFP when (current-nfp-tn VOP-self-pointer) ;; number stack grows down in memory. @@ -955,7 +981,7 @@ LIP <- calc target addr (value of VOP arg + skip over LRA header word) CFP <- value of VOP arg jump and run off LIP - +\end{verbatim} \chapter{Standard Primitives} @@ -1070,13 +1096,15 @@ for each of the first N values. Nearly all open-coded functions should be handled using standard template selection. Some (all?) exceptions: - -- List, List* and Vector take arbitrary numbers of arguments. Could +\begin{itemize} +\item List, List* and Vector take arbitrary numbers of arguments. Could implement Vector as a source transform. Could even do List in a transform - if we explicitly represent the stack args using %More-Args or something. - -- %Typep varies a lot depending on the type specifier. We don't want to - transform it, since we want %Typep as a canonical form so that we can do + if we explicitly represent the stack args using \%More-Args or something. +\item \%Typep varies a lot depending on the type specifier. We don't want to + transform it, since we want \%Typep as a canonical form so that we can do type optimizations. - -- Apply is weird. - -- Funny functions emitted by the compiler: %Listify-Rest-Args, Arg, - %More-Args, %Special-Bind, %Catch, %Unknown-Values (?), %Unwind-Protect, - %Unwind, %%Primitive. +\item Apply is weird. +\item Funny functions emitted by the compiler: \%Listify-Rest-Args, Arg, + \%More-Args, \%Special-Bind, \%Catch, \%Unknown-Values (?), \%Unwind-Protect, + \%Unwind, \%\%Primitive. +\end{itemize} diff --git a/docs/internals/run-time.tex b/docs/internals/run-time.tex index eb21e1cf8b0b5ca4ebe33f13fa5196704385a862..72250c5ec753ea82085709caf8e9ca2b5e127e76 100644 --- a/docs/internals/run-time.tex +++ b/docs/internals/run-time.tex @@ -1,7 +1,7 @@ \part{Run-Time system} -\include{environment} -\include{interpreter} -\include{debugger} -\include{object} -\include{lowlev} -\include{fasl} +\input{environment} +\input{interpreter} +\input{debugger} +\input{object} +\input{lowlev} +\input{fasl} diff --git a/docs/internals/vm.tex b/docs/internals/vm.tex index b1e1c2fe8418f384a49871fbb5843ef8865068ac..e36dcffe0a192eee2063efee97d3d3186f6bab2f 100644 --- a/docs/internals/vm.tex +++ b/docs/internals/vm.tex @@ -522,12 +522,13 @@ put (symbol indicator value) cons +\begin{example} list<n> (elt0 ... elt<n-1>) => list list (elt0 ... elt<n-1> more-elts) => list For some small N, we have fixed-arg versions of List. For larger lists, we pass in additional elements in a stack TN (possibly required to be on stack top). List* is similar. - +\end{example} These VOPs implement the corresponding \clisp functions: \begin{example} @@ -606,7 +607,7 @@ if-= (x y) {small, fast} if-=/fixnum {small, fast} if-=/xxx-float Do numeric comparison of X and Y. The codegen-info contains the - continuations to transfer to in the true and false cases. Same for <, >. + continuations to transfer to in the true and false cases. Same for $<$, $>$. + (x y) => z {small, fast} +/fixnum @@ -765,8 +766,8 @@ aset1 (vector index new-value) {small, fast} aset1/simple-array-unsigned-byte (vector index new-value) Byte size is codegen info. -aref<N> (array index0 ... index<n-1>) => value -aset<N> (array index0 ... index<n-1> new-value) +aref$<$N$>$ (array index0 ... index<n-1>) => value +aset$<$N$>$ (array index0 ... index<n-1> new-value) For some small value of N. Of course, higher dimensional arrays can also be specialized in seven different ways.... Multi-dimensional simple array reference with known dimensions can be open-coded using a transform (useful @@ -863,14 +864,14 @@ If deep-bind, current special binding. Format of binding frame assumed. Everything depends on the current environment, which is CONT. - +\begin{verbatim} PC OLD-CONT ENV A<n> CONT CS - +\end{verbatim} \section{Other Dynamic State} @@ -1051,15 +1052,20 @@ Environment manipulation code is always emitted at the location of the Bind or Return node for a Lambda. Implicit args to functions in IR2: - old-cont: cont to restore on return - return-pc: pc to return to - env: pointer to current closure (if heap) - closure<n>: closed values for current closure (if stack) +\begin{description} +\item[old-cont] cont to restore on return +\item[return-pc] pc to return to +\item[env] pointer to current closure (if heap) +\item[closure$<$n$>$] closed values for current closure (if stack) +\end{description} Other info needed for IR2 conversion of functions: - base pointers for all heap closures consed by this function - also have passing locs for each explicit arg - return strategy (known or unknown) and return locs +\begin{itemize} +\item base pointers for all heap closures consed by this function also + have passing locs for each explicit arg return strategy (known or + unknown) and return locs +\end{itemize} + All arguments including implicit ones must have both a passing TN and a permanent TN. Passing locs for let calls can be the actual TN that holds the @@ -1155,7 +1161,7 @@ in the called function. Similarly, we pretend that a block ending in a return has no successors. call-local (arg*) "fun" => value -multiple-call-local (arg*) "fun" => start end val0 ... val<n> +multiple-call-local (arg*) "fun" =$>$ start end val0 ... val$<$n$>$ Call-Local is used for calls to local functions that are forced to use the unknown-values passing convention. Value is the first return value register; we don't really do anything to it, but we specify it as a result @@ -1165,7 +1171,7 @@ multiple-call-local (arg*) "fun" => start end val0 ... val<n> unknown-values convention. Default-Values may be used to receive a specific number of values. -known-call-local (arg*) "fun" => value* +known-call-local (arg*) "fun" =$>$ value* This VOP is used for local calls to functions where we can determine at compile time that the number of values returned is always the same. In this case, we don't need to indicate the number of values, and can pass @@ -1194,7 +1200,7 @@ Known values return causes no problems, since the callee knows how many values are wanted. We store the values directly into the current frame, since it is also the caller's frame. -known-call-simple () "fun" => return-pc +known-call-simple () "fun" =$>$ return-pc known-return-simple (return-pc) "fun" Similar to the non-simple VOPs, but don't allocate or deallocate frames, and assume that argument and value passing is done with explicit Move VOPs. @@ -1225,7 +1231,7 @@ Argument passing in full call is conceptually similar to local call, but the caller can't allocate the entire frame for the callee, since it doesn't know how much stack is needed. Instead we allocate the frame in two parts. The caller only allocates the beginning of the frame, which contains the stack -arguments in fixed locations. We leave the first <n> locations unused so that +arguments in fixed locations. We leave the first $<$n$>$ locations unused so that the called function can move register more args onto the stack without having to BLT down any stack arguments. @@ -1255,12 +1261,12 @@ tail-recursive: In full call we must always use the unknown-values convention for return. The -first <n> values are passed in the standard argument registers. The Old-Cont +first $<$n$>$ values are passed in the standard argument registers. The Old-Cont register holds the Start of the values block and SP points to the End. -{small, fast} call (function arg0 ... arg<n>) "nargs" => value -{small, fast} call-named (arg0 ... arg<n>) "nargs" "name" => value +{small, fast} call (function arg0 ... arg$<$n$>$) "nargs" =$>$ value +{small, fast} call-named (arg0 ... arg$<$n$>$) "nargs" "name" =$>$ value Call-Closure calls Function with the specified register arguments, returning the first value as the result. "nargs" is the total number of arguments passed. Only the register arguments actually passed should be @@ -1269,27 +1275,27 @@ register holds the Start of the values block and SP points to the End. Call-Named is similar, but calls a global function specified at compile time by "name". -{small, fast} tail-call (function pc arg0 ... arg<n>) "nargs" -{small, fast} tail-call-named (pc arg0 ... arg<n>) "nargs" "name" +{small, fast} tail-call (function pc arg0 ... arg$<$n$>$) "nargs" +{small, fast} tail-call-named (pc arg0 ... arg$<$n$>$) "nargs" "name" Similar to the standard call VOPs, but passes PC as the return PC, rather than returning to the call site. These VOPs have no results since they don't return. -{small, fast} multiple-call (function arg0 ... arg<n>) "nargs" - => start end val0 ... val<n> -{small, fast} multiple-call-named (arg0 ... arg<n>) "nargs" "name" - => start end val0 ... val<n> +{small, fast} multiple-call (function arg0 ... arg$<$n$>$) "nargs" + =$>$ start end val0 ... val$<$n$>$ +{small, fast} multiple-call-named (arg0 ... arg$<$n$>$) "nargs" "name" + =$>$ start end val0 ... val$<$n$>$ These VOPs are similar to the standard call VOPs, but allow any number of values to be received by returning all the value passing registers as results. A specific number of values may be received by using Default-Values. -call-unknown (function count arg0 ... arg<n>) => start end val0 ... val<n> -tail-call-unknown (function pc count arg0 ... arg<n>) +call-unknown (function count arg0 ... arg$<$n$>$) =$>$ start end val0 ... val$<$n$>$ +tail-call-unknown (function pc count arg0 ... arg$<$n$>$) Call a function with an unknown number of arguments. Used for apply and hairy multiple-value-call. -Function-Entry () "function" => env return-pc old-cont arg* +Function-Entry () "function" =$>$ env return-pc old-cont arg* This marks the place where we jump into a component for an external entry point. It represents whatever magic is necessary to do argument count checking and dispatching. The external entry points for each @@ -1312,17 +1318,17 @@ Function-Entry () "function" => env return-pc old-cont arg* return (return-pc value) -multiple-return (return-pc start end val0 ... val<n>) +multiple-return (return-pc start end val0 ... val$<$n$>$) Return Value from the current function, jumping back to the location specified by Return-PC. {Perhaps allow to return any fixed, known number of values.} Multiple-Return is similar, but allows an arbitrary number of values to be returned. End - Start is the total number of values returned. Start - points to the beginning of the block of return values, but the first <n> - values val0 ... val<n> are actually returned in registers. + points to the beginning of the block of return values, but the first $<$n$>$ + values val0 ... val$<$n$>$ are actually returned in registers. -default-values (start end val0 ... val<n>) => val0 ... val<j> +default-values (start end val0 ... val$<$n$>$) =$>$ val0 ... val$<$j$>$ This VOP is used when we want to receive exactly J values. If fewer than J values were supplied, then missing values are defaulted to NIL. As a side-effect, this VOP pops off any returned stack values.