summaryrefslogtreecommitdiff
path: root/backend/codegen.scm
diff options
context:
space:
mode:
Diffstat (limited to 'backend/codegen.scm')
-rw-r--r--backend/codegen.scm600
1 files changed, 600 insertions, 0 deletions
diff --git a/backend/codegen.scm b/backend/codegen.scm
new file mode 100644
index 0000000..283594f
--- /dev/null
+++ b/backend/codegen.scm
@@ -0,0 +1,600 @@
+;;; codegen.scm -- compile flic code to Lisp
+;;;
+;;; Author : Sandra Loosemore
+;;; Date : 29 Apr 1992
+;;;
+;;; to do: check completeness of special cases for constructors
+;;; constants still need work
+;;; optimized entry points
+;;;
+;;; The code generated here uses the following helper functions:
+;;; (make-curried-fn opt-fn strictness)
+;;; make a curried function that calls opt-fn after collecting the
+;;; arguments and processing them according to strictness. Both
+;;; the arguments are evaluated.
+;;; (make-tuple-constructor arity)
+;;; return a function that makes an untagged data structure with "arity"
+;;; slots. "arity" is a constant.
+;;; (make-tuple . args)
+;;; uncurried version of the above
+;;; (make-tagged-data-constructor n arity)
+;;; return a function that makes a data structure with tag "n" and
+;;; "arity" slots.
+;;; (make-tagged-data n . args)
+;;; uncurried version of the above
+;;; (tuple-select arity i object)
+;;; extract component "i" from untagged "object"
+;;; (tagged-data-select arity i object)
+;;; extract component "i" from tagged "object"
+;;; (constructor-number object)
+;;; return the tag from "object"
+;;; (delay form)
+;;; returns a delay object with unevaluated "form".
+;;; (box form)
+;;; returns a delay object with evaluated "form".
+;;; (force delay)
+;;; return the value of the delay object.
+;;; (make-haskell-string string)
+;;; Converts a Lisp string lazily to a haskell string (using a magic
+;;; delay function). Returns an unboxed result.
+
+
+
+;;;======================================================================
+;;; Code walker
+;;;======================================================================
+
+
+;;; Here is the main entry point.
+
+(define (codegen-top big-let)
+ (do ((bindings (flic-let-bindings big-let) (cdr bindings))
+ (result '())
+ (decls '()))
+ ((null? bindings) `(begin ,@(nreverse decls) ,@(nreverse result)))
+ (let ((var (car bindings)))
+ (push `(predefine ,(fullname var)) decls)
+ (push (codegen-definition var (var-value var)) result))))
+
+
+;;; See box.scm for more information about this...
+
+(define (do-codegen object)
+ (let ((x (codegen object))
+ (unboxed? (flic-exp-unboxed? object))
+ (strict-result? (flic-exp-strict-result? object))
+ (cheap? (flic-exp-cheap? object)))
+ (if unboxed?
+ (if strict-result?
+ x
+ (if cheap?
+ `(unbox ,x)
+ `(force ,x)))
+ (if strict-result?
+ (if cheap?
+ `(box ,x)
+ `(delay ,x))
+ (if cheap?
+ x
+ `(delay (force ,x)))))))
+
+
+(define (do-codegen-list list)
+ (map (function do-codegen) list))
+
+
+(define-flic-walker codegen (object))
+
+
+;;; For top-level definitions bound to lambda expressions, make both
+;;; a standard entry point (with possibly unboxed arguments) and
+;;; a standard entry point.
+
+(define (codegen-definition var exp)
+ (let ((fullname (fullname var)))
+ (when (or (memq 'codegen (dynamic *printers*))
+ (memq 'codegen-flic (dynamic *printers*)))
+; (format '#t "~%Codegen of ~A [~A] " (def-name var) (struct-hash var))
+ (format '#t "~%Codegen of ~A " (def-name var))
+ (when (not (var-strict? var))
+ (format '#t "Nonstrict "))
+ (when (not (eq? (var-strictness var) '()))
+ (format '#t "Strictness: ")
+ (dolist (s (var-strictness var))
+ (format '#t (if s "S " "N "))))
+ (when (var-simple? var)
+ (format '#t " Inline "))
+ (format '#t "~%")
+ (when (memq 'codegen-flic (dynamic *printers*))
+ (pprint* exp)))
+ (let ((lisp-code
+ (if (not (flic-lambda? exp))
+ `(define ,fullname ,(do-codegen exp))
+ (let* ((optname (optname var))
+ (lambda (codegen-lambda-aux exp))
+ (def `(define (,optname ,@(cadr lambda))
+ ,@(cddr lambda))))
+ (if (var-selector-fn? var)
+ ;; Standard entry point for selectors is never used.
+ def
+ `(begin
+ ,def
+ (define ,fullname
+ ,(maybe-make-box-value
+ (codegen-curried-fn
+ `(function ,optname) (var-strictness var))
+ (var-strict? var)))))))))
+ (when (or (memq 'codegen (dynamic *printers*))
+ (memq 'codegen-flic (dynamic *printers*)))
+ (pprint* lisp-code))
+ lisp-code)))
+
+(define (codegen-lambda-list vars)
+ (map (function fullname) vars))
+
+(define (codegen-curried-fn opt-fn strictness)
+ (if (null? (cdr strictness))
+ ;; one-argument special cases
+ (if (car strictness)
+ `(make-curried-fn-1-strict ,opt-fn)
+ `(make-curried-fn-1-nonstrict ,opt-fn))
+ ;; general case
+ `(make-curried-fn ,opt-fn ',strictness)))
+
+
+;;; Curry lambdas. Functions always return an unboxed value.
+
+(define-codegen flic-lambda (object)
+ (codegen-curried-fn
+ (codegen-lambda-aux object)
+ (map (lambda (x) (var-strict? x)) (flic-lambda-vars object))))
+
+(define (codegen-lambda-aux object)
+ (let* ((vars (flic-lambda-vars object))
+ (ignore '())
+ (args (codegen-lambda-list vars)))
+ (dolist (v vars)
+ (if (eqv? (var-referenced v) 0)
+ (push (fullname v) ignore)))
+ `(lambda ,args
+ ,@(if (not (null? ignore))
+ `((declare (ignore ,@ignore)))
+ '())
+ ,(do-codegen (flic-lambda-body object)))))
+
+
+;;; This is only for non-top-level lets.
+;;; The boxing of the value of each of the bindings is controlled by its
+;;; strict? property.
+
+(define-codegen flic-let (object)
+ (let ((bindings (flic-let-bindings object))
+ (body (flic-let-body object))
+ (recursive? (flic-let-recursive? object)))
+ (if recursive?
+ (codegen-letrec bindings body)
+ (codegen-let* bindings body))))
+
+
+;;; For efficiency reasons, we want to make all the function bindings
+;;; in the function namespace (some implementations do not do tail-recursion
+;;; or other optimizations correctly otherwise). This means we have
+;;; to sort out the variable bindings from the function bindings here.
+
+(define (codegen-letrec bindings body)
+ (let ((let-bindings '())
+ (labels-bindings '()))
+ (dolist (var bindings)
+ (let ((value (var-value var))
+ (fullname (fullname var))
+ (strict? (var-strict? var)))
+ (if (flic-lambda? value)
+ ;; Some functions may need only the optimized or standard
+ ;; entry points, but not both.
+ (let ((optname (optname var))
+ (lambda (codegen-lambda-aux value))
+ (optimized? (var-optimized-refs? var))
+ (standard? (var-standard-refs? var)))
+ (when standard?
+ (push (list fullname
+ (maybe-make-box-value
+ (codegen-curried-fn
+ (if optimized? `(function ,optname) lambda)
+ (var-strictness var))
+ strict?))
+ let-bindings))
+ (when optimized?
+ (push (cons optname (cdr lambda)) labels-bindings)))
+ (push (list fullname (do-codegen value)) let-bindings))))
+ (setf let-bindings (nreverse let-bindings))
+ (setf labels-bindings (nreverse labels-bindings))
+ (cond ((null? let-bindings)
+ `(labels ,labels-bindings ,(do-codegen body)))
+ ((null? labels-bindings)
+ `(letrec ,let-bindings ,(do-codegen body)))
+ (t
+ `(let ,(map (lambda (b) `(,(car b) '#f)) let-bindings)
+ (labels ,labels-bindings
+ ,@(map (lambda (b) `(setf ,@b)) let-bindings)
+ ,(do-codegen body))))
+ )))
+
+(define (codegen-let* bindings body)
+ (if (null? bindings)
+ (do-codegen body)
+ (let* ((var (car bindings))
+ (value (var-value var))
+ (fullname (fullname var))
+ (strict? (var-strict? var))
+ (body (codegen-let* (cdr bindings) body)))
+ (if (flic-lambda? value)
+ ;; Some functions may need only the optimized or standard
+ ;; entry points, but not both.
+ (let ((optname (optname var))
+ (lambda (codegen-lambda-aux value))
+ (optimized? (var-optimized-refs? var))
+ (standard? (var-standard-refs? var)))
+ (when standard?
+ (setf body
+ (add-let-binding
+ (list fullname
+ (maybe-make-box-value
+ (codegen-curried-fn
+ (if optimized? `(function ,optname) lambda)
+ (var-strictness var))
+ strict?))
+ body)))
+ (when optimized?
+ (setf body `(flet ((,optname ,@(cdr lambda))) ,body)))
+ body)
+ (add-let-binding (list fullname (do-codegen value)) body)))))
+
+(define (add-let-binding binding body)
+ (if (and (pair? body) (eq? (car body) 'let*))
+ `(let* (,binding ,@(cadr body)) ,@(cddr body))
+ `(let* (,binding) ,body)))
+
+
+(define-codegen flic-app (object)
+ (let ((fn (flic-app-fn object))
+ (args (flic-app-args object))
+ (saturated? (flic-app-saturated? object)))
+ (cond ((and saturated? (flic-pack? fn))
+ ;; Saturated call to constructor
+ (codegen-constructor-app-aux
+ (flic-pack-con fn)
+ (do-codegen-list args)))
+ ((and saturated? (flic-ref? fn))
+ ;; Saturated call to named function
+ (let* ((var (flic-ref-var fn))
+ (optname (optname var))
+ (argcode (do-codegen-list args)))
+ `(,optname ,@argcode)))
+ (else
+ ;; Have to make a curried call to standard entry point.
+ (let ((fncode (do-codegen fn))
+ (argcode (do-codegen-list args)))
+ (if (and (pair? fncode)
+ (eq? (car fncode) 'force))
+ `(funcall-force ,(cadr fncode) ,@argcode)
+ `(funcall ,fncode ,@argcode))))
+ )))
+
+(define (codegen-constructor-app-aux con argcode)
+ (let ((alg (con-alg con)))
+ (cond ((eq? con (core-symbol ":"))
+ `(cons ,@argcode))
+ ((algdata-implemented-by-lisp? alg)
+ (apply-maybe-lambda (cadr (con-lisp-fns con)) argcode))
+ ((algdata-tuple? alg)
+ `(make-tuple ,@argcode))
+ (else
+ `(make-tagged-data ,(con-tag con) ,@argcode)))))
+
+
+(define-codegen flic-ref (object)
+ (fullname (flic-ref-var object)))
+
+
+(define-codegen flic-const (object)
+ (let ((value (flic-const-value object)))
+ (cond ((string? value)
+ `(make-haskell-string ,value))
+ ((char? value)
+ ;; *** I think the parser ought to convert characters to their
+ ;; *** ASCII codes instead of doing it here. There are problems
+ ;; *** with valid Haskell characters that can't be represented
+ ;; *** portably as Lisp characters.
+ (char->integer value))
+ ((number? value)
+ value)
+ (else
+ ;; It must be a ratio. This is a bit of a hack - this depends on
+ ;; the fact that 2 tuples are represented in the same manner as
+ ;; rationals. Hacked for strict rationals - jcp
+ `(make-tuple ,(car value) ,(cadr value)))
+ )))
+
+
+;;; Returns a function or constant, so doesn't need to delay result.
+;;; See flic-app for handling of saturated constructor calls.
+
+(define-codegen flic-pack (object)
+ (let* ((con (flic-pack-con object))
+ (arity (con-arity con))
+ (alg (con-alg con))
+ (tuple? (algdata-tuple? alg))
+ (strictness (con-slot-strict? con))
+ (index (con-tag con)))
+ (cond ((eq? con (core-symbol "Nil"))
+ ''())
+ ((eq? con (core-symbol "True"))
+ ''#t)
+ ((eq? con (core-symbol "False"))
+ ''#f)
+ ((eq? con (core-symbol ":"))
+ '(function make-cons-constructor))
+ ((algdata-implemented-by-lisp? alg)
+ (let ((fn (cadr (con-lisp-fns con))))
+ (if (eqv? (con-arity con) 0)
+ fn
+ (codegen-curried-fn
+ (if (and (pair? fn) (eq? (car fn) 'lambda))
+ fn
+ `(function ,fn))
+ strictness))))
+ ((algdata-enum? alg)
+ ;; All constructors have 0 arity; represent them just
+ ;; by numbers.
+ index)
+ (tuple?
+ ;; Only a single constructor for this type.
+ (codegen-curried-fn
+ `(make-tuple-constructor ,arity)
+ strictness))
+ ((eqv? arity 0)
+ ;; No arguments to this constructor.
+ `(make-tagged-data ,index))
+ (else
+ ;; General case.
+ (codegen-curried-fn
+ `(make-tagged-data-constructor ,index ,arity)
+ strictness))
+ )))
+
+
+
+;;; These expressions translate directly into their Lisp equivalents.
+
+(define-codegen flic-case-block (object)
+ `(block ,(flic-case-block-block-name object)
+ ,@(do-codegen-list (flic-case-block-exps object))))
+
+(define-codegen flic-return-from (object)
+ `(return-from ,(flic-return-from-block-name object)
+ ,(do-codegen (flic-return-from-exp object))))
+
+(define-codegen flic-and (object)
+ `(and ,@(do-codegen-list (flic-and-exps object))))
+
+(define-codegen flic-if (object)
+ `(if ,(do-codegen (flic-if-test-exp object))
+ ,(do-codegen (flic-if-then-exp object))
+ ,(do-codegen (flic-if-else-exp object))))
+
+(define-codegen flic-sel (object)
+ (codegen-flic-sel-aux
+ (flic-sel-con object)
+ (flic-sel-i object)
+ (do-codegen (flic-sel-exp object))))
+
+(define (codegen-flic-sel-aux con index exp)
+ (let* ((alg (con-alg con))
+ (tuple? (algdata-tuple? alg))
+ (arity (con-arity con)))
+ (cond ((eq? con (core-symbol ":"))
+ (if (eqv? index 0)
+ `(car ,exp)
+ `(cdr ,exp)))
+ ((algdata-implemented-by-lisp? alg)
+ (apply-maybe-lambda (list-ref (cddr (con-lisp-fns con)) index)
+ (list exp)))
+ (tuple?
+ `(tuple-select ,arity ,index ,exp))
+ (else
+ `(tagged-data-select ,arity ,index ,exp))
+ )))
+
+(define-codegen flic-is-constructor (object)
+ (codegen-flic-is-constructor-aux
+ (flic-is-constructor-con object)
+ (do-codegen (flic-is-constructor-exp object))))
+
+(define (codegen-flic-is-constructor-aux con exp)
+ (let ((type (con-alg con)))
+ (cond ((eq? type (core-symbol "Bool"))
+ (if (eq? con (core-symbol "True"))
+ exp
+ `(not ,exp)))
+ ((eq? type (core-symbol "List"))
+ (if (eq? con (core-symbol ":"))
+ `(pair? ,exp)
+ `(null? ,exp)))
+ ((algdata-implemented-by-lisp? type)
+ (let ((fn (car (con-lisp-fns con))))
+ (apply-maybe-lambda fn (list exp))))
+ ((algdata-tuple? type)
+ ;; This should never happen.
+ ''#t)
+ ((algdata-enum? type)
+ `(eqv? (the fixnum ,exp) (the fixnum ,(con-tag con))))
+ (else
+ `(eqv? (the fixnum (constructor-number ,exp))
+ (the fixnum ,(con-tag con))))
+ )))
+
+
+(define-codegen flic-con-number (object)
+ (let ((type (flic-con-number-type object))
+ (exp (do-codegen (flic-con-number-exp object))))
+ `(the fixnum
+ ,(cond ((eq? type (core-symbol "Bool"))
+ `(if ,exp 1 0))
+ ((eq? type (core-symbol "List"))
+ `(if (pair? ,exp) 0 1))
+ ((algdata-tuple? type)
+ ;; This should never happen.
+ 0)
+ ((algdata-implemented-by-lisp? type)
+ (let ((var (gensym)))
+ `(let ((,var ,exp))
+ (cond ,@(map (lambda (con)
+ `(,(apply-maybe-lambda
+ (car (con-lisp-fns con))
+ (list var))
+ ',(con-tag con)))
+ (algdata-constrs type))
+ (else (error "No constructor satisfies ~A.~%"
+ ',(def-name type)))))))
+ ((algdata-enum? type)
+ exp)
+ (else
+ `(constructor-number ,exp))
+ ))
+ ))
+
+
+
+;;;======================================================================
+;;; Utility functions
+;;;======================================================================
+
+;;; Here are some helper functions for handing boxing and unboxing
+;;; of values.
+;;; maybe-make-box-delay is used to box forms that are "expensive" to
+;;; compute; maybe-make-box-value is used to box forms like constants
+;;; or functions that are "cheap" to compute eagerly.
+;;; Maybe-unbox is used to unbox a form that returns a boxed result.
+
+(define (maybe-make-box-delay form unboxed?)
+ (if unboxed?
+ form
+ `(delay ,form)))
+
+(define (maybe-make-box-value form unboxed?)
+ (if unboxed?
+ form
+ `(box ,form)))
+
+(define (maybe-unbox form unboxed?)
+ (if unboxed?
+ `(force ,form)
+ form))
+
+
+;;; These two var slots are filled in lazily by the code generator,
+;;; since most vars generated don't need them. You should always
+;;; use these functions instead of accessing the structure slot
+;;; directly.
+
+(define (fullname var)
+ (or (var-fullname var)
+ (setf (var-fullname var)
+ (if (var-toplevel? var)
+ ;; For toplevel names, use module name glued onto base names.
+ ;; These are always interned symbols.
+ (if (def-core? var)
+ (symbol-append '|*Core:| (def-name var))
+ (symbol-append (def-module var) '\: (def-name var)))
+ ;; Otherwise, make sure we have a gensym.
+ ;; The uniquification of interned symbols is required
+ ;; because there may be multiple nested bindings of the
+ ;; same name, and we want to be able to distinguish between
+ ;; the different bindings.
+ (let ((name (def-name var)))
+ (if (gensym? name)
+ name
+ (gensym (symbol->string name))))))
+ ))
+
+(define (optname var)
+ (or (var-optimized-entry var)
+ (let ((name (string-append (symbol->string (fullname var)) "/OPT")))
+ (setf (var-optimized-entry var)
+ (if (var-toplevel? var)
+ (string->symbol name)
+ (gensym name))))))
+
+
+
+;;;======================================================================
+;;; Exported functions
+;;;======================================================================
+
+;;; This handles types exported to lisp from Haskell
+;;; *** Is this really supposed to create variable bindings as
+;;; *** opposed to function bindings???
+;;; *** I assume all of these functions want strict arguments and return
+;;; *** strict results, even if the data structures contain boxed values.
+
+(define (codegen-exported-types mods)
+ (let ((defs '()))
+ (dolist (m mods)
+ (dolist (a (module-alg-defs m))
+ (when (algdata-export-to-lisp? a)
+ (dolist (c (algdata-constrs a))
+ (setf defs (nconc (codegen-constr c) defs))))))
+ `(begin ,@defs)))
+
+(define (codegen-constr c)
+ (let ((lisp-fns (con-lisp-fns c)))
+ (if c
+ (let ((res
+ `(,(codegen-lisp-predicate (car lisp-fns) c)
+ ,(codegen-lisp-constructor (cadr lisp-fns) c)
+ ,@(codegen-lisp-accessors
+ (cddr lisp-fns) (con-slot-strict? c) c 0))))
+ (when (memq 'codegen (dynamic *printers*))
+ (dolist (d res)
+ (pprint* d)))
+ res)
+ '())))
+
+(define (codegen-lisp-predicate name c)
+ `(define (,name x)
+ ,(codegen-flic-is-constructor-aux c 'x)))
+
+(define (codegen-lisp-constructor name c)
+ (let ((strictness (con-slot-strict? c))
+ (args '())
+ (exps '()))
+ (dolist (s strictness)
+ (let ((arg (gensym)))
+ (push arg args)
+ (push (if s arg `(box ,arg)) exps)))
+ `(define (,name ,@(nreverse args))
+ ,(codegen-constructor-app-aux c (nreverse exps)))))
+
+(define (codegen-lisp-accessors names strictness c i)
+ (declare (type fixnum i))
+ (if (null? names)
+ '()
+ (let ((body (codegen-flic-sel-aux c i 'x)))
+ (when (not (car strictness))
+ (setf body `(force ,body)))
+ (cons `(define (,(car names) x) ,body)
+ (codegen-lisp-accessors (cdr names) (cdr strictness) c (+ i 1))))
+ ))
+
+
+
+;;; This is a special hack needed due to brain-dead common lisp problems.
+;;; This allows the user to place lambda defined functions in ImportLispType
+;;; *** I'm not convinced this is necessary; ((lambda ...) args)
+;;; *** is perfectly valid Common Lisp syntax!
+
+(define (apply-maybe-lambda fn args)
+ (if (and (pair? fn)
+ (eq? (car fn) 'lambda))
+ `(funcall ,fn ,@args)
+ `(,fn ,@args)))