1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-24 19:03:29 +00:00

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.
This commit is contained in:
Stefan Monnier 2011-02-26 10:19:08 -05:00
parent 876c194cba
commit a9de04fa62
10 changed files with 307 additions and 352 deletions

View File

@ -1,3 +1,36 @@
2011-02-26 Stefan Monnier <monnier@iro.umontreal.ca>
* 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.
* 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'.
* emacs-lisp/byte-run.el (make-obsolete): Don't modify byte-compile.
* Makefile.in (BIG_STACK_DEPTH): Increase to 1200.
(compile-onefile): Pass $(BIG_STACK_OPTS) before "-l bytecomp".
* emacs-lisp/macroexp.el: Use lexbind.
(macroexpand-all-1): Check macro obsolescence.
* vc/diff-mode.el: Use lexbind.
* follow.el (follow-calc-win-end): Simplify.
2011-02-25 Stefan Monnier <monnier@iro.umontreal.ca>
* emacs-lisp/bytecomp.el (byte-compile-lapcode): Handle new form of

View File

@ -74,7 +74,7 @@ AUTOGENEL = loaddefs.el \
# During bootstrapping the byte-compiler is run interpreted when compiling
# itself, and uses more stack than usual.
#
BIG_STACK_DEPTH = 1000
BIG_STACK_DEPTH = 1200
BIG_STACK_OPTS = --eval "(setq max-lisp-eval-depth $(BIG_STACK_DEPTH))"
# Files to compile before others during a bootstrap. This is done to
@ -205,8 +205,8 @@ compile-onefile:
@echo Compiling $(THEFILE)
@# Use byte-compile-refresh-preloaded to try and work around some of
@# the most common bootstrapping problems.
$(emacs) -l bytecomp.el -f byte-compile-refresh-preloaded \
$(BIG_STACK_OPTS) $(BYTE_COMPILE_EXTRA_FLAGS) \
@$(emacs) $(BIG_STACK_OPTS) -l bytecomp $(BYTE_COMPILE_EXTRA_FLAGS) \
-f byte-compile-refresh-preloaded \
-f batch-byte-compile $(THEFILE)
# Files MUST be compiled one by one. If we compile several files in a
@ -222,7 +222,7 @@ compile-onefile:
# cannot have prerequisites.
.el.elc:
@echo Compiling $<
$(emacs) $(BIG_STACK_OPTS) $(BYTE_COMPILE_EXTRA_FLAGS) \
@$(emacs) $(BIG_STACK_OPTS) $(BYTE_COMPILE_EXTRA_FLAGS) \
-f batch-byte-compile $<
.PHONY: compile-first compile-main compile compile-always

View File

@ -123,12 +123,10 @@ If CURRENT-NAME is a string, that is the `use instead' message
If provided, WHEN should be a string indicating when the function
was first made obsolete, for example a date or a release number."
(interactive "aMake function obsolete: \nxObsoletion replacement: ")
(let ((handler (get obsolete-name 'byte-compile)))
(if (eq 'byte-compile-obsolete handler)
(setq handler (nth 1 (get obsolete-name 'byte-obsolete-info)))
(put obsolete-name 'byte-compile 'byte-compile-obsolete))
(put obsolete-name 'byte-obsolete-info
(list (purecopy current-name) handler (purecopy when))))
(put obsolete-name 'byte-obsolete-info
;; The second entry used to hold the `byte-compile' handler, but
;; is not used any more nowadays.
(list (purecopy current-name) nil (purecopy when)))
obsolete-name)
(set-advertised-calling-convention
;; New code should always provide the `when' argument.

View File

@ -424,6 +424,7 @@ This list lives partly on the stack.")
'(
;; (byte-compiler-options . (lambda (&rest forms)
;; (apply 'byte-compiler-options-handler forms)))
(declare-function . byte-compile-macroexpand-declare-function)
(eval-when-compile . (lambda (&rest body)
(list
'quote
@ -1140,13 +1141,6 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
(byte-compile-log-warning
(error-message-string error-info)
nil :error))
;;; Used by make-obsolete.
(defun byte-compile-obsolete (form)
(byte-compile-set-symbol-position (car form))
(byte-compile-warn-obsolete (car form))
(funcall (or (cadr (get (car form) 'byte-obsolete-info)) ; handler
'byte-compile-normal-call) form))
;;; sanity-checking arglists
@ -1328,7 +1322,8 @@ extra args."
;; Warn if the function or macro is being redefined with a different
;; number of arguments.
(defun byte-compile-arglist-warn (form macrop)
(let ((old (byte-compile-fdefinition (nth 1 form) macrop)))
(let* ((name (nth 1 form))
(old (byte-compile-fdefinition name macrop)))
(if (and old (not (eq old t)))
(progn
(and (eq 'macro (car-safe old))
@ -1342,36 +1337,39 @@ extra args."
(t '(&rest def)))))
(sig2 (byte-compile-arglist-signature (nth 2 form))))
(unless (byte-compile-arglist-signatures-congruent-p sig1 sig2)
(byte-compile-set-symbol-position (nth 1 form))
(byte-compile-set-symbol-position name)
(byte-compile-warn
"%s %s used to take %s %s, now takes %s"
(if (eq (car form) 'defun) "function" "macro")
(nth 1 form)
name
(byte-compile-arglist-signature-string sig1)
(if (equal sig1 '(1 . 1)) "argument" "arguments")
(byte-compile-arglist-signature-string sig2)))))
;; This is the first definition. See if previous calls are compatible.
(let ((calls (assq (nth 1 form) byte-compile-unresolved-functions))
(let ((calls (assq name byte-compile-unresolved-functions))
nums sig min max)
(if calls
(progn
(setq sig (byte-compile-arglist-signature (nth 2 form))
nums (sort (copy-sequence (cdr calls)) (function <))
min (car nums)
max (car (nreverse nums)))
(when (or (< min (car sig))
(and (cdr sig) (> max (cdr sig))))
(byte-compile-set-symbol-position (nth 1 form))
(byte-compile-warn
"%s being defined to take %s%s, but was previously called with %s"
(nth 1 form)
(byte-compile-arglist-signature-string sig)
(if (equal sig '(1 . 1)) " arg" " args")
(byte-compile-arglist-signature-string (cons min max))))
(when calls
(when (and (symbolp name)
(eq (get name 'byte-optimizer)
'byte-compile-inline-expand))
(byte-compile-warn "defsubst `%s' was used before it was defined"
name))
(setq sig (byte-compile-arglist-signature (nth 2 form))
nums (sort (copy-sequence (cdr calls)) (function <))
min (car nums)
max (car (nreverse nums)))
(when (or (< min (car sig))
(and (cdr sig) (> max (cdr sig))))
(byte-compile-set-symbol-position name)
(byte-compile-warn
"%s being defined to take %s%s, but was previously called with %s"
name
(byte-compile-arglist-signature-string sig)
(if (equal sig '(1 . 1)) " arg" " args")
(byte-compile-arglist-signature-string (cons min max))))
(setq byte-compile-unresolved-functions
(delq calls byte-compile-unresolved-functions)))))
)))
(setq byte-compile-unresolved-functions
(delq calls byte-compile-unresolved-functions)))))))
(defvar byte-compile-cl-functions nil
"List of functions defined in CL.")
@ -1470,7 +1468,7 @@ symbol itself."
(if any-value
(or (memq symbol byte-compile-const-variables)
;; FIXME: We should provide a less intrusive way to find out
;; is a variable is "constant".
;; if a variable is "constant".
(and (boundp symbol)
(condition-case nil
(progn (set symbol (symbol-value symbol)) nil)
@ -2198,9 +2196,8 @@ list that represents a doc string reference.
;; byte-hunk-handlers can call this.
(defun byte-compile-file-form (form)
(let (bytecomp-handler)
(cond ((not (consp form))
(byte-compile-keep-pending form))
((and (symbolp (car form))
(cond ((and (consp form)
(symbolp (car form))
(setq bytecomp-handler (get (car form) 'byte-hunk-handler)))
(cond ((setq form (funcall bytecomp-handler form))
(byte-compile-flush-pending)
@ -2212,16 +2209,6 @@ list that represents a doc string reference.
;; so make-docfile can recognise them. Most other things can be output
;; as byte-code.
(put 'defsubst 'byte-hunk-handler 'byte-compile-file-form-defsubst)
(defun byte-compile-file-form-defsubst (form)
(when (assq (nth 1 form) byte-compile-unresolved-functions)
(setq byte-compile-current-form (nth 1 form))
(byte-compile-warn "defsubst `%s' was used before it was defined"
(nth 1 form)))
(byte-compile-file-form form)
;; Return nil so the form is not output twice.
nil)
(put 'autoload 'byte-hunk-handler 'byte-compile-file-form-autoload)
(defun byte-compile-file-form-autoload (form)
(and (let ((form form))
@ -2914,7 +2901,6 @@ If FORM is a lambda or a macro, byte-compile it as a function."
;; Given BYTECOMP-BODY, compile it and return a new body.
(defun byte-compile-top-level-body (bytecomp-body &optional for-effect)
;; FIXME: lexbind. Check all callers!
(setq bytecomp-body
(byte-compile-top-level (cons 'progn bytecomp-body) for-effect t))
(cond ((eq (car-safe bytecomp-body) 'progn)
@ -2922,20 +2908,18 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(bytecomp-body
(list bytecomp-body))))
;; FIXME: Like defsubst's, this hunk-handler won't be called any more
;; because the macro is expanded away before we see it.
(put 'declare-function 'byte-hunk-handler 'byte-compile-declare-function)
(defun byte-compile-declare-function (form)
(push (cons (nth 1 form)
(if (and (> (length form) 3)
(listp (nth 3 form)))
(list 'declared (nth 3 form))
;; Special macro-expander used during byte-compilation.
(defun byte-compile-macroexpand-declare-function (fn file &rest args)
(push (cons fn
(if (and (consp args) (listp (car args)))
(list 'declared (car args))
t)) ; arglist not specified
byte-compile-function-environment)
;; We are stating that it _will_ be defined at runtime.
(setq byte-compile-noruntime-functions
(delq (nth 1 form) byte-compile-noruntime-functions))
nil)
(delq fn byte-compile-noruntime-functions))
;; Delegate the rest to the normal macro definition.
(macroexpand `(declare-function ,fn ,file ,@args)))
;; This is the recursive entry point for compiling each subform of an
@ -3005,6 +2989,8 @@ That command is designed for interactive use only" bytecomp-fn))
'(custom-declare-group custom-declare-variable
custom-declare-face))
(byte-compile-nogroup-warn form))
(when (get (car form) 'byte-obsolete-info)
(byte-compile-warn-obsolete (car form)))
(byte-compile-callargs-warn form))
(if byte-compile-generate-call-tree
(byte-compile-annotate-call-tree form))
@ -3562,7 +3548,6 @@ discarding."
(byte-defop-compiler-1 setq)
(byte-defop-compiler-1 setq-default)
(byte-defop-compiler-1 quote)
(byte-defop-compiler-1 quote-form)
(defun byte-compile-setq (form)
(let ((bytecomp-args (cdr form)))
@ -3606,10 +3591,6 @@ discarding."
(defun byte-compile-quote (form)
(byte-compile-constant (car (cdr form))))
(defun byte-compile-quote-form (form)
(byte-compile-constant (byte-compile-top-level (nth 1 form))))
;;; control structures
@ -3845,6 +3826,7 @@ Return the offset in the form (VAR . OFFSET)."
(byte-compile-push-constant nil)))))
(defun byte-compile-not-lexical-var-p (var)
;; FIXME: this doesn't catch defcustoms!
(or (not (symbolp var))
(special-variable-p var)
(memq var byte-compile-bound-variables)
@ -4097,15 +4079,16 @@ binding slots have been popped."
(defun byte-compile-defmacro (form)
;; This is not used for file-level defmacros with doc strings.
;; FIXME handle decls, use defalias?
(let ((decls (byte-compile-defmacro-declaration form))
(code (byte-compile-lambda (cdr (cdr form)) t))
(for-effect nil))
(byte-compile-push-constant (nth 1 form))
(byte-compile-push-constant (cons 'macro code))
(byte-compile-out 'byte-fset)
(byte-compile-discard))
(byte-compile-constant (nth 1 form)))
(byte-compile-body-do-effect
(let ((decls (byte-compile-defmacro-declaration form))
(code (byte-compile-byte-code-maker
(byte-compile-lambda (cdr (cdr form)) t))))
`((defalias ',(nth 1 form)
,(if (eq (car-safe code) 'make-byte-code)
`(cons 'macro ,code)
`'(macro . ,(eval code))))
,@decls
',(nth 1 form)))))
(defun byte-compile-defvar (form)
;; This is not used for file-level defvar/consts with doc strings.
@ -4153,7 +4136,7 @@ binding slots have been popped."
`(if (not (default-boundp ',var)) (setq-default ,var ,value))))
(when (eq fun 'defconst)
;; This will signal an appropriate error at runtime.
`(eval ',form))) ;FIXME: lexbind
`(eval ',form)))
`',var))))
(defun byte-compile-autoload (form)

View File

@ -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).")
;; 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)
(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
;; 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)
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)
(`(cond . ,cond-forms) ; cond special form
(dolist (exp1 cond-forms)
(dolist (exp2 exp1)
(setq fvrs (cconv-freevars exp2 fvrs)))) fvrs)
(`(quote . ,_) fvrs) ; quote form
(`(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)))
(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-form (form env inclosure)
"Find mutated variables and variables captured by closure. Analyse
lambdas if they are suitable for lambda lifting.
(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

View File

@ -269,6 +269,7 @@ That buffer should be current already."
(setq buffer-undo-list t)
(let ((standard-output (current-buffer))
(print-escape-newlines t)
(print-quoted t) ;Doesn't seem to work :-(
(print-level 1000) ;8
;; (print-length 50)
)

View File

@ -1,4 +1,4 @@
;;; macroexp.el --- Additional macro-expansion support
;;; macroexp.el --- Additional macro-expansion support -*- lexical-binding: t -*-
;;
;; Copyright (C) 2004-2011 Free Software Foundation, Inc.
;;
@ -108,7 +108,14 @@ Assumes the caller has bound `macroexpand-all-environment'."
(macroexpand (macroexpand-all-forms form 1)
macroexpand-all-environment)
;; Normal form; get its expansion, and then expand arguments.
(setq form (macroexpand form macroexpand-all-environment))
(let ((new-form (macroexpand form macroexpand-all-environment)))
(when (and (not (eq form new-form)) ;It was a macro call.
(car-safe form)
(symbolp (car form))
(get (car form) 'byte-obsolete-info)
(fboundp 'byte-compile-warn-obsolete))
(byte-compile-warn-obsolete (car form)))
(setq form new-form))
(pcase form
(`(cond . ,clauses)
(maybe-cons 'cond (macroexpand-all-clauses clauses) form))

View File

@ -871,8 +871,7 @@ Returns (end-pos end-of-buffer-p)"
;; XEmacs can calculate the end of the window by using
;; the 'guarantee options. GOOD!
(let ((end (window-end win t)))
(if (= end (funcall (symbol-function 'point-max)
(window-buffer win)))
(if (= end (point-max (window-buffer win)))
(list end t)
(list (+ end 1) nil)))
;; Emacs: We have to calculate the end by ourselves.

View File

@ -1,4 +1,4 @@
;;; diff-mode.el --- a mode for viewing/editing context diffs
;;; diff-mode.el --- a mode for viewing/editing context diffs -*- lexical-binding: t -*-
;; Copyright (C) 1998-2011 Free Software Foundation, Inc.
@ -1278,7 +1278,7 @@ a diff with \\[diff-reverse-direction].
(add-hook 'after-change-functions 'diff-after-change-function nil t)
(add-hook 'post-command-hook 'diff-post-command-hook nil t))
;; Neat trick from Dave Love to add more bindings in read-only mode:
(lexical-let ((ro-bind (cons 'buffer-read-only diff-mode-shared-map)))
(let ((ro-bind (cons 'buffer-read-only diff-mode-shared-map)))
(add-to-list 'minor-mode-overriding-map-alist ro-bind)
;; Turn off this little trick in case the buffer is put in view-mode.
(add-hook 'view-mode-hook

View File

@ -51,7 +51,7 @@ by Hallvard:
*
* define BYTE_CODE_METER to enable generation of a byte-op usage histogram.
*/
/* #define BYTE_CODE_SAFE */
#define BYTE_CODE_SAFE 1
/* #define BYTE_CODE_METER */