;;; Ikarus Scheme -- A compiler for R6RS Scheme.
;;; Copyright (C) 2006,2007,2008  Abdulaziz Ghuloum
;;; 
;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License version 3 as
;;; published by the Free Software Foundation.
;;; 
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;; 
;;; You should have received a copy of the GNU General Public License
;;; along with this program.  If not, see <http://www.gnu.org/licenses/>.


;;; Work in progress

;;; Oscar Waddell. "Extending the Scope of Syntactic Abstraction". PhD.
;;; Thesis. Indiana University Computer Science Department. August 1999.
;;; Available online: 
;;;   http://www.cs.indiana.edu/~owaddell/papers/thesis.ps.gz

(module (source-optimize optimize-level cp0-effort-limit cp0-size-limit)
  (define who 'source-optimize)
  ;;;
  (define-structure (app rand* ctxt)
    ([inlined #f]))
  ;;;
  (define-structure (operand expr env ec)
    ([value                  #f]
     [residualize-for-effect #f]
     [size                    0]
     [inner-pending          #f]
     [outer-pending          #f]))
  ;;;
  (define-structure (counter value ctxt k))
  ;;;
  (define (passive-counter)
    (make-counter (greatest-fixnum) #f
      (lambda args 
        (error 'passive-counter "invalid abort"))))
  ;;;
  (define (passive-counter-value x)
    (- (greatest-fixnum) (counter-value x)))
  ;;;
  (define (active-counter? x)
    (and (counter? x) (counter-ctxt x)))
  ;;;
  (define (decrement x amt)
    (let ([n (- (counter-value x) amt)])
      (set-counter-value! x n)
      (when (< n 0)
        (reset-integrated! (counter-ctxt x))
        ((counter-k x) #f))))
  ;;;
  (define (abort-counter! x)
    (reset-integrated! (counter-ctxt x))
    ((counter-k x) #f))
  ;;;
  (define (reset-integrated! ctxt)
    (set-app-inlined! ctxt #f)
    (let ([ctxt (app-ctxt ctxt)])
      (when (app? ctxt)
        (reset-integrated! ctxt))))
  ;;;
  ;;;
  (module (with-extended-env copy-var)
    (define (copy-var x)
      (let ([y (make-prelex (prelex-name x) #f)])
        (set-prelex-source-referenced?! y
          (prelex-source-referenced? x))
        (set-prelex-source-assigned?! y
          (prelex-source-assigned? x))
        (let ([loc (prelex-global-location x)])
          (when loc
            (set-prelex-global-location! y loc)
            (set-prelex-source-referenced?! y #t)
            (set-prelex-residual-referenced?! y #t)))
        y))
    (define (extend env lhs* rands) 
      (if (null? lhs*)
          (values env '())
          (let ([nlhs* (map copy-var lhs*)])
            (when rands 
              (for-each 
                (lambda (lhs rhs) 
                  (set-prelex-operand! lhs rhs))
                nlhs* rands))
            (values (vector lhs* nlhs* env) nlhs*))))
    (define (copy-back ls)
      (for-each 
        (lambda (x)
          (set-prelex-source-assigned?! x 
             (prelex-residual-assigned? x))
          (set-prelex-source-referenced?! x 
             (prelex-residual-referenced? x)))
        ls))
    (define-syntax with-extended-env
      (syntax-rules ()
        [(_ ((e2 args2) (e1 args1 rands)) b b* ...)
         (let-values ([(e2 args2) (extend e1 args1 rands)])
           (let ([v (let () b b* ...)])
             (copy-back args2)
             v))])))

  (define cp0-effort-limit (make-parameter 50))
  (define cp0-size-limit (make-parameter 8))

  (define primitive-info-list
    '(
      [(cons _ _)                           effect-free result-true]
      [(cons* _)                   foldable effect-free            ]
      [(cons* _ . _)                        effect-free result-true]
      [(list)                      foldable effect-free result-true]
      [(list . _)                           effect-free result-true]
      [(reverse ())                foldable effect-free result-true]
      [(string)                    foldable effect-free result-true]
      [(string . _)                                     result-true]
      [(make-string 0)             foldable effect-free result-true]
      [(make-string 0 _)           foldable effect-free result-true]
      [(make-string . _)                                result-true]
      [(make-bytevector 0)         foldable effect-free result-true]
      [(make-bytevector 0 _)       foldable             result-true]
      [(make-bytevector . _)                            result-true]
      [(string-length _)           foldable             result-true]
      [(string-ref _ _)            foldable             result-true]
      [(vector)                    foldable effect-free result-true]
      [(vector . _)                         effect-free result-true]
      [(make-vector 0)             foldable effect-free result-true]
      [(make-vector 0 _)           foldable effect-free result-true]
      [(make-vector . _)                                result-true]
      [(vector-length _)           foldable             result-true]
      [(vector-ref _ _)            foldable                        ]
      [(eq? _ _)                   foldable effect-free            ]
      [(eqv? _ _)                  foldable effect-free            ]
      [(assq _ _)                  foldable                        ]
      [(assv _ _)                  foldable                        ]
      [(assoc _ _)                 foldable                        ]
      [(not _)                     foldable effect-free            ]
      [(null? _)                   foldable effect-free            ]
      [(pair? _)                   foldable effect-free            ]
      [(fixnum? _)                 foldable effect-free            ]
      [(vector? _)                 foldable effect-free            ]
      [(string? _)                 foldable effect-free            ]
      [(char? _)                   foldable effect-free            ]
      [(symbol? _)                 foldable effect-free            ]
      [(procedure? _)              foldable effect-free            ]
      [(eof-object? _)             foldable effect-free            ]
      [(flonum? _)                 foldable effect-free            ]
      [(cflonum? _)                foldable effect-free            ]
      [(compnum? _)                foldable effect-free            ]
      [(integer? _)                foldable effect-free            ]
      [(bignum? _)                 foldable effect-free            ]
      [(ratnum? _)                 foldable effect-free            ]
      [(void)                      foldable effect-free result-true]
      [(car _)                     foldable                        ]
      [(cdr _)                     foldable                        ]
      [(caar _)                    foldable                        ]
      [(cadr _)                    foldable                        ]
      [(cdar _)                    foldable                        ]
      [(cddr _)                    foldable                        ]
      [(caaar _)                   foldable                        ]
      [(caadr _)                   foldable                        ]
      [(cadar _)                   foldable                        ]
      [(caddr _)                   foldable                        ]
      [(cdaar _)                   foldable                        ]
      [(cdadr _)                   foldable                        ]
      [(cddar _)                   foldable                        ]
      [(cdddr _)                   foldable                        ]
      [(caaaar _)                  foldable                        ]
      [(caaadr _)                  foldable                        ]
      [(caadar _)                  foldable                        ]
      [(caaddr _)                  foldable                        ]
      [(cadaar _)                  foldable                        ]
      [(cadadr _)                  foldable                        ]
      [(caddar _)                  foldable                        ]
      [(cadddr _)                  foldable                        ]
      [(cdaaar _)                  foldable                        ]
      [(cdaadr _)                  foldable                        ]
      [(cdadar _)                  foldable                        ]
      [(cdaddr _)                  foldable                        ]
      [(cddaar _)                  foldable                        ]
      [(cddadr _)                  foldable                        ]
      [(cdddar _)                  foldable                        ]
      [(cddddr _)                  foldable                        ]
      [(memq _ _)                  foldable                        ]
      [(memv _ _)                  foldable                        ]
      [(length _)                  foldable             result-true]
      [(+ . _)                     foldable             result-true]
      [(* . _)                     foldable             result-true]
      [(/ _ . _)                   foldable             result-true]
      [(- _ . _)                   foldable             result-true]
      [(fx+ _ _)                   foldable             result-true]
      [(fx- _ _)                   foldable             result-true]
      [(fx* _ _)                   foldable             result-true]
      [(fxior . _)                 foldable             result-true]
      [(fxlogor . _)               foldable             result-true]
      [(fxnot _)                   foldable             result-true]
      [(fxadd1 _)                  foldable             result-true]
      [(fxsub1 _)                  foldable             result-true]
      [(fxzero? _)                 foldable                        ]
      [(fx=? _ . _)                foldable                        ] 
      [(fx<? _ . _)                foldable                        ] 
      [(fx<=? _ . _)               foldable                        ] 
      [(fx>? _ . _)                foldable                        ] 
      [(fx>=? _ . _)               foldable                        ]
      [(fx= _ . _)                 foldable                        ] 
      [(fx< _ . _)                 foldable                        ] 
      [(fx<= _ . _)                foldable                        ] 
      [(fx> _ . _)                 foldable                        ] 
      [(fx>= _ . _)                foldable                        ]
      [(real-part _)               foldable             result-true]
      [(imag-part _)               foldable             result-true]
      [(fxsll _ _)                 foldable             result-true]
      [(fxsra _ _)                 foldable             result-true]
      [(fxremainder _ _)           foldable             result-true]
      [(fxquotient _ _)            foldable             result-true]
      [(greatest-fixnum)           foldable effect-free result-true]
      [(least-fixnum)              foldable effect-free result-true]
      [(fixnum-width)              foldable effect-free result-true]
      [(char->integer _)           foldable             result-true]
      [(integer->char _)           foldable             result-true]
      [(eof-object)                foldable effect-free result-true]
      [(zero? _)                   foldable                        ]
      [(= _ . _)                   foldable                        ] 
      [(< _ . _)                   foldable                        ] 
      [(<= _ . _)                  foldable                        ] 
      [(> _ . _)                   foldable                        ] 
      [(>= _ . _)                  foldable                        ]
      [(expt _ _)                  foldable             result-true]
      [(log _)                     foldable             result-true]
      [(sll _ _)                   foldable             result-true]
      [(sra _ _)                   foldable             result-true]
      [(inexact _)                 foldable             result-true]
      [(exact _)                   foldable             result-true]
      [(add1 _)                    foldable             result-true]
      [(sub1 _)                    foldable             result-true]
      [(bitwise-and _ _)           foldable             result-true]
      [(make-rectangular _ _)      foldable             result-true]
      [(sin _)                     foldable             result-true]
      [(cos _)                     foldable             result-true]
      [(tan _)                     foldable             result-true]
      [(asin _)                    foldable             result-true]
      [(acos _)                    foldable             result-true]
      [(atan _)                    foldable             result-true]
      [(make-eq-hashtable)                  effect-free result-true]
      [(string->number _)          foldable                        ]
      [(string->number _ _)        foldable                        ]
      [($fixnum->flonum _)         foldable effect-free result-true]
      [($char->fixnum _)           foldable effect-free result-true]
      [($fixnum->char _)           foldable effect-free result-true]
      [($fxzero? _)                foldable effect-free            ]
      [($fx+ _ _)                  foldable effect-free result-true]
      [($fx* _ _)                  foldable effect-free result-true]
      [($fx- _ _)                  foldable effect-free result-true]
      [($fx= _ _)                  foldable effect-free            ]
      [($fx>= _ _)                 foldable effect-free            ]
      [($fx> _ _)                  foldable effect-free            ]
      [($fx<= _ _)                 foldable effect-free            ]
      [($fx< _ _)                  foldable effect-free            ]
      [($car _)                    foldable effect-free            ]
      [($cdr _)                    foldable effect-free            ]
      [($struct-ref _ _)           foldable effect-free            ]
      [($struct/rtd? _ _)          foldable effect-free            ]
      [($fxsll _ _)                foldable effect-free result-true]
      [($fxsra _ _)                foldable effect-free result-true]
      [($fxlogor _ _)              foldable effect-free result-true]
      [($fxlogand _ _)             foldable effect-free result-true]
      [($fxadd1 _)                 foldable effect-free result-true]
      [($fxsub1 _)                 foldable effect-free result-true]
      [($vector-length _)          foldable effect-free result-true]
      [($vector-ref _ _)           foldable effect-free result-true]
      [($make-bytevector 0)        foldable effect-free result-true]
      [($make-bytevector 0 _)      foldable effect-free result-true]
      [($make-bytevector . _)               effect-free result-true]
      [($bytevector-u8-ref _ _)    foldable effect-free result-true]
      [($bytevector-length _)      foldable effect-free result-true]
      ;;;
      [(annotation? #f)             foldable effect-free result-false]
      [(annotation-stripped #f)     foldable effect-free result-false]
      ;;; unoptimizable
      [(condition . _)]
      [($make-flonum . _)]
      [(top-level-value . _)]
      [($struct . _)]
      [(make-message-condition . _)]
      [(make-lexical-violation . _)]
      [(make-who-condition . _)]
      [(make-error . _)]
      [(make-i/o-error . _)]
      [(make-i/o-write-error . _)]
      [(make-i/o-read-error . _)]
      [(make-i/o-file-already-exists-error . _)]
      [(make-i/o-file-is-read-only-error . _)]
      [(make-i/o-file-protection-error . _)]
      [(make-i/o-file-does-not-exist-error . _)]
      [(make-undefined-violation . _)]
      [(die . _)]
      [(gensym . _)]
      [(values . _)]
      [(error . _)]
      [(assertion-violation . _)]
      [(console-input-port . _)]
      [(console-output-port . _)]
      [(console-error-port . _)]
      [(printf . _)] ;;; FIXME: reduce to display
      [(newline . _)]
      [(native-transcoder . _)]
      [(open-string-output-port . _)]
      [(open-string-input-port . _)]
      [(environment . _)]
      [(print-gensym . _)]
      [(exit . _)]
      [(interrupt-handler . _)]
      [(display . _)]
      [(write-char . _)]
      [(current-input-port . _)]
      [(current-output-port . _)]
      [(current-error-port . _)]
      [(standard-input-port . _)]
      [(standard-output-port . _)]
      [(standard-error-port . _)]
      [($current-frame . _)]
      [(pretty-width . _)]
      [($fp-at-base . _)]
      [(read-annotated . _)]
      [($collect-key . _)]
      [(make-non-continuable-violation . _)]
      [(format . _)] ;;; FIXME, reduce to string-copy
      [(uuid . _)]
      [(print-graph . _)]
      [(interaction-environment . _)]
      [(make-guardian)]
      [(command-line-arguments)]
      [(make-record-type-descriptor . _)] ;;; FIXME
      [(make-assertion-violation . _)]
      [(new-cafe . _)]
      [(getenv . _)]
      [(gensym-prefix . _)]
      [($arg-list . _)]
      [($make-symbol . _)]
      [(string->utf8 . _)]
      [($make-call-with-values-procedure . _)]
      [($make-values-procedure . _)]
      [($unset-interrupted! . _)]
      [(make-interrupted-condition . _)]
      [($interrupted? . _)]
      [($symbol-value . _)]
      [(library-extensions . _)]
      [(base-rtd . _)]
      [($data->transcoder . _)]
      [(current-time . _)]
    ))
 
  (module (primprop)
    (define-syntax ct-gensym
      (lambda (x)
        (with-syntax ([g (datum->syntax #'here (gensym))])
          #'(quote g))))
    (define g (ct-gensym))
    (define (primprop p)
      (or (getprop p g) '()))
    (define (get prim ls)
      (cond
        [(null? ls) (values '() '())]
        [else
         (let ([a (car ls)])
           (let ([cc (car a)])
             (cond
               [(eq? (car cc) prim)
                (let-values ([(p* ls) (get prim (cdr ls))])
                  (values (cons (cons (cdr cc) (cdr a)) p*) ls))]
               [else (values '() ls)])))]))
    (let f ([ls primitive-info-list])
      (unless (null? ls)
        (let ([a (car ls)])
          (let ([cc (car a)] [cv (cdr a)])
            (let ([prim (car cc)] [args (cdr cc)])
              (let-values ([(p* ls) (get prim (cdr ls))])
                (putprop prim g 
                  (cons (cons args cv) p*))
                (f ls))))))))
  (define (primitive-info op args)
    (define (matches? x)
      (let f ([args args] [params (car x)])
        (cond
          [(pair? params)
           (and (pair? args)
                (case (car params)
                  [(_) (f (cdr args) (cdr params))]
                  [(#f 0 ()) 
                   (let ([v (value-visit-operand! (car args))])
                     (and (constant? v)
                          (equal? (constant-value v) (car params))
                          (f (cdr args) (cdr params))))]
                  [else
                   (error 'primitive-info "cannot happen" op (car params))]))]
          [(eq? params '_) #t]
          [(null? params) (null? args)]
          [else (error 'primitive-info "cannot happen" op params)])))
    (cond
      [(find matches? (primprop op))]
      [else '()]))

  (define (info-foldable? info) (memq 'foldable info))
  (define (info-effect-free? info) (memq 'effect-free info))
  (define (info-result-true? info) (memq 'result-true info))
  (define (info-result-false? info) (memq 'result-false info))

  (define-syntax ctxt-case
    (lambda (stx)
      (define (test x)
        (case (syntax->datum x)
          [(p)   #'(eq? t 'p)]
          [(v)   #'(eq? t 'v)]
          [(e)   #'(eq? t 'e)]
          [(app) #'(app? t)]
          [else (syntax-violation stx "invalid ctxt" x)]))
      (define (extract cls*)
        (syntax-case cls* (else)
          [() #'(error 'extract "unmatched ctxt" t)]
          [([else e e* ...]) #'(begin e e* ...)]
          [([(t* ...) e e* ...] rest ...) 
           (with-syntax ([(t* ...) (map test #'(t* ...))]
                         [body (extract #'(rest ...))])
             #'(if (or t* ...) 
                   (begin e e* ...)
                   body))]))
      (syntax-case stx ()
        [(_ expr cls* ...)
         (with-syntax ([body (extract #'(cls* ...))])
           #'(let ([t expr])
               body))])))
  (define (mkseq e0 e1)
    ;;; returns a (seq e0 e1) with a seq-less e1 if both 
    ;;; e0 and e1 are constructed properly.
    (if (simple? e0)
        e1
        (let ([e0 (struct-case e0
                    [(seq e0a e0b) (if (simple? e0b) e0a e0)]
                    [else e0])])
          (struct-case e1
            [(seq e1a e1b) (make-seq (make-seq e0 e1a) e1b)]
            [else (make-seq e0 e1)]))))
  ;;; simple?: check quickly whether something is effect-free
  (define (simple? x) 
    (struct-case x
      [(constant) #t]
      [(prelex)   #t]
      [(primref)  #t]
      [(clambda)  #t]
      [else       #f]))
  ;;; result returns the "last" value of an expression
  (define (result-expr x)
    (struct-case x
      [(seq e0 e1) e1]
      [else        x]))
  ;;;
  (define (records-equal? x y ctxt)
    (struct-case x
      [(constant kx)
       (struct-case y
         [(constant ky)
          (ctxt-case ctxt
            [(e) #t]
            [(p) (if kx ky (not ky))]
            [else (eq? kx ky)])]
         [else #f])]
      [else #f]))
  ;;;
  (define (residualize-operands e rand* sc)
    (cond
      [(null? rand*) e]
      [(not (operand-residualize-for-effect (car rand*)))
       (residualize-operands e (cdr rand*) sc)]
      [else
       (let ([opnd (car rand*)])
         (let ([e1 (or (operand-value opnd)
                       (struct-case opnd
                         [(operand expr env ec)
                          (E expr 'e env ec sc)]))])
           (if (simple? e1)
               (residualize-operands e (cdr rand*) sc)
               (begin
                 (decrement sc (operand-size opnd))
                 (mkseq e1 (residualize-operands e (cdr rand*) sc))))))]))
  (define (value-visit-operand! rand)
    (or (operand-value rand)
        (let ([sc (passive-counter)])
          (let ([e (struct-case rand
                     [(operand expr env ec) 
                      (E expr 'v env sc ec)])])
            (set-operand-value! rand e)
            (set-operand-size! rand (passive-counter-value sc))
            e))))
  (define (score-value-visit-operand! rand sc)
    (let ([val (value-visit-operand! rand)])
      (let ([score (operand-size rand)])
        (decrement sc score))
      val))
  (define (E-call rator rand* env ctxt ec sc)
    (let ([ctxt (make-app rand* ctxt)])
      (let ([rator (E rator ctxt env ec sc)])
        (if (app-inlined ctxt)
            (residualize-operands rator rand* sc)
            (begin
              (decrement sc (if (primref? rator) 1 3))
              (make-funcall rator
                (map (lambda (x) (score-value-visit-operand! x sc))
                     rand*)))))))
  ;;;
  (define (E-debug-call ctxt ec sc)
    (let ([rand* (app-rand* ctxt)])
      (cond
        [(< (length rand*) 2)
         (decrement sc 1)
         (make-primref 'debug-call)]
        [else
         (let ([src/expr (car rand*)]
               [rator (cadr rand*)]
               [rands (cddr rand*)])
           (let ([ctxt2 (make-app rands (app-ctxt ctxt))])
             (let ([rator (E (operand-expr rator) 
                             ctxt2 
                             (operand-env rator)
                             (operand-ec rator) 
                             sc)])
               (if (app-inlined ctxt2)
                   (begin
                     (set-app-inlined! ctxt #t)
                     (residualize-operands rator (cons src/expr rands) sc))
                   (begin
                     (decrement sc 1)
                     (make-primref 'debug-call))))))])))
  ;;;
  (define (E-var x ctxt env ec sc)
    (ctxt-case ctxt
      [(e) (make-constant (void))]
      [else 
       (let ([x (lookup x env)])
         (let ([opnd (prelex-operand x)])
           (if (and opnd (not (operand-inner-pending opnd)))
               (begin
                 (dynamic-wind
                   (lambda () (set-operand-inner-pending! opnd #t))
                   (lambda () (value-visit-operand! opnd))
                   (lambda () (set-operand-inner-pending! opnd #f)))
                 (if (prelex-source-assigned? x)
                     (residualize-ref x sc)
                     (copy x opnd ctxt ec sc)))
               (residualize-ref x sc))))]))
  ;;;
  (define (copy x opnd ctxt ec sc)
    (let ([rhs (result-expr (operand-value opnd))])
      (struct-case rhs
        [(constant) rhs]
        [(prelex)
         (if (prelex-source-assigned? rhs)
             (residualize-ref x sc)
             (let ([opnd (prelex-operand rhs)])
               (if (and opnd (operand-value opnd))
                   (copy2 rhs opnd ctxt ec sc)
                   (residualize-ref rhs sc))))]
        [else (copy2 x opnd ctxt ec sc)])))
  ;;;
  (define (copy2 x opnd ctxt ec sc)
    (let ([rhs (result-expr (operand-value opnd))])
      (struct-case rhs
        [(clambda) 
         (ctxt-case ctxt
           [(v) (residualize-ref x sc)]
           [(p) (make-constant #t)]
           [(e) (make-constant (void))]
           [(app) 
            (or (and (not (operand-outer-pending opnd))
                     (dynamic-wind
                       (lambda () (set-operand-outer-pending! opnd #t))
                       (lambda ()
                         (call/cc
                           (lambda (abort)
                             (inline rhs ctxt empty-env 
                               (if (active-counter? ec)
                                   ec
                                   (make-counter
                                     (cp0-effort-limit)
                                     ctxt abort))
                               (make-counter 
                                 (if (active-counter? sc)
                                     (counter-value sc)
                                     (cp0-size-limit))
                                 ctxt abort)))))
                       (lambda () (set-operand-outer-pending! opnd #f))))
                (residualize-ref x sc))])]
        [(primref p)
         (ctxt-case ctxt
           [(v) rhs]
           [(p) (make-constant #t)]
           [(e) (make-constant (void))]
           [(app) (fold-prim p ctxt ec sc)])]
        [else (residualize-ref x sc)])))
  (define (inline proc ctxt env ec sc)
    (define (get-case cases rand*)
      (define (compatible? x)
        (struct-case (clambda-case-info x) 
          [(case-info label args proper)
           (cond
             [proper (= (length rand*) (length args))]
             [else (>= (length rand*) (- (length args) 1))])]))
      (cond
        [(memp compatible? cases) => car]
        [else #f]))
    (define (partition args rand*)
      (cond
        [(null? (cdr args))
         (let ([r (car args)])
           (let ([t* (map (lambda (x) (copy-var r)) rand*)])
             (values '() t* r)))]
        [else
         (let ([x (car args)])
           (let-values ([(x* t* r) (partition (cdr args) (cdr rand*))])
             (values (cons x x*) t* r)))]))
    (struct-case proc
      [(clambda g cases cp free name)
       (let ([rand* (app-rand* ctxt)])
         (struct-case (get-case cases rand*)
           [(clambda-case info body)
            (struct-case info
              [(case-info label args proper) 
               (cond
                 [proper
                  (with-extended-env ((env args) (env args rand*))
                    (let ([body (E body (app-ctxt ctxt) env ec sc)])
                      (let ([result (make-let-binding args rand* body sc)])
                        (set-app-inlined! ctxt #t)
                        result)))]
                 [else 
                  (let-values ([(x* t* r) (partition args rand*)])
                    (with-extended-env ((env a*)
                                        (env (append x* t*) rand*))
                      (let ([rarg (make-operand 
                                    (make-funcall (make-primref 'list) t*)
                                    env ec)])
                        (with-extended-env ((env b*)
                                            (env (list r) (list rarg)))
                          (let ([result
                                 (make-let-binding a* rand*
                                   (make-let-binding b* (list rarg)
                                     (E body (app-ctxt ctxt) env ec sc)
                                     sc)
                                   sc)])
                            (set-app-inlined! ctxt #t)
                            result)))))])])]
           [else
             (E proc 'v env ec sc)]))]))
  ;;;
  (define (do-bind lhs* rhs* body ctxt env ec sc)
    (let ([rand* (map (lambda (x) (make-operand x env ec)) rhs*)])
      (with-extended-env ((env lhs*) (env lhs* rand*))
        (residualize-operands
          (make-let-binding lhs* rand*
            (E body ctxt env ec sc)
            sc)
          rand* sc))))
  ;;;
  (define (make-let-binding var* rand* body sc)
    (define (process1 var rand lhs* rhs*)
      (cond
        [(prelex-residual-referenced? var)
         (values
            (cons var lhs*)
            (cons (score-value-visit-operand! rand sc) rhs*))]
        [(prelex-residual-assigned? var)
         (set-operand-residualize-for-effect! rand #t)
         (values 
            (cons var lhs*)
            (cons (make-constant (void)) rhs*))]
        [else
         (set-operand-residualize-for-effect! rand #t)
         (values lhs* rhs*)]))
    (define (process var* rand*)
      (cond
        [(null? var*) (values '() '())]
        [else
         (let ([var (car var*)] [rand (car rand*)])
           (let-values ([(lhs* rhs*) (process (cdr var*) (cdr rand*))])
             (process1 var rand lhs* rhs*)))]))
    (let-values ([(lhs* rhs*) (process var* rand*)])
       (if (null? lhs*) body (make-bind lhs* rhs* body))))
  ;;;
  (define (fold-prim p ctxt ec sc)
    (define (get-value p ls)
      (call/cc
        (lambda (k)
          (with-exception-handler 
            (lambda (con) 
              (decrement ec 10)
              (k #f))
            (lambda () 
              (make-constant (apply (system-value p) ls)))))))
    (let ([rand* (app-rand* ctxt)])
      (let ([info (primitive-info p rand*)])
        (let ([result
               (or (and (info-effect-free? info)
                        (ctxt-case (app-ctxt ctxt)
                          [(e) (make-constant (void))]
                          [(p) 
                           (cond
                             [(info-result-true? info)
                              (make-constant #t)]
                             [(info-result-false? info)
                              (make-constant #f)]
                             [else #f])]
                          [else #f]))
                   (and (info-foldable? info)
                        (let ([val*
                               (map (lambda (x) (value-visit-operand! x)) rand*)])
                          (cond
                            [(andmap constant? val*)
                             (get-value p (map constant-value val*))]
                            [else #f]))))])
          (if result
              (begin
                (decrement ec 1)
                (for-each
                  (lambda (x)
                    (set-operand-residualize-for-effect! x #t))
                  rand*)
                (set-app-inlined! ctxt #t)
                result)
              (begin
                (decrement sc 1)
                (make-primref p)))))))
  ;;;
  (define (residualize-ref x sc)
    (decrement sc 1)
    (set-prelex-residual-referenced?! x #t)
    x)
  ;;;
  (define (build-conditional e0 e1 e2)
    (or (struct-case e0
          [(funcall rator rand*) 
           (struct-case rator 
             [(primref op) 
              (and (eq? op 'not)
                   (= (length rand*) 1)
                   (build-conditional (car rand*) e2 e1))]
             [else #f])]
          [else #f])
        (make-conditional e0 e1 e2)))
         
  (define (E x ctxt env ec sc)
    (decrement ec 1)
    (struct-case x
      [(constant) (decrement sc 1) x]
      [(prelex) (E-var x ctxt env ec sc)]
      [(seq e0 e1)
       (mkseq (E e0 'e env ec sc) (E e1 ctxt env ec sc))]
      [(conditional e0 e1 e2)
       (let ([e0 (E e0 'p env ec sc)])
         (struct-case (result-expr e0)
           [(constant k) 
            (mkseq e0 (E (if k e1 e2) ctxt env ec sc))]
           [else 
            (let ([ctxt (ctxt-case ctxt [(app) 'v] [else ctxt])])
              (let ([e1 (E e1 ctxt env ec sc)]
                    [e2 (E e2 ctxt env ec sc)])
                (if (records-equal? e1 e2 ctxt)
                    (mkseq e0 e1)
                    (begin 
                      (decrement sc 1)
                      (build-conditional e0 e1 e2)))))]))]
      [(assign x v)
       (mkseq
         (let ([x (lookup x env)])
           (cond
             [(not (prelex-source-referenced? x))
              ;;; dead on arrival
              (E v 'e env ec sc)]
             [else
              (decrement sc 1)
              (set-prelex-residual-assigned?! x
                (prelex-source-assigned? x))
              (make-assign x (E v 'v env ec sc))]))
         (make-constant (void)))]
      [(funcall rator rand*)
       (E-call rator 
         (map (lambda (x) (make-operand x env ec)) rand*)
         env ctxt ec sc)]
      [(forcall name rand*)
       (decrement sc 1)
       (make-forcall name (map (lambda (x) (E x 'v env ec sc)) rand*))]
      [(primref name)
       (ctxt-case ctxt
         [(app) 
          (case name
            [(debug-call) (E-debug-call ctxt ec sc)]
            [else (fold-prim name ctxt ec sc)])]
         [(v) (decrement sc 1) x]
         [else (make-constant #t)])]
      [(clambda g cases cp free name) 
       (ctxt-case ctxt
         [(app) (inline x ctxt env ec sc)]
         [(p e) (make-constant #t)]
         [else
          (decrement sc 2)
          (make-clambda (gensym)
            (map 
              (lambda (x)
                (struct-case x
                  [(clambda-case info body)
                   (struct-case info
                     [(case-info label args proper) 
                      (with-extended-env ((env args) (env args #f))
                        (make-clambda-case 
                          (make-case-info (gensym) args proper)
                          (E body 'v env ec sc)))])]))
              cases)
            cp free name)])]
      [(bind lhs* rhs* body) 
       (do-bind lhs* rhs* body ctxt env ec sc)]
      [(fix lhs* rhs* body)
       (with-extended-env ((env lhs*) (env lhs* #f))
         (for-each
           (lambda (lhs rhs)
             (set-prelex-operand! lhs (make-operand rhs env ec)))
            lhs* rhs*)
         (let ([body (E body ctxt env ec sc)])
           (let ([lhs* (remp 
                         (lambda (x)
                           (not (prelex-residual-referenced? x)))
                         lhs*)])
             (cond
               [(null? lhs*) body]
               [else
                (decrement sc 1)
                (make-fix lhs* 
                  (map (lambda (x)
                         (let ([opnd (prelex-operand x)])
                           (decrement sc (+ (operand-size opnd) 1))
                           (value-visit-operand! opnd)))
                       lhs*)
                  body)]))))]
      [else
       (error who "invalid expression" x)]))
  (define empty-env '())
  (define (lookup x env)
    (cond
      [(vector? env)
       (let f ([lhs* (vector-ref env 0)] [rhs* (vector-ref env 1)])
         (cond
           [(null? lhs*) (lookup x (vector-ref env 2))]
           [(eq? x (car lhs*)) (car rhs*)]
           [else (f (cdr lhs*) (cdr rhs*))]))]
      [else x]))
  (define optimize-level 
    (make-parameter 1
      (lambda (x)
        (if (memv x '(0 1 2))
            x
            (die 'optimize-level "valid levels are 0, 1, and 2")))))
  (define (source-optimize expr)
    (define (source-optimize expr)
      (E expr 'v empty-env (passive-counter) (passive-counter)))
    (case (optimize-level)
      [(2) (source-optimize expr)]
      [(1) 
       (parameterize ([cp0-size-limit 0])
         (source-optimize expr))]
      [else expr]))
)




