rosella/libcompiler/simplifier.scm

494 lines
20 KiB
Scheme

#lang scheme/base
(require scheme/list)
(require (file "utilities.scm"))
(require (file "primitives.scm"))
(provide simplify-function
promote-free-variables)
(define (simplify-function lambda-form)
((compose promote-shared-variables
simplify-lambda)
lambda-form))
(define (simplify-form form)
(define (same-form recurse . form) form)
(map-form form
#:bind same-form
#:lambda same-form
#:set same-form
#:primitive same-form
#:simple (lambda (recurse kind form) form)
#:literal (lambda (recurse kind form)
(if (and (pair? form)
(eq? (first form) 'quote)
(eq? (second form) '()))
'%nil
form))
#:other (lambda (recurse op . others)
(case op
[(let) (simplify-let form)]
[(let*) (simplify-let* form)]
[(letrec) (simplify-letrec form)]
[(if) (simplify-if form)]
[(lambda) (simplify-lambda form)]
[(begin) (simplify-form `(let () ,@(cdr form)))]
[(set!) (simplify-set! form)]
[(let/cc) (simplify-form
`(call/cc (lambda (,(second form)) ,@(cddr form))))]
[(fix>) (simplify-form
(let ([a (gensym)] [b (gensym)])
`(let ([,a ,(second form)]
[,b ,(third form)])
(fix< ,b ,a))))]
[(fix<=) (simplify-form
(let ([a (gensym)] [b (gensym)])
`(let ([,a ,(second form)]
[,b ,(third form)])
(fix>= ,b ,a))))]
[(values) (simplify-primitive '%values (cdr form))]
[(call/cc) (simplify-primitive '%call/cc (cdr form))]
[else
(let ([primitive (memf (lambda (x) (eq? (third x) (first form)))
all-primitives)])
(if primitive
(simplify-primitive (first (first primitive))
(cdr form))
(simplify-funcall form)))]))))
(define (simplify-set! form)
(let ([variable (second form)]
[value-form (simplify-form (third form))])
(if (and (pair? value-form) (eq? (first value-form) '%bind))
(if (memq variable (second value-form))
(let ([tmp (gensym)])
`(%bind (,tmp)
; guaranteed not to cause unbounded recursion: tmp is unique
,(simplify-set! `(set! ,tmp ,value-form))
(%set! ,variable ,tmp)))
`(%bind ,(second value-form)
,@(foldr (lambda (subform after)
(cond
[(pair? after) (cons subform after)]
[(and (pair? subform) (eq? (first subform) '%values))
; Requires at least one value; ignores extras.
(if (null? (cdr subform))
(error "Attempted to set variable to void in:" form)
`((%set! ,variable ,(second subform))))]
[(value-form? subform)
(list (simplify-set! `(set! ,variable ,subform)))]
[else (error "Attempted to set variable to void in:" form)]))
'()
(cddr value-form))))
`(%set! ,variable ,value-form))))
(define (simplify-primitive simple-op value-forms)
(define (value->binding value-form)
(let ([simple-value-form (simplify-form value-form)])
(if (simple-value? simple-value-form)
(list simple-value-form #f)
(let ([tmp (gensym)])
(list tmp (simplify-set! `(set! ,tmp ,simple-value-form)))))))
(define bindings (map value->binding value-forms))
(simplify-form
`(let ,(map first (filter second bindings))
,@(filter-map second bindings)
(,simple-op ,@(map first bindings)))))
; (let ([var expr]...) bodyexpr...) ; first eval exprs, then assign bindings in parallel
; => (%bind (tmp...)
; (%set! tmp ,(simplify-form expr))...
; (%bind (var...)
; (%set! var tmp)...
; bodyexpr...))
(define (simplify-let form)
(define (simplify-binding binding)
(if (pair? binding)
(list (first binding) (simplify-form (second binding)))
(list binding)))
(define bindings (map simplify-binding (second form)))
(define bodyexprs (cddr form))
(define (has-value? binding) (pair? (cdr binding)))
(define vars (map first bindings))
(define (bound-var? var) (and (memq var vars) #t))
; If the value of any binding refers to one of the variable names being bound...
(if (ormap (lambda (value) (ormap bound-var? (free-variables value)))
(map second (filter has-value? bindings)))
; ...then bind the values to temps first, before binding the real names.
(let ([temp-bindings (map (lambda (binding)
(let ([tmp (gensym)])
(list tmp
(simplify-form `(set! ,tmp ,(second binding)))
`(%set! ,(first binding) ,tmp))))
(filter has-value? bindings))])
`(%bind ,(map first temp-bindings)
,@(map second temp-bindings)
(%bind ,vars
,@(map third temp-bindings)
,@(map simplify-form bodyexprs))))
; Otherwise, just bind the real names directly.
`(%bind ,vars
,@(map (lambda (binding)
(simplify-set! `(set! ,@binding)))
(filter has-value? bindings))
,@(map simplify-form bodyexprs))))
; (let* ...) ; eval exprs & bind variables serially
; => (let ([var-0 expr-0])
; (let ([var-1 expr-1])
; (...
; bodyexprs...)))
(define (simplify-let* form)
(define bindings (second form))
(define bodyexprs (cddr form))
(define (add-binding bind bodyexpr)
`(let (,bind) ,bodyexpr))
(simplify-form (foldr add-binding `(begin ,@bodyexprs) bindings)))
; (letrec ...) ; init bindings to undefined, then assign values in series
; => (let (var...)
; (set! var expr)...
; bodyexprs)
(define (simplify-letrec form)
(define bindings (second form))
(define bodyexprs (cddr form))
(simplify-form
`(let (,@(map (lambda (x) (if (pair? x) (first x) x)) bindings))
,@(append-map
(lambda (x)
(if (pair? x)
`((set! ,(first x) ,(second x)))
'()))
bindings)
,@bodyexprs)))
(define (simplify-if form)
(define-values (cond-expr true-expr false-expr)
(apply values (cdr form)))
(let ([true-form (simplify-form true-expr)]
[false-form (simplify-form false-expr)]
[cond-val (gensym)])
(simplify-form
(if (and (simple-value? true-form)
(simple-value? false-form))
`(let ([,cond-val ,cond-expr])
(%if ,cond-val ,true-form ,false-form))
(let ([next-fn (gensym)]
[true-fn (gensym)]
[false-fn (gensym)])
`(let ([,cond-val ,cond-expr]
[,true-fn (lambda () ,true-form)]
[,false-fn (lambda () ,false-form)])
(let ([,next-fn (%if ,cond-val ,true-fn ,false-fn)])
(%apply ,next-fn %nil))))))))
; (lambda (required... [optional default-expr]... . rest) bodyexpr...)
; => (lambda argv
; (let ([argv-temp argv])
; (let ([required-0 (car argv-temp)])
; (set! argv-temp (cdr argv-temp)))
; (let ([required-1 (car argv-temp)])
; (set! argv-temp (cdr argv-temp)))
; (...
; (let (optional-0)
; (if (eq? argv-temp %nil)
; (set! optional-0 default-expr-0)
; (set! optional-0 (car argv-temp)))
; (set! argv-temp (cdr argv-temp))
; (let (optional-1)
; (if (eq? argv-temp %nil)
; (set! optional-1 default-expr-1)
; (set! optional-1 (car argv-temp)))
; (set! argv-temp (cdr argv-temp))
; (...
; (let ([rest argv-temp])
; bodyexpr...)...)))...)))
(define (split-arglist arglist)
(define (split-optional arglist)
(if (pair? arglist)
(let-values ([(opt rst) (split-optional (cdr arglist))])
(values (cons (car arglist) opt) rst))
(values '() arglist)))
(cond
[(null? arglist) (values '() '() #f)]
[(not (pair? arglist)) (values '() '() arglist)]
[(pair? (car arglist)) (let-values ([(opt rst) (split-optional arglist)])
(values '() opt rst))]
[else (let-values ([(req opt rst) (split-arglist (cdr arglist))])
(values (cons (car arglist) req) opt rst))]))
(define (add-return ctx k nested-bind)
(define flat-bind (flatten-binds nested-bind))
(define argv (gensym))
`(%bind (,@(second flat-bind) ,argv)
,@(foldr (lambda (subform after)
(cond
[(pair? after)
(cons subform after)]
[(simple-value? subform)
`((%set! ,argv (%cons ,subform %nil))
(%tail-call ,k ,argv #f #f))]
[(eq? (first subform) '%apply)
`((%tail-call ,(second subform) ,(third subform) ,ctx ,k))]
[(eq? (first subform) '%call/cc)
`((%set! ,argv (%cons %k %nil))
(%tail-call ,(second subform) ,argv ,ctx %k))]
[(eq? (first subform) '%values)
`((%set! ,argv %nil)
,@(map (lambda (sv) `(%set! ,argv (%cons ,sv ,argv)))
(reverse (cdr subform)))
(%tail-call ,k ,argv #f #f))]
[(value-form? subform)
`((%set! ,argv ,subform)
(%set! ,argv (%cons ,argv %nil))
(%tail-call ,k ,argv #f #f))]
[(eq? (first subform) '%tail-call)
`(,subform)]
[else
`(,subform
(%tail-call ,k %nil #f #f))]))
'()
(cddr flat-bind))))
; <= (%bind (var...)
; @before
; (%apply x y)
; @after))
; => (%bind (var... k)
; @before
; (%set! k (lambda _ @after))
; (%tail-call x y ctx k)))
; <= (%bind (var...)
; @before
; (%set! v (%apply x y))
; @after))
; => (%bind (var... k)
; @before
; (%set! k (lambda (x)
; (%set! v x)
; @after))
; (%tail-call x y ctx k)))
; <= (%bind (var...)
; @before
; (call/cc l)
; @after)
; => (%bind (var... k k2)
; @before
; (%set! k (lambda _ @after))
; (%set! k-argv (%cons k %nil))
; (%tail-call l k-argv ctx k))
(define (transform-to-cps ctx nested-bind)
(define flat-bind (flatten-binds nested-bind))
(define (cps-prepend subform after)
(cond
; (%set! v (%apply x y))
[(and (pair? subform)
(eq? (first subform) '%set!)
(pair? (third subform))
(eq? (first (third subform)) '%apply))
(let ([k (gensym)]
[x (gensym)])
`((%bind (,k ,x)
(%set! ,k ,(simplify-form
`(lambda (,x . ,(gensym))
(%set! ,(second subform) ,x)
,@after)))
(%tail-call ,(second (third subform))
,(third (third subform))
,ctx
,k))))]
; (%apply x y)
[(and (pair? subform)
(eq? (first subform) '%apply))
(let ([k (gensym)])
`((%bind (,k)
(%set! ,k ,(simplify-form
`(lambda ,(gensym)
,@after)))
(%tail-call ,(second subform)
,(third subform)
,ctx
,k))))]
; (%set! v (%call/cc x))
[(and (pair? subform)
(eq? (first subform) '%set!)
(pair? (third subform))
(eq? (first (third subform)) '%call/cc))
(let ([k (gensym)]
[k-argv (gensym)]
[x (gensym)])
`((%bind (,k ,k-argv)
(%set! ,k ,(simplify-form
`(lambda (,x . ,(gensym))
(%set! ,(second subform) ,x)
,@after)))
(%set! ,k-argv (%cons ,k %nil))
(%tail-call ,(second (third subform))
,k-argv
,ctx
,k))))]
; (%call/cc x)
[(and (pair? subform)
(eq? (first subform) '%call/cc))
(let ([k (gensym)]
[k-argv (gensym)])
`((%bind (,k ,k-argv)
(%set! ,k ,(simplify-form
`(lambda ,(gensym)
,@after)))
(%set! ,k-argv (%cons ,k %nil))
(%tail-call ,(second subform)
,k-argv
,ctx
,k))))]
; keep all other forms with side-effects as-is
[(statement-form? subform)
(cons subform after)]
; discard any form without side-effects
[else after]))
(flatten-binds
`(%bind ,(second flat-bind)
,@(foldr cps-prepend '() (cddr flat-bind)))))
(define (simplify-lambda form)
(define arglist (car (cdr form)))
(define bodyexprs (cdr (cdr form)))
(define-values (requireds optionals rest) (split-arglist arglist))
(define argv (gensym))
(define ctx (gensym))
(define k (gensym))
(define (add-req req inner) `(let ([,req (car ,argv)])
(set! ,argv (cdr ,argv))
,inner))
(define (add-opt opt-list inner) `(let (,(car opt-list))
(if (pair? ,argv)
(begin
(set! ,(first opt-list) (car ,argv))
(set! ,argv (cdr ,argv)))
(set! ,(first opt-list) ,(second opt-list)))
,inner))
(define rest+bodyexprs (if rest `(let ([,rest ,argv]) ,@bodyexprs)
`(begin ,@bodyexprs)))
(narrow-binds
`(%lambda () ()
,((compose (lambda (bind) (transform-to-cps ctx bind))
(lambda (bind) (add-return ctx k bind)))
(simplify-form
`(let ([,argv %argv]
[,ctx %ctx]
[,k %k])
,(foldr add-req
(foldr add-opt
rest+bodyexprs
optionals)
requireds)))))))
; (fn-expr arg-expr...)
; => (let ([fn-var fn-expr] arg-var... argv)
; (set! fn-var fn-expr)
; (set! arg-var arg-expr)...
; (set! argv %nil)
; (set! argv (cons arg-var argv))... [reversed]
; (%apply fn-var argv))
(define (simplify-funcall form)
(define fn-expr (car form))
(define arg-exprs (cdr form))
(define fn-var (gensym))
(define arg-vars (map (lambda (x) (gensym)) arg-exprs))
(define argv (gensym))
(simplify-form
`(let (,fn-var ,@arg-vars ,argv)
(set! ,fn-var ,fn-expr)
,@(map (lambda (x y) `(set! ,x ,y)) arg-vars arg-exprs)
(%set! ,argv %nil)
,@(reverse (map (lambda (x) `(%set! ,argv (%cons ,x ,argv))) arg-vars))
(%apply ,fn-var ,argv))))
(define (promote-to-box variable form)
(map-form form
#:bind (lambda (recurse op vars . subforms)
(flatten-binds
`(%bind ,(subst variable variable vars)
,@(if (memq variable vars)
`((%set! ,variable (%make-box %undef)))
'())
,@(map recurse subforms))))
#:set (lambda (recurse op var value)
(let ([new-value (recurse value)])
(if (eq? var variable)
(if (simple-value? new-value)
`(%set-box! ,variable ,new-value)
(let ([tmp (gensym)])
`(%bind (,tmp)
,(simplify-set! `(set! ,tmp ,new-value))
(%set-box! ,variable ,tmp))))
(simplify-set! `(set! ,var ,new-value)))))
#:primitive (lambda (recurse op . simple-values)
(let ([new-args (map recurse simple-values)])
;; if any new-arg is not simple, must bind to a temp first
(let ([temps (map (lambda (x)
(if (simple-value? x)
(list x #f)
(let ([tmp (gensym)])
(list tmp `(%set! ,tmp ,x)))))
new-args)])
(if (ormap second temps)
`(%bind ,(map first (filter second temps))
,@(filter-map second temps)
(,op ,@(map first temps)))
`(,op ,@new-args)))))
#:variable (lambda (recurse op var)
(if (eq? var variable) `(%unbox ,variable) var))))
; form needs to be flattened (%bind ...)
(define (is-shared-var? var bind)
(define captured-input? (ormap (lambda (f) (form-captures-input? f var)) (cddr bind)))
(define captured-output? (ormap (lambda (f) (form-captures-output? f var)) (cddr bind)))
(define (set-after-first-use?)
(let/cc return
(foldr (lambda (subform set-after?)
(if (or set-after? (form-sets? subform var captured-output?))
(if (form-uses? subform var captured-input?)
(return #t)
#t)
#f))
#f
(cddr bind))
#f))
(and (not (special-variable? var))
(or captured-input?
captured-output?)
(set-after-first-use?)))
(define (promote-shared-variables simple-lambda-form)
(define bind (fourth simple-lambda-form))
`(%lambda ,(second simple-lambda-form) ,(third simple-lambda-form)
,(foldl (lambda (var frm)
(if (is-shared-var? var frm)
(promote-to-box var frm)
frm))
bind
(second bind))))
(define (promote-free-variables simple-lambda-form)
(define bind (fourth simple-lambda-form))
`(%lambda ,(second simple-lambda-form) ,(third simple-lambda-form)
,(foldl promote-to-box bind (free-variables bind))))
; vim:set sw=2 expandtab: