Merge commit '58147d67806e1f54c447d7eabac35b1a5086c3a6'
[bpt/guile.git] / module / language / tree-il / optimize.scm
dissimilarity index 95%
index 2503e14..4fb8f59 100644 (file)
-;;; Tree-il optimizer
-
-;; Copyright (C) 2009, 2011 Free Software Foundation, Inc.
-
-;;;; This library is free software; you can redistribute it and/or
-;;;; modify it under the terms of the GNU Lesser General Public
-;;;; License as published by the Free Software Foundation; either
-;;;; version 3 of the License, or (at your option) any later version.
-;;;; 
-;;;; This library 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
-;;;; Lesser General Public License for more details.
-;;;; 
-;;;; You should have received a copy of the GNU Lesser General Public
-;;;; License along with this library; if not, write to the Free Software
-;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-;;; Code:
-
-(define-module (language tree-il optimize)
-  #:use-module (language tree-il)
-  #:use-module (language tree-il primitives)
-  #:use-module (language tree-il inline)
-  #:use-module (language tree-il fix-letrec)
-  #:use-module (ice-9 vlist)
-  #:use-module (ice-9 match)
-  #:use-module (srfi srfi-1)
-  #:use-module (srfi srfi-11)
-  #:use-module (srfi srfi-26)
-  #:export (optimize!))
-
-(define (optimize! x env opts)
-  (let ((peval (match (memq #:partial-eval? opts)
-                 ((#:partial-eval? #f _ ...)
-                  ;; Disable partial evaluation.
-                  (lambda (x e) x))
-                 (_ peval))))
-   (inline!
-    (fix-letrec!
-     (peval (expand-primitives! (resolve-primitives! x env))
-            env)))))
-
-\f
-;;;
-;;; Partial evaluation.
-;;;
-
-(define (fresh-gensyms syms)
-  (map (lambda (x) (gensym (string-append (symbol->string x) " ")))
-       syms))
-
-(define (alpha-rename exp)
-  "Alpha-rename EXP.  For any lambda in EXP, generate new symbols and
-replace all lexical references to the former symbols with lexical
-references to the new symbols."
-  ;; XXX: This should be factorized somehow.
-  (let loop ((exp     exp)
-             (mapping vlist-null))             ; maps old to new gensyms
-    (match exp
-      (($ <lambda-case> src req opt rest kw inits gensyms body alt)
-       ;; Create new symbols to replace GENSYMS and propagate them down
-       ;; in BODY and ALT.
-       (let* ((new     (fresh-gensyms
-                        (append req
-                                (or opt '())
-                                (if rest (list rest) '())
-                                (if kw (map cadr (cdr kw)) '()))))
-              (mapping (fold vhash-consq mapping gensyms new)))
-         (make-lambda-case src req opt rest kw inits new
-                           (loop body mapping)
-                           (and alt (loop alt mapping)))))
-      (($ <lexical-ref> src name gensym)
-       ;; Possibly replace GENSYM by the new gensym defined in MAPPING.
-       (let ((val (vhash-assq gensym mapping)))
-         (if val
-             (make-lexical-ref src name (cdr val))
-             exp)))
-      (($ <lambda> src meta body)
-       (make-lambda src meta (loop body mapping)))
-      (($ <let> src names gensyms vals body)
-       ;; As for `lambda-case' rename GENSYMS to avoid any collision.
-       (let* ((new     (fresh-gensyms names))
-              (mapping (fold vhash-consq mapping gensyms new))
-              (vals    (map (cut loop <> mapping) vals))
-              (body    (loop body mapping)))
-         (make-let src names new vals body)))
-      (($ <letrec> src in-order? names gensyms vals body)
-       ;; Likewise.
-       (let* ((new     (fresh-gensyms names))
-              (mapping (fold vhash-consq mapping gensyms new))
-              (vals    (map (cut loop <> mapping) vals))
-              (body    (loop body mapping)))
-         (make-letrec src in-order? names new vals body)))
-      (($ <fix> src names gensyms vals body)
-       ;; Likewise.
-       (let* ((new     (fresh-gensyms names))
-              (mapping (fold vhash-consq mapping gensyms new))
-              (vals    (map (cut loop <> mapping) vals))
-              (body    (loop body mapping)))
-         (make-fix src names new vals body)))
-      (($ <let-values> src exp body)
-       (make-let-values src (loop exp mapping) (loop body mapping)))
-      (($ <const>)
-       exp)
-      (($ <void>)
-       exp)
-      (($ <toplevel-ref>)
-       exp)
-      (($ <module-ref>)
-       exp)
-      (($ <primitive-ref>)
-       exp)
-      (($ <toplevel-set> src name exp)
-       (make-toplevel-set src name (loop exp mapping)))
-      (($ <toplevel-define> src name exp)
-       (make-toplevel-define src name (loop exp mapping)))
-      (($ <module-set> src mod name public? exp)
-       (make-module-set src mod name public? (loop exp mapping)))
-      (($ <dynlet> src fluids vals body)
-       (make-dynlet src
-                    (map (cut loop <> mapping) fluids)
-                    (map (cut loop <> mapping) vals)
-                    (loop body mapping)))
-      (($ <dynwind> src winder body unwinder)
-       (make-dynwind src
-                     (loop winder mapping)
-                     (loop body mapping)
-                     (loop unwinder mapping)))
-      (($ <dynref> src fluid)
-       (make-dynref src (loop fluid mapping)))
-      (($ <conditional> src condition subsequent alternate)
-       (make-conditional src
-                         (loop condition mapping)
-                         (loop subsequent mapping)
-                         (loop alternate mapping)))
-      (($ <application> src proc args)
-       (make-application src (loop proc mapping)
-                         (map (cut loop <> mapping) args)))
-      (($ <sequence> src exps)
-       (make-sequence src (map (cut loop <> mapping) exps))))))
-
-(define-syntax-rule (let/ec k e e* ...)
-  (let ((tag (make-prompt-tag)))
-    (call-with-prompt
-     tag
-     (lambda ()
-       (let ((k (lambda args (apply abort-to-prompt tag args))))
-         e e* ...))
-     (lambda (_ res) res))))
-
-(define (tree-il-any proc exp)
-  (let/ec k
-    (tree-il-fold (lambda (exp res) #f)
-                  (lambda (exp res)
-                    (let ((res (proc exp)))
-                      (if res (k res) #f)))
-                  (lambda (exp res) #f)
-                  #f exp)))
-
-(define (code-contains-calls? body proc lookup)
-  "Return true if BODY contains calls to PROC.  Use LOOKUP to look up
-lexical references."
-  (tree-il-any
-   (lambda (exp)
-     (match exp
-       (($ <application> _
-           (and ref ($ <lexical-ref> _ _ gensym)) _)
-        (or (equal? ref proc)
-            (equal? (lookup gensym) proc)))
-       (($ <application>
-           (and proc* ($ <lambda>)))
-        (equal? proc* proc))
-       (_ #f)))
-   body))
-
-(define (vlist-any proc vlist)
-  (let ((len (vlist-length vlist)))
-    (let lp ((i 0))
-      (and (< i len)
-           (or (proc (vlist-ref vlist i))
-               (lp (1+ i)))))))
-
-(define* (peval exp #:optional (cenv (current-module)) (env vlist-null))
-  "Partially evaluate EXP in compilation environment CENV, with
-top-level bindings from ENV and return the resulting expression.  Since
-it does not handle <fix> and <let-values>, it should be called before
-`fix-letrec'."
-
-  ;; This is a simple partial evaluator.  It effectively performs
-  ;; constant folding, copy propagation, dead code elimination, and
-  ;; inlining, but not across top-level bindings---there should be a way
-  ;; to allow this (TODO).
-  ;;
-  ;; Unlike a full-blown partial evaluator, it does not emit definitions
-  ;; of specialized versions of lambdas encountered on its way.  Also,
-  ;; it's very conservative: it bails out if `set!', `prompt', etc. are
-  ;; met.
-
-  (define local-toplevel-env
-    ;; The top-level environment of the module being compiled.
-    (match exp
-      (($ <toplevel-define> _ name)
-       (vhash-consq name #t env))
-      (($ <sequence> _ exps)
-       (fold (lambda (x r)
-               (match x
-                 (($ <toplevel-define> _ name)
-                  (vhash-consq name #t r))
-                 (_ r)))
-             env
-             exps))
-      (_ env)))
-
-  (define (local-toplevel? name)
-    (vhash-assq name local-toplevel-env))
-
-  (define (apply-primitive name args)
-    ;; todo: further optimize commutative primitives
-    (catch #t
-      (lambda ()
-        (call-with-values
-            (lambda ()
-              (apply (module-ref the-scm-module name) args))
-          (lambda results
-            (values #t results))))
-      (lambda _
-        (values #f '()))))
-
-  (define (inline-values exp src names gensyms body)
-    (let loop ((exp exp))
-      (match exp
-        ;; Some expression types are always singly-valued.
-        ((or ($ <const>)
-             ($ <void>)
-             ($ <lambda>)
-             ($ <lexical-ref>)
-             ($ <toplevel-ref>)
-             ($ <module-ref>)
-             ($ <primitive-ref>)
-             ($ <dynref>)
-             ($ <toplevel-set>)         ; FIXME: these set! expressions
-             ($ <toplevel-define>)      ; could return zero values in
-             ($ <module-set>))          ; the future
-         (and (= (length names) 1)
-              (make-let src names gensyms (list exp) body)))
-        (($ <application> src
-                ($ <primitive-ref> _ (? singly-valued-primitive? name)))
-         (and (= (length names) 1)
-              (make-let src names gensyms (list exp) body)))
-
-        ;; Statically-known number of values.
-        (($ <application> src ($ <primitive-ref> _ 'values) vals)
-         (and (= (length names) (length vals))
-              (make-let src names gensyms vals body)))
-
-        ;; Not going to copy code into both branches.
-        (($ <conditional>) #f)
-
-        ;; Bail on other applications.
-        (($ <application>) #f)
-
-        ;; Propagate to tail positions.
-        (($ <let> src names gensyms vals body)
-         (let ((body (loop body)))
-           (and body
-                (make-let src names gensyms vals body))))
-        (($ <letrec> src in-order? names gensyms vals body)
-         (let ((body (loop body)))
-           (and body
-                (make-letrec src in-order? names gensyms vals body))))
-        (($ <fix> src names gensyms vals body)
-         (let ((body (loop body)))
-           (and body
-                (make-fix src names gensyms vals body))))
-        (($ <let-values> src exp
-            ($ <lambda-case> src2 req opt rest kw inits gensyms body #f))
-         (let ((body (loop body)))
-           (and body
-                (make-let-values src exp
-                                 (make-lambda-case src2 req opt rest kw
-                                                   inits gensyms body #f)))))
-        (($ <dynwind> src winder body unwinder)
-         (let ((body (loop body)))
-           (and body
-                (make-dynwind src winder body unwinder))))
-        (($ <dynlet> src fluids vals body)
-         (let ((body (loop body)))
-           (and body
-                (make-dynlet src fluids vals body))))
-        (($ <sequence> src exps)
-         (match exps
-           ((head ... tail)
-            (let ((tail (loop tail)))
-              (and tail
-                   (make-sequence src (append head (list tail)))))))))))
-
-  (define (make-values src values)
-    (match values
-      ((single) single)                           ; 1 value
-      ((_ ...)                                    ; 0, or 2 or more values
-       (make-application src (make-primitive-ref src 'values)
-                         values))))
-
-  (define (const*? x)
-    (or (const? x) (lambda? x) (void? x)))
-
-  (define (pure-expression? x)
-    ;; Return true if X is pure---i.e., if it is known to have no
-    ;; effects and does not allocate storage for a mutable object.
-    ;; Note: <module-ref> is not "pure" because it loads a module as a
-    ;; side-effect.
-    (let loop ((x x))
-      (match x
-        (($ <void>) #t)
-        (($ <const>) #t)
-        (($ <lambda>) #t)
-        (($ <lambda-case> _ req opt rest kw inits _ body alternate)
-         (and (every loop inits) (loop body) (loop alternate)))
-        (($ <lexical-ref>) #t)
-        (($ <toplevel-ref>) #t)
-        (($ <primitive-ref>) #t)
-        (($ <dynref> _ fluid) (loop fluid))
-        (($ <conditional> _ condition subsequent alternate)
-         (and (loop condition) (loop subsequent) (loop alternate)))
-        (($ <application> _ ($ <primitive-ref> _ name) args)
-         (and (effect-free-primitive? name)
-              (not (constructor-primitive? name))
-              (every loop args)))
-        (($ <application> _ ($ <lambda> _ body) args)
-         (and (loop body) (every loop args)))
-        (($ <sequence> _ exps)
-         (every loop exps))
-        (($ <let> _ _ _ vals body)
-         (and (every loop vals) (loop body)))
-        (($ <letrec> _ _ _ _ vals body)
-         (and (every loop vals) (loop body)))
-        (($ <fix> _ _ _ vals body)
-         (and (every loop vals) (loop body)))
-        (($ <let-values> _ exp body)
-         (and (loop exp) (loop body)))
-        (_ #f))))
-
-  (define (mutable? exp)
-    ;; Return #t if EXP is a mutable object.
-    ;; todo: add an option to assume pairs are immutable
-    (or (pair? exp)
-        (vector? exp)
-        (struct? exp)
-        (string? exp)))
-
-  (define (make-value-construction src exp)
-    ;; Return an expression that builds a fresh copy of EXP at run-time,
-    ;; or #f.
-    (let loop ((exp exp))
-      (match exp
-        ((_ _ ...)                                 ; non-empty proper list
-         (let ((args (map loop exp)))
-           (and (every struct? args)
-                (make-application src (make-primitive-ref src 'list)
-                                  args))))
-        ((h . (? (negate pair?) t))                ; simple pair
-         (let ((h (loop h))
-               (t (loop t)))
-           (and h t
-                (make-application src (make-primitive-ref src 'cons)
-                                  (list h t)))))
-        ((? vector?)                               ; vector
-         (let ((args (map loop (vector->list exp))))
-           (and (every struct? args)
-                (make-application src (make-primitive-ref src 'vector)
-                                  args))))
-        ((? number?) (make-const src exp))
-        ((? string?) (make-const src exp))
-        ((? symbol?) (make-const src exp))
-        ;((? bytevector?) (make-const src exp))
-        (_ #f))))
-
-  (define (maybe-unconst orig new)
-    ;; If NEW is a constant, change it to a non-constant if need be.
-    ;; Expressions that build a mutable object, such as `(list 1 2)',
-    ;; must not be replaced by a constant; this procedure "undoes" the
-    ;; change from `(list 1 2)' to `'(1 2)'.
-    (match new
-      (($ <const> src (? mutable? value))
-       (if (equal? new orig)
-           new
-           (or (make-value-construction src value) orig)))
-      (_ new)))
-
-  (define (maybe-unlambda orig new env)
-    ;; If NEW is a named lambda and ORIG is what it looked like before
-    ;; partial evaluation, then attempt to replace NEW with a lexical
-    ;; ref, to avoid code duplication.
-    (match new
-      (($ <lambda> src (= (cut assq-ref <> 'name) (? symbol? name))
-          ($ <lambda-case> _ req opt rest kw inits gensyms body))
-       ;; Look for NEW in the current environment, starting from the
-       ;; outermost frame.
-       (or (vlist-any (lambda (x)
-                        (and (equal? (cdr x) new)
-                             (make-lexical-ref src name (car x))))
-                      env)
-           new))
-      (($ <lambda> src ()
-          (and lc ($ <lambda-case>)))
-       ;; This is an anonymous lambda that we're going to inline.
-       ;; Inlining creates new variable bindings, so we need to provide
-       ;; the new code with fresh names.
-       (make-lambda src '() (alpha-rename lc)))
-      (_ new)))
-
-  (catch 'match-error
-    (lambda ()
-      (let loop ((exp   exp)
-                 (env   vlist-null)  ; static environment
-                 (calls '()))        ; inlined call stack
-        (define (lookup var)
-          (and=> (vhash-assq var env) cdr))
-
-        (match exp
-          (($ <const>)
-           exp)
-          (($ <void>)
-           exp)
-          (($ <lexical-ref> _ _ gensym)
-           ;; Propagate only pure expressions.
-           (let ((val (lookup gensym)))
-             (or (and (pure-expression? val) val) exp)))
-          ;; Lexical set! causes a bailout.
-          (($ <let> src names gensyms vals body)
-           (let* ((vals* (map (cut loop <> env calls) vals))
-                  (vals  (map maybe-unconst vals vals*))
-                  (body* (loop body
-                               (fold vhash-consq env gensyms vals)
-                               calls))
-                  (body  (maybe-unconst body body*)))
-             (if (const? body*)
-                 body
-                 (let*-values (((stripped) (remove (compose const? car)
-                                                   (zip vals gensyms names)))
-                               ((vals gensyms names) (unzip3 stripped)))
-                   (if (null? stripped)
-                       body
-                       (make-let src names gensyms vals body))))))
-          (($ <letrec> src in-order? names gensyms vals body)
-           ;; Things could be done more precisely when IN-ORDER? but
-           ;; it's OK not to do it---at worst we lost an optimization
-           ;; opportunity.
-           (let* ((vals* (map (cut loop <> env calls) vals))
-                  (vals  (map maybe-unconst vals vals*))
-                  (body* (loop body
-                               (fold vhash-consq env gensyms vals)
-                               calls))
-                  (body  (maybe-unconst body body*)))
-             (if (const? body*)
-                 body
-                 (make-letrec src in-order? names gensyms vals body))))
-          (($ <fix> src names gensyms vals body)
-           (let* ((vals (map (cut loop <> env calls) vals))
-                  (body* (loop body
-                           (fold vhash-consq env gensyms vals)
-                           calls))
-                  (body  (maybe-unconst body body*)))
-             (if (const? body*)
-                 body
-                 (make-fix src names gensyms vals body))))
-          (($ <let-values> src producer
-              ($ <lambda-case> src2 req #f #f #f () gensyms body #f))
-           ;; Peval both producer and consumer, then try to inline.  If
-           ;; that succeeds, peval again.
-           (let* ((producer (maybe-unconst producer (loop producer env calls)))
-                  (body (maybe-unconst body (loop body env calls))))
-             (cond
-              ((inline-values producer src2 req gensyms body)
-               => (lambda (exp) (loop exp env calls)))
-              (else
-               (make-let-values
-                src producer
-                (make-lambda-case src2 req #f #f #f '() gensyms body #f))))))
-          (($ <let-values>)
-           exp)
-          (($ <dynwind> src winder body unwinder)
-           (make-dynwind src (loop winder env calls)
-                         (loop body env calls)
-                         (loop unwinder env calls)))
-          (($ <dynlet> src fluids vals body)
-           (make-dynlet src
-                        (map maybe-unconst fluids
-                             (map (cut loop <> env calls) fluids))
-                        (map maybe-unconst vals
-                             (map (cut loop <> env calls) vals))
-                        (maybe-unconst body (loop body env calls))))
-          (($ <dynref> src fluid)
-           (make-dynref src (maybe-unconst fluid (loop fluid env calls))))
-          (($ <toplevel-ref> src (? effect-free-primitive? name))
-           (if (local-toplevel? name)
-               exp
-               (resolve-primitives! exp cenv)))
-          (($ <toplevel-ref>)
-           ;; todo: open private local bindings.
-           exp)
-          (($ <module-ref>)
-           exp)
-          (($ <module-set> src mod name public? exp)
-           (make-module-set src mod name public?
-                            (maybe-unconst exp (loop exp env '()))))
-          (($ <toplevel-define> src name exp)
-           (make-toplevel-define src name
-                                 (maybe-unconst exp (loop exp env '()))))
-          (($ <toplevel-set> src name exp)
-           (make-toplevel-set src name
-                              (maybe-unconst exp (loop exp env '()))))
-          (($ <primitive-ref>)
-           exp)
-          (($ <conditional> src condition subsequent alternate)
-           (let ((condition (loop condition env calls)))
-             (if (const*? condition)
-                 (if (or (lambda? condition) (void? condition)
-                         (const-exp condition))
-                     (loop subsequent env calls)
-                     (loop alternate env calls))
-                 (make-conditional src condition
-                                   (loop subsequent env calls)
-                                   (loop alternate env calls)))))
-          (($ <application> src
-                ($ <primitive-ref> _ '@call-with-values)
-                (producer
-                 ($ <lambda> _ _
-                    (and consumer
-                         ;; No optional or kwargs.
-                         ($ <lambda-case>
-                            _ req #f rest #f () gensyms body #f)))))
-           (loop (make-let-values src (make-application src producer '())
-                                  consumer)
-                 env calls))
-
-          (($ <application> src orig-proc orig-args)
-           ;; todo: augment the global env with specialized functions
-           (let* ((proc  (loop orig-proc env calls))
-                  (proc* (maybe-unlambda orig-proc proc env))
-                  (args  (map (cut loop <> env calls) orig-args))
-                  (args* (map (cut maybe-unlambda <> <> env)
-                              orig-args
-                              (map maybe-unconst orig-args args)))
-                  (app   (make-application src proc* args*)))
-             ;; If at least one of ARGS is static (to avoid infinite
-             ;; inlining) and this call hasn't already been expanded
-             ;; before (to avoid infinite recursion), then expand it
-             ;; (todo: emit an infinite recursion warning.)
-             (if (and (or (null? args) (any const*? args))
-                      (not (member (cons proc args) calls)))
-                 (match proc
-                   (($ <primitive-ref> _ (? effect-free-primitive? name))
-                    (if (every const? args)  ; only simple constants
-                        (let-values (((success? values)
-                                      (apply-primitive name
-                                                       (map const-exp args))))
-                          (if success?
-                              (make-values src (map (cut make-const src <>)
-                                                    values))
-                              app))
-                        app))
-                   (($ <primitive-ref>)
-                    ;; An effectful primitive.
-                    app)
-                   (($ <lambda> _ _
-                       ($ <lambda-case> _ req opt #f #f inits gensyms body))
-                    ;; Simple case: no rest, no keyword arguments.
-                    ;; todo: handle the more complex cases
-                    (let ((nargs  (length args))
-                          (nreq   (length req))
-                          (nopt   (if opt (length opt) 0)))
-                      (if (and (>= nargs nreq) (<= nargs (+ nreq nopt))
-                               (every pure-expression? args))
-                          (let* ((params
-                                  (append args
-                                          (drop inits
-                                                (max 0
-                                                     (- nargs
-                                                        (+ nreq nopt))))))
-                                 (body
-                                  (loop body
-                                        (fold vhash-consq env gensyms params)
-                                        (cons (cons proc args) calls))))
-                            ;; If the residual code contains recursive
-                            ;; calls, give up inlining.
-                            (if (code-contains-calls? body proc lookup)
-                                app
-                                body))
-                          app)))
-                   (($ <lambda>)
-                    app)
-                   (($ <toplevel-ref>)
-                    app)
-                   
-                   ;; In practice, this is the clause that stops peval:
-                   ;; module-ref applications (produced by macros,
-                   ;; typically) don't match, and so this throws,
-                   ;; aborting peval for an entire expression.
-                   )
-
-                 app)))
-          (($ <lambda> src meta body)
-           (make-lambda src meta (loop body env calls)))
-          (($ <lambda-case> src req opt rest kw inits gensyms body alt)
-           (make-lambda-case src req opt rest kw inits gensyms
-                             (maybe-unconst body (loop body env calls))
-                             alt))
-          (($ <sequence> src exps)
-           (let ((exps (map (cut loop <> env calls) exps)))
-             (if (every pure-expression? exps)
-                 (last exps)
-                 (match (reverse exps)
-                   ;; Remove all expressions but the last one.
-                   ((keep rest ...)
-                    (let ((rest (remove pure-expression? rest)))
-                      (make-sequence src (reverse (cons keep rest))))))))))))
-    (lambda _
-      ;; We encountered something we don't handle, like `<lexical-set>',
-      ;; <abort>, or some other effecting construct, so bail out.
-      exp)))
+;;; Tree-il optimizer
+
+;; Copyright (C) 2009, 2011, 2012, 2013 Free Software Foundation, Inc.
+
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 3 of the License, or (at your option) any later version.
+;;;; 
+;;;; This library 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
+;;;; Lesser General Public License for more details.
+;;;; 
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (language tree-il optimize)
+  #:use-module (language tree-il)
+  #:use-module (language tree-il primitives)
+  #:use-module (language tree-il peval)
+  #:use-module (language tree-il cse)
+  #:use-module (language tree-il fix-letrec)
+  #:use-module (language tree-il debug)
+  #:use-module (ice-9 match)
+  #:export (optimize))
+
+(define (optimize x env opts)
+  (let ((peval (match (memq #:partial-eval? opts)
+                 ((#:partial-eval? #f _ ...)
+                  ;; Disable partial evaluation.
+                  (lambda (x e) x))
+                 (_ peval)))
+        (cse (match (memq #:cse? opts)
+               ((#:cse? #f _ ...)
+                ;; Disable CSE.
+                (lambda (x) x))
+               (_ cse))))
+    (fix-letrec
+     (verify-tree-il
+      (cse
+       (verify-tree-il
+        (peval (expand-primitives (resolve-primitives x env))
+               env)))))))