~ 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