From 4e987026148fe65c323afbc93cd560c07bf06b3f Mon Sep 17 00:00:00 2001 From: Yale AI Dept Date: Wed, 14 Jul 1993 13:08:00 -0500 Subject: Import to github. --- backend/box.scm | 417 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 417 insertions(+) create mode 100644 backend/box.scm (limited to 'backend/box.scm') diff --git a/backend/box.scm b/backend/box.scm new file mode 100644 index 0000000..c47848a --- /dev/null +++ b/backend/box.scm @@ -0,0 +1,417 @@ +;;; 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 can simply +;;; be unboxed instead of checking for a delay that must be forced. +;;; +;;; This phase may mark previously non-strict variables as strict if their +;;; initializers can be evaluated eagerly. However, doing this evaluation +;;; eagerly never causes any other non-strict variables to be forced, +;;; so there is no need to propagate this strictness information backwards +;;; (as happens in the var-strictness-walk pass). + + +;;;====================================================================== +;;; Top-level function +;;;====================================================================== + + +;;; Complexity computation + +(define-integrable delay-complexity 10) +(define-integrable unbox-complexity 1) +(define-integrable box-complexity 2) +(define-integrable sel-complexity 1) +(define-integrable is-constructor-complexity 1) +(define-integrable pack-complexity 2) +(define-integrable con-number-complexity 1) + +(define (add-complexity c1 c2) + (cond ((not c1) + '#f) + ((not c2) + '#f) + (else + ;; *** We might want to establish an arbitrary cutoff here. + ;; *** e.g., if complexity > N then set it to '#f. + (the fixnum (+ (the fixnum c1) (the fixnum c2)))))) + + + +;;; The second argument to the walker is a list of things +;;; that are known to have been forced already. +;;; The third argument is a list of variables that have not yet +;;; been initialized. +;;; Walkers return two values: a new value for already-forced and +;;; the complexity of the expression. + +;;; This helper function sets the unboxed? and cheap? bits for the +;;; code generator, and adjusts the basic complexity to account for +;;; forces, boxes, and delays. +;;; +;;; The basic decision tree for the code generator should be: +;;; if unboxed? +;;; then if strict-result? +;;; then generate x (1) +;;; else if cheap? +;;; then generate (unbox x) (2) +;;; else generate (force x) (3) +;;; else if strict-result? +;;; then if cheap? +;;; then generate (box x) (4) +;;; else generate (delay x) (5) +;;; else if cheap? +;;; then generate x (6) +;;; then generate (delay (force x)) (7) +;;; See function do-codegen in codegen.scm. + + +(define (do-box-analysis object already-forced uninitialized unboxed?) + (setf (flic-exp-unboxed? object) unboxed?) + (multiple-value-bind (result complexity) + (box-analysis object already-forced uninitialized) + (setf complexity + (if unboxed? + ;; If the expression returns a boxed value and we want + ;; an unboxed one, we may need to do a force. + (if (flic-exp-strict-result? object) + (begin ; case (1) + ;; this flic-exp-cheap? bit is used only by + ;; exp-would-be-cheap? below -- not by codegen + (setf (flic-exp-cheap? object) + (if complexity '#t '#f)) + complexity) + (if (already-forced? object already-forced) + (begin ; case (2) + (setf (flic-exp-cheap? object) '#t) + (add-complexity complexity unbox-complexity)) + (begin ; case (3) + (setf (flic-exp-cheap? object) '#f) + '#f))) + ;; We want a boxed value. If the expression already + ;; returns a boxed value, return its complexity directly; + ;; otherwise return the cost of either boxing or delaying it. + (if (flic-exp-strict-result? object) + (if complexity + (begin ; case (4) + (setf (flic-exp-cheap? object) '#t) + (add-complexity complexity box-complexity)) + (begin ; case (5) + (setf (flic-exp-cheap? object) '#f) + delay-complexity)) + (if complexity + (begin + (setf (flic-exp-cheap? object) '#t) ; case (6) + complexity) + (begin ; case (7) + (setf (flic-exp-cheap? object) '#f) + 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)) + ;; The value expression is cheap unboxed value, so mark + ;; the variable as strict. + (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))) + + +(define-box-analysis flic-ref (object already-forced uninitialized) + (values + already-forced + (if (memq (flic-ref-var object) uninitialized) + '#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)) + + + + +;;;====================================================================== +;;; 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))) + )) + + + -- cgit v1.2.3