src/codegen-proto.lisp, doc/list-exports.lisp: Export `inst' readers.
[sod] / src / codegen-proto.lisp
index 24b8c38..7c8f65c 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.
 
-(export 'temporary-name)
+(export '(temporary-name temp-tag))
 (defclass temporary-name ()
   ((tag :initarg :tag :reader temp-tag))
   (:documentation
 
    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.
 
@@ -73,6 +74,8 @@
   (make-instance 'temporary-name :tag "sod__ap"))
 (defparameter *sod-master-ap*
   (make-instance 'temporary-name :tag "sod__master_ap"))
+(defparameter *sod-tmp-ap*
+  (make-instance 'temporary-name :tag "sod__tmp_ap"))
 
 ;;;--------------------------------------------------------------------------
 ;;; Instructions.
    "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.
 
 (export 'definst)
-(defmacro definst (code (streamvar) args &body body)
+(defmacro definst (code (streamvar &key export) 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."
+       point.
+
+   If EXPORT is non-nil, then export the `CODE-inst' and `make-CODE-inst'
+   symbols."
 
   (let ((inst-var (gensym "INST"))
        (class-name (symbolicate code '-inst))
+       (constructor-name (symbolicate 'make- code '-inst))
        (keys (mapcar (lambda (arg) (intern (symbol-name arg) :keyword))
                      args)))
     `(progn
         ,(mapcar (lambda (arg key)
                    `(,arg :initarg ,key :reader ,(symbolicate 'inst- arg)))
                  args keys))
-       (defun ,(symbolicate 'make- code '-inst) (,@args)
+       (defun ,constructor-name (,@args)
         (make-instance ',class-name ,@(mappend #'list keys args)))
        (defmethod inst-metric ((,inst-var ,class-name))
         (with-slots (,@args) ,inst-var
               (print-unreadable-object (,inst-var ,streamvar :type t)
                 (format stream "~@<~@{~S ~@_~S~^ ~_~}~:>"
                         ,@(mappend #'list keys args)))
-              (progn ,@body)))))))
+              (progn ,@body))))
+       ,@(and export `((export '(,class-name ,constructor-name
+                                ,@(mapcar (lambda (arg)
+                                            (symbolicate 'inst- arg))
+                                          args)))))
+       ',code)))
 
 ;; Important instruction classes.
 
-(export '(block-inst make-block-inst var-inst make-var-inst
-         function-inst make-function-inst set-inst make-set-inst
-         return-inst make-return-inst expr-inst make-expr-inst
-         inst-decls inst-body inst-name inst-type inst-init inst-var
-         inst-expr))
-
-(definst var (stream) (name type init)
+(definst var (stream :export t) (name type init)
   (pprint-c-type type stream name)
   (when init
-    (format stream " = ~A" init)))
-(definst set (stream) (var expr)
+    (format stream " = ~A" init))
+  (write-char #\; stream))
+(definst set (stream :export t) (var expr)
   (format stream "~@<~A = ~@_~2I~A;~:>" var expr))
-(definst return (stream) (expr)
+(definst update (stream :export t) (var op expr)
+  (format stream "~@<~A ~A= ~@_~2I~A;~:>" var op expr))
+(definst return (stream :export t) (expr)
   (format stream "return~@[ (~A)~];" expr))
-(definst expr (stream) (expr)
+(definst break (stream :export t) ()
+  (format stream "break;"))
+(definst continue (stream :export t) ()
+  (format stream "continue;"))
+(definst expr (stream :export t) (expr)
   (format stream "~A;" expr))
-(definst block (stream) (decls body)
-  (format stream "{~:@_~@<  ~2I~@[~{~A;~:@_~}~:@_~]~{~A~^~:@_~}~:>~:@_}"
+(definst block (stream :export t) (decls body)
+  (format stream "{~:@_~@<  ~2I~@[~{~A~:@_~}~:@_~]~{~A~^~:@_~}~:>~:@_}"
          decls body))
-(definst function (stream) (name type body)
+(definst function (stream :export t) (name type body)
   (pprint-logical-block (stream nil)
     (princ "static " stream)
     (pprint-c-type type stream name)
 ;; 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))
   (:method (codegen insts)
     (dolist (inst insts) (emit-inst codegen inst))))
 
+(export '(emit-decl emit-decls))
+(defgeneric emit-decl (codegen inst)
+  (:documentation
+   "Add INST to the end of CODEGEN's list of declarations."))
+(defgeneric emit-decls (codegen insts)
+  (:documentation
+   "Add a list of INSTS to the end of CODEGEN's list of declarations."))
+
 (export 'codegen-push)
 (defgeneric codegen-push (codegen)
   (:documentation
    "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)