home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-27 | 16.6 KB | 360 lines | [TEXT/CCL2] |
- ;;; box.scm -- determine which expressions need to be boxed
- ;;;
- ;;; author : Sandra Loosemore
- ;;; date : 03 Apr 1993
- ;;;
- ;;;
- ;;; This phase determines whether expressions need to be boxed or unboxed.
- ;;;
- ;;; In the case of an expression that needs to be boxed, it determines
- ;;; whether it can be evaluated eagerly and boxed or whether a delay
- ;;; must be constructed.
- ;;;
- ;;; In the case of an expression that needs to be unboxed, it determines
- ;;; whether it is already known to have been evaluated and
- delay-complexity)))
- ))
- (values
- (if unboxed?
- (note-already-forced object result)
- result)
- complexity)))
-
-
-
-
- ;;;======================================================================
- ;;; Code walk
- ;;;======================================================================
-
-
- (define *local-function-calls* '())
-
- (define-flic-walker box-analysis (object already-forced uninitialized))
-
- (define-box-analysis flic-lambda (object already-forced uninitialized)
- (do-box-analysis (flic-lambda-body object) already-forced uninitialized '#t)
- (values already-forced 0))
-
- (define-box-analysis flic-let (object already-forced uninitialized)
- (let ((bindings (flic-let-bindings object)))
- (dynamic-let ((*local-function-calls* (dynamic *local-function-calls*)))
- (dolist (var bindings)
- ;; Note local functions
- (when (and (not (var-toplevel? var))
- (is-type? 'flic-lambda (var-value var))
- (not (var-standard-refs? var)))
- (push (cons var '()) (dynamic *local-function-calls*))))
- (multiple-value-bind (already-forced complexity)
- (box-analysis-let-aux object already-forced uninitialized)
- (dolist (var bindings)
- ;; Go back and reexamine local functions to see whether
- ;; we can make more arguments strict, based on the values
- ;; the function is actually called with.
- (let ((stuff (assq var (dynamic *local-function-calls*))))
- (when stuff
- (maybe-make-more-arguments-strict var (cdr stuff)))))
- (values already-forced complexity)))))
-
- (define (box-analysis-let-aux object already-forced uninitialized)
- (let ((recursive? (flic-let-recursive? object))
- (bindings (flic-let-bindings object))
- (body (flic-let-body object)))
- (when recursive? (setf uninitialized (append bindings uninitialized)))
- (dolist (var bindings)
- (let* ((value (var-value var))
- (strict? (var-strict? var))
- (result (do-box-analysis value already-forced uninitialized
- strict?)))
- (cond (strict?
- ;; Propagate information about things forced.
- (setf already-forced result))
- ((and (flic-exp-cheap? value)
- (flic-exp-strict-result? value)
- (or (not (var-toplevel? var))
- (not (def-exported? var))))
- ;; The value expression is cheap unboxed value, so mark
- ;; the variable as strict.
- ;; We have to be careful with exported top-level definitions,
- ;; though. Always make these boxed so that forward
- ;; references to them (via interface files) will work.
- ;; This shouldn't be a problem with forward references
- ;; to locally defined variables, though, because their
- ;; values should never turn out to be "cheap".
- (setf (var-strict? var) '#t)
- (setf (flic-exp-unboxed? value) '#t))))
- (when recursive? (pop uninitialized)))
- ;; *** Could be smarter about computing complexity.
- (values
- (do-box-analysis body already-forced uninitialized '#t)
- '#f)))
-
- (define (maybe-make-more-arguments-strict var calls)
- (setf (var-strictness var)
- (maybe-make-more-arguments-strict-aux
- (flic-lambda-vars (var-value var))
- calls)))
-
- (define (maybe-make-more-arguments-strict-aux vars calls)
- (if (null? vars)
- '()
- (let ((var (car vars)))
- ;; If the variable is not already strict, check to see
- ;; whether it's always called with "cheap" arguments.
- (when (and (not (var-strict? var))
- (every-1 (lambda (call)
- (exp-would-be-cheap? (car call) var))
- calls))
- (setf (var-strict? var) '#t)
- (dolist (call calls)
- (setf (flic-exp-unboxed? (car call)) '#t)))
- (cons (var-strict? var)
- (maybe-make-more-arguments-strict-aux
- (cdr vars)
- (map (function cdr) calls))))
- ))
-
-
- ;;; Look for one special fixed-point case: argument used as counter-type
- ;;; variable. Otherwise ignore fixed points.
-
- (define (exp-would-be-cheap? exp var)
- (or (and (flic-exp-cheap? exp)
- (flic-exp-strict-result? exp))
- (and (is-type? 'flic-ref exp)
- (eq? (flic-ref-var exp) var))
- (and (is-type? 'flic-app exp)
- (is-type? 'flic-ref (flic-app-fn exp))
- (var-complexity (flic-ref-var (flic-app-fn exp)))
- (every-1 (lambda (a) (exp-would-be-cheap? a var))
- (flic-app-args exp)))
- ))
-
-
-
- (define-box-analysis flic-app (object already-forced uninitialized)
- (let ((fn (flic-app-fn object))
- (args (flic-app-args object))
- (saturated? (flic-app-saturated? object)))
- (cond ((and saturated? (is-type? 'flic-ref fn))
- (let* ((var (flic-ref-var fn))
- (stuff (assq var (dynamic *local-function-calls*))))
- (when stuff
- (push args (cdr stuff)))
- (box-analysis-app-aux
- (var-strictness var) (var-complexity var)
- args already-forced uninitialized)))
- ((and saturated? (is-type? 'flic-pack fn))
- (box-analysis-app-aux
- (con-slot-strict? (flic-pack-con fn)) pack-complexity
- args already-forced uninitialized))
- (else
- ;; The function is going to be forced but all the arguments
- ;; are non-strict.
- (dolist (a args)
- (do-box-analysis a already-forced uninitialized '#f))
- (values
- (do-box-analysis fn already-forced uninitialized '#t)
- '#f))
- )))
-
-
-
- ;;; Propagation of already-forced information depends on whether or
- ;;; not the implementation evaluates function arguments in left-to-right
- ;;; order. If not, we can still propagate this information upwards.
-
- (define (box-analysis-app-aux
- strictness complexity args already-forced uninitialized)
- (let ((result already-forced))
- (dolist (a args)
- (let ((strict? (pop strictness)))
- (multiple-value-bind (new-result new-complexity)
- (do-box-analysis a already-forced uninitialized strict?)
- (when strict?
- (setf result
- (if left-to-right-evaluation
- (setf already-forced new-result)
- (union-already-forced
- new-result already-forced result))))
- (setf complexity (add-complexity complexity new-complexity)))))
- (values result complexity)))
-
-
- ;;; Treat references to variables declared in an interface files
- ;;; the same as forward references to uninitialized local variables.
-
- ;;; Jcp: I have made it so that all outside variables are assumed to
- ;;; unavailable. This will prevent problems with initialization
- ;;; order. It remains to be seen how much this may degrade the
- ;;; generated code.
-
- (define-box-analysis flic-ref (object already-forced uninitialized)
- (let ((var (flic-ref-var object)))
- (values
- already-forced
- (if (or (memq var uninitialized)
- (not (eq? (def-unit var) (dynamic *unit*))))
- '#f
- 0))))
-
-
- (define-box-analysis flic-const (object already-forced uninitialized)
- (declare (ignore object uninitialized))
- (values already-forced 0))
-
- (define-box-analysis flic-pack (object already-forced uninitialized)
- (declare (ignore object uninitialized))
- (values already-forced 0))
-
-
- ;;; For case-block and and, already-forced information can be propagated
- ;;; sequentially in the clauses. But only the first expression is
- ;;; guaranteed to be evaluated, so only it can propagate the information
- ;;; outwards.
-
- (define-box-analysis flic-case-block (object already-forced uninitialized)
- (values
- (box-analysis-sequence
- (flic-case-block-exps object) already-forced uninitialized)
- '#f))
-
- (define-box-analysis flic-and (object already-forced uninitialized)
- (values
- (box-analysis-sequence
- (flic-and-exps object) already-forced uninitialized)
- '#f))
-
- (define (box-analysis-sequence exps already-forced uninitialized)
- (let ((result
- (setf already-forced
- (do-box-analysis
- (car exps) already-forced uninitialized '#t))))
- (dolist (e (cdr exps))
- (setf already-forced
- (do-box-analysis e already-forced uninitialized '#t)))
- (values result already-forced)))
-
-
- (define-box-analysis flic-return-from (object already-forced uninitialized)
- (values
- (do-box-analysis
- (flic-return-from-exp object) already-forced uninitialized '#t)
- '#f))
-
-
- ;;; For if, the test propagates to both branches and the result.
- ;;; Look for an important optimization:
- ;;; in (if (and e1 e2 ...) e-then e-else),
- ;;; e-then can inherit already-forced information from all of the ei
- ;;; rather than only from e1.
- ;;; *** Could be smarter about the complexity, I suppose....
- ;;; *** Also could intersect already-forced results from both
- ;;; *** branches.
-
- (define-box-analysis flic-if (object already-forced uninitialized)
- (if (is-type? 'flic-and (flic-if-test-exp object))
- (box-analysis-if-and-aux object already-forced uninitialized)
- (box-analysis-if-other-aux object already-forced uninitialized)))
-
- (define (box-analysis-if-other-aux object already-forced uninitialized)
- (setf already-forced
- (do-box-analysis
- (flic-if-test-exp object) already-forced uninitialized '#t))
- (do-box-analysis (flic-if-then-exp object) already-forced uninitialized '#t)
- (do-box-analysis (flic-if-else-exp object) already-forced uninitialized '#t)
- (values already-forced '#f))
-
- (define (box-analysis-if-and-aux object already-forced uninitialized)
- (let* ((test-exp (flic-if-test-exp object))
- (subexps (flic-and-exps test-exp))
- (then-exp (flic-if-then-exp object))
- (else-exp (flic-if-else-exp object)))
- (setf (flic-exp-unboxed? test-exp) '#t)
- (multiple-value-bind (result1 resultn)
- (box-analysis-sequence subexps already-forced uninitialized)
- (do-box-analysis then-exp resultn uninitialized '#t)
- (do-box-analysis else-exp result1 uninitialized '#t)
- (values result1 '#f))))
-
-
- (define-box-analysis flic-sel (object already-forced uninitialized)
- (multiple-value-bind (result complexity)
- (do-box-analysis
- (flic-sel-exp object) already-forced uninitialized '#t)
- (values result (add-complexity sel-complexity complexity))))
-
- (define-box-analysis flic-is-constructor (object already-forced uninitialized)
- (multiple-value-bind (result complexity)
- (do-box-analysis
- (flic-is-constructor-exp object) already-forced uninitialized '#t)
- (values result (add-complexity is-constructor-complexity complexity))))
-
- (define-box-analysis flic-con-number (object already-forced uninitialized)
- (multiple-value-bind (result complexity)
- (do-box-analysis
- (flic-con-number-exp object) already-forced uninitialized '#t)
- (values result (add-complexity con-number-complexity complexity))))
-
- (define-box-analysis flic-void (object already-forced uninitialized)
- (declare (ignore object uninitialized))
- (values already-forced 0))
-
-
- ;;; This is very similar to app of flic-pack. Strictness of slot update
- ;;; expressions comes from slot strictness of constructor, and the object
- ;;; being copied is always strict.
-
- (define-box-analysis flic-update (object already-forced uninitialized)
- (let* ((con (flic-update-con object))
- (strict (con-slot-strict? con))
- (slots (flic-update-slots object))
- (exp (flic-update-exp object)))
- (multiple-value-bind (result complexity)
- (do-box-analysis exp already-forced uninitialized '#t)
- (setf already-forced result)
- (dolist (s slots)
- (let ((s? (list-ref strict (car s))))
- (multiple-value-bind (new-result new-complexity)
- (do-box-analysis (cdr s) already-forced uninitialized s?)
- (when s?
- (setf result
- (if left-to-right-evaluation
- (setf already-forced new-result)
- (union-already-forced
- new-result already-forced result))))
- (setf complexity (add-complexity complexity new-complexity)))))
- (values result complexity))))
-
-
-
-
- ;;;======================================================================
- ;;; Already-forced bookkeeping
- ;;;======================================================================
-
-
- ;;; For now, we only keep track of variables that have been forced,
- ;;; and not data structure accesses.
-
- (define (already-forced? object already-forced)
- (and (is-type? 'flic-ref object)
- (memq (flic-ref-var object) already-forced)))
-
- (define (note-already-forced object already-forced)
- (if (is-type? 'flic-ref object)
- (cons (flic-ref-var object) already-forced)
- already-forced))
-
- (define (union-already-forced new tail result)
- (cond ((eq? new tail)
- result)
- ((memq (car new) result)
- (union-already-forced (cdr new) tail result))
- (else
- (union-already-forced (cdr new) tail (cons (car new) result)))
- ))
-
-
-
-