An actual running implementation, which makes code that compiles.
[sod] / src / codegen-proto.lisp
index 24b8c38..b8206fa 100644 (file)
 (export 'var-in-use-p)
 (defgeneric var-in-use-p (var)
   (:documentation
-   "Answer whether VAR is currently being used.  See WITH-TEMPORARY-VAR.")
+   "Answer whether VAR is currently being used.  See `with-temporary-var'.")
   (:method (var)
     "Non-temporary variables are always in use."
+    (declare (ignore var))
     t))
 (defgeneric (setf var-in-use-p) (value var)
   (:documentation
-   "Record whether VAR is currently being used.  See WITH-TEMPORARY-VAR."))
+   "Record whether VAR is currently being used.  See `with-temporary-var'."))
 
 ;; Root class.
 
 
    This is automatically reset to zero before the output functions are
    invoked to write a file.  This way, we can ensure that the same output
-   file is always produced from the same input."
-  ;; FIXME: this is currently a lie.  Need some protocol to ensure that this
-  ;; happens.
-)
+   file is always produced from the same input.")
+
+(define-clear-the-decks reset-codegen-index
+  (setf *temporary-index* 0))
 
 ;; Important temporary names.
 
    "A base class for instructions.
 
    An `instruction' is anything which might be useful to string into a code
-   generator.  Both statements and expressions map can be represented by
-   trees of instructions.  The DEFINST macro is a convenient way of defining
-   new instructions.
+   generator.  Both statements and expressions can be represented by trees of
+   instructions.  The `definst' macro is a convenient way of defining new
+   instructions.
 
    The only important protocol for instructions is output, which is achieved
-   by calling PRINT-OBJECT with *PRINT-ESCAPE* nil.
+   by calling `print-object' with `*print-escape*' nil.
 
    This doesn't really do very much, but it acts as a handy marker for
    instruction subclasses."))
   (:documentation
    "Returns a `metric' describing how complicated INST is.
 
-   The default metric of an inst node is simply 1; INST subclasses generated
-   by DEFINST (q.v.) have an automatically generated method which returns one
-   plus the sum of the metrics of the node's children.
+   The default metric of an inst node is simply 1; `inst' subclasses
+   generated by `definst' (q.v.) have an automatically generated method which
+   returns one plus the sum of the metrics of the node's children.
 
    This isn't intended to be a particularly rigorous definition.  Its purpose
    is to allow code generators to make decisions about inlining or calling
    code fairly simply.")
-  (:method (inst) 1))
+  (:method ((inst t))
+    (declare (ignore inst))
+    1)
+  (:method ((inst null))
+    (declare (ignore inst))
+    1)
+  (:method ((inst list))
+    (reduce #'+ inst :key #'inst-metric)))
 
 ;; Instruction definition.
 
 (defmacro definst (code (streamvar) args &body body)
   "Define an instruction type and describe how to output it.
 
-   An INST can represent any structured piece of output syntax: a statement,
-   expression or declaration, for example.  This macro defines the following
-   things:
+   An `inst' can represent any structured piece of output syntax: a
+   statement, expression or declaration, for example.  This macro defines the
+   following things:
 
-     * A class CODE-INST to represent the instruction.
+     * A class `CODE-inst' to represent the instruction.
 
      * Instance slots named after the ARGS, with matching keyword initargs,
-       and INST-ARG readers.
+       and `inst-ARG' readers.
 
-     * A constructor MAKE-CODE-INST which accepts the ARGS (in order, not
+     * A constructor `make-CODE-inst' which accepts the ARGS (in order, not
        with keywords) as arguments and returns a fresh instance.
 
-     * A print method, which prints a diagnostic dump if *PRINT-ESCAPE* is
+     * A print method, which prints a diagnostic dump if `*print-escape*' is
        set, or invokes the BODY (with STREAMVAR bound to the output stream)
        otherwise.  The BODY is expected to produce target code at this
        point."
 ;; Formatting utilities.
 
 (defun format-compound-statement* (stream child morep thunk)
-  "Underlying function for FORMAT-COMPOUND-STATEMENT."
+  "Underlying function for `format-compound-statement'."
   (cond ((typep child 'block-inst)
         (funcall thunk stream)
         (write-char #\space stream)
   "Format a compound statement to STREAM.
 
    The introductory material is printed by BODY.  The CHILD is formatted
-   properly according to whether it's a BLOCK-INST.  If MOREP is true, then
+   properly according to whether it's a `block-inst'.  If MOREP is true, then
    allow for more stuff following the child."
   `(format-compound-statement* ,stream ,child ,morep
                               (lambda (,stream) ,@body)))
 (export 'codegen-functions)
 (defgeneric codegen-functions (codegen)
   (:documentation
-   "Return the list of FUNCTION-INSTs of completed functions."))
+   "Return the list of `function-inst's of completed functions."))
 
 (export 'ensure-var)
 (defgeneric ensure-var (codegen name type &optional init)
   (:documentation
    "Add a variable to CODEGEN's list.
 
-   The variable is called NAME (which should be comparable using EQUAL and
+   The variable is called NAME (which should be comparable using `equal' and
    print to an identifier) and has the given TYPE.  If INIT is present and
-   non-nil it is an expression INST used to provide the variable with an
+   non-nil it is an expression `inst' used to provide the variable with an
    initial value."))
 
 (export '(emit-inst emit-insts))
    "Adds a function to CODEGEN's list.
 
    Actually, we're not picky: FUNCTION can be any kind of object that you're
-   willing to find in the list returned by CODEGEN-FUNCTIONS."))
+   willing to find in the list returned by `codegen-functions'."))
 
 (export 'temporary-var)
 (defgeneric temporary-var (codegen type)
 
    The temporary variable will have the given TYPE, and will be marked
    in-use.  You should clear the in-use flag explicitly when you've finished
-   with the variable -- or, better, use WITH-TEMPORARY-VAR to do the cleanup
-   automatically."))
+   with the variable -- or, better, use `with-temporary-var' to do the
+   cleanup automatically."))
 
 (export 'codegen-build-function)
 (defun codegen-build-function (codegen name type vars insts)
 (export 'codegen-pop-block)
 (defgeneric codegen-pop-block (codegen)
   (:documentation
-   "Makes a block (BLOCK-INST) out of the completed code in CODEGEN.")
+   "Makes a block (`block-inst') out of the completed code in CODEGEN.")
   (:method (codegen)
     (multiple-value-bind (vars insts) (codegen-pop codegen)
       (make-block-inst vars insts))))
   "Evaluate BODY with VAR bound to a temporary variable name.
 
    During BODY, VAR will be marked in-use; when BODY ends, VAR will be marked
-  available for re-use."
+   available for re-use."
   `(let ((,var (temporary-var ,codegen ,type)))
      (unwind-protect
          (progn ,@body)
 
    The TARGET may be one of the following.
 
-     * :VOID, indicating that the value is to be discarded.  The expression
+     * `:void', indicating that the value is to be discarded.  The expression
        will still be evaluated.
 
-     * :VOID-RETURN, indicating that the value is to be discarded (as for
-       :VOID) and furthermore a `return' from the current function should be
-       forced after computing the value.
+     * `:void-return', indicating that the value is to be discarded (as for
+       `:void') and furthermore a `return' from the current function should
+       be forced after computing the value.
 
-     * :RETURN, indicating that the value is to be returned from the current
-       function.
+     * `:return', indicating that the value is to be returned from the
+       current function.
 
      * A variable name, indicating that the value is to be stored in the
        variable.
 
-   In the cases of :RETURN, :VOID and :VOID-RETURN targets, it is valid for
-   EXPR to be nil; this signifies that no computation needs to be performed.
-   Variable-name targets require an expression."
+   In the cases of `:return', `:void' and `:void-return' targets, it is valid
+   for EXPR to be nil; this signifies that no computation needs to be
+   performed.  Variable-name targets require an expression."
 
   (case target
     (:return (emit-inst codegen (make-return-inst expr)))
 
 (export 'convert-stmts)
 (defun convert-stmts (codegen target type func)
-  "Invoke FUNC to deliver a value to a non-:RETURN target.
+  "Invoke FUNC to deliver a value to a non-`:return' target.
 
-   FUNC is a function which accepts a single argument, a non-:RETURN target,
-   and generates statements which deliver a value (see DELIVER-EXPR) of the
-   specified TYPE to this target.  In general, the generated code will have
-   the form
+   FUNC is a function which accepts a single argument, a non-`:return'
+   target, and generates statements which deliver a value (see
+   `deliver-expr') of the specified TYPE to this target.  In general, the
+   generated code will have the form
 
      setup instructions...
-     (DELIVER-EXPR CODEGEN TARGET (compute value...))
+     (deliver-expr CODEGEN TARGET (compute value...))
      cleanup instructions...
 
    where the cleanup instructions are essential to the proper working of the
    generated program.
 
-   CONVERT-STMTS will call FUNC to generate code, and arrange that its value
-   is correctly delivered to TARGET, regardless of what the TARGET is --
-   i.e., it lifts the restriction to non-:RETURN targets.  It does this by
-   inventing a new temporary variable."
+   The `convert-stmts' function will call FUNC to generate code, and arrange
+   that its value is correctly delivered to TARGET, regardless of what the
+   TARGET is -- i.e., it lifts the restriction to non-`:return' targets.  It
+   does this by inventing a new temporary variable."
 
   (case target
     (:return (with-temporary-var (codegen var type)