diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2011-02-26 10:19:08 -0500 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2011-02-26 10:19:08 -0500 |
commit | a9de04fa62f123413d82b7b7b1e7a77705eb82dd (patch) | |
tree | 84292e07c3583dee99376669fb799d8c93cdd5ff /lisp/emacs-lisp/cconv.el | |
parent | 876c194cbac17a6220dbf406b0a602325978011c (diff) | |
download | emacs-a9de04fa62f123413d82b7b7b1e7a77705eb82dd.tar.gz |
Compute freevars in cconv-analyse.
* lisp/emacs-lisp/cconv.el: Compute freevars in cconv-analyse.
(cconv-mutated, cconv-captured): Remove.
(cconv-captured+mutated, cconv-lambda-candidates): Don't give them
a global value.
(cconv-freevars-alist): New var.
(cconv-freevars): Remove.
(cconv--lookup-let): Remove.
(cconv-closure-convert-function): Extract from cconv-closure-convert-rec.
(cconv-closure-convert-rec): Adjust to above changes.
(fboundp): New function.
(cconv-analyse-function, form): Rewrite.
* lisp/emacs-lisp/bytecomp.el (byte-compile-initial-macro-environment):
Handle declare-function here.
(byte-compile-obsolete): Remove.
(byte-compile-arglist-warn): Check late defsubst here.
(byte-compile-file-form): Simplify.
(byte-compile-file-form-defsubst): Remove.
(byte-compile-macroexpand-declare-function): Rename from
byte-compile-declare-function, turn it into a macro-expander.
(byte-compile-normal-call): Check obsolescence.
(byte-compile-quote-form): Remove.
(byte-compile-defmacro): Revert to trunk's definition which seems to
work just as well and handles `declare'.
* lisp/emacs-lisp/byte-run.el (make-obsolete): Don't modify byte-compile.
* lisp/Makefile.in (BIG_STACK_DEPTH): Increase to 1200.
(compile-onefile): Pass $(BIG_STACK_OPTS) before "-l bytecomp".
* lisp/emacs-lisp/macroexp.el: Use lexbind.
(macroexpand-all-1): Check macro obsolescence.
* lisp/vc/diff-mode.el: Use lexbind.
* lisp/follow.el (follow-calc-win-end): Simplify.
Diffstat (limited to 'lisp/emacs-lisp/cconv.el')
-rw-r--r-- | lisp/emacs-lisp/cconv.el | 468 |
1 files changed, 201 insertions, 267 deletions
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el index bc7ecb1ad55..0e4b5d31699 100644 --- a/lisp/emacs-lisp/cconv.el +++ b/lisp/emacs-lisp/cconv.el @@ -82,110 +82,19 @@ (defconst cconv-liftwhen 3 "Try to do lambda lifting if the number of arguments + free variables is less than this number.") -(defvar cconv-mutated nil - "List of mutated variables in current form") -(defvar cconv-captured nil - "List of closure captured variables in current form") -(defvar cconv-captured+mutated nil - "An intersection between cconv-mutated and cconv-captured lists.") -(defvar cconv-lambda-candidates nil - "List of candidates for lambda lifting. -Each candidate has the form (VAR INCLOSURE BINDER PARENTFORM).") - -(defun cconv-freevars (form &optional fvrs) - "Find all free variables of given form. -Arguments: --- FORM is a piece of Elisp code after macroexpansion. --- FVRS(optional) is a list of variables already found. Used for recursive tree -traversal - -Returns a list of free variables." - ;; If a leaf in the tree is a symbol, but it is not a global variable, not a - ;; keyword, not 'nil or 't we consider this leaf as a variable. - ;; Free variables are the variables that are not declared above in this tree. - ;; For example free variables of (lambda (a1 a2 ..) body-forms) are - ;; free variables of body-forms excluding a1, a2 .. - ;; Free variables of (let ((v1 ..) (v2) ..)) body-forms) are - ;; free variables of body-forms excluding v1, v2 ... - ;; and so on. - - ;; A list of free variables already found(FVRS) is passed in parameter - ;; to try to use cons or push where possible, and to minimize the usage - ;; of append. - - ;; This function can return duplicates (because we use 'append instead - ;; of union of two sets - for performance reasons). - (pcase form - (`(let ,varsvalues . ,body-forms) ; let special form - (let ((fvrs-1 '())) - (dolist (exp body-forms) - (setq fvrs-1 (cconv-freevars exp fvrs-1))) - (dolist (elm varsvalues) - (setq fvrs-1 (delq (if (consp elm) (car elm) elm) fvrs-1))) - (setq fvrs (nconc fvrs-1 fvrs)) - (dolist (exp varsvalues) - (when (consp exp) (setq fvrs (cconv-freevars (cadr exp) fvrs)))) - fvrs)) - - (`(let* ,varsvalues . ,body-forms) ; let* special form - (let ((vrs '()) - (fvrs-1 '())) - (dolist (exp varsvalues) - (if (consp exp) - (progn - (setq fvrs-1 (cconv-freevars (cadr exp) fvrs-1)) - (dolist (elm vrs) (setq fvrs-1 (delq elm fvrs-1))) - (push (car exp) vrs)) - (progn - (dolist (elm vrs) (setq fvrs-1 (delq elm fvrs-1))) - (push exp vrs)))) - (dolist (exp body-forms) - (setq fvrs-1 (cconv-freevars exp fvrs-1))) - (dolist (elm vrs) (setq fvrs-1 (delq elm fvrs-1))) - (append fvrs fvrs-1))) - - (`((lambda . ,_) . ,_) ; first element is lambda expression - (dolist (exp `((function ,(car form)) . ,(cdr form))) - (setq fvrs (cconv-freevars exp fvrs))) fvrs) +;; List of all the variables that are both captured by a closure +;; and mutated. Each entry in the list takes the form +;; (BINDER . PARENTFORM) where BINDER is the (VAR VAL) that introduces the +;; variable (or is just (VAR) for variables not introduced by let). +(defvar cconv-captured+mutated) - (`(cond . ,cond-forms) ; cond special form - (dolist (exp1 cond-forms) - (dolist (exp2 exp1) - (setq fvrs (cconv-freevars exp2 fvrs)))) fvrs) - - (`(quote . ,_) fvrs) ; quote form +;; List of candidates for lambda lifting. +;; Each candidate has the form (BINDER . PARENTFORM). A candidate +;; is a variable that is only passed to `funcall' or `apply'. +(defvar cconv-lambda-candidates) - (`(function . ((lambda ,vars . ,body-forms))) - (let ((functionform (cadr form)) (fvrs-1 '())) - (dolist (exp body-forms) - (setq fvrs-1 (cconv-freevars exp fvrs-1))) - (dolist (elm vars) (setq fvrs-1 (delq elm fvrs-1))) - (append fvrs fvrs-1))) ; function form - - (`(function . ,_) fvrs) ; same as quote - ;condition-case - (`(condition-case ,var ,protected-form . ,conditions-bodies) - (let ((fvrs-1 '())) - (dolist (exp conditions-bodies) - (setq fvrs-1 (cconv-freevars (cadr exp) fvrs-1))) - (setq fvrs-1 (delq var fvrs-1)) - (setq fvrs-1 (cconv-freevars protected-form fvrs-1)) - (append fvrs fvrs-1))) - - (`(,(and sym (or `defun `defconst `defvar)) . ,_) - ;; We call cconv-freevars only for functions(lambdas) - ;; defun, defconst, defvar are not allowed to be inside - ;; a function (lambda). - ;; (error "Invalid form: %s inside a function" sym) - (cconv-freevars `(progn ,@(cddr form)) fvrs)) - - (`(,_ . ,body-forms) ; First element is (like) a function. - (dolist (exp body-forms) - (setq fvrs (cconv-freevars exp fvrs))) fvrs) - - (_ (if (byte-compile-not-lexical-var-p form) - fvrs - (cons form fvrs))))) +;; Alist associating to each function body the list of its free variables. +(defvar cconv-freevars-alist) ;;;###autoload (defun cconv-closure-convert (form) @@ -195,16 +104,12 @@ Returns a list of free variables." Returns a form where all lambdas don't have any free variables." ;; (message "Entering cconv-closure-convert...") - (let ((cconv-mutated '()) + (let ((cconv-freevars-alist '()) (cconv-lambda-candidates '()) - (cconv-captured '()) (cconv-captured+mutated '())) ;; Analyse form - fill these variables with new information. - (cconv-analyse-form form '() 0) - ;; Calculate an intersection of cconv-mutated and cconv-captured. - (dolist (mvr cconv-mutated) - (when (memq mvr cconv-captured) ; - (push mvr cconv-captured+mutated))) + (cconv-analyse-form form '()) + (setq cconv-freevars-alist (nreverse cconv-freevars-alist)) (cconv-closure-convert-rec form ; the tree '() ; @@ -213,15 +118,6 @@ Returns a form where all lambdas don't have any free variables." '() ))) -(defun cconv--lookup-let (table var binder form) - (let ((res nil)) - (dolist (elem table) - (when (and (eq (nth 2 elem) binder) - (eq (nth 3 elem) form)) - (assert (eq (car elem) var)) - (setq res elem))) - res)) - (defconst cconv--dummy-var (make-symbol "ignored")) (defun cconv--set-diff (s1 s2) @@ -261,6 +157,57 @@ Returns a form where all lambdas don't have any free variables." (unless (memq (car b) s) (push b res))) (nreverse res))) +(defun cconv-closure-convert-function (fvrs vars emvrs envs lmenvs body-forms + parentform) + (assert (equal body-forms (caar cconv-freevars-alist))) + (let* ((fvrs-new (cconv--set-diff fvrs vars)) ; Remove vars from fvrs. + (fv (cdr (pop cconv-freevars-alist))) + (body-forms-new '()) + (letbind '()) + (envector nil)) + (when fv + ;; Here we form our environment vector. + + (dolist (elm fv) + (push + (cconv-closure-convert-rec + ;; Remove `elm' from `emvrs' for this call because in case + ;; `elm' is a variable that's wrapped in a cons-cell, we + ;; want to put the cons-cell itself in the closure, rather + ;; than just a copy of its current content. + elm (remq elm emvrs) fvrs envs lmenvs) + envector)) ; Process vars for closure vector. + (setq envector (reverse envector)) + (setq envs fv) + (setq fvrs-new fv)) ; Update substitution list. + + (setq emvrs (cconv--set-diff emvrs vars)) + (setq lmenvs (cconv--map-diff-set lmenvs vars)) + + ;; The difference between envs and fvrs is explained + ;; in comment in the beginning of the function. + (dolist (var vars) + (when (member (cons (list var) parentform) cconv-captured+mutated) + (push var emvrs) + (push `(,var (list ,var)) letbind))) + (dolist (elm body-forms) ; convert function body + (push (cconv-closure-convert-rec + elm emvrs fvrs-new envs lmenvs) + body-forms-new)) + + (setq body-forms-new + (if letbind `((let ,letbind . ,(reverse body-forms-new))) + (reverse body-forms-new))) + + (cond + ;if no freevars - do nothing + ((null envector) + `(function (lambda ,vars . ,body-forms-new))) + ; 1 free variable - do not build vector + (t + `(internal-make-closure + ,vars ,envector . ,body-forms-new))))) + (defun cconv-closure-convert-rec (form emvrs fvrs envs lmenvs) ;; This function actually rewrites the tree. "Eliminates all free variables of all lambdas in given forms. @@ -303,15 +250,18 @@ Returns a form where all lambdas don't have any free variables." (dolist (binder binders) (let* ((value nil) (var (if (not (consp binder)) - binder + (prog1 binder (setq binder (list binder))) (setq value (cadr binder)) (car binder))) (new-val (cond ;; Check if var is a candidate for lambda lifting. - ((cconv--lookup-let cconv-lambda-candidates var binder form) - - (let* ((fv (delete-dups (cconv-freevars value '()))) + ((member (cons binder form) cconv-lambda-candidates) + (assert (and (eq (car value) 'function) + (eq (car (cadr value)) 'lambda))) + (assert (equal (cddr (cadr value)) + (caar cconv-freevars-alist))) + (let* ((fv (cdr (pop cconv-freevars-alist))) (funargs (cadr (cadr value))) (funcvars (append fv funargs)) (funcbodies (cddadr value)) ; function bodies @@ -338,7 +288,7 @@ Returns a form where all lambdas don't have any free variables." ,(reverse funcbodies-new)))))))) ;; Check if it needs to be turned into a "ref-cell". - ((cconv--lookup-let cconv-captured+mutated var binder form) + ((member (cons binder form) cconv-captured+mutated) ;; Declared variable is mutated and captured. (prog1 `(list ,(cconv-closure-convert-rec @@ -404,13 +354,12 @@ Returns a form where all lambdas don't have any free variables." )) ; end of dolist over binders (when (eq letsym 'let) - (let (var fvrs-1 emvrs-1 lmenvs-1) - ;; Here we update emvrs, fvrs and lmenvs lists - (setq fvrs (cconv--set-diff-map fvrs binders-new)) - (setq emvrs (cconv--set-diff-map emvrs binders-new)) - (setq emvrs (append emvrs emvrs-new)) - (setq lmenvs (cconv--set-diff-map lmenvs binders-new)) - (setq lmenvs (append lmenvs lmenvs-new))) + ;; Here we update emvrs, fvrs and lmenvs lists + (setq fvrs (cconv--set-diff-map fvrs binders-new)) + (setq emvrs (cconv--set-diff-map emvrs binders-new)) + (setq emvrs (append emvrs emvrs-new)) + (setq lmenvs (cconv--set-diff-map lmenvs binders-new)) + (setq lmenvs (append lmenvs lmenvs-new)) ;; Here we do the same letbinding as for let* above ;; to avoid situation when a free variable of a lambda lifted @@ -478,56 +427,8 @@ Returns a form where all lambdas don't have any free variables." (`(quote . ,_) form) (`(function (lambda ,vars . ,body-forms)) ; function form - (let* ((fvrs-new (cconv--set-diff fvrs vars)) ; Remove vars from fvrs. - (fv (delete-dups (cconv-freevars form '()))) - (leave fvrs-new) ; leave=non-nil if we should leave env unchanged. - (body-forms-new '()) - (letbind '()) - (mv nil) - (envector nil)) - (when fv - ;; Here we form our environment vector. - - (dolist (elm fv) - (push - (cconv-closure-convert-rec - ;; Remove `elm' from `emvrs' for this call because in case - ;; `elm' is a variable that's wrapped in a cons-cell, we - ;; want to put the cons-cell itself in the closure, rather - ;; than just a copy of its current content. - elm (remq elm emvrs) fvrs envs lmenvs) - envector)) ; Process vars for closure vector. - (setq envector (reverse envector)) - (setq envs fv) - (setq fvrs-new fv)) ; Update substitution list. - - (setq emvrs (cconv--set-diff emvrs vars)) - (setq lmenvs (cconv--map-diff-set lmenvs vars)) - - ;; The difference between envs and fvrs is explained - ;; in comment in the beginning of the function. - (dolist (elm cconv-captured+mutated) ; Find mutated arguments - (setq mv (car elm)) ; used in inner closures. - (when (and (memq mv vars) (eq form (caddr elm))) - (progn (push mv emvrs) - (push `(,mv (list ,mv)) letbind)))) - (dolist (elm body-forms) ; convert function body - (push (cconv-closure-convert-rec - elm emvrs fvrs-new envs lmenvs) - body-forms-new)) - - (setq body-forms-new - (if letbind `((let ,letbind . ,(reverse body-forms-new))) - (reverse body-forms-new))) - - (cond - ;if no freevars - do nothing - ((null envector) - `(function (lambda ,vars . ,body-forms-new))) - ; 1 free variable - do not build vector - (t - `(internal-make-closure - ,vars ,envector . ,body-forms-new))))) + (cconv-closure-convert-function + fvrs vars emvrs envs lmenvs body-forms form)) (`(internal-make-closure . ,_) (error "Internal byte-compiler error: cconv called twice")) @@ -548,21 +449,21 @@ Returns a form where all lambdas don't have any free variables." ;defun, defmacro (`(,(and sym (or `defun `defmacro)) ,func ,vars . ,body-forms) + + ;; The freevar data was pushed onto cconv-freevars-alist + ;; but we don't need it. + (assert (equal body-forms (caar cconv-freevars-alist))) + (assert (null (cdar cconv-freevars-alist))) + (setq cconv-freevars-alist (cdr cconv-freevars-alist)) + (let ((body-new '()) ; The whole body. (body-forms-new '()) ; Body w\o docstring and interactive. (letbind '())) ; Find mutable arguments. (dolist (elm vars) - (let ((lmutated cconv-captured+mutated) - (ismutated nil)) - (while (and lmutated (not ismutated)) - (when (and (eq (caar lmutated) elm) - (eq (caddar lmutated) form)) - (setq ismutated t)) - (setq lmutated (cdr lmutated))) - (when ismutated - (push elm letbind) - (push elm emvrs)))) + (when (member (cons (list elm) form) cconv-captured+mutated) + (push elm letbind) + (push elm emvrs))) ;Transform body-forms. (when (stringp (car body-forms)) ; Treat docstring well. (push (car body-forms) body-new) @@ -629,12 +530,13 @@ Returns a form where all lambdas don't have any free variables." (setq value (cconv-closure-convert-rec (cadr forms) emvrs fvrs envs lmenvs)) - (if (memq sym emvrs) - (push `(setcar ,sym-new ,value) prognlist) - (if (symbolp sym-new) - (push `(setq ,sym-new ,value) prognlist) - (debug) ;FIXME: When can this be right? - (push `(set ,sym-new ,value) prognlist))) + (cond + ((memq sym emvrs) (push `(setcar ,sym-new ,value) prognlist)) + ((symbolp sym-new) (push `(setq ,sym-new ,value) prognlist)) + ;; This should never happen, but for variables which are + ;; mutated+captured+unused, we may end up trying to `setq' + ;; on a closed-over variable, so just drop the setq. + (t (push value prognlist))) (setq forms (cddr forms))) (if (cdr prognlist) `(progn . ,(reverse prognlist)) @@ -697,54 +599,110 @@ Returns a form where all lambdas don't have any free variables." `(car ,form) ; replace form => (car form) form)))))) -(defun cconv-analyse-function (args body env parentform inclosure) - (dolist (arg args) - (cond - ((byte-compile-not-lexical-var-p arg) - (byte-compile-report-error - (format "Argument %S is not a lexical variable" arg))) - ((eq ?& (aref (symbol-name arg) 0)) nil) ;Ignore &rest, &optional, ... - (t (push (list arg inclosure parentform) env)))) ;Push vrs to vars. - (dolist (form body) ;Analyse body forms. - (cconv-analyse-form form env inclosure))) - -(defun cconv-analyse-form (form env inclosure) - "Find mutated variables and variables captured by closure. Analyse -lambdas if they are suitable for lambda lifting. +(unless (fboundp 'byte-compile-not-lexical-var-p) + ;; Only used to test the code in non-lexbind Emacs. + (defalias 'byte-compile-not-lexical-var-p 'boundp)) + +(defun cconv-analyse-use (vardata form) + ;; use = `(,binder ,read ,mutated ,captured ,called) + (pcase vardata + (`(,binder nil ,_ ,_ nil) + ;; FIXME: Don't warn about unused fun-args. + ;; FIXME: Don't warn about uninterned vars or _ vars. + ;; FIXME: This gives warnings in the wrong order and with wrong line + ;; number and without function name info. + (byte-compile-log-warning (format "Unused variable %S" (car binder)))) + ;; If it's unused, there's no point converting it into a cons-cell, even if + ;; it's captures and mutated. + (`(,binder ,_ t t ,_) + (push (cons binder form) cconv-captured+mutated)) + (`(,(and binder `(,_ (function (lambda . ,_)))) nil nil nil t) + ;; This is very rare in typical Elisp code. It's probably not really + ;; worth the trouble to try and use lambda-lifting in Elisp, but + ;; since we coded it up, we might as well use it. + (push (cons binder form) cconv-lambda-candidates)) + (`(,_ ,_ ,_ ,_ ,_) nil) + (dontcare))) + +(defun cconv-analyse-function (args body env parentform) + (let* ((newvars nil) + (freevars (list body)) + ;; We analyze the body within a new environment where all uses are + ;; nil, so we can distinguish uses within that function from uses + ;; outside of it. + (envcopy + (mapcar (lambda (vdata) (list (car vdata) nil nil nil nil)) env)) + (newenv envcopy)) + ;; Push it before recursing, so cconv-freevars-alist contains entries in + ;; the order they'll be used by closure-convert-rec. + (push freevars cconv-freevars-alist) + (dolist (arg args) + (cond + ((byte-compile-not-lexical-var-p arg) + (byte-compile-report-error + (format "Argument %S is not a lexical variable" arg))) + ((eq ?& (aref (symbol-name arg) 0)) nil) ;Ignore &rest, &optional, ... + (t (let ((varstruct (list arg nil nil nil nil))) + (push (cons (list arg) (cdr varstruct)) newvars) + (push varstruct newenv))))) + (dolist (form body) ;Analyse body forms. + (cconv-analyse-form form newenv)) + ;; Summarize resulting data about arguments. + (dolist (vardata newvars) + (cconv-analyse-use vardata parentform)) + ;; Transfer uses collected in `envcopy' (via `newenv') back to `env'; + ;; and compute free variables. + (while env + (assert (and envcopy (eq (caar env) (caar envcopy)))) + (let ((free nil) + (x (cdr (car env))) + (y (cdr (car envcopy)))) + (while x + (when (car y) (setcar x t) (setq free t)) + (setq x (cdr x) y (cdr y))) + (when free + (push (caar env) (cdr freevars)) + (setf (nth 3 (car env)) t)) + (setq env (cdr env) envcopy (cdr envcopy)))))) + +(defun cconv-analyse-form (form env) + "Find mutated variables and variables captured by closure. +Analyse lambdas if they are suitable for lambda lifting. -- FORM is a piece of Elisp code after macroexpansion. --- ENV is a list of variables visible in current lexical environment. - Each entry has the form (VAR INCLOSURE BINDER PARENTFORM) - for let-bound vars and (VAR INCLOSURE PARENTFORM) for function arguments. --- INCLOSURE is the nesting level within lambdas." +-- ENV is an alist mapping each enclosing lexical variable to its info. + I.e. each element has the form (VAR . (READ MUTATED CAPTURED CALLED)). +This function does not return anything but instead fills the +`cconv-captured+mutated' and `cconv-lambda-candidates' variables +and updates the data stored in ENV." (pcase form ; let special form (`(,(and (or `let* `let) letsym) ,binders . ,body-forms) (let ((orig-env env) + (newvars nil) (var nil) (value nil)) (dolist (binder binders) (if (not (consp binder)) (progn (setq var binder) ; treat the form (let (x) ...) well + (setq binder (list binder)) (setq value nil)) (setq var (car binder)) (setq value (cadr binder)) - (cconv-analyse-form value (if (eq letsym 'let*) env orig-env) - inclosure)) + (cconv-analyse-form value (if (eq letsym 'let*) env orig-env))) (unless (byte-compile-not-lexical-var-p var) - (let ((varstruct (list var inclosure binder form))) - (push varstruct env) ; Push a new one. + (let ((varstruct (list var nil nil nil nil))) + (push (cons binder (cdr varstruct)) newvars) + (push varstruct env)))) - (pcase value - (`(function (lambda . ,_)) - ;; If var is a function push it to lambda list. - (push varstruct cconv-lambda-candidates))))))) + (dolist (form body-forms) ; Analyse body forms. + (cconv-analyse-form form env)) - (dolist (form body-forms) ; Analyse body forms. - (cconv-analyse-form form env inclosure))) + (dolist (vardata newvars) + (cconv-analyse-use vardata form)))) ; defun special form (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms) @@ -753,33 +711,28 @@ lambdas if they are suitable for lambda lifting. (format "Function %S will ignore its context %S" func (mapcar #'car env)) t :warning)) - (cconv-analyse-function vrs body-forms nil form 0)) + (cconv-analyse-function vrs body-forms nil form)) (`(function (lambda ,vrs . ,body-forms)) - (cconv-analyse-function vrs body-forms env form (1+ inclosure))) + (cconv-analyse-function vrs body-forms env form)) (`(setq . ,forms) ;; If a local variable (member of env) is modified by setq then ;; it is a mutated variable. (while forms (let ((v (assq (car forms) env))) ; v = non nil if visible - (when v - (push v cconv-mutated) - ;; Delete from candidate list for lambda lifting. - (setq cconv-lambda-candidates (delq v cconv-lambda-candidates)) - (unless (eq inclosure (cadr v)) ;Bound in a different closure level. - (push v cconv-captured)))) - (cconv-analyse-form (cadr forms) env inclosure) + (when v (setf (nth 2 v) t))) + (cconv-analyse-form (cadr forms) env) (setq forms (cddr forms)))) (`((lambda . ,_) . ,_) ; first element is lambda expression (dolist (exp `((function ,(car form)) . ,(cdr form))) - (cconv-analyse-form exp env inclosure))) + (cconv-analyse-form exp env))) (`(cond . ,cond-forms) ; cond special form (dolist (forms cond-forms) (dolist (form forms) - (cconv-analyse-form form env inclosure)))) + (cconv-analyse-form form env)))) (`(quote . ,_) nil) ; quote form (`(function . ,_) nil) ; same as quote @@ -788,63 +741,44 @@ lambdas if they are suitable for lambda lifting. ;; FIXME: The bytecode for condition-case forces us to wrap the ;; form and handlers in closures (for handlers, it's probably ;; unavoidable, but not for the protected form). - (setq inclosure (1+ inclosure)) - (cconv-analyse-form protected-form env inclosure) - (push (list var inclosure form) env) + (cconv-analyse-function () (list protected-form) env form) (dolist (handler handlers) - (dolist (form (cdr handler)) - (cconv-analyse-form form env inclosure)))) + (cconv-analyse-function (if var (list var)) (cdr handler) env form))) ;; FIXME: The bytecode for catch forces us to wrap the body. (`(,(or `catch `unwind-protect) ,form . ,body) - (cconv-analyse-form form env inclosure) - (setq inclosure (1+ inclosure)) - (dolist (form body) - (cconv-analyse-form form env inclosure))) + (cconv-analyse-form form env) + (cconv-analyse-function () body env form)) ;; FIXME: The bytecode for save-window-excursion and the lack of ;; bytecode for track-mouse forces us to wrap the body. (`(track-mouse . ,body) - (setq inclosure (1+ inclosure)) - (dolist (form body) - (cconv-analyse-form form env inclosure))) + (cconv-analyse-function () body env form)) (`(,(or `defconst `defvar) ,var ,value . ,_) (push var byte-compile-bound-variables) - (cconv-analyse-form value env inclosure)) + (cconv-analyse-form value env)) (`(,(or `funcall `apply) ,fun . ,args) ;; Here we ignore fun because funcall and apply are the only two ;; functions where we can pass a candidate for lambda lifting as ;; argument. So, if we see fun elsewhere, we'll delete it from ;; lambda candidate list. - (if (symbolp fun) - (let ((lv (assq fun cconv-lambda-candidates))) - (when lv - (unless (eq (cadr lv) inclosure) - (push lv cconv-captured) - ;; If this funcall and the definition of fun are in - ;; different closures - we delete fun from candidate - ;; list, because it is too complicated to manage free - ;; variables in this case. - (setq cconv-lambda-candidates - (delq lv cconv-lambda-candidates))))) - (cconv-analyse-form fun env inclosure)) + (let ((fdata (and (symbolp fun) (assq fun env)))) + (if fdata + (setf (nth 4 fdata) t) + (cconv-analyse-form fun env))) (dolist (form args) - (cconv-analyse-form form env inclosure))) + (cconv-analyse-form form env))) (`(,_ . ,body-forms) ; First element is a function or whatever. (dolist (form body-forms) - (cconv-analyse-form form env inclosure))) + (cconv-analyse-form form env))) ((pred symbolp) (let ((dv (assq form env))) ; dv = declared and visible (when dv - (unless (eq inclosure (cadr dv)) ; capturing condition - (push dv cconv-captured)) - ;; Delete lambda if it is found here, since it escapes. - (setq cconv-lambda-candidates - (delq dv cconv-lambda-candidates))))))) + (setf (nth 1 dv) t)))))) (provide 'cconv) ;;; cconv.el ends here |