(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.
(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))))
+ ',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)