~ chicken-core (chicken-5) 4e2bad37c0c1b93a55166f778bdc906df7534c84
commit 4e2bad37c0c1b93a55166f778bdc906df7534c84 Author: Peter Bex <peter@more-magic.net> AuthorDate: Tue Jul 22 12:23:02 2025 +0200 Commit: Peter Bex <peter@more-magic.net> CommitDate: Mon Jul 28 09:07:54 2025 +0200 Undo boxing of variables that don't escape shared closures When a variable is created in a sharing closure container or user and is *only* used by sharing users further down the line, and *not* any regular lambdas (or new containers), it is not necessary to box these variables because the container itself already acts as a box. In such cases, we undo the 'boxed property of the variables. diff --git a/core.scm b/core.scm index 6746a06a..fd4885c9 100644 --- a/core.scm +++ b/core.scm @@ -2532,7 +2532,8 @@ (sharing-containers 0) (sharing-users 0) (customizable '()) - (lexicals '())) + (lexicals '()) + (escaping-shared-vars '())) (define (test sym item) (db-get db sym item)) @@ -2652,8 +2653,7 @@ (third params) (lambda (vars argc rest) (let* ((id (first params)) - (this-closure (test id 'captured-variables))) - ;; TODO: unbox vars that are only referenced inside the shared closure + (this-closure (or (test id 'captured-variables) '()))) (cond ((and shared-closure (test id 'shareable-user) ;; The user must close over all the shared closure vars, otherwise @@ -2677,20 +2677,37 @@ sub-closure))) ((test id 'shareable-container) - (let ((sub-closure (merge-shareable (first subs) this-closure))) - (unless (null? sub-closure) - ;; NOTE: We don't touch 'captured-variables, because the vars - ;; on initial entry of the sharing closures are unchanged. - ;; However, we do need to know the full closure - (db-put! db id 'closure-size (length sub-closure)) - (db-put! db id 'sharing-mode 'container) - (db-put! db id 'shared-closure sub-closure) - (set! sharing-containers (add1 sharing-containers)))) + ;; If we're starting a new container, any captured vars from the + ;; surrounding container will escape, like with a non-sharing closure + (set! escaping-shared-vars (lset-union/eq? escaping-shared-vars this-closure)) + + (fluid-let ((escaping-shared-vars '())) + (let ((sub-closure (merge-shareable (first subs) this-closure))) + (unless (null? sub-closure) + ;; NOTE: We don't touch 'captured-variables, because the vars + ;; on initial entry of the sharing closures are unchanged. + ;; However, we do need to know the full closure + (db-put! db id 'closure-size (length sub-closure)) + (db-put! db id 'sharing-mode 'container) + (db-put! db id 'shared-closure sub-closure) + (set! sharing-containers (add1 sharing-containers)) + + ;; Shared vars introduced by users which don't escape don't have to be boxed + ;; because the shared closure container itself already acts as a box. + (let* ((user-introduced-vars (lset-difference/eq? sub-closure this-closure)) + (unboxable-vars (lset-difference/eq? user-introduced-vars escaping-shared-vars))) + (for-each (lambda (v) + (when (test v 'boxed) ; Not strictly needed, but cleaner this way + (db-put! db v 'boxed #f))) + unboxable-vars))))) ;; This is a new container, so do not allow higher-up containers ;; to collect variables from this closure. '()) - (else (merge-shareable (first subs) #f) + ;; All closed-over vars in non-user procedures "escape" the container (if any) + ;; and must remain boxed. + (else (set! escaping-shared-vars (lset-union/eq? escaping-shared-vars this-closure)) + (merge-shareable (first subs) #f) '())))))) (else (concatenate (map (lambda (n) (merge-shareable n shared-closure)) subs)) ) ) ))Trap