home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-27 | 77.3 KB | 2,081 lines | [TEXT/CCL2] |
- ;;; optimize.scm -- flic optimizer
- ;;;
- ;;; author : Sandra Loosemore
- ;;; date : 7 May 1992
- ;;;
- ;;;
- ;;; The optimizer does these kinds of program transformations:
- ;;;
- ;;; * remove unreferenced variable bindings.
- ;;;
- ;;; * constant folding and various other kinds of compile-time
- ;;; evaluation.
- ;;;
- ;;; * beta reduction (replace references to variables bound to simple
- ;;; expressions with the expression)
- ;;;
-
-
- ;;; Since some of the optimizations can make additional transformations
- ;;; possible, we want to make multiple iteration passes. But since each
- ;;; pass is likely to have diminishing benefits, we don't want to keep
- ;;; iterating indefinitely. So establish a fairly arbitrary cutoff point.
- ;;; The value is based on empirical results from compiling the prelude.
-
- (define *max-optimize-iterations* 5)
- (define *optimize-foldr-iteration* 0) ; when to inline foldr
- (define *optimize-build-iteration* 0) ; when to inline build
- (define *current-optimize-iteration* 0)
-
-
- ;;; Flags for enabling various optimizations
-
- (define *all-optimizers* '(foldr inline constant lisp delays))
- (define *optimizers* *all-optimizers*)
- (define *compiled-code-optimizers* *all-optimizers*)
- (define *interpreted-code-optimizers* '())
-
-
- ;;; Used to note whether we are doing the various optimizations
-
- (define-local-syntax (do-optimization? o)
- `(memq ,o (dynamic *optimizers*)))
-
- (define *do-foldr-optimizations* (do-optimization? 'foldr))
- (define *do-inline-optimizations* (do-optimization? 'inline))
- (define *do-constant-optimizations* (do-optimization? 'constant))
-
-
- ;;; If the foldr optimization is enabled, bind the corresponding
- ;;; variables to these values instead of the defaults.
-
- (define *foldr-max-optimize-iterations* 15)
- (define *foldr-optimize-foldr-iteration* 8)
- (define *foldr-optimize-build-iteration* 5)
-
-
- ;;; Some random other variables
-
- (define *structured-constants* '())
- (define *structured-constants-table* '#f)
- (define *lambda-depth* 0)
- (define *local-bindings* '())
-
-
- ;;; This is for doing some crude profiling.
- ;;; Comment out the body of the macro to disable profiling.
-
- ;;; Here are current counts from compiling the prelude:
- ;;; (LET-REMOVE-UNUSED-BINDING . 7709)
- ;;; (REF-INLINE . 5532)
- ;;; (REF-INLINE-SINGLE-REF . 4736)
- ;;; (LET-EMPTY-BINDINGS . 4489)
- ;;; (APP-LAMBDA-TO-LET . 2712)
- ;;; (APP-HOIST-STRUCTURED-CONSTANT . 1371)
- ;;; (AND-UNARY . 1070)
- ;;; (CASE-BLOCK-DEAD-CODE . 625)
- ;;; (SEL-FOLD-VAR . 608)
- ;;; (CASE-BLOCK-IDENTITY . 543)
- ;;; (APP-MAKE-SATURATED . 528)
- ;;; (LET-HOIST-RETURN-FROM . 505)
- ;;; (APP-HOIST-LET . 447)
- ;;; (AND-CONTAINS-TRUE . 412)
- ;;; (APP-FOLD-SELECTOR . 385)
- ;;; (CASE-BLOCK-TO-IF . 366)
- ;;; (AND-HOIST-STRICT2 . 349)
- ;;; (INTEGER-TO-INT-CONSTANT-FOLD . 349)
- ;;; (IS-CONSTRUCTOR-FOLD-TUPLE . 332)
- ;;; (CASE-BLOCK-HOIST-STRICT2 . 289)
- ;;; (LAMBDA-COMPRESS . 255)
- ;;; (FOLDR-INLINE . 212)
- ;;; (AND-COMPRESS . 193)
- ;;; (BUILD-INLINE-LAMBDA . 193)
- ;;; (IF-FOLD . 182)
- ;;; (LET-HOIST-LAMBDA . 164)
- ;;; (APP-COMPRESS . 156)
- ;;; (STRICT2-SEL-IDENTITY . 111)
- ;;; (LET-COMPRESS . 106)
- ;;; (IF-COMPRESS-TEST . 104)
- ;;; (IF-HOIST-LAMBDA . 100)
- ;;; (CASE-BLOCK-DISCARD-REDUNDANT-TEST . 99)
- ;;; (FOLDR-BUILD-IDENTITY . 95)
- ;;; (FOLDR-CONS-IDENTITY . 87)
- ;;; (FOLDR-PRIM-APPEND-INLINE . 83)
- ;;; (IF-HOIST-RETURN-FROM . 66)
- ;;; (FOLDR-NIL-IDENTITY . 48)
- ;;; (STRICT2-HOIST-RETURN-FROM . 40)
- ;;; (FOLDR-HOIST-LET . 35)
- ;;; (LET-HOIST-INVARIANT-ARGS . 33)
- ;;; (IF-IDENTITY-INVERSE . 27)
- ;;; (FOLDR-CONS-NIL-IDENTITY . 23)
- ;;; (CON-NUMBER-FOLD-TUPLE . 21)
- ;;; (CASE-BLOCK-HOIST-LET . 21)
- ;;; (INTEGER-TO-INT-IDENTITY . 13)
- ;;; (APP-PACK-IDENTITY . 8)
- ;;; (APP-SPECIALIZE . 6)
- ;;; (IF-IDENTITY . 4)
- ;;; (CON-NUMBER-FOLD . 2)
- ;;; (LET-HOIST-STRUCTURED-CONSTANT . 2)
- ;;; (INT-TO-INTEGER-CONSTANT-FOLD . 1)
-
-
- (define-local-syntax (record-hack type . args)
- (declare (ignore args))
- `',type
- ; `(record-hack-aux ,type ,@args)
- )
-
- (define *hacks-done* '())
-
- (define (record-hack-aux type . args)
- ;; *** debug
- ;; (format '#t "~s ~s~%" type args)
- (declare (ignore args))
- (let ((stuff (assq type (car (dynamic *hacks-done*)))))
- (if stuff
- (incf (cdr stuff))
- (push (cons type 1) (car (dynamic *hacks-done*))))))
-
- (define (total-hacks)
- (let ((totals '()))
- (dolist (alist *hacks-done*)
- (dolist (entry alist)
- (let ((stuff (assq (car entry) totals)))
- (if stuff
- (setf (cdr stuff) (+ (cdr stuff) (cdr entry)))
- (push (cons (car entry) (cdr entry)) totals)))))
- totals))
-
-
- ;;; This is the main entry point.
-
- (define (optimize-top object)
- (if (flic-void? object)
- object
- (begin
- (initialize-magic-optimizers)
- (dynamic-let ((*structured-constants* '())
- (*structured-constants-table* (make-table))
- (*lambda-depth* 0)
- (*local-bindings* '())
- (*do-inline-optimizations*
- (do-optimization? 'inline))
- (*do-constant-optimizations*
- (do-optimization? 'constant))
- (*max-optimize-iterations*
- (if (do-optimization? 'foldr)
- (dynamic *foldr-max-optimize-iterations*)
- (dynamic *max-optimize-iterations*)))
- (*optimize-foldr-iteration*
- (if (do-optimization? 'foldr)
- (dynamic *foldr-optimize-foldr-iteration*)
- (dynamic *optimize-foldr-iteration*)))
- (*optimize-build-iteration*
- (if (do-optimization? 'foldr)
- (dynamic *foldr-optimize-build-iteration*)
- (dynamic *optimize-build-iteration*))))
- (setf *hacks-done* '())
- (dotimes (i (dynamic *max-optimize-iterations*))
- (dynamic-let ((*current-optimize-iteration* i))
- (when (memq 'optimize-extra (dynamic *printers*))
- (format '#t "~%Optimize pass ~s:" i)
- (pprint object))
- (push '() *hacks-done*)
- (setf object (optimize-flic-let-aux object '#t))))
- (setf (flic-let-bindings object)
- (nconc (nreverse (dynamic *structured-constants*))
- (flic-let-bindings object))))
- (install-uninterned-globals (flic-let-bindings object))
- (postoptimize object)
- object)))
-
- (define-flic-walker optimize (object))
-
- ;;; debugging stuff
- ;;;
- ;;; (define *duplicate-object-table* (make-table))
- ;;;
- ;;; (define (new-optimize object)
- ;;; (if (table-entry (dynamic *duplicate-object-table*) object)
- ;;; (error "Duplicate object ~s detected." object)
- ;;; (begin
- ;;; (setf (table-entry (dynamic *duplicate-object-table*) object) '#t)
- ;;; (old-optimize object))))
- ;;;
- ;;; (lisp:setf (lisp:symbol-function 'old-optimize)
- ;;; (lisp:symbol-function 'optimize))
- ;;; (lisp:setf (lisp:symbol-function 'optimize)
- ;;; (lisp:symbol-function 'new-optimize))
-
- (define (optimize-list objects)
- (optimize-list-aux objects)
- objects)
-
- (define (optimize-list-aux objects)
- (if (null? objects)
- '()
- (begin
- (setf (car objects) (optimize (car objects)))
- (optimize-list-aux (cdr objects)))))
-
-
- ;;; Compress nested lambdas. This hack is desirable because saturating
- ;;; applications within the lambda body effectively adds additional
- ;;; parameters to the function.
-
- ;;; *** Maybe this should look for hoistable constant lambdas too.
-
- (define-optimize flic-lambda (object)
- (let ((vars (flic-lambda-vars object)))
- (dynamic-let ((*lambda-depth* (1+ (dynamic *lambda-depth*)))
- (*local-bindings* (cons vars (dynamic *local-bindings*))))
- (dolist (var vars)
- (setf (var-referenced var) 0))
- (let ((new-body (optimize (flic-lambda-body object))))
- (setf (flic-lambda-body object) new-body)
- (cond ((is-type? 'flic-lambda new-body)
- (record-hack 'lambda-compress)
- (setf (flic-lambda-vars object)
- (nconc (flic-lambda-vars object)
- (flic-lambda-vars new-body)))
- (setf (flic-lambda-body object) (flic-lambda-body new-body)))
- ((and (is-strict2-app? new-body)
- (is-type? 'flic-lambda (strict2-app-arg2 new-body)))
- (record-hack 'lambda-compress-strict2)
- (let ((inner (strict2-app-arg2 new-body)))
- (setf (flic-lambda-vars object)
- (nconc (flic-lambda-vars object)
- (flic-lambda-vars inner)))
- (setf (flic-lambda-body object)
- (make-strict2-app
- (strict2-app-arg1 new-body)
- (flic-lambda-body inner)))))
- (else
- '#f))
- object))))
-
-
- ;;; For let, first mark all variables as unused and check for "simple"
- ;;; binding values that permit beta reduction. Then walk the subexpressions.
- ;;; Finally discard any bindings that are still marked as unused.
- ;;; *** This fails to detect unused recursive variables.
-
- (define-optimize flic-let (object)
- (optimize-flic-let-aux object '#f))
-
- (define (optimize-flic-let-aux object toplevel?)
- (let ((bindings (flic-let-bindings object))
- (recursive? (flic-let-recursive? object)))
- ;; *** This handling of *local-bindings* isn't quite right since
- ;; *** it doesn't account for the sequential nature of bindings
- ;; *** in a non-recursive let, but it's close enough. We won't
- ;; *** get any semantic errors, but it might miss a few optimizations.
- (dynamic-let ((*local-bindings*
- (if (and recursive? (not toplevel?))
- (cons bindings (dynamic *local-bindings*))
- (dynamic *local-bindings*))))
- (optimize-flic-let-bindings bindings recursive? toplevel?)
- (dynamic-let ((*local-bindings*
- (if (and (not recursive?) (not toplevel?))
- (cons bindings (dynamic *local-bindings*))
- (dynamic *local-bindings*))))
- (setf (flic-let-body object) (optimize (flic-let-body object))))
- ;; Check for unused bindings and other rewrites.
- ;; Only do this for non-toplevel lets.
- (if toplevel?
- object
- (optimize-flic-let-rewrite object bindings recursive?)))))
-
- (define (optimize-flic-let-bindings bindings recursive? toplevel?)
- ;; Initialize
- (dolist (var bindings)
- (setf (var-referenced var) 0)
- (setf (var-fn-referenced var) 0)
- (when (is-type? 'flic-lambda (var-value var))
- (dolist (v (flic-lambda-vars (var-value var)))
- (setf (var-arg-invariant? v) '#t)
- (setf (var-arg-invariant-value v) '#f))))
- ;; Traverse value subforms
- (do ((bindings bindings (cdr bindings)))
- ((null? bindings) '#f)
- (let* ((var (car bindings))
- (val (var-value var)))
- (if (and (is-type? 'flic-app val)
- (dynamic *do-constant-optimizations*)
- (let ((fn (flic-app-fn val))
- (args (flic-app-args val)))
- (if recursive?
- (structured-constant-app-recursive?
- fn args bindings (list var))
- (structured-constant-app? fn args))))
- ;; Variable is bound to a structured constant. If this
- ;; isn't already a top-level binding, replace the value
- ;; of the constant with a reference to a top-level variable
- ;; that is in turn bound to the constant expression.
- ;; binding to top-level if this is a new constant.
- ;; *** Maybe we should also look for variables bound
- ;; *** to lambdas, that can also be hoisted to top level.
- (when (not toplevel?)
- (multiple-value-bind (con args cvar)
- (enter-structured-constant-aux val '#t)
- (record-hack 'let-hoist-structured-constant)
- (if cvar
- (setf (var-value var) (make-flic-ref cvar))
- (add-new-structured-constant var con args))))
- (begin
- ;; If this is a function that's a candidate for foldr/build
- ;; optimization, stash the value away prior to
- ;; inlining the calls.
- ;; *** We might try to automagically detect functions
- ;; *** that are candidates for these optimizations here,
- ;; *** but have to watch out for infinite loops!
- (when (and (var-inline? var)
- (eqv? (the fixnum
- (dynamic *current-optimize-iteration*))
- (the fixnum
- (dynamic *optimize-build-iteration*)))
- (is-type? 'flic-lambda val)
- (or (is-foldr-or-build-app? (flic-lambda-body val))))
- (setf (var-inline-value var) (copy-flic-top val)))
- ;; Then walk value normally.
- (let ((new-val (optimize val)))
- (setf (var-value var) new-val)
- (setf (var-simple? var)
- (or (var-inline? var)
- (and (not (var-selector-fn? var))
- (can-inline?
- new-val
- (if recursive? bindings '())
- toplevel?))))))
- ))))
-
-
- (define (is-foldr-or-build-app? exp)
- (typecase exp
- (flic-app
- (let ((fn (flic-app-fn exp)))
- (and (is-type? 'flic-ref fn)
- (or (eq? (flic-ref-var fn) (core-symbol "foldr"))
- (eq? (flic-ref-var fn) (core-symbol "build"))))))
- (flic-let
- (is-foldr-or-build-app? (flic-let-body exp)))
- (flic-ref
- (let ((val (var-value (flic-ref-var exp))))
- (and val (is-foldr-or-build-app? val))))
- (else
- '#f)))
-
-
- (define (optimize-flic-let-rewrite object bindings recursive?)
- ;; Delete unused variables from the list.
- (setf bindings
- (list-delete-if
- (lambda (var)
- (cond ((var-toplevel? var)
- ;; This was a structured constant hoisted to top-level.
- '#t)
- ((eqv? (the fixnum (var-referenced var)) (the fixnum 0))
- (record-hack 'let-remove-unused-binding var)
- '#t)
- ((eqv? (the fixnum (var-referenced var)) (the fixnum 1))
- (setf (var-single-ref var) (dynamic *lambda-depth*))
- '#f)
- (else
- (setf (var-single-ref var) '#f)
- '#f)))
- bindings))
- ;; Add extra bindings for reducing functions with invariant
- ;; arguments. Hopefully some of the extra bindings will go
- ;; away in future passes!
- (setf (flic-let-bindings object)
- (setf bindings (add-stuff-for-invariants bindings)))
- ;; Look for other special cases.
- (cond ((null? bindings)
- ;; Simplifying the expression by getting rid of the LET may
- ;; make it possible to do additional optimizations on the
- ;; next pass.
- (record-hack 'let-empty-bindings)
- (flic-let-body object))
- ((is-type? 'flic-return-from (flic-let-body object))
- ;; Hoist return-from outside of LET. This may permit
- ;; further optimizations by an enclosing case-block.
- (record-hack 'let-hoist-return-from)
- (let* ((body (flic-let-body object))
- (inner-body (flic-return-from-exp body)))
- (setf (flic-return-from-exp body) object)
- (setf (flic-let-body object) inner-body)
- body))
- ((and (not recursive?)
- (is-type? 'flic-let (flic-let-body object))
- (not (flic-let-recursive? (flic-let-body object))))
- ;; This is purely to produce more compact code.
- (record-hack 'let-compress)
- (let ((body (flic-let-body object)))
- (setf (flic-let-bindings object)
- (nconc bindings (flic-let-bindings body)))
- (setf (flic-let-body object) (flic-let-body body))
- object))
- ((is-type? 'flic-lambda (flic-let-body object))
- ;; Hoist lambda outside of LET. This may permit
- ;; merging of nested lambdas on a future pass.
- (record-hack 'let-hoist-lambda)
- (let* ((body (flic-let-body object))
- (inner-body (flic-lambda-body body)))
- (setf (flic-lambda-body body) object)
- (setf (flic-let-body object) inner-body)
- body))
- (else
- object))
- )
-
- ;;; Look for constant-folding and structured constants here.
-
- (define-optimize flic-app (object)
- (optimize-flic-app-aux object))
-
- (define (optimize-flic-app-aux object)
- (let ((new-fn (optimize (flic-app-fn object)))
- (new-args (optimize-list (flic-app-args object))))
- (typecase new-fn
- (flic-ref
- ;; The function is a variable.
- (let* ((var (flic-ref-var new-fn))
- (val (var-value var))
- (n (length new-args))
- (arity (guess-function-arity var))
- (magic '#f))
- (cond ((and (setf magic (var-specializers var))
- (or (eqv? (dynamic *current-optimize-iteration*) 0)
- (var-toplevel? var)))
- ;; Try to replace call to generic function with call to
- ;; specialized version.
- ;; For locally defined functions, do this only on the initial
- ;; pass through the optimizer, because otherwise we may end up
- ;; generating calls to other local functions that have been
- ;; optimized away on a previous pass.
- (multiple-value-bind (fn args)
- (try-to-specialize new-fn new-args magic)
- (setf new-fn fn)
- (setf new-args args)))
- ((and arity (< (the fixnum n) (the fixnum arity)))
- ;; This is a first-class call that is not fully saturated.
- ;; Make it saturated by wrapping a lambda around it.
- (setf new-fn
- (do-app-make-saturated object new-fn new-args arity n))
- (setf new-args '()))
- ((var-selector-fn? var)
- ;; This is a saturated call to a selector. We might
- ;; be able to inline the call.
- (multiple-value-bind (fn args)
- (try-to-fold-selector var new-fn new-args)
- (setf new-fn fn)
- (setf new-args args)))
- ((and (not (var-toplevel? var))
- (is-type? 'flic-lambda val))
- ;; This is a saturated call to a local function.
- ;; Increment its reference count and note if any of
- ;; the arguments are invariant.
- (incf (var-fn-referenced var))
- (note-invariant-args new-args (flic-lambda-vars val)))
- ((setf magic (magic-optimize-function var))
- ;; Do special-purpose constant-folding, etc.
- (multiple-value-bind (fn args)
- (funcall magic new-fn new-args)
- (setf new-fn fn)
- (setf new-args args)))
- )))
- (flic-lambda
- ;; Turn application of lambda into a let.
- (multiple-value-bind (fn args)
- (do-lambda-to-let-aux new-fn new-args)
- (setf new-fn fn)
- (setf new-args args)))
- (flic-pack
- (let ((con (flic-pack-con new-fn))
- (temp '#f))
- (when (eqv? (length new-args) (con-arity con))
- (cond ((and (dynamic *do-constant-optimizations*)
- (every-1 (function structured-constant?) new-args))
- ;; This is a structured constant that
- ;; can be replaced with a top-level binding.
- (setf (flic-app-fn object) new-fn)
- (setf (flic-app-args object) new-args)
- (record-hack 'app-hoist-structured-constant object)
- (setf new-fn (enter-structured-constant object '#t))
- (setf new-args '()))
- ((and (setf temp (is-selector? con 0 (car new-args)))
- (is-selector-list? con 1 temp (cdr new-args)))
- ;; This is an expression like (cons (car x) (cdr x)).
- ;; Replace it with just plain x to avoid reconsing.
- (record-hack 'app-pack-identity new-fn)
- (setf new-fn (copy-flic-top temp))
- (setf new-args '()))
- ))))
- (flic-let
- ;; Hoist let to surround entire application.
- ;; Simplifying the function being applied may permit further
- ;; optimizations on next pass.
- ;; (We might try to hoist lets in the argument expressions, too,
- ;; but I don't think that would lead to any real simplification
- ;; of the code.)
- (record-hack 'app-hoist-let)
- (setf (flic-app-fn object) (flic-let-body new-fn))
- (setf (flic-app-args object) new-args)
- (setf new-args '())
- (setf (flic-let-body new-fn) object)
- )
- (flic-app
- ;; Try to compress nested applications.
- ;; This may make the call saturated and permit further optimizations
- ;; on the next pass.
- (record-hack 'app-compress)
- (setf new-args (nconc (flic-app-args new-fn) new-args))
- (setf new-fn (flic-app-fn new-fn)))
- )
- (if (null? new-args)
- new-fn
- (begin
- (setf (flic-app-fn object) new-fn)
- (setf (flic-app-args object) new-args)
- object))
- ))
-
- (define (guess-function-arity var)
- (or (let ((value (var-value var)))
- (and value
- (is-type? 'flic-lambda value)
- (length (flic-lambda-vars value))))
- (var-arity var)))
-
- (define (do-app-make-saturated app fn args arity nargs)
- (declare (type fixnum arity nargs))
- (record-hack 'app-make-saturated fn args)
- (let ((newvars '())
- (newargs '()))
- (dotimes (i (- arity nargs))
- (declare (type fixnum i))
- (let ((v (init-flic-var (create-temp-var 'arg) '#f '#f)))
- (push v newvars)
- (push (make-flic-ref v) newargs)))
- (setf (flic-app-fn app) fn)
- (setf (flic-app-args app) (nconc args newargs))
- (make-flic-lambda newvars app)))
-
-
-
- ;;; If the function is a selector applied to a literal dictionary,
- ;;; inline it.
-
- (define (try-to-fold-selector var new-fn new-args)
- (let ((exp (car new-args)))
- (if (or (and (is-type? 'flic-ref exp)
- ;; *** should check that var is top-level?
- (is-bound-to-constructor-app? (flic-ref-var exp)))
- (and (is-type? 'flic-app exp)
- (is-constructor-app-prim? exp)))
- (begin
- (record-hack 'app-fold-selector)
- (setf new-fn (copy-flic-top (var-value var)))
- (do-lambda-to-let-aux new-fn new-args))
- (values new-fn new-args))))
-
-
-
- ;;; Try to pattern match to do generic-to-specific specialization.
- ;;; The specializers are an a-list of (special-fn . lambda) pairs.
- ;;; Choose the first one that matches.
-
-
- (define (try-to-specialize fn args specializers)
- (if (null? specializers)
- (values fn args)
- (let* ((s (car specializers))
- (var (car s))
- (lambda (cdr s)))
- (if (null? lambda)
- ;; Ignore anything with a null pattern.
- (try-to-specialize fn args (cdr specializers))
- ;; Normal case.
- (let* ((vars (flic-lambda-vars lambda))
- (app (flic-lambda-body lambda))
- (pat (flic-app-args app)))
- (multiple-value-bind (new-fn new-args)
- (try-to-specialize-1 var args vars pat '())
- (if new-fn
- (values new-fn new-args)
- (try-to-specialize fn args (cdr specializers)))))
- ))))
-
-
- (define (try-to-specialize-1 new-fn-var actual-args vars pattern-args alist)
- (let ((arg '#f)
- (var '#f))
- (cond ((null? pattern-args)
- ;; Match successful!
- (record-hack 'app-specialize new-fn-var)
- (incf (var-referenced new-fn-var))
- (values
- (make-flic-ref new-fn-var)
- (nconc
- (map (lambda (v)
- (let ((stuff (assq v alist)))
- (if stuff
- (copy-flic-top (cdr stuff))
- (error "Bad specializer for ~s!" new-fn-var))))
- vars)
- actual-args)))
- ((null? actual-args)
- ;; Match failed; call is not saturated.
- (values '#f '()))
- ((and (flic-ref? (setf arg (car pattern-args)))
- (memq (setf var (flic-ref-var arg)) vars))
- ;; This is one of the variables in the pattern. Make sure
- ;; we don't have it bound to two different things, then
- ;; go on to match the next argument in the pattern.
- (let ((match (assq var alist)))
- (cond ((not match)
- (try-to-specialize-1
- new-fn-var
- (cdr actual-args)
- vars
- (cdr pattern-args)
- (cons (cons var (car actual-args)) alist)))
- ((flic-exp-eq? (cdr match) (car actual-args))
- (try-to-specialize-1
- new-fn-var
- (cdr actual-args)
- vars
- (cdr pattern-args)
- alist))
- (else
- (values '#f '())))))
- ((flic-exp-eq? arg (car actual-args))
- ;; The actual argument matches the literal pattern exactly.
- (try-to-specialize-1
- new-fn-var
- (cdr actual-args)
- vars
- (cdr pattern-args)
- alist))
- (else
- ;; Match failed; actual arguments don't match pattern.
- (values '#f '()))
- )))
-
-
- ;;; Various primitive functions have special optimizer functions
- ;;; associated with them, that do constant folding and certain
- ;;; other identities. The optimizer function is called with the
- ;;; function expression and list of argument expressions (at least
- ;;; as many arguments as the arity of the function) and should return
- ;;; the two values.
- ;;; This table has to be initialized in this weird way because the
- ;;; core-symbols aren't defined when this file is loaded.
-
- (define *magic-optimizer-table* '#f)
-
- (define (magic-optimize-function v)
- (table-entry *magic-optimizer-table* v))
-
- (define-syntax (set-magic-optimizer name function-name)
- `(setf (table-entry *magic-optimizer-table* (core-symbol ,name))
- (function ,function-name)))
-
- (define (initialize-magic-optimizers)
- (when (not *magic-optimizer-table*)
- (setf *magic-optimizer-table* (make-table))
- (set-magic-optimizer "foldr" optimize-foldr-aux)
- (set-magic-optimizer "build" optimize-build)
- (set-magic-optimizer "primIntegerToInt" optimize-integer-to-int)
- (set-magic-optimizer "primIntToInteger" optimize-int-to-integer)
- (set-magic-optimizer "primRationalToFloat" optimize-rational-to-float)
- (set-magic-optimizer "primRationalToDouble" optimize-rational-to-double)
- (set-magic-optimizer "primNegInt" optimize-neg)
- (set-magic-optimizer "primNegInteger" optimize-neg)
- (set-magic-optimizer "primNegFloat" optimize-neg)
- (set-magic-optimizer "primNegDouble" optimize-neg)
- (set-magic-optimizer "strict2" optimize-strict2)
- (set-magic-optimizer ">>=" optimize-thenio)
- (set-magic-optimizer ">>" optimize-thenio)
- (set-magic-optimizer "applyIO" optimize-applyio)
- ))
-
-
- ;;; Foldr identities for deforestation
-
- (define (optimize-foldr fn args)
- (multiple-value-bind (fn args)
- (optimize-foldr-aux fn args)
- (maybe-make-app fn args)))
-
- (define (optimize-foldr-aux fn args)
- (let ((k (car args))
- (z (cadr args))
- (l (caddr args))
- (tail (cdddr args)))
- (cond ((and (is-type? 'flic-pack k)
- (eq? (flic-pack-con k) (core-symbol ":"))
- (is-type? 'flic-pack z)
- (eq? (flic-pack-con z) (core-symbol "Nil")))
- ;; foldr (:) [] l ==> l
- ;; (We arrange for build to be inlined before foldr
- ;; so that this pattern can be detected.)
- (record-hack 'foldr-cons-nil-identity)
- (values l tail))
- ((and (is-type? 'flic-app l)
- (is-type? 'flic-ref (flic-app-fn l))
- (eq? (flic-ref-var (flic-app-fn l))
- (core-symbol "build"))
- (null? (cdr (flic-app-args l))))
- ;; foldr k z (build g) ==> g k z
- (record-hack 'foldr-build-identity)
- (values
- (car (flic-app-args l))
- (cons k (cons z tail))))
- ((and (is-type? 'flic-pack l)
- (eq? (flic-pack-con l) (core-symbol "Nil")))
- ;; foldr k z [] ==> z
- (record-hack 'foldr-nil-identity)
- (values z tail))
- ((short-string-constant? l)
- ;; If the list argument is a string constant, expand it inline.
- ;; Only do this if the string is fairly short, though.
- (optimize-foldr-aux
- fn
- (cons k (cons z (cons (expand-string-constant l) tail)))))
- ((and (is-type? 'flic-app l)
- (is-type? 'flic-pack (flic-app-fn l))
- (eq? (flic-pack-con (flic-app-fn l)) (core-symbol ":"))
- (eqv? (length (flic-app-args l)) 2))
- ;; foldr k z x:xs ==> let c = k in c x (foldr c z xs)
- (record-hack 'foldr-cons-identity)
- (let ((x (car (flic-app-args l)))
- (xs (cadr (flic-app-args l))))
- (values
- (if (can-inline? k '() '#f)
- (do-foldr-cons-identity k z x xs)
- (let ((cvar (init-flic-var (create-temp-var 'c) k '#f)))
- (make-flic-let
- (list cvar)
- (do-foldr-cons-identity (make-flic-ref cvar) z x xs)
- '#f)))
- tail)))
- ((is-type? 'flic-let l)
- ;; foldr k z (let bindings in body) ==>
- ;; let bindings in foldr k z body
- (record-hack 'foldr-hoist-let)
- (setf (flic-let-body l)
- (optimize-foldr fn (list k z (flic-let-body l))))
- (values l tail))
- ((not (eqv? (the fixnum (dynamic *current-optimize-iteration*))
- (the fixnum (dynamic *optimize-foldr-iteration*))))
- ;; Hope for more optimizations later.
- (values fn args))
- ((and (is-type? 'flic-pack k)
- (eq? (flic-pack-con k) (core-symbol ":")))
- ;; Inline to special case, highly optimized append primitive.
- ;; Could also look for (++ (++ l1 l2) l3) => (++ l1 (++ l2 l3))
- ;; here, but I don't think that happens very often.
- (record-hack 'foldr-prim-append-inline)
- (values
- (make-flic-ref (core-symbol "primAppend"))
- (cons l (cons z tail))))
- (else
- ;; Default inline.
- (record-hack 'foldr-inline k z)
- (let ((new-fn
- (copy-flic-top
- (or (var-inline-value (core-symbol "inlineFoldr"))
- (var-value (core-symbol "inlineFoldr"))
- (error "Can't find inlineFoldr!")))))
- (if (is-type? 'flic-lambda new-fn)
- (do-lambda-to-let-aux new-fn args)
- (values new-fn args))))
- )))
-
-
- ;;; Mess with compile-time expansion of short string constants.
-
- (define-integrable max-short-string-length 3)
-
- (define (short-string-constant? l)
- (and (is-type? 'flic-const l)
- (let ((string (flic-const-value l)))
- (and (string? string)
- (<= (the fixnum (string-length string))
- (the fixnum max-short-string-length))))))
-
- (define (expand-string-constant l)
- (let* ((string (flic-const-value l))
- (length (string-length string)))
- (expand-string-constant-aux string 0 length)))
-
- (define (expand-string-constant-aux string i length)
- (declare (type fixnum i length))
- (if (eqv? i length)
- (make-flic-pack (core-symbol "Nil"))
- (make-flic-app
- (make-flic-pack (core-symbol ":"))
- (list (make-flic-const (string-ref string i))
- (expand-string-constant-aux string (+ 1 i) length))
- '#f)))
-
-
- ;;; Helper function for the case of expanding foldr applied to cons call.
-
- (define (do-foldr-cons-identity c z x xs)
- (make-flic-app
- c
- (list x
- (optimize-foldr
- (make-flic-ref (core-symbol "foldr"))
- (list (copy-flic-top c) z xs)))
- '#f))
-
-
-
- ;;; Short-circuit build inlining for the usual case where the
- ;;; argument is a lambda. (It would take several optimizer passes
- ;;; for this simplification to fall out, otherwise.)
-
- (define (optimize-build fn args)
- (let ((arg (car args)))
- (cond ((not (eqv? (dynamic *current-optimize-iteration*)
- (dynamic *optimize-build-iteration*)))
- (val trict2 exp (sel exp)) => (sel exp)
- ;; since flic-sel always forces exp
- (record-hack 'strict2-sel-identity)
- (values arg2 (cddr args)))
- ((and (is-type? 'flic-ref arg1)
- (var-force-strict? (flic-ref-var arg1)))
- ;; The variable is already going to be marked strict
- (record-hack 'strict2-already-strict-identity)
- (values arg2 (cddr args)))
- (else
- (values fn args)))))
-
-
- ;;; IO system identities.
- ;;; The idea is to supply the missing state argument, so that
- ;;; (>>=) p q s ==> applyIO (p s) (\ x -> q (getIOResult x) s)
- ;;; (>>) p q s ==> applyIO (p s) (\ x -> q s)
- ;;; The code generator also knows about applyIO and expands the
- ;;; nested lambda into a LET*.
-
- (define (optimize-thenio fn args)
- (let* ((p (car args))
- (q (cadr args))
- (s (caddr args))
- (sv (init-flic-var (create-temp-var 's) s '#f))
- (xv (init-flic-var (create-temp-var 'x) '#f '#f)))
- ;; Tweak the magic bit to make sure xv is strict even if it's never used.
- (setf (var-force-strict? xv) '#t)
- (record-hack 'thenio-expand)
- (values
- (make-flic-let
- (list sv)
- (make-flic-app
- (make-flic-ref (core-symbol "applyIO"))
- (list
- (make-flic-app p (list (make-flic-ref sv)) '#f)
- (make-flic-lambda
- (list xv)
- (if (eq? (flic-ref-var fn) (core-symbol ">>="))
- (make-flic-app
- q
- (list
- (make-flic-app
- (make-flic-ref (core-symbol "getIOResult"))
- (list (make-flic-ref xv))
- '#f)
- (make-flic-ref sv))
- '#f)
- (make-flic-app
- q (list (make-flic-ref sv)) '#f))))
- '#f)
- '#f)
- (cdddr args))))
-
-
- ;;; Look for monad identity
- ;;; applyIO (applyIO p (\x1 -> q)) (\x2 -> r) ====>
- ;;; applyIO p (\x1 -> (applyIO q (\x2 -> r)))
- ;;; since the second form generates better code.
- ;;; Note that this identity can be applied recursively to the nested
- ;;; applyIO call.
-
- (define (optimize-applyio fn args)
- (let ((arg1 (car args))
- (arg2 (cadr args)))
- (cond ((not (is-type? 'flic-lambda arg2))
- (record-hack 'applyio-lambda)
- (let ((xv (init-flic-var (create-temp-var 'x) '#f '#f)))
- (setf (var-force-strict? xv) '#t)
- (setf arg2
- (make-flic-lambda
- (list xv)
- (make-flic-app arg2 (list (make-flic-ref xv)) '#f))))
- (values fn (cons arg1 (cons arg2 (cddr args)))))
- ((and (is-type? 'flic-app arg1)
- (is-type? 'flic-ref (flic-app-fn arg1))
- (eq? (flic-ref-var (flic-app-fn arg1))
- (core-symbol "applyIO")))
- (do-applyio-identity fn arg1 arg2 (cddr args)))
- (else
- (values fn args)))))
-
- (define (do-applyio-identity fn arg1 arg2 rest)
- (record-hack 'applyio-identity)
- (let* ((p (car (flic-app-args arg1)))
- (l (cadr (flic-app-args arg1)))
- (q (flic-lambda-body l)))
- ;; Mung the args to the nested applyIO app
- (multiple-value-bind (nested-fn nested-args)
- (optimize-applyio
- (flic-app-fn arg1)
- (cons q (cons arg2 (cddr (flic-app-args arg1)))))
- (setf (flic-app-fn arg1) nested-fn)
- (setf (flic-app-args arg1) nested-args))
- ;; Munge the lambda
- (setf (flic-lambda-body l) arg1)
- ;; Return the new arguments
- (values fn (cons p (cons l rest)))))
-
-
-
- ;;; Convert lambda applications to lets.
- ;;; If application is not saturated, break it up into two nested
- ;;; lambdas before doing the transformation.
- ;;; It's better to do this optimization immediately than hoping
- ;;; the call will become fully saturated on the next pass.
- ;;; Maybe we could also look for a flic-let with a flic-lambda as
- ;;; the body to catch the cases where additional arguments can
- ;;; be found on a later pass.
-
- (define (do-lambda-to-let new-fn new-args)
- (multiple-value-bind (fn args)
- (do-lambda-to-let-aux new-fn new-args)
- (maybe-make-app fn args)))
-
- (define (maybe-make-app fn args)
- (if (null? args)
- fn
- (make-flic-app fn args '#f)))
-
- (define (do-lambda-to-let-aux new-fn new-args)
- (let ((vars (flic-lambda-vars new-fn))
- (body (flic-lambda-body new-fn))
- (matched '()))
- (record-hack 'app-lambda-to-let)
- (do ()
- ((or (null? new-args) (null? vars)))
- (let ((var (pop va on ~s in and expression!" exp))))
- ((is-type? 'flic-and exp)
- ;; Flatten nested ands.
- (record-hack 'and-compress)
- (optimize-and-exps
- (cdr exps)
- (nconc (nreverse (flic-and-exps exp)) result)))
- ((is-strict2-app? exp)
- ;; Hoist strict2. This helps with simplifying is-constructor
- ;; tests on tuples without losing strictness properties.
- (record-hack 'and-hoist-strict2)
- (list (make-strict2-app
- (strict2-app-arg1 exp)
- (make-flic-and
- (optimize-and-exps
- (cons (strict2-app-arg2 exp) (cdr exps))
- result)))))
- (else
- ;; No optimization possible.
- (optimize-and-exps (cdr exps) (cons exp result)))
- ))))
-
-
- (define (make-strict2-app arg1 arg2)
- (make-flic-app
- (make-flic-ref (core-symbol "strict2"))
- (list arg1 arg2)
- '#f))
-
- (define (strict2-app-arg1 exp)
- (car (flic-app-args exp)))
-
- (define (strict2-app-arg2 exp)
- (cadr (flic-app-args exp)))
-
- (define (is-strict2-app? exp)
- (and (is-type? 'flic-app exp)
- (let ((fn (flic-app-fn exp)))
- (and (is-type? 'flic-ref fn)
- (eq? (flic-ref-var fn) (core-symbol "strict2"))
- (null? (cddr (flic-app-args exp)))))))
-
-
-
- ;;; Case-block optimizations. These optimizations are possible because
- ;;; of the restricted way this construct is used; return-froms are
- ;;; never nested, etc.
-
- (define-optimize flic-case-block (object)
- (let* ((sym (flic-case-block-block-name object))
- (exps (optimize-case-block-exps
- sym (flic-case-block-exps object) '())))
- (optimize-flic-case-block-aux object sym exps)))
-
- (define (optimize-flic-case-block-aux object sym exps)
- (cond ((null? exps)
- ;; This should never happen. It means all of the tests were
- ;; optimized away, including the failure case!
- (error "No exps left in case block ~s!" object))
- ((and (is-type? 'flic-and (car exps))
- (is-return-from-block?
- sym
- (car (last (flic-and-exps (car exps))))))
- ;; The first clause is a simple and. Hoist it out of the
- ;; case-block and rewrite as if/then/else.
- (record-hack 'case-block-to-if)
- (let ((then-exp (car (last (flic-and-exps (car exps))))))
- (setf (flic-case-block-exps object) (cdr exps))
- (make-flic-if
- (maybe-simplify-and
- (car exps)
- (butlast (flic-and-exps (car exps))))
- (flic-return-from-exp then-exp)
- (optimize-flic-case-block-aux object sym (cdr exps)))))
- ((is-return-from-block? sym (car exps))
- ;; Do an identity reduction.
- (record-hack 'case-block-identity)
- (flic-return-from-exp (car exps)))
- ((is-type? 'flic-let (car exps))
- ;; The first clause is a let. Since this clause is going
- ;; to be executed anyway, hoisting the bindings to surround
- ;; the entire case-block should not change their strictness
- ;; properties, and it may permit some further optimizations.
- (record-hack 'case-block-hoist-let)
- (let* ((exp (car exps))
- (body (flic-let-body exp)))
- (setf (flic-let-body exp)
- (optimize-flic-case-block-aux
- object sym (cons body (cdr exps))))
- exp))
- ((is-strict2-app? (car exps))
- ;; The first clause is a strict2. Hoist this to surround the
- ;; entire case-block.
- (record-hack 'case-block-hoist-strict2)
- (let* ((exp (car exps))
- (arg1 (strict2-app-arg1 exp))
- (arg2 (strict2-app-arg2 exp)))
- (setf (flic-case-block-exps object) (cons arg2 (cdr exps)))
- (make-strict2-app arg1 object)))
- (else
- (setf (flic-case-block-exps object) exps)
- object)
- ))
-
-
- (define (optimize-case-block-exps sym exps result)
- (if (null? exps)
- (nreverse result)
- (let ((exp (optimize (car exps))))
- (cond ((is-return-from-block? sym exp)
- ;; Any remaining clauses are dead code and should be removed.
- (if (not (null? (cdr exps)))
- (record-hack 'case-block-dead-code))
- (nreverse (cons exp result)))
- ((is-type? 'flic-and exp)
- ;; See if we can remove redundant tests.
- (push (maybe-simplify-and
- exp
- (look-for-redundant-tests (flic-and-exps exp) result))
- result)
- (optimize-case-block-exps sym (cdr exps) result))
- (else
- ;; No optimization possible.
- (optimize-case-block-exps sym (cdr exps) (cons exp result)))
- ))))
-
-
- ;;; Look for case-block tests that are known to be either true or false
- ;;; because of tests made in previous clauses.
- ;;; For now, we only look at is-constructor tests. Such a test is known
- ;;; to be true if previous clauses have eliminated all other possible
- ;;; constructors. And such a test is known to be false if a previous
- ;;; clause has already matched this constructor.
-
- ;;; I added a test to ensure at least one is-constructor is preserved to
- ;;; ensure strictness properties are maintained. Jcp.
-
- ;;; I added a check to avoid looking through long lists of previous clauses
- ;;; that was croaking the optimizer on huge data types. Jcp.
-
- (define (look-for-redundant-tests exps previous-clauses)
- (if (null? exps)
- '()
- (if (> (length previous-clauses) 25)
- exps
- (let ((exp (car exps)))
- (cond ((and (is-type? 'flic-is-constructor exp)
- previous-clauses ; Always keep first clause - jcp
- (constructor-test-redundant? exp previous-clauses))
- ;; Known to be true.
- (record-hack 'case-block-discard-redundant-test)
- (cons (make-flic-pack (core-symbol "True"))
- (look-for-redundant-tests (cdr exps) previous-clauses)))
- ((and (is-type? 'flic-is-constructor exp)
- (constructor-test-duplicated? exp previous-clauses))
- ;; Known to be false.
- (record-hack 'case-block-discard-duplicate-test)
- (list (make-flic-pack (core-symbol "False"))))
- (else
- ;; No optimization.
- (cons exp
- (look-for-redundant-tests (cdr exps) previous-clauses)))
- )))))
-
-
- ;;; In looking for redundant/duplicated tests, only worry about
- ;;; is-constructor tests that have an argument that is a variable.
- ;;; It's too hairy to consider any other cases.
-
- (define (constructor-test-duplicated? exp previous-clauses)
- (let ((con (flic-is-constructor-con exp))
- (arg (flic-is-constructor-exp exp)))
- (and (is-type? 'flic-ref arg)
- (constructor-test-present? con arg previous-clauses))))
-
- (define (constructor-test-redundant? exp previous-clauses)
- (let ((con (flic-is-constructor-con exp))
- (arg (flic-is-constructor-exp exp)))
- (and (is-type? 'flic-ref arg)
- (every-1 (lambda (c)
- (or (eq? c con)
- (constructor-test-present? c arg previous-clauses)))
- (algdata-constrs (con-alg con))))))
-
- (define (constructor-test-present? con arg previous-clauses)
- (cond ((null? previous-clauses)
- '#f)
- ((constructor-test-present-1? con arg (car previous-clauses))
- '#t)
- (else
- (constructor-test-present? con arg (cdr previous-clauses)))))
-
-
- ;;; The tricky thing here is that, even if the constructor test is
- ;;; present in the clause, we have to make sure that the entire clause won't
- ;;; fail due to the presence of some other test which fails. So look
- ;;; for a very specific pattern here, namely
- ;;; (and (is-constructor con arg) (return-from ....))
-
- (define (constructor-test-present-1? con arg clause)
- (and (is-type? 'flic-and clause)
- (let ((exps (flic-and-exps clause)))
- (and (is-type? 'flic-is-constructor (car exps))
- (is-type? 'flic-return-from (cadr exps))
- (null? (cddr exps))
- (let* ((inner-exp (car exps))
- (inner-con (flic-is-constructor-con inner-exp))
- (inner-arg (flic-is-constructor-exp inner-exp)))
- (and (eq? inner-con con)
- (flic-exp-eq? arg inner-arg)))))))
-
-
-
- ;;; No fancy optimizations for return-from by itself.
-
- (define-optimize flic-return-from (object)
- (setf (flic-return-from-exp object)
- (optimize (flic-return-from-exp object)))
- object)
-
-
-
- ;;; Obvious simplification on if
-
- (define-optimize flic-if (object)
- (let ((test-exp (optimize (flic-if-test-exp object)))
- (then-exp (optimize (flic-if-then-exp object)))
- (else-exp (optimize (flic-if-else-exp object))))
- (cond ((and (is-type? 'flic-pack test-exp)
- (eq? (flic-pack-con test-exp) (core-symbol "True")))
- ;; Fold constant test
- (record-hack 'if-fold)
- then-exp)
- ((and (is-type? 'flic-pack test-exp)
- (eq? (flic-pack-con test-exp) (core-symbol "False")))
- ;; Fold constant test
- (record-hack 'if-fold)
- else-exp)
- ((and (is-type? 'flic-is-constructor test-exp)
- (eq? (flic-is-constructor-con test-exp) (core-symbol "True")))
- ;; Remove redundant is-constructor test.
- ;; Doing this as a general is-constructor identity
- ;; backfires because it prevents some of the important case-block
- ;; optimizations from being recognized, but it works fine here.
- (record-hack 'if-compress-test)
- (setf (flic-if-test-exp object) (flic-is-constructor-exp test-exp))
- (setf (flic-if-then-exp object) then-exp)
- (setf (flic-if-else-exp object) else-exp)
- object)
- ((and (is-type? 'flic-is-constructor test-exp)
- (eq? (flic-is-constructor-con test-exp) (core-symbol "False")))
- ;; Remove redundant is-constructor test, flip branches.
- (record-hack 'if-compress-test)
- (setf (flic-if-test-exp object) (flic-is-constructor-exp test-exp))
- (setf (flic-if-then-exp object) else-exp)
- (setf (flic-if-else-exp object) then-exp)
- object)
- ((and (is-type? 'flic-return-from then-exp)
- (is-type? 'flic-return-from else-exp)
- (eq? (flic-return-from-block-name then-exp)
- (flic-return-from-block-name else-exp)))
- ;; Hoist return-from outside of IF.
- ;; This may permit further case-block optimizations.
- (record-hack 'if-hoist-return-from)
- (let ((return-exp then-exp))
- (setf (flic-if-test-exp object) test-exp)
- (setf (flic-if-then-exp object) (flic-return-from-exp then-exp))
- (setf (flic-if-else-exp object) (flic-return-from-exp else-exp))
- (setf (flic-return-from-exp return-exp) object)
- return-exp))
- ((and (is-type? 'flic-pack then-exp)
- (is-type? 'flic-pack else-exp)
- (eq? (flic-pack-con then-exp) (core-symbol "True"))
- (eq? (flic-pack-con else-exp) (core-symbol "False")))
- ;; This if does nothing useful at all!
- (record-hack 'if-identity)
- test-exp)
- ((and (is-type? 'flic-pack then-exp)
- (is-type? 'flic-pack else-exp)
- (eq? (flic-pack-con then-exp) (core-symbol "False"))
- (eq? (flic-pack-con else-exp) (core-symbol "True")))
- ;; Inverse of previous case
- (record-hack 'if-identity-inverse)
- (make-flic-is-constructor (core-symbol "False") test-exp))
- ((or (is-type? 'flic-lambda then-exp)
- (is-type? 'flic-lambda else-exp))
- ;; Hoist lambdas to surround entire if. This allows us to
- ;; do a better job of saturating them.
- (record-hack 'if-hoist-lambda)
- (multiple-value-bind (vars then-exp else-exp)
- (do-if-hoist-lambda then-exp else-exp)
- (setf (flic-if-test-exp object) test-exp)
- (setf (flic-if-then-exp object) then-exp)
- (setf (flic-if-else-exp object) else-exp)
- (make-flic-lambda vars object)))
- ((is-strict2-app? test-exp)
- ;; Hoist strict2 to surround entire if.
- (record-hack 'if-hoist-strict2)
- (setf (flic-if-test-exp object) (strict2-app-arg2 test-exp))
- (setf (flic-if-then-exp object) then-exp)
- (setf (flic-if-else-exp object) else-exp)
- (make-strict2-app (strict2-app-arg1 test-exp) object))
- (else
- ;; No optimization possible
- (setf (flic-if-test-exp object) test-exp)
- (setf (flic-if-then-exp object) then-exp)
- (setf (flic-if-else-exp object) else-exp)
- object)
- )))
-
-
-
- ;;; Try to pull as many variables as possible out to surround the entire
- ;;; let.
-
- (define (do-if-hoist-lambda then-exp else-exp)
- (let ((vars '())
- (then-args '())
- (else-args '()))
- (do ((then-vars (if (is-type? 'flic-lambda then-exp)
- (flic-lambda-vars then-exp)
- '())
- (cdr then-vars))
- (else-vars (if (is-type? 'flic-lambda else-exp)
- (flic-lambda-vars else-exp)
- '())
- (cdr else-vars)))
- ((and (null? then-vars) (null? else-vars)) '#f)
- (let ((var (init-flic-var (create-temp-var 'arg) '#f '#f)))
- (push var vars)
- (push (make-flic-ref var) then-args)
- (push (make-flic-ref var) else-args)))
- (values
- vars
- (if (is-type? 'flic-lambda then-exp)
- (do-lambda-to-let then-exp then-args)
- (make-flic-app then-exp then-args '#f))
- (if (is-type? 'flic-lambda else-exp)
- (do-lambda-to-let else-exp else-args)
- (make-flic-app else-exp else-args '#f)))))
-
-
-
- ;;; Look for (sel (pack x)) => x
-
- (define-optimize flic-sel (object)
- (optimize-flic-sel-aux object))
-
- (define (optimize-flic-sel-aux object)
- (let ((new-exp (optimize (flic-sel-exp object))))
- (setf (flic-sel-exp object) new-exp)
- (typecase new-exp
- (flic-ref
- ;; Check to see whether this is bound to a pack application
- (let ((val (is-bound-to-constructor-app? (flic-ref-var new-exp))))
- (if val
- ;; Yup, it is. Now extract the appropriate component,
- ;; provided it is inlineable.
- (let* ((i (flic-sel-i object))
- (args (flic-app-args val))
- (newval (list-ref args i)))
- (if (can-inline? newval '() '#t)
- (begin
- (record-hack 'sel-fold-var)
- (optimize (copy-flic-top newval)))
- object))
- ;; The variable was bound to something else.
- object)))
- (flic-app
- ;; The obvious optimization.
- (if (is-constructor-app-prim? new-exp)
- (begin
- (record-hack 'sel-fold-app)
- (list-ref (flic-app-args new-exp) (flic-sel-i object)))
- object))
- (else
- object))))
-
-
-
-
- ;;; Do similar stuff for is-constructor.
-
- (define-optimize flic-is-constructor (object)
- (let ((con (flic-is-constructor-con object))
- (exp (optimize (flic-is-constructor-exp object)))
- (exp-con '#f))
- (cond ((algdata-tuple? (con-alg con))
- ;; Tuples have only one constructor, so this is always true.
- ;; But we can't get rid of the reference entirely because it
- ;; might change strictness.
- ;; Use strict2 to do this, which is kind of a grody hack.
- ;; All of the other optimizations dealing with strict2 are
- ;; just to allow this constant-folding to be happen
- ;; without losing track of the strictness properties.
- (record-hack 'is-constructor-fold-tuple)
- (make-strict2-app exp (make-flic-pack (core-symbol "True"))))
- ((setf exp-con (is-constructor-app? exp))
- ;; The expression is a constructor application.
- ;; *** Does this have similar strictness problems in the
- ;; *** presence of strict data constructors?
- (record-hack 'is-constructor-fold)
- (make-flic-pack
- (if (eq? exp-con con)
- (core-symbol "True")
- (core-symbol "False"))))
- (else
- ;; No optimization possible
- (setf (flic-is-constructor-exp object) exp)
- object))))
-
- (define-optimize flic-con-number (object)
- (let ((exp (flic-con-number-exp object))
- (type (flic-con-number-type object)))
- ;; ***Maybe ast-to-flic should look for this one.
- (if (algdata-tuple? type)
- (begin
- (record-hack 'con-number-fold-tuple)
- (make-flic-const 0))
- (let* ((new-exp (optimize exp))
- (con (is-constructor-app? new-exp)))
- (if con
- (begin
- (record-hack 'con-number-fold)
- (make-flic-const (con-tag con)))
- (begin
- (setf (flic-con-number-exp object) new-exp)
- object)))
- )))
-
- (define-optimize flic-void (object)
- object)
-
-
- (define-optimize flic-update (object)
- (let ((slots (flic-update-slots object))
- (exp (flic-update-exp object)))
- ;; Try to merge nested operators, discarding duplicate slot updates.
- ;; *** Maybe we should also look for nested let-expressions, etc.
- (when (is-type? 'flic-update exp)
- (record-hack 'update-compress)
- (let ((inner-slots (flic-update-slots exp))
- (inner-exp (flic-update-exp exp)))
- (dolist (s inner-slots)
- (unless (assv (car s) slots)
- (setf slots (nconc slots (list s)))))
- (setf exp inner-exp)))
- ;; Walk the slots
- (dolist (s slots)
- (setf (cdr s) (optimize (cdr s))))
- ;; Walk the subexp
- (setf (flic-update-exp object) (optimize exp))
- object))
-
-
- ;;;===================================================================
- ;;; General helper functions
- ;;;===================================================================
-
-
- ;;; Lucid's built-in every function seems to do a lot of unnecessary
- ;;; consing. This one is much faster.
-
- (define (every-1 fn list)
- (cond ((null? list)
- '#t)
- ((funcall fn (car list))
- (every-1 fn (cdr list)))
- (else
- '#f)))
-
-
-
- ;;; Equality predicate on flic expressions
-
- (define (flic-exp-eq? a1 a2)
- (typecase a1
- (flic-const
- (and (is-type? 'flic-const a2)
- (equal? (flic-const-value a1) (flic-const-value a2))))
- (flic-ref
- (and (is-type? 'flic-ref a2)
- (eq? (flic-ref-var a1) (flic-ref-var a2))))
- (flic-pack
- (and (is-type? 'flic-pack a2)
- (eq? (flic-pack-con a1) (flic-pack-con a2))))
- (flic-sel
- (and (is-type? 'flic-sel a2)
- (eq? (flic-sel-con a1) (flic-sel-con a2))
- (eqv? (flic-sel-i a1) (flic-sel-i a2))
- (flic-exp-eq? (flic-sel-exp a1) (flic-sel-exp a2))))
- (else
- '#f)))
-
-
-
- ;;; Predicates for testing whether an expression matches a pattern.
-
- (define (is-constructor-app? exp)
- (typecase exp
- (flic-app
- ;; See if we have a saturated call to a constructor.
- (is-constructor-app-prim? exp))
- (flic-ref
- ;; See if we can determine anything about the value the variable
- ;; is bound to.
- (let ((value (var-value (flic-ref-var exp))))
- (if value
- (is-constructor-app? value)
- '#f)))
- (flic-let
- ;; See if we can determine anything about the body of the let.
- (is-constructor-app? (flic-let-body exp)))
- (flic-pack
- ;; See if this is a nullary constructor.
- (let ((con (flic-pack-con exp)))
- (if (eqv? (con-arity con) 0)
- con
- '#f)))
- (else
- '#f)))
-
- (define (is-return-from-block? sym exp)
- (and (is-type? 'flic-return-from exp)
- (eq? (flic-return-from-block-name exp) sym)))
-
- (define (is-constructor-app-prim? exp)
- (let ((fn (flic-app-fn exp))
- (args (flic-app-args exp)))
- (if (and (is-type? 'flic-pack fn)
- (eqv? (length args) (con-arity (flic-pack-con fn))))
- (flic-pack-con fn)
- '#f)))
-
- (define (is-bound-to-constructor-app? var)
- (let ((val (var-value var)))
- (if (and val
- (is-type? 'flic-app val)
- (is-constructor-app-prim? val))
- val
- '#f)))
-
- (define (is-selector? con i exp)
- (or (and (is-type? 'flic-ref exp)
- (is-selector? con i (var-value (flic-ref-var exp))))
- (and (is-type? 'flic-sel exp)
- (eq? (flic-sel-con exp) con)
- (eqv? (the fixnum i) (the fixnum (flic-sel-i exp)))
- (flic-sel-exp exp))
- ))
-
- (define (is-selector-list? con i subexp exps)
- (declare (type fixnum i))
- (if (null? exps)
- subexp
- (let ((temp (is-selector? con i (car exps))))
- (and (flic-exp-eq? subexp temp)
- (is-selector-list? con (+ 1 i) subexp (cdr exps))))))
-
-
-
- ;;;===================================================================
- ;;; Inlining criteria
- ;;;===================================================================
-
- ;;; Expressions that can be inlined unconditionally are constants, variable
- ;;; references, and some functions.
- ;;; I've made some attempt here to arrange the cases in the order they
- ;;; are likely to occur.
-
- (define (can-inline? exp recursive-vars toplevel?)
- (typecase exp
- (flic-sel
- ;; Listed first because it happens more frequently than
- ;; anything else.
- ;; *** Inlining these is an experiment.
- ;; *** This transformation interacts with the strictness
- ;; *** analyzer; if the variable referenced is not strict, then
- ;; *** it is probably not a good thing to do since it adds extra
- ;; *** forces.
- ;; (let ((subexp (flic-sel-exp exp)))
- ;; (and (is-type? 'flic-ref subexp)
- ;; (not (memq (flic-ref-var subexp) recursive-vars))))
- '#f)
- (flic-lambda
- ;; Do not try to inline lambdas if the fancy inline optimization
- ;; is disabled.
- ;; Watch for problems with infinite loops with recursive variables.
- (if (dynamic *do-inline-optimizations*)
- (simple-function-body? (flic-lambda-body exp)
- (flic-lambda-vars exp)
- recursive-vars
- toplevel?)
- '#f))
- (flic-ref
- ;; We get into infinite loops trying to inline recursive variables.
- (not (memq (flic-ref-var exp) recursive-vars)))
- ((or flic-pack flic-const)
- '#t)
- (else
- '#f)))
-
-
- ;;; Determining whether to inline a function is difficult. This is
- ;;; very conservative to avoid code bloat. What we need to do is
- ;;; compare the cost (in program size mainly) of the inline call with
- ;;; an out of line call. For an out of line call, we pay for one function
- ;;; call and a setup for each arg. When inlining, we pay for function
- ;;; calls in the body and for args referenced more than once. In terms of
- ;;; execution time, we win big when a functional parameter is called
- ;;; since this `firstifies' the program.
-
- ;;; Here's the criteria:
- ;;; An inline function gets to reference no more that 2 non-parameter
- ;;; values (including constants and repeated parameter references).
- ;;; For non-toplevel functions, be slightly more generous since the
- ;;; fixed overhead of binding the local function would go away.
-
- (define (simple-function-body? exp lambda-vars recursive-vars toplevel?)
- (let ((c (if toplevel? 2 4)))
- (>= (the fixnum (simple-function-body-1 exp lambda-vars recursive-vars c))
- 0)))
-
-
- ;;; I've made some attempt here to order the cases by how frequently
- ;;; they appear.
-
- (define (simple-function-body-1 exp lambda-vars recursive-vars c)
- (declare (type fixnum c))
- (if (< c 0)
- (values c '())
- (typecase exp
- (flic-ref
- (let ((var (flic-ref-var exp)))
- (cond ((memq var lambda-vars)
- (values c (list-remove-1 var lambda-vars)))
- ((memq var recursive-vars)
- (values -1 '()))
- (else
- (values (the fixnum (1- c)) lambda-vars)))))
- (flic-app
- (simple-function-body-1/l
- (cons (flic-app-fn exp) (flic-app-args exp))
- lambda-vars recursive-vars c))
- (flic-sel
- (simple-function-body-1
- (flic-sel-exp exp)
- lambda-vars recursive-vars (the fixnum (1- c))))
- (flic-is-constructor
- (simple-function-body-1
- (flic-is-constructor-exp exp)
- lambda-vars recursive-vars (the fixnum (1- c))))
- ((or flic-const flic-pack)
- (values (the fixnum (1- c)) lambda-vars))
- (else
- ;; case & let & lambda not allowed.
- (values -1 '())))))
-
- (define (list-remove-1 item list)
- (cond ((null? list)
- '())
- ((eq? item (car list))
- (cdr list))
- (else
- (cons (car list) (list-remove-1 item (cdr list))))
- ))
-
- (define (simple-function-body-1/l exps lambda-vars recursive-vars c)
- (declare (type fixnum c))
- (if (or (null? exps) (< c 0))
- (values c lambda-vars)
- (multiple-value-bind (c-1 lambda-vars-1)
- (simple-function-body-1 (car exps) lambda-vars recursive-vars c)
- (simple-function-body-1/l
- (cdr exps) lambda-vars-1 recursive-vars c-1))))
-
-
-
- ;;;===================================================================
- ;;; Constant structured data detection
- ;;;===================================================================
-
-
- ;;; Look to determine whether an object is a structured constant,
- ;;; recursively examining its components if it's an app. This is
- ;;; necessary in order to detect constants with arbitrary circular
- ;;; reference to the vars in recursive-vars.
-
- (define (structured-constant-recursive? object recursive-vars stack)
- (typecase object
- (flic-const
- '#t)
- (flic-ref
- (let ((var (flic-ref-var object)))
- (or (memq var stack)
- (var-toplevel? var)
- (and (memq var recursive-vars)
- (structured-constant-recursive?
- (var-value var) recursive-vars (cons var stack))))))
- (flic-pack
- '#t)
- (flic-app
- (structured-constant-app-recursive?
- (flic-app-fn object)
- (flic-app-args object)
- recursive-vars
- stack))
- (flic-lambda
- (lambda-hoistable? object))
- (else
- '#f)))
-
- (define (structured-constant-app-recursive? fn args recursive-vars stack)
- (and (is-type? 'flic-pack fn)
- (eqv? (length args) (con-arity (flic-pack-con fn)))
- (every-1 (lambda (a)
- (structured-constant-recursive? a recursive-vars stack))
- args)))
-
-
- ;;; Here's a non-recursive (and more efficient) version of the above.
- ;;; Instead of looking at the whole structure, it only looks one level
- ;;; deep. This can't detect circular constants, but is useful in
- ;;; contexts where circularities cannot appear.
-
- (define (structured-constant? object)
- (typecase object
- (flic-ref
- (var-toplevel? (flic-ref-var object)))
- (flic-const
- '#t)
- (flic-pack
- '#t)
- (flic-lambda
- (lambda-hoistable? object))
- (else
- '#f)))
-
- (define (structured-constant-app? fn args)
- (and (is-type? 'flic-pack fn)
- (eqv? (length args) (con-arity (flic-pack-con fn)))
- (every-1 (function structured-constant?) args)))
-
-
- ;;; Determine whether a lambda can be hoisted to top-level.
- ;;; The main purpose of this code is to mark structured constants
- ;;; containing simple lambdas to permit later folding of sel expressions
- ;;; on those constants. Since the latter expression is permissible
- ;;; only on inlinable functions, stop if we hit an expression that
- ;;; would make the function not inlinable.
-
- (define (lambda-hoistable? object)
- (and (can-inline? object '() '#t)
- (lambda-hoistable-aux
- (flic-lambda-body object)
- (flic-lambda-vars object))))
-
- (define (lambda-hoistable-aux object local-vars)
- (typecase object
- (flic-ref
- (or (var-toplevel? (flic-ref-var object))
- (memq (flic-ref-var object) local-vars)))
- ((or flic-const flic-pack)
- '#t)
- (flic-sel
- (lambda-hoistable-aux (flic-sel-exp object) local-vars))
- (flic-is-constructor
- (lambda-hoistable-aux (flic-is-constructor-exp object) local-vars))
- (flic-app
- (and (lambda-hoistable-aux (flic-app-fn object) local-vars)
- (every-1 (lambda (x) (lambda-hoistable-aux x local-vars))
- (flic-app-args object))))
- (else
- '#f)))
-
-
- ;;; Having determined that something is a structured constant,
- ;;; enter it (and possibly its subcomponents) in the hash table
- ;;; and return a var-ref.
-
- (define (enter-structured-constant value recursive?)
- (multiple-value-bind (con args var)
- (enter-structured-constant-aux value recursive?)
- (when (not var)
- (setf var (create-temp-var 'constant))
- (add-new-structured-constant var con args))
- (make-flic-ref var)))
-
- (define (enter-structured-constant-aux value recursive?)
- (let* ((fn (flic-app-fn value))
- (con (flic-pack-con fn))
- (args (if recursive?
- (map (function enter-structured-constant-arg)
- (flic-app-args value))
- (flic-app-args value))))
- (values con args (lookup-structured-constant con args))))
-
- (define (enter-structured-constant-arg a)
- (if (is-type? 'flic-app a)
- (enter-structured-constant a '#t)
- a))
-
- (define (lookup-structured-constant con args)
- (lookup-structured-constant-aux
- (table-entry *structured-constants-table* con) args))
-
- (define (lookup-structured-constant-aux alist args)
- (cond ((null? alist)
- '#f)
- ((every (function flic-exp-eq?) (car (car alist)) args)
- (cdr (car alist)))
- (else
- (lookup-structured-constant-aux (cdr alist) args))))
-
- (define (add-new-structured-constant var con args)
- (push (cons args var) (table-entry *structured-constants-table* con))
- (setf (var-toplevel? var) '#t)
- (setf (var-value var) (make-flic-app (make-flic-pack con) args '#t))
- (push var *structured-constants*)
- var)
-
-
-
- ;;;===================================================================
- ;;; Invariant argument stuff
- ;;;===================================================================
-
-
- ;;; When processing a saturated call to a locally defined function,
- ;;; note whether any of the arguments are always passed the same value.
-
- (define (note-invariant-args args vars)
- (when (and (not (null? args)) (not (null? vars)))
- (let* ((arg (car args))
- (var (car vars))
- (val (var-arg-invariant-value var)))
- (cond ((not (var-arg-invariant? var))
- ;; This argument already marked as having more than one
- ;; value.
- )
- ((and (is-type? 'flic-ref arg)
- (eq? (flic-ref-var arg) var))
- ;; This is a recursive call with the same argument.
- ;; Don't update the arg-invariant-value slot.
- )
- ((or (not val)
- (flic-exp-eq? arg val))
- ;; Either this is the first call, or a second call with
- ;; the same argument.
- (setf (var-arg-invariant-value var) arg))
- (else
- ;; Different values for this argument are passed in
- ;; different places, so we can't mess with it.
- (setf (var-arg-invariant? var) '#f)))
- (note-invariant-args (cdr args) (cdr vars)))))
-
-
- ;;; After processing a let form, check to see if any of the bindings
- ;;; are for local functions with invariant arguments.
- ;;; Suppose we have something like
- ;;; let foo = \ x y z -> <fn-body>
- ;;; in <let-body>
- ;;; and y is known to be invariant; then we rewrite this as
- ;;; let foo1 = \ x z -> let y = <invariant-value> in <fn-body>
- ;;; foo = \ x1 y1 z1 -> foo1 x1 z1
- ;;; in <let-body>
- ;;; The original foo binding is inlined on subsequent passes and
- ;;; should go away. Likewise, the binding of y should be inlined also.
- ;;; *** This is kind of bogus because of the way it depends on the
- ;;; *** magic inline bit. It would be better to do a code walk
- ;;; *** now on the entire let expression to rewrite all the calls to foo.
-
- (define (add-stuff-for-invariants bindings)
- (if (null? bindings)
- '()
- (let* ((var (car bindings))
- (val (var-value var)))
- (setf (cdr bindings)
- (add-stuff-for-invariants (cdr bindings)))
- (if (and (is-type? 'flic-lambda val)
- ;; Don't mess with single-reference variable bindings,
- ;; or things we are going to inline anyway.
- (not (var-single-ref var))
- (not (var-simple? var))
- ;; All references must be in saturated calls to do this.
- (eqv? (var-referenced var) (var-fn-referenced var))
- ;; There is at least one argument marked invariant.
- (some (function var-arg-invariant?) (flic-lambda-vars val))
- ;; Every argument marked invariant must also be hoistable.
- (every-1 (function arg-hoistable?) (flic-lambda-vars val)))
- (hoist-invariant-args
- var
- val
- bindings)
- bindings))))
-
- (define (arg-hoistable? var)
- (if (var-arg-invariant? var)
- (or (not (var-arg-invariant-value var))
- (flic-invariant? (var-arg-invariant-value var)
- (dynamic *local-bindings*)))
- '#t))
-
- (define (hoist-invariant-args var val bindings)
- (let ((foo1-var (copy-temp-var (def-name var)))
- (foo1-def-vars '())
- (foo1-app-args '())
- (foo1-let-vars '())
- (foo-def-vars '()))
- (push foo1-var bindings)
- (dolist (v (flic-lambda-vars val))
- (let ((new-v (copy-temp-var (def-name v))))
- (push (init-flic-var new-v '#f '#f) foo-def-vars)
- (if (var-arg-invariant? v)
- (when (var-arg-invariant-value v)
- (push (init-flic-var
- v (copy-flic-top (var-arg-invariant-value v)) '#f)
- foo1-let-vars))
- (begin
- (push v foo1-def-vars)
- (push (make-flic-ref new-v) foo1-app-args))
- )))
- (setf foo1-def-vars (nreverse foo1-def-vars))
- (setf foo1-app-args (nreverse foo1-app-args))
- (setf foo1-let-vars (nreverse foo1-let-vars))
- (setf foo-def-vars (nreverse foo-def-vars))
- (record-hack 'let-hoist-invariant-args var foo1-let-vars)
- ;; Fix up the value of foo1
- (init-flic-var
- foo1-var
- (let ((body (make-flic-let foo1-let-vars (flic-lambda-body val) '#f)))
- (if (null? foo1-def-vars)
- ;; *All* of the arguments were invariant.
- body
- ;; Otherwise, make a new lambda
- (make-flic-lambda foo1-def-vars body)))
- '#f)
- ;; Fix up the value of foo and arrange for it to be inlined.
- (setf (flic-lambda-vars val) foo-def-vars)
- (setf (flic-lambda-body val)
- (if (null? foo1-app-args)
- (make-flic-ref foo1-var)
- (make-flic-app (make-flic-ref foo1-var) foo1-app-args '#t)))
- (setf (var-simple? var) '#t)
- (setf (var-inline? var) '#t)
- ;; Return modified list of bindings
- bindings))
-
-
-
- ;;;===================================================================
- ;;; Install globals
- ;;;===================================================================
-
-
- ;;; The optimizer, CFN, etc. can introduce new top-level variables that
- ;;; are not installed in the symbol table. This causes problems if
- ;;; those variables are referenced in the .hci file (as in the inline
- ;;; expansion of some other variables). So we need to fix up the
- ;;; symbol table before continuing.
-
- (define (install-uninterned-globals vars)
- (dolist (v vars)
- (let* ((module (locate-module (def-module v)))
- (name (def-name v))
- (table (module-symbol-table module))
- (def (table-entry table name)))
- (cond ((not def)
- ;; This def was not installed. Rename it if it's a gensym
- ;; and install it.
- (when (gensym? name)
- (setf name (rename-gensym-var v name table)))
- (setf (table-entry table name) v))
- ((eq? def v)
- ;; Already installed.
- '#t)
- (else
- ;; Ooops! The symbol installed in the symbol table isn't
- ;; this one!
- (error "Duplicate defs ~s and ~s in symbol table for ~s!"
- v def module))
- ))))
-
-
- (define (rename-gensym-var var name table)
- (setf name (string->symbol (symbol->string name)))
- (if (table-entry table name)
- ;; This name already in use; gensym a new one!
- (rename-gensym-var var (gensym (symbol->string name)) table)
- ;; OK, no problem
- (setf (def-name var) name)))
-
-
-
- ;;;===================================================================
- ;;; Postoptimizer
- ;;;===================================================================
-
- ;;; This is another quick traversal of the structure to determine
- ;;; whether references to functions are fully saturated or not.
- ;;; Also makes sure that reference counts on variables are correct;
- ;;; this is needed so the code generator can generate ignore declarations
- ;;; for unused lambda variables.
-
- (define-flic-walker postoptimize (object))
-
- (define-postoptimize flic-lambda (object)
- (dolist (var (flic-lambda-vars object))
- (setf (var-referenced var) 0))
- (postoptimize (flic-lambda-body object)))
-
- (define-postoptimize flic-let (object)
- (dolist (var (flic-let-bindings object))
- (setf (var-referenced var) 0)
- (let ((val (var-value var)))
- (setf (var-arity var)
- (if (is-type? 'flic-lambda val)
- (length (flic-lambda-vars val))
- 0))))
- (dolist (var (flic-let-bindings object))
- (postoptimize (var-value var)))
- (postoptimize (flic-let-body object)))
-
- (define-postoptimize flic-app (object)
- (let ((fn (flic-app-fn object)))
- (typecase fn
- (flic-ref
- (let* ((var (flic-ref-var fn))
- (arity (var-arity var)))
- (if (not (var-toplevel? var)) (incf (var-referenced var)))
- (when (not (eqv? arity 0))
- (postoptimize-app-aux object var arity (flic-app-args object)))))
- (flic-pack
- (let* ((con (flic-pack-con fn))
- (arity (con-arity con)))
- (postoptimize-app-aux object '#f arity (flic-app-args object))))
- (else
- (postoptimize fn)))
- (dolist (a (flic-app-args object))
- (postoptimize a))))
-
- (define (postoptimize-app-aux object var arity args)
- (declare (type fixnum arity))
- (let ((nargs (length args)))
- (declare (type fixnum nargs))
- (cond ((< nargs arity)
- ;; not enough arguments
- (when var (setf (var-standard-refs? var) '#t)))
- ((eqv? nargs arity)
- ;; exactly the right number of arguments
- (when var (setf (var-optimized-refs? var) '#t))
- (setf (flic-app-saturated? object) '#t))
- (else
- ;; make the fn a nested flic-app
- (multiple-value-bind (arghead argtail)
- (split-list args arity)
- (setf (flic-app-fn object)
- (make-flic-app (flic-app-fn object) arghead '#t))
- (setf (flic-app-args object) argtail)
- (when var (setf (var-optimized-refs? var) '#t))
- (dolist (a arghead)
- (postoptimize a))))
- )))
-
- (define-postoptimize flic-ref (object)
- (let ((var (flic-ref-var object)))
- (if (not (var-toplevel? var)) (incf (var-referenced var)))
- (setf (var-standard-refs? var) '#t)))
-
- (define-postoptimize flic-const (object)
- object)
-
- (define-postoptimize flic-pack (object)
- object)
-
- (define-postoptimize flic-and (object)
- (for-each (function postoptimize) (flic-and-exps object)))
-
- (define-postoptimize flic-case-block (object)
- (for-each (function postoptimize) (flic-case-block-exps object)))
-
- (define-postoptimize flic-if (object)
- (postoptimize (flic-if-test-exp object))
- (postoptimize (flic-if-then-exp object))
- (postoptimize (flic-if-else-exp object)))
-
- (define-postoptimize flic-return-from (object)
- (postoptimize (flic-return-from-exp object)))
-
- (define-postoptimize flic-sel (object)
- (postoptimize (flic-sel-exp object)))
-
- (define-postoptimize flic-is-constructor (object)
- (postoptimize (flic-is-constructor-exp object)))
-
- (define-postoptimize flic-con-number (object)
- (postoptimize (flic-con-number-exp object)))
-
- (define-postoptimize flic-void (object)
- object)
-
- (define-postoptimize flic-update (object)
- (dolist (s (flic-update-slots object))
- (postoptimize (cdr s)))
- (setf (flic-update-slots object)
- (sort-list (flic-update-slots object)
- (lambda (s1 s2)
- (< (the fixnum (car s1)) (the fixnum (car s2))))))
- (postoptimize (flic-update-exp object)))
-