diff --git a/racket/src/schemify/lift.rkt b/racket/src/schemify/lift.rkt index 4e352466ad..9d14809905 100644 --- a/racket/src/schemify/lift.rkt +++ b/racket/src/schemify/lift.rkt @@ -28,6 +28,11 @@ ;; synthesized accessors, because they're relevant only for the second ;; pass and recorded in an `indirected`. ;; +;; The `lifts` table can also contain `lambda` and `case-lambda` forms +;; mapped to '#:empty, meaning that the closure is empty relative to the +;; enclosing linklet and can be lifted so that each is allocated once per +;; linklet. +;; ;; An identifier registered in `locals` maps to either 'ready or 'early, ;; where 'early is used during the right-hand side of a letrec that is ;; not all `lambda`s. @@ -118,7 +123,8 @@ (for/or ([v (in-wrap-list vs)]) (lift-in-expr? v))) - ;; Under a `lambda`; any local bindings to functions? + ;; Under a `lambda`; any local bindings to functions or + ;; `[case-]lambda`s that might be closed? (define (lift? v) (match v [`(let . ,_) (lift?/let v)] @@ -126,8 +132,10 @@ [`(letrec* . ,_) (lift?/let v)] [`(let-values . ,_) (lift?/let v)] [`(letrec-values . ,_) (lift?/let v)] - [`(lambda ,_ . ,body) (lift?/seq body)] + [`(lambda ,_ . ,body) #t #;(lift?/seq body)] [`(case-lambda [,_ . ,bodys] ...) + #t + #; (for/or ([body (in-list bodys)]) (lift?/seq body))] [`(begin . ,vs) (lift?/seq vs)] @@ -183,8 +191,13 @@ (cond [(zero? (hash-count lifts)) v] [else - `(letrec ,(extract-lifted-bindings lifts) - ,(reannotate v `(lambda ,args . ,(convert-lifted-calls-in-seq/box-mutated body args lifts #hasheq()))))]))] + (define empties (box null)) + (define lifted-bindings (extract-lifted-bindings lifts empties)) + (define new-body + (reannotate v `(lambda ,args . ,(convert-lifted-calls-in-seq/box-mutated body args lifts #hasheq() empties)))) + `(letrec ,(append (unbox empties) + lifted-bindings) + ,new-body)]))] [`(case-lambda [,argss . ,bodys] ...) ;; Lift each clause separately, then splice results: (let ([lams (for/list ([args (in-list argss)] @@ -249,7 +262,7 @@ ;; Returns a set of free variables and a set of bound variables ;; (paired together) while potentially mutating `lifts` - (define (compute-lifts! v frees+binds lifts locals) + (define (compute-lifts! v frees+binds lifts locals [called? #f]) (match v [`(let ([,ids ,rhss] ...) . ,body) (for ([id (in-list ids)] @@ -269,13 +282,24 @@ [`((letrec* ([,id ,rhs]) ,rator) ,rands ...) (compute-lifts! `(letrec ([,id ,rhs]) (,rator . ,rands)) frees+binds lifts locals)] [`(lambda ,args . ,body) - (let ([frees+binds (compute-seq-lifts! body frees+binds lifts (add-args args locals))]) - (remove-frees/add-binds args frees+binds lifts))] + (let* ([body-frees+binds (cons (car empty-frees+binds) (cdr frees+binds))] + [body-frees+binds (compute-seq-lifts! body body-frees+binds lifts (add-args args locals))] + [body-frees+binds (remove-frees/add-binds args body-frees+binds lifts)]) + (when (and (zero? (frees-count body-frees+binds)) (not called?)) + (record-empty-closure! lifts v)) + (cons (union (car body-frees+binds) (car frees+binds)) + (cdr body-frees+binds)))] [`(case-lambda [,argss . ,bodys] ...) - (for/fold ([frees+binds frees+binds]) ([args (in-list argss)] - [body (in-list bodys)]) - (let ([frees+binds (compute-seq-lifts! body frees+binds lifts (add-args args locals))]) - (remove-frees/add-binds args frees+binds lifts)))] + (define init-frees+binds (cons (car empty-frees+binds) (cdr frees+binds))) + (define new-frees+binds + (for/fold ([frees+binds init-frees+binds]) ([args (in-list argss)] + [body (in-list bodys)]) + (let ([frees+binds (compute-seq-lifts! body frees+binds lifts (add-args args locals))]) + (remove-frees/add-binds args frees+binds lifts)))) + (when (and (zero? (frees-count new-frees+binds)) (not called?)) + (record-empty-closure! lifts v)) + (cons (union (car new-frees+binds) (car frees+binds)) + (cdr new-frees+binds))] [`(begin . ,vs) (compute-seq-lifts! vs frees+binds lifts locals)] [`(begin0 . ,vs) @@ -302,6 +326,10 @@ (compute-lifts! rhs frees+binds lifts locals))] [`(#%variable-reference . ,_) (error 'internal-error "lift: unexpected variable reference")] + [`(call-with-values ,producer ,consumer) + (let* ([frees+binds (compute-lifts! producer frees+binds lifts locals #t)] + [frees+binds (compute-lifts! consumer frees+binds lifts locals #t)]) + frees+binds)] [`(,rator . ,rands) (define f (unwrap rator)) (let ([frees+binds @@ -394,7 +422,7 @@ ;; other lifted functions that it calls. Also, clear `mutated` and ;; `var-ref` information from `lifts` in the returned table. (define (close-and-convert-lifts lifts) - (define new-lifts (make-hash)) + (define new-lifts (make-hasheq)) ;; Copy over `liftable`s: (for ([(f info) (in-hash lifts)]) (when (liftable? info) @@ -450,36 +478,44 @@ #:unless (liftable? (hash-ref lifts f #f))) f) symbol