; Copyright (c) 1993-1999 by Richard Kelsey. See file COPYING. ; Simplifying LET nodes, i.e. calls to the LET primop. ; 1. Change the procedure to a JUMP procedure if necessary. ; 2. Check that the right number of arguments are present. ; 3. Substitute any values that can be substituted without reference to ; how they are used in the body; then remove the call if it is no ; longer necessary. ; 4. Try harder. (define (simplify-let call) (let ((proc (call-arg call 0))) (if (eq? (lambda-type proc) 'jump) (change-lambda-type proc 'cont)) (cond ((n= (length (lambda-variables proc)) (- (call-arg-count call) 1)) (bug "wrong number of arguments in ~S" call)) ((or (null? (lambda-variables proc)) (substitute-let-arguments proc call quick-substitute)) (remove-body call)) (else (really-simplify-let proc call))))) ; A value can be quickly substituted if it is a leaf node or if it has no ; side-effects and is used only once. (define (quick-substitute var val) (or (literal-node? val) (reference-node? val) (and (not (side-effects? val)) (null? (cdr (variable-refs var)))))) ; Simplify the arguments and then repeatedly simplify the body of PROC ; and try substituting the arguments. ; If all the arguments can be substituted the call node is removed. ; ; SUBSTITUTE-JOIN-ARGUMENTS copies arguments in an attempt to remove ; conditionals via constant folding. (define (really-simplify-let proc call) (simplify-args call 1) (let loop () (set-node-simplified?! proc #t) (simplify-lambda-body proc) (cond ((substitute-let-arguments proc call slow-substitute) (remove-body call)) ((substitute-join-arguments proc call) (loop)) ((not (node-simplified? proc)) (loop))))) (define *duplicate-lambda-size* '-1) ; don't duplicate anything (define *duplicate-jump-lambda-size* 1) ; duplicate one call (define (slow-substitute var val) (cond ((or (literal-node? val) (reference-node? val)) #t) ((call-node? val) (let ((refs (variable-refs var))) (and (not (null? refs)) (null? (cdr refs)) (or (not (side-effects? val 'allocate)) (and (not (side-effects? val 'allocate 'read)) (not-used-between? val (car refs))))))) ((every? called-node? (variable-refs var)) (simplify-known-cont-calls (variable-refs var) val) (or (null? (cdr (variable-refs var))) (case (lambda-type val) ((proc known-proc) (small-node? val *duplicate-lambda-size*)) ((jump) (small-node? val *duplicate-jump-lambda-size*)) (else #f)))) (else #f))) ; This only detects the following situation: ; (let (lambda (... var ...) (primop ... var ...)) ; ... value ...) ; where the reference to VAR is contained within nested, non-writing calls ; This depends on there being no simple calls with WRITE side-effects (define (not-used-between? call ref) (let ((top (lambda-body (call-arg (node-parent call) 0)))) (let loop ((call (node-parent ref))) (cond ((eq? call top) #t) ((or (not (call-node? call)) (eq? 'write (primop-side-effects (call-primop call)))) #f) (else (loop (node-parent call))))))) (define (simplify-known-cont-calls refs l-node) (case (lambda-type l-node) ((proc) (determine-lambda-protocol l-node refs)) ((cont) (bug "CONT lambda bound by LET ~S" l-node))) (if (calls-known? l-node) (simplify-known-lambda l-node))) ; ($some-RETURN . ) ; => ; ($JUMP . ) ; could check argument reps as well (define (add-return-mark call l-node arg-count) (if (not (= (call-arg-count call) (+ arg-count 1))) (bug '"call ~S to join ~S has the wrong number of arguments" call l-node)) (set-call-primop! call (get-primop (enum primop jump)))) ; Removed arguments to a lambda-node in call position. ; If any arguments are actually removed ; REMOVE-NULL-ARGUMENTS shortens the argument vector. (define (substitute-let-arguments node call gone-proc) (let* ((vec (call-args call)) (c (do ((vars (lambda-variables node) (cdr vars)) (i 1 (+ i 1)) (c 0 (if (keep-var-val (car vars) (vector-ref vec i) gone-proc) c (+ 1 c)))) ((null? vars) c)))) (cond ((= (+ c 1) (call-arg-count call)) #t) ((= c 0) #f) (else (remove-unused-variables node) (remove-null-arguments call (- (call-arg-count call) c)) #f)))) (define (keep-var-val var val gone-proc) (cond ((and (unused? var) (or (not (call-node? val)) (not (side-effects? val 'allocate 'read)))) (erase (detach val)) #f) ((gone-proc var val) (substitute var val #t) #f) (else '#t))) ; VAL is simple enough to be substituted in more than one location if ; its body is a call with all leaf nodes. ; -- no longer used -- ;(define (simple-lambda? val) ; (vector-every? (lambda (n) ; (and (not (lambda-node? n)) ; (call-args (lambda-body val)))) (define (called-anywhere? var) (any? called-node? (variable-refs var)))