summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r--lisp/emacs-lisp/byte-lexbind.el696
-rw-r--r--lisp/emacs-lisp/byte-opt.el263
-rw-r--r--lisp/emacs-lisp/bytecomp.el879
-rw-r--r--lisp/emacs-lisp/disass.el15
-rw-r--r--lisp/emacs-lisp/lisp-mode.el10
5 files changed, 1578 insertions, 285 deletions
diff --git a/lisp/emacs-lisp/byte-lexbind.el b/lisp/emacs-lisp/byte-lexbind.el
new file mode 100644
index 00000000000..df463c17549
--- /dev/null
+++ b/lisp/emacs-lisp/byte-lexbind.el
@@ -0,0 +1,696 @@
+;;; byte-lexbind.el --- Lexical binding support for byte-compiler
+;;
+;; Copyright (C) 2001, 2002, 2010 Free Software Foundation, Inc.
+;;
+;; Author: Miles Bader <miles@gnu.org>
+;; Keywords: lisp, compiler, lexical binding
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+;;
+
+;;; Code:
+
+(require 'bytecomp-preload "bytecomp")
+
+;; Downward closures aren't implemented yet, so this should always be nil
+(defconst byte-compile-use-downward-closures nil
+ "If true, use `downward closures', which are closures that don't cons.")
+
+(defconst byte-compile-save-window-excursion-uses-eval t
+ "If true, the bytecode for `save-window-excursion' uses eval.
+This means that the body of the form must be put into a closure.")
+
+(defun byte-compile-arglist-vars (arglist)
+ "Return a list of the variables in the lambda argument list ARGLIST."
+ (remq '&rest (remq '&optional arglist)))
+
+
+;;; Variable extent analysis.
+
+;; A `lforminfo' holds information about lexical bindings in a form, and some
+;; other info for analysis. It is a cons-cell, where the car is a list of
+;; `lvarinfo' stuctures, which form an alist indexed by variable name, and the
+;; cdr is the number of closures found in the form:
+;;
+;; LFORMINFO : ((LVARINFO ...) . NUM-CLOSURES)"
+;;
+;; A `lvarinfo' holds information about a single lexical variable. It is a
+;; list whose car is the variable name (so an lvarinfo is suitable as an alist
+;; entry), and the rest of the of which holds information about the variable:
+;;
+;; LVARINFO : (VAR NUM-REFS NUM-SETS CLOSED-OVER)
+;;
+;; NUM-REFS is the number of times the variable's value is used
+;; NUM-SETS is the number of times the variable's value is set
+;; CLOSED-OVER is non-nil if the variable is referenced
+;; anywhere but in its original function-level"
+
+;;; lvarinfo:
+
+;; constructor
+(defsubst byte-compile-make-lvarinfo (var &optional already-set)
+ (list var 0 (if already-set 1 0) 0 nil))
+;; accessors
+(defsubst byte-compile-lvarinfo-var (vinfo) (car vinfo))
+(defsubst byte-compile-lvarinfo-num-refs (vinfo) (cadr vinfo))
+(defsubst byte-compile-lvarinfo-num-sets (vinfo) (nth 3 vinfo))
+(defsubst byte-compile-lvarinfo-closed-over-p (vinfo) (nth 4 vinfo))
+;; setters
+(defsubst byte-compile-lvarinfo-note-ref (vinfo)
+ (setcar (cdr vinfo) (1+ (cadr vinfo))))
+(defsubst byte-compile-lvarinfo-note-set (vinfo)
+ (setcar (cddr vinfo) (1+ (nth 3 vinfo))))
+(defsubst byte-compile-lvarinfo-note-closure (vinfo)
+ (setcar (nthcdr 4 vinfo) t))
+
+;;; lforminfo:
+
+;; constructor
+(defsubst byte-compile-make-lforminfo ()
+ (cons nil 0))
+;; accessors
+(defalias 'byte-compile-lforminfo-vars 'car)
+(defalias 'byte-compile-lforminfo-num-closures 'cdr)
+;; setters
+(defsubst byte-compile-lforminfo-add-var (finfo var &optional already-set)
+ (setcar finfo (cons (byte-compile-make-lvarinfo var already-set)
+ (car finfo))))
+
+(defun byte-compile-lforminfo-make-closure-flag ()
+ "Return a new `closure-flag'."
+ (cons nil nil))
+
+(defsubst byte-compile-lforminfo-note-closure (lforminfo lvarinfo closure-flag)
+ "If a variable reference or definition is inside a closure, record that fact.
+LFORMINFO describes the form currently being analyzed, and LVARINFO
+describes the variable. CLOSURE-FLAG is either nil, if currently _not_
+inside a closure, and otherwise a `closure flag' returned by
+`byte-compile-lforminfo-make-closure-flag'."
+ (when closure-flag
+ (byte-compile-lvarinfo-note-closure lvarinfo)
+ (unless (car closure-flag)
+ (setcdr lforminfo (1+ (cdr lforminfo)))
+ (setcar closure-flag t))))
+
+(defun byte-compile-compute-lforminfo (form &optional special)
+ "Return information about variables lexically bound by FORM.
+SPECIAL is a list of variables that are special, and so shouldn't be
+bound lexically (in addition to variable that are considered special
+because they are declared with `defvar', et al).
+
+The result is an `lforminfo' data structure."
+ (and
+ (consp form)
+ (let ((lforminfo (byte-compile-make-lforminfo)))
+ (cond ((eq (car form) 'let)
+ ;; Find the bound variables
+ (dolist (clause (cadr form))
+ (let ((var (if (consp clause) (car clause) clause)))
+ (unless (or (special-variable-p var) (memq var special))
+ (byte-compile-lforminfo-add-var lforminfo var t))))
+ ;; Analyze the body
+ (unless (null (byte-compile-lforminfo-vars lforminfo))
+ (byte-compile-lforminfo-analyze-forms lforminfo form 2
+ special nil)))
+ ((eq (car form) 'let*)
+ (dolist (clause (cadr form))
+ (let ((var (if (consp clause) (car clause) clause)))
+ ;; Analyze each initializer based on the previously
+ ;; bound variables.
+ (when (and (consp clause) lforminfo)
+ (byte-compile-lforminfo-analyze lforminfo (cadr clause)
+ special nil))
+ (unless (or (special-variable-p var) (memq var special))
+ (byte-compile-lforminfo-add-var lforminfo var t))))
+ ;; Analyze the body
+ (unless (null (byte-compile-lforminfo-vars lforminfo))
+ (byte-compile-lforminfo-analyze-forms lforminfo form 2
+ special nil)))
+ ((eq (car form) 'condition-case)
+ ;; `condition-case' currently must dynamically bind the
+ ;; error variable, so do nothing.
+ )
+ ((memq (car form) '(defun defmacro))
+ (byte-compile-lforminfo-from-lambda lforminfo (cdr form) special))
+ ((eq (car form) 'lambda)
+ (byte-compile-lforminfo-from-lambda lforminfo form special))
+ ((and (consp (car form)) (eq (caar form) 'lambda))
+ ;; An embedded lambda, which is basically just a `let'
+ (byte-compile-lforminfo-from-lambda lforminfo (cdr form) special)))
+ (if (byte-compile-lforminfo-vars lforminfo)
+ lforminfo
+ nil))))
+
+(defun byte-compile-lforminfo-from-lambda (lforminfo lambda special)
+ "Initialize LFORMINFO from the lambda expression LAMBDA.
+SPECIAL is a list of variables to ignore.
+The first element of LAMBDA is ignored; it need not actually be `lambda'."
+ ;; Add the arguments
+ (dolist (arg (byte-compile-arglist-vars (cadr lambda)))
+ (byte-compile-lforminfo-add-var lforminfo arg t))
+ ;; Analyze the body
+ (unless (null (byte-compile-lforminfo-vars lforminfo))
+ (byte-compile-lforminfo-analyze-forms lforminfo lambda 2 special nil)))
+
+(defun byte-compile-lforminfo-analyze (lforminfo form &optional ignore closure-flag)
+ "Update variable information in LFORMINFO by analyzing FORM.
+IGNORE is a list of variables that shouldn't be analyzed (usually because
+they're special, or because some inner binding shadows the version in
+LFORMINFO). CLOSURE-FLAG should be either nil or a `closure flag' created
+with `byte-compile-lforminfo-make-closure-flag'; the latter indicates that
+FORM is inside a lambda expression that may close over some variable in
+LFORMINFO."
+ (cond ((symbolp form)
+ ;; variable reference
+ (unless (member form ignore)
+ (let ((vinfo (assq form (byte-compile-lforminfo-vars lforminfo))))
+ (when vinfo
+ (byte-compile-lvarinfo-note-ref vinfo)
+ (byte-compile-lforminfo-note-closure lforminfo vinfo
+ closure-flag)))))
+ ;; function call/special form
+ ((consp form)
+ (let ((fun (car form)))
+ (cond
+ ((eq fun 'setq)
+ (pop form)
+ (while form
+ (let ((var (pop form)))
+ (byte-compile-lforminfo-analyze lforminfo (pop form)
+ ignore closure-flag)
+ (unless (member var ignore)
+ (let ((vinfo
+ (assq var (byte-compile-lforminfo-vars lforminfo))))
+ (when vinfo
+ (byte-compile-lvarinfo-note-set vinfo)
+ (byte-compile-lforminfo-note-closure lforminfo vinfo
+ closure-flag)))))))
+ ((eq fun 'catch)
+ ;; tag
+ (byte-compile-lforminfo-analyze lforminfo (cadr form)
+ ignore closure-flag)
+ ;; `catch' uses a closure for the body
+ (byte-compile-lforminfo-analyze-forms
+ lforminfo form 2
+ ignore
+ (or closure-flag
+ (and (not byte-compile-use-downward-closures)
+ (byte-compile-lforminfo-make-closure-flag)))))
+ ((eq fun 'cond)
+ (byte-compile-lforminfo-analyze-clauses lforminfo (cdr form) 0
+ ignore closure-flag))
+ ((eq fun 'condition-case)
+ ;; `condition-case' separates its body/handlers into
+ ;; separate closures.
+ (unless (or closure-flag byte-compile-use-downward-closures)
+ ;; condition case is implemented by calling a function
+ (setq closure-flag (byte-compile-lforminfo-make-closure-flag)))
+ ;; value form
+ (byte-compile-lforminfo-analyze lforminfo (nth 2 form)
+ ignore closure-flag)
+ ;; the error variable is always bound dynamically (because
+ ;; of the implementation)
+ (when (cadr form)
+ (push (cadr form) ignore))
+ ;; handlers
+ (byte-compile-lforminfo-analyze-clauses lforminfo
+ (nthcdr 2 form) 1
+ ignore closure-flag))
+ ((eq fun '(defvar defconst))
+ (byte-compile-lforminfo-analyze lforminfo (nth 2 form)
+ ignore closure-flag))
+ ((memq fun '(defun defmacro))
+ (byte-compile-lforminfo-analyze-forms lforminfo form 3
+ ignore closure-flag))
+ ((eq fun 'function)
+ ;; Analyze an embedded lambda expression [note: we only recognize
+ ;; it within (function ...) as the (lambda ...) for is actually a
+ ;; macro returning (function (lambda ...))].
+ (when (and (consp (cadr form)) (eq (car (cadr form)) 'lambda))
+ ;; shadow bound variables
+ (setq ignore
+ (append (byte-compile-arglist-vars (cadr (cadr form)))
+ ignore))
+ ;; analyze body of lambda
+ (byte-compile-lforminfo-analyze-forms
+ lforminfo (cadr form) 2
+ ignore
+ (or closure-flag
+ (byte-compile-lforminfo-make-closure-flag)))))
+ ((eq fun 'let)
+ ;; analyze variable inits
+ (byte-compile-lforminfo-analyze-clauses lforminfo (cadr form) 1
+ ignore closure-flag)
+ ;; shadow bound variables
+ (dolist (clause (cadr form))
+ (push (if (symbolp clause) clause (car clause))
+ ignore))
+ ;; analyze body
+ (byte-compile-lforminfo-analyze-forms lforminfo form 2
+ ignore closure-flag))
+ ((eq fun 'let*)
+ (dolist (clause (cadr form))
+ (if (symbolp clause)
+ ;; shadow bound (to nil) variable
+ (push clause ignore)
+ ;; analyze variable init
+ (byte-compile-lforminfo-analyze lforminfo (cadr clause)
+ ignore closure-flag)
+ ;; shadow bound variable
+ (push (car clause) ignore)))
+ ;; analyze body
+ (byte-compile-lforminfo-analyze-forms lforminfo form 2
+ ignore closure-flag))
+ ((eq fun 'quote)
+ ;; do nothing
+ )
+ ((eq fun 'save-window-excursion)
+ ;; `save-window-excursion' currently uses a funny implementation
+ ;; that requires its body forms be put into a closure (it should
+ ;; be fixed to work more like `save-excursion' etc., do).
+ (byte-compile-lforminfo-analyze-forms
+ lforminfo form 2
+ ignore
+ (or closure-flag
+ (and byte-compile-save-window-excursion-uses-eval
+ (not byte-compile-use-downward-closures)
+ (byte-compile-lforminfo-make-closure-flag)))))
+ ((and (consp fun) (eq (car fun) 'lambda))
+ ;; Embedded lambda. These are inlined by the compiler, so
+ ;; we don't treat them like a real closure, more like `let'.
+ ;; analyze inits
+ (byte-compile-lforminfo-analyze-forms lforminfo form 2
+ ignore closure-flag)
+
+ ;; shadow bound variables
+ (setq ignore (nconc (byte-compile-arglist-vars (cadr fun))
+ ignore))
+ ;; analyze body
+ (byte-compile-lforminfo-analyze-forms lforminfo fun 2
+ ignore closure-flag))
+ (t
+ ;; For everything else, we just expand each argument (for
+ ;; setq/setq-default this works alright because the
+ ;; variable names are symbols).
+ (byte-compile-lforminfo-analyze-forms lforminfo form 1
+ ignore closure-flag)))))))
+
+(defun byte-compile-lforminfo-analyze-forms
+ (lforminfo forms skip ignore closure-flag)
+ "Update variable information in LFORMINFO by analyzing each form in FORMS.
+The first SKIP elements of FORMS are skipped without analysis. IGNORE
+is a list of variables that shouldn't be analyzed (usually because
+they're special, or because some inner binding shadows the version in
+LFORMINFO). CLOSURE-FLAG should be either nil or a `closure flag' created with
+`byte-compile-lforminfo-make-closure-flag'; the latter indicates that FORM is
+inside a lambda expression that may close over some variable in LFORMINFO."
+ (when skip
+ (setq forms (nthcdr skip forms)))
+ (while forms
+ (byte-compile-lforminfo-analyze lforminfo (pop forms)
+ ignore closure-flag)))
+
+(defun byte-compile-lforminfo-analyze-clauses
+ (lforminfo clauses skip ignore closure-flag)
+ "Update variable information in LFORMINFO by analyzing each clause in CLAUSES.
+Each clause is a list of forms; any clause that's not a list is ignored. The
+first SKIP elements of each clause are skipped without analysis. IGNORE is a
+list of variables that shouldn't be analyzed (usually because they're special,
+or because some inner binding shadows the version in LFORMINFO).
+CLOSURE-FLAG should be either nil or a `closure flag' created with
+`byte-compile-lforminfo-make-closure-flag'; the latter indicates that FORM is
+inside a lambda expression that may close over some variable in LFORMINFO."
+ (while clauses
+ (let ((clause (pop clauses)))
+ (when (consp clause)
+ (byte-compile-lforminfo-analyze-forms lforminfo clause skip
+ ignore closure-flag)))))
+
+
+;;; Lexical environments
+
+;; A lexical environment is an alist, where each element is of the form
+;; (VAR . (OFFSET . ENV)) where VAR is either a symbol, for normal
+;; variables, or an `heapenv' descriptor for references to heap environment
+;; vectors. ENV is either an atom, meaning a `stack allocated' variable
+;; (the particular atom serves to indicate the particular function context
+;; on whose stack it's allocated), or an `heapenv' descriptor (see above),
+;; meaning a variable allocated in a heap environment vector. For the
+;; later case, an anonymous `variable' holding a pointer to the environment
+;; vector may be located by recursively looking up ENV in the environment
+;; as if it were a variable (so the entry for that `variable' will have a
+;; non-symbol VAR).
+
+;; We call a lexical environment a `lexenv', and an entry in it a `lexvar'.
+
+;; constructor
+(defsubst byte-compile-make-lexvar (name offset &optional env)
+ (cons name (cons offset env)))
+;; accessors
+(defsubst byte-compile-lexvar-name (lexvar) (car lexvar))
+(defsubst byte-compile-lexvar-offset (lexvar) (cadr lexvar))
+(defsubst byte-compile-lexvar-environment (lexvar) (cddr lexvar))
+(defsubst byte-compile-lexvar-variable-p (lexvar) (symbolp (car lexvar)))
+(defsubst byte-compile-lexvar-environment-p (lexvar)
+ (not (symbolp (car lexvar))))
+(defsubst byte-compile-lexvar-on-stack-p (lexvar)
+ (atom (byte-compile-lexvar-environment lexvar)))
+(defsubst byte-compile-lexvar-in-heap-p (lexvar)
+ (not (byte-compile-lexvar-on-stack-p lexvar)))
+
+(defun byte-compile-make-lambda-lexenv (form closed-over-lexenv)
+ "Return a new lexical environment for a lambda expression FORM.
+CLOSED-OVER-LEXENV is the lexical environment in which FORM occurs.
+The returned lexical environment contains two sets of variables:
+ * Variables that were in CLOSED-OVER-LEXENV and used by FORM
+ (all of these will be `heap' variables)
+ * Arguments to FORM (all of these will be `stack' variables)."
+ ;; See if this is a closure or not
+ (let ((closure nil)
+ (lforminfo (byte-compile-make-lforminfo))
+ (args (byte-compile-arglist-vars (cadr form))))
+ ;; Add variables from surrounding lexical environment to analysis set
+ (dolist (lexvar closed-over-lexenv)
+ (when (and (byte-compile-lexvar-in-heap-p lexvar)
+ (not (memq (car lexvar) args)))
+ ;; The variable is located in a heap-allocated environment
+ ;; vector, so FORM may use it. Add it to the set of variables
+ ;; that we'll search for in FORM.
+ (byte-compile-lforminfo-add-var lforminfo (car lexvar))))
+ ;; See how FORM uses these potentially closed-over variables.
+ (byte-compile-lforminfo-analyze lforminfo form args)
+ (let ((lexenv nil))
+ (dolist (vinfo (byte-compile-lforminfo-vars lforminfo))
+ (when (> (byte-compile-lvarinfo-num-refs vinfo) 0)
+ ;; FORM uses VINFO's variable, so it must be a closure.
+ (setq closure t)
+ ;; Make sure that the environment in which the variable is
+ ;; located is accessible (since we only ever pass the
+ ;; innermost environment to closures, if it's in some other
+ ;; envionment, there must be path to it from the innermost
+ ;; one).
+ (unless (byte-compile-lexvar-in-heap-p vinfo)
+ ;; To access the variable from FORM, it must be in the heap.
+ (error
+ "Compiler error: lexical variable `%s' should be heap-allocated but is not"
+ (car vinfo)))
+ (let ((closed-over-lexvar (assq (car vinfo) closed-over-lexenv)))
+ (byte-compile-heapenv-ensure-access
+ byte-compile-current-heap-environment
+ (byte-compile-lexvar-environment closed-over-lexvar))
+ ;; Put this variable in the new lexical environment
+ (push closed-over-lexvar lexenv))))
+ ;; Fill in the initial stack contents
+ (let ((stackpos 0))
+ (when closure
+ ;; Add the magic first argument that holds the environment pointer
+ (push (byte-compile-make-lexvar byte-compile-current-heap-environment
+ 0)
+ lexenv)
+ (setq stackpos (1+ stackpos)))
+ ;; Add entries for each argument
+ (dolist (arg args)
+ (push (byte-compile-make-lexvar arg stackpos) lexenv)
+ (setq stackpos (1+ stackpos)))
+ ;; Return the new lexical environment
+ lexenv))))
+
+(defun byte-compile-closure-initial-lexenv-p (lexenv)
+ "Return non-nil if LEXENV is the initial lexical environment for a closure.
+This only works correctly when passed a new lexical environment as
+returned by `byte-compile-make-lambda-lexenv' (it works by checking to
+see whether there are any heap-allocated lexical variables in LEXENV)."
+ (let ((closure nil))
+ (while (and lexenv (not closure))
+ (when (byte-compile-lexvar-environment-p (pop lexenv))
+ (setq closure t)))
+ closure))
+
+
+;;; Heap environment vectors
+
+;; A `heap environment vector' is heap-allocated vector used to store
+;; variable that can't be put onto the stack.
+;;
+;; They are represented in the compiler by a list of the form
+;;
+;; (SIZE SIZE-CONST-ID INIT-POSITION . ENVS)
+;;
+;; SIZE is the current size of the vector (which may be
+;; incremented if another variable or environment-reference is added to
+;; the end). SIZE-CONST-ID is an `unknown constant id' (as returned by
+;; `byte-compile-push-unknown-constant') representing the constant used
+;; in the vector initialization code, and INIT-POSITION is a position
+;; in the byte-code output (as returned by `byte-compile-delay-out')
+;; at which more initialization code can be added.
+;; ENVS is a list of other environment vectors accessible form this one,
+;; where each element is of the form (ENV . OFFSET).
+
+;; constructor
+(defsubst byte-compile-make-heapenv (size-const-id init-position)
+ (list 0 size-const-id init-position))
+;; accessors
+(defsubst byte-compile-heapenv-size (heapenv) (car heapenv))
+(defsubst byte-compile-heapenv-size-const-id (heapenv) (cadr heapenv))
+(defsubst byte-compile-heapenv-init-position (heapenv) (nth 2 heapenv))
+(defsubst byte-compile-heapenv-accessible-envs (heapenv) (nthcdr 3 heapenv))
+
+(defun byte-compile-heapenv-add-slot (heapenv)
+ "Add a slot to the heap environment HEAPENV and return its offset."
+ (prog1 (car heapenv) (setcar heapenv (1+ (car heapenv)))))
+
+(defun byte-compile-heapenv-add-accessible-env (heapenv env offset)
+ "Add to HEAPENV's list of accessible environments, ENV at OFFSET."
+ (setcdr (nthcdr 2 heapenv)
+ (cons (cons env offset)
+ (byte-compile-heapenv-accessible-envs heapenv))))
+
+(defun byte-compile-push-heapenv ()
+ "Generate byte-code to push a new heap environment vector.
+Sets `byte-compile-current-heap-environment' to the compiler descriptor
+for the new heap environment.
+Return a `lexvar' descriptor for the new heap environment."
+ (let ((env-stack-pos byte-compile-depth)
+ size-const-id init-position)
+ ;; Generate code to push the vector
+ (byte-compile-push-constant 'make-vector)
+ (setq size-const-id (byte-compile-push-unknown-constant))
+ (byte-compile-push-constant nil)
+ (byte-compile-out 'byte-call 2)
+ (setq init-position (byte-compile-delay-out 3))
+ ;; Now make a heap-environment for the compiler to use
+ (setq byte-compile-current-heap-environment
+ (byte-compile-make-heapenv size-const-id init-position))
+ (byte-compile-make-lexvar byte-compile-current-heap-environment
+ env-stack-pos)))
+
+(defun byte-compile-heapenv-ensure-access (heapenv other-heapenv)
+ "Make sure that HEAPENV can be used to access OTHER-HEAPENV.
+If not, then add a new slot to HEAPENV pointing to OTHER-HEAPENV."
+ (unless (memq heapenv (byte-compile-heapenv-accessible-envs heapenv))
+ (let ((offset (byte-compile-heapenv-add-slot heapenv)))
+ (byte-compile-heapenv-add-accessible-env heapenv other-heapenv offset))))
+
+
+;;; Variable binding/unbinding
+
+(defun byte-compile-non-stack-bindings-p (clauses lforminfo)
+ "Return non-nil if any lexical bindings in CLAUSES are not stack-allocated.
+LFORMINFO should be information about lexical variables being bound."
+ (let ((vars (byte-compile-lforminfo-vars lforminfo)))
+ (or (not (= (length clauses) (length vars)))
+ (progn
+ (while (and vars clauses)
+ (when (byte-compile-lvarinfo-closed-over-p (pop vars))
+ (setq clauses nil)))
+ (not clauses)))))
+
+(defun byte-compile-let-clauses-trivial-init-p (clauses)
+ "Return true if let binding CLAUSES all have a `trivial' init value.
+Trivial means either a constant value, or a simple variable initialization."
+ (or (null clauses)
+ (and (or (atom (car clauses))
+ (atom (cadr (car clauses)))
+ (eq (car (cadr (car clauses))) 'quote))
+ (byte-compile-let-clauses-trivial-init-p (cdr clauses)))))
+
+(defun byte-compile-rearrange-let-clauses (clauses lforminfo)
+ "Return CLAUSES rearranged so non-stack variables come last if possible.
+Care is taken to only do so when it's clear that the meaning is the same.
+LFORMINFO should be information about lexical variables being bound."
+ ;; We currently do a very simple job by only exchanging clauses when
+ ;; one has a constant init, or one has a variable init and the other
+ ;; doesn't have a function call init (because that could change the
+ ;; value of the variable). This could be more clever and actually
+ ;; attempt to analyze which variables could possible be changed, etc.
+ (let ((unchanged nil)
+ (lex-non-stack nil)
+ (dynamic nil))
+ (while clauses
+ (let* ((clause (pop clauses))
+ (var (if (consp clause) (car clause) clause))
+ (init (and (consp clause) (cadr clause)))
+ (vinfo (assq var (byte-compile-lforminfo-vars lforminfo))))
+ (cond
+ ((or (and vinfo
+ (not (byte-compile-lvarinfo-closed-over-p vinfo)))
+ (not
+ (or (eq init nil) (eq init t)
+ (and (atom init) (not (symbolp init)))
+ (and (consp init) (eq (car init) 'quote))
+ (byte-compile-let-clauses-trivial-init-p clauses))))
+ (push clause unchanged))
+ (vinfo
+ (push clause lex-non-stack))
+ (t
+ (push clause dynamic)))))
+ (nconc (nreverse unchanged) (nreverse lex-non-stack) (nreverse dynamic))))
+
+(defun byte-compile-maybe-push-heap-environment (&optional lforminfo)
+ "Push a new heap environment if necessary.
+LFORMINFO should be information about lexical variables being bound.
+Return a lexical environment containing only the heap vector (or
+nil if nothing was pushed).
+Also, `byte-compile-current-heap-environment' and
+`byte-compile-current-num-closures' are updated to reflect any change (so they
+should probably be bound by the caller to ensure that the new values have the
+proper scope)."
+ ;; We decide whether a new heap environment is required by seeing if
+ ;; the number of closures inside the form described by LFORMINFO is
+ ;; the same as the number inside the binding form that created the
+ ;; currently active heap environment.
+ (let ((nclosures
+ (and lforminfo (byte-compile-lforminfo-num-closures lforminfo))))
+ (if (or (null lforminfo)
+ (= nclosures byte-compile-current-num-closures))
+ ;; No need to push a heap environment.
+ nil
+ ;; Have to push one. A heap environment is really just a vector, so
+ ;; we emit bytecodes to create a vector. However, the size is not
+ ;; fixed yet (the vector can grow if subforms use it to store
+ ;; values, and if `access points' to parent heap environments are
+ ;; added), so we use `byte-compile-push-unknown-constant' to push the
+ ;; vector size.
+ (setq byte-compile-current-num-closures nclosures)
+ (list (byte-compile-push-heapenv)))))
+
+(defun byte-compile-bind (var init-lexenv &optional lforminfo)
+ "Emit byte-codes to bind VAR and update `byte-compile-lexical-environment'.
+INIT-LEXENV should be a lexical-environment alist describing the
+positions of the init value that have been pushed on the stack, and
+LFORMINFO should be information about lexical variables being bound.
+Return non-nil if the TOS value was popped."
+ ;; The presence of lexical bindings mean that we may have to
+ ;; juggle things on the stack, either to move them to TOS for
+ ;; dynamic binding, or to put them in a non-stack environment
+ ;; vector.
+ (let ((vinfo (assq var (byte-compile-lforminfo-vars lforminfo))))
+ (cond ((and (null vinfo) (eq var (caar init-lexenv)))
+ ;; VAR is dynamic and is on the top of the
+ ;; stack, so we can just bind it like usual
+ (byte-compile-dynamic-variable-bind var)
+ t)
+ ((null vinfo)
+ ;; VAR is dynamic, but we have to get its
+ ;; value out of the middle of the stack
+ (let ((stack-pos (cdr (assq var init-lexenv))))
+ (byte-compile-stack-ref stack-pos)
+ (byte-compile-dynamic-variable-bind var)
+ ;; Now we have to store nil into its temporary
+ ;; stack position to avoid problems with GC
+ (byte-compile-push-constant nil)
+ (byte-compile-stack-set stack-pos))
+ nil)
+ ((byte-compile-lvarinfo-closed-over-p vinfo)
+ ;; VAR is lexical, but needs to be in a
+ ;; heap-allocated environment.
+ (unless byte-compile-current-heap-environment
+ (error "No current heap-environment to allocate `%s' in!" var))
+ (let ((init-stack-pos
+ ;; nil if the init value is on the top of the stack,
+ ;; otherwise the position of the init value on the stack.
+ (and (not (eq var (caar init-lexenv)))
+ (byte-compile-lexvar-offset (assq var init-lexenv))))
+ (env-vec-pos
+ ;; Position of VAR in the environment vector
+ (byte-compile-lexvar-offset
+ (assq var byte-compile-lexical-environment)))
+ (env-vec-stack-pos
+ ;; Position of the the environment vector on the stack
+ ;; (the heap-environment must _always_ be available on
+ ;; the stack!)
+ (byte-compile-lexvar-offset
+ (assq byte-compile-current-heap-environment
+ byte-compile-lexical-environment))))
+ (unless env-vec-stack-pos
+ (error "Couldn't find location of current heap environment!"))
+ (when init-stack-pos
+ ;; VAR is not on the top of the stack, so get it
+ (byte-compile-stack-ref init-stack-pos))
+ (byte-compile-stack-ref env-vec-stack-pos)
+ ;; Store the variable into the vector
+ (byte-compile-out 'byte-vec-set env-vec-pos)
+ (when init-stack-pos
+ ;; Store nil into VAR's temporary stack
+ ;; position to avoid problems with GC
+ (byte-compile-push-constant nil)
+ (byte-compile-stack-set init-stack-pos))
+ ;; Push a record of VAR's new lexical binding
+ (push (byte-compile-make-lexvar
+ var env-vec-pos byte-compile-current-heap-environment)
+ byte-compile-lexical-environment)
+ (not init-stack-pos)))
+ (t
+ ;; VAR is a simple stack-allocated lexical variable
+ (push (assq var init-lexenv)
+ byte-compile-lexical-environment)
+ nil))))
+
+(defun byte-compile-unbind (clauses init-lexenv
+ &optional lforminfo preserve-body-value)
+ "Emit byte-codes to unbind the variables bound by CLAUSES.
+CLAUSES is a `let'-style variable binding list. INIT-LEXENV should be a
+lexical-environment alist describing the positions of the init value that
+have been pushed on the stack, and LFORMINFO should be information about
+the lexical variables that were bound. If PRESERVE-BODY-VALUE is true,
+then an additional value on the top of the stack, above any lexical binding
+slots, is preserved, so it will be on the top of the stack after all
+binding slots have been popped."
+ ;; Unbind dynamic variables
+ (let ((num-dynamic-bindings 0))
+ (if lforminfo
+ (dolist (clause clauses)
+ (unless (assq (if (consp clause) (car clause) clause)
+ (byte-compile-lforminfo-vars lforminfo))
+ (setq num-dynamic-bindings (1+ num-dynamic-bindings))))
+ (setq num-dynamic-bindings (length clauses)))
+ (unless (zerop num-dynamic-bindings)
+ (byte-compile-out 'byte-unbind num-dynamic-bindings)))
+ ;; Pop lexical variables off the stack, possibly preserving the
+ ;; return value of the body.
+ (when init-lexenv
+ ;; INIT-LEXENV contains all init values left on the stack
+ (byte-compile-discard (length init-lexenv) preserve-body-value)))
+
+
+(provide 'byte-lexbind)
+
+;;; arch-tag: b8f1dff6-9edb-4430-a96f-323d42a681a9
+;;; byte-lexbind.el ends here
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 1ff34fa6a81..9ce3c2eb323 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -187,8 +187,8 @@
(eval-when-compile (require 'cl))
(defun byte-compile-log-lap-1 (format &rest args)
- (if (aref byte-code-vector 0)
- (error "The old version of the disassembler is loaded. Reload new-bytecomp as well"))
+;; (if (aref byte-code-vector 0)
+;; (error "The old version of the disassembler is loaded. Reload new-bytecomp as well"))
(byte-compile-log-1
(apply 'format format
(let (c a)
@@ -282,7 +282,8 @@
(byte-code ,string ,(aref fn 2) ,(aref fn 3)))
(cdr form)))
(if (eq (car-safe fn) 'lambda)
- (cons fn (cdr form))
+ (macroexpand-all (cons fn (cdr form))
+ byte-compile-macro-environment)
;; Give up on inlining.
form))))))
@@ -1333,14 +1334,15 @@
((>= op byte-constant)
(prog1 (- op byte-constant) ;offset in opcode
(setq op byte-constant)))
- ((and (>= op byte-constant2)
- (<= op byte-goto-if-not-nil-else-pop))
+ ((or (and (>= op byte-constant2)
+ (<= op byte-goto-if-not-nil-else-pop))
+ (= op byte-stack-set2))
(setq ptr (1+ ptr)) ;offset in next 2 bytes
(+ (aref bytes ptr)
(progn (setq ptr (1+ ptr))
(lsh (aref bytes ptr) 8))))
((and (>= op byte-listN)
- (<= op byte-insertN))
+ (<= op byte-discardN))
(setq ptr (1+ ptr)) ;offset in next byte
(aref bytes ptr))))
@@ -1401,7 +1403,16 @@
(if (= ptr (1- length))
(setq op nil)
(setq offset (or endtag (setq endtag (byte-compile-make-tag)))
- op 'byte-goto))))
+ op 'byte-goto)))
+ ((eq op 'byte-stack-set2)
+ (setq op 'byte-stack-set))
+ ((and (eq op 'byte-discardN) (>= offset #x80))
+ ;; The top bit of the operand for byte-discardN is a flag,
+ ;; saying whether the top-of-stack is preserved. In
+ ;; lapcode, we represent this by using a different opcode
+ ;; (with the flag removed from the operand).
+ (setq op 'byte-discardN-preserve-tos)
+ (setq offset (- offset #x80))))
;; lap = ( [ (pc . (op . arg)) ]* )
(setq lap (cons (cons optr (cons op (or offset 0)))
lap))
@@ -1457,7 +1468,7 @@
byte-cdr-safe byte-cons byte-list1 byte-list2 byte-point byte-point-max
byte-point-min byte-following-char byte-preceding-char
byte-current-column byte-eolp byte-eobp byte-bolp byte-bobp
- byte-current-buffer byte-interactive-p))
+ byte-current-buffer byte-interactive-p byte-stack-ref))
(defconst byte-compile-side-effect-free-ops
(nconc
@@ -1466,7 +1477,7 @@
byte-eqlsign byte-gtr byte-lss byte-leq byte-geq byte-diff byte-negate
byte-plus byte-max byte-min byte-mult byte-char-after byte-char-syntax
byte-buffer-substring byte-string= byte-string< byte-nthcdr byte-elt
- byte-member byte-assq byte-quo byte-rem)
+ byte-member byte-assq byte-quo byte-rem byte-vec-ref)
byte-compile-side-effect-and-error-free-ops))
;; This crock is because of the way DEFVAR_BOOL variables work.
@@ -1499,12 +1510,50 @@
;; The variable `byte-boolean-vars' is now primitive and updated
;; automatically by DEFVAR_BOOL.
+(defmacro byte-opt-update-stack-params (stack-adjust stack-depth lap0 rest lap)
+ "...macro used by byte-optimize-lapcode..."
+ `(progn
+ (byte-compile-log-lap "Before %s [depth = %s]" ,lap0 ,stack-depth)
+ (cond ((eq (car ,lap0) 'TAG)
+ ;; A tag can encode the expected stack depth.
+ (when (cddr ,lap0)
+ ;; First, check to see if our notion of the current stack
+ ;; depth agrees with this tag. We don't check at the
+ ;; beginning of the function, because the presence of
+ ;; lexical arguments means the first tag will have a
+ ;; non-zero offset.
+ (when (and (not (eq ,rest ,lap)) ; not at first insn
+ ,stack-depth ; not just after a goto
+ (not (= (cddr ,lap0) ,stack-depth)))
+ (error "Compiler error: optimizer is confused about %s:
+ %s != %s at lapcode %s" ',stack-depth (cddr ,lap0) ,stack-depth ,lap0))
+ ;; Now set out current depth from this tag
+ (setq ,stack-depth (cddr ,lap0)))
+ (setq ,stack-adjust 0))
+ ((memq (car ,lap0) '(byte-goto byte-return))
+ ;; These insns leave us in an unknown state
+ (setq ,stack-adjust nil))
+ ((car ,lap0)
+ ;; Not a no-op, set ,stack-adjust for lap0. ,stack-adjust will
+ ;; be added to ,stack-depth at the end of the loop, so any code
+ ;; that modifies the instruction sequence must adjust this too.
+ (setq ,stack-adjust
+ (byte-compile-stack-adjustment (car ,lap0) (cdr ,lap0)))))
+ (byte-compile-log-lap "Before %s [depth => %s, adj = %s]" ,lap0 ,stack-depth ,stack-adjust)
+ ))
+
(defun byte-optimize-lapcode (lap &optional for-effect)
"Simple peephole optimizer. LAP is both modified and returned.
If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(let (lap0
lap1
lap2
+ stack-adjust
+ stack-depth
+ (initial-stack-depth
+ (if (and lap (eq (car (car lap)) 'TAG))
+ (cdr (cdr (car lap)))
+ 0))
(keep-going 'first-time)
(add-depth 0)
rest tmp tmp2 tmp3
@@ -1515,12 +1564,15 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(or (eq keep-going 'first-time)
(byte-compile-log-lap " ---- next pass"))
(setq rest lap
+ stack-depth initial-stack-depth
keep-going nil)
(while rest
(setq lap0 (car rest)
lap1 (nth 1 rest)
lap2 (nth 2 rest))
+ (byte-opt-update-stack-params stack-adjust stack-depth lap0 rest lap)
+
;; You may notice that sequences like "dup varset discard" are
;; optimized but sequences like "dup varset TAG1: discard" are not.
;; You may be tempted to change this; resist that temptation.
@@ -1534,22 +1586,22 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
((and (eq 'byte-discard (car lap1))
(memq (car lap0) side-effect-free))
(setq keep-going t)
- (setq tmp (aref byte-stack+-info (symbol-value (car lap0))))
(setq rest (cdr rest))
- (cond ((= tmp 1)
+ (cond ((= stack-adjust 1)
(byte-compile-log-lap
" %s discard\t-->\t<deleted>" lap0)
(setq lap (delq lap0 (delq lap1 lap))))
- ((= tmp 0)
+ ((= stack-adjust 0)
(byte-compile-log-lap
" %s discard\t-->\t<deleted> discard" lap0)
(setq lap (delq lap0 lap)))
- ((= tmp -1)
+ ((= stack-adjust -1)
(byte-compile-log-lap
" %s discard\t-->\tdiscard discard" lap0)
(setcar lap0 'byte-discard)
(setcdr lap0 0))
- ((error "Optimizer error: too much on the stack"))))
+ ((error "Optimizer error: too much on the stack")))
+ (setq stack-adjust (1- stack-adjust)))
;;
;; goto*-X X: --> X:
;;
@@ -1574,10 +1626,14 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
;; const/dup varbind-X varref-X --> const/dup varbind-X const/dup
;; The latter two can enable other optimizations.
;;
- ((and (eq 'byte-varref (car lap2))
- (eq (cdr lap1) (cdr lap2))
- (memq (car lap1) '(byte-varset byte-varbind)))
- (if (and (setq tmp (memq (car (cdr lap2)) byte-boolean-vars))
+ ((or (and (eq 'byte-varref (car lap2))
+ (eq (cdr lap1) (cdr lap2))
+ (memq (car lap1) '(byte-varset byte-varbind)))
+ (and (eq (car lap2) 'byte-stack-ref)
+ (eq (car lap1) 'byte-stack-set)
+ (eq (cdr lap1) (cdr lap2))))
+ (if (and (eq 'byte-varref (car lap2))
+ (setq tmp (memq (car (cdr lap2)) byte-boolean-vars))
(not (eq (car lap0) 'byte-constant)))
nil
(setq keep-going t)
@@ -1609,10 +1665,11 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
;;
((and (eq 'byte-dup (car lap0))
(eq 'byte-discard (car lap2))
- (memq (car lap1) '(byte-varset byte-varbind)))
+ (memq (car lap1) '(byte-varset byte-varbind byte-stack-set byte-vec-set)))
(byte-compile-log-lap " dup %s discard\t-->\t%s" lap1 lap1)
(setq keep-going t
- rest (cdr rest))
+ rest (cdr rest)
+ stack-adjust -1)
(setq lap (delq lap0 (delq lap2 lap))))
;;
;; not goto-X-if-nil --> goto-X-if-non-nil
@@ -1634,7 +1691,8 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
'byte-goto-if-not-nil
'byte-goto-if-nil))
(setq lap (delq lap0 lap))
- (setq keep-going t))
+ (setq keep-going t
+ stack-adjust 0))
;;
;; goto-X-if-nil goto-Y X: --> goto-Y-if-non-nil X:
;; goto-X-if-non-nil goto-Y X: --> goto-Y-if-nil X:
@@ -1650,7 +1708,8 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(byte-compile-log-lap " %s %s %s:\t-->\t%s %s:"
lap0 lap1 lap2
(cons inverse (cdr lap1)) lap2)
- (setq lap (delq lap0 lap))
+ (setq lap (delq lap0 lap)
+ stack-adjust 0)
(setcar lap1 inverse)
(setq keep-going t)))
;;
@@ -1667,15 +1726,14 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(setq rest (cdr rest)
lap (delq lap0 (delq lap1 lap))))
(t
- (if (memq (car lap1) byte-goto-always-pop-ops)
- (progn
- (byte-compile-log-lap " %s %s\t-->\t%s"
- lap0 lap1 (cons 'byte-goto (cdr lap1)))
- (setq lap (delq lap0 lap)))
- (byte-compile-log-lap " %s %s\t-->\t%s" lap0 lap1
- (cons 'byte-goto (cdr lap1))))
+ (byte-compile-log-lap " %s %s\t-->\t%s"
+ lap0 lap1
+ (cons 'byte-goto (cdr lap1)))
+ (when (memq (car lap1) byte-goto-always-pop-ops)
+ (setq lap (delq lap0 lap)))
(setcar lap1 'byte-goto)))
- (setq keep-going t))
+ (setq keep-going t
+ stack-adjust 0))
;;
;; varref-X varref-X --> varref-X dup
;; varref-X [dup ...] varref-X --> varref-X [dup ...] dup
@@ -1683,14 +1741,14 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
;; because that would inhibit some goto optimizations; we
;; optimize the const-X case after all other optimizations.
;;
- ((and (eq 'byte-varref (car lap0))
+ ((and (memq (car lap0) '(byte-varref byte-stack-ref))
(progn
- (setq tmp (cdr rest))
+ (setq tmp (cdr rest) tmp2 0)
(while (eq (car (car tmp)) 'byte-dup)
- (setq tmp (cdr tmp)))
+ (setq tmp (cdr tmp) tmp2 (1+ tmp2)))
t)
- (eq (cdr lap0) (cdr (car tmp)))
- (eq 'byte-varref (car (car tmp))))
+ (eq (car lap0) (car (car tmp)))
+ (eq (cdr lap0) (cdr (car tmp))))
(if (memq byte-optimize-log '(t byte))
(let ((str ""))
(setq tmp2 (cdr rest))
@@ -1702,7 +1760,8 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(setq keep-going t)
(setcar (car tmp) 'byte-dup)
(setcdr (car tmp) 0)
- (setq rest tmp))
+ (setq rest tmp
+ stack-adjust (+ 2 tmp2)))
;;
;; TAG1: TAG2: --> TAG1: <deleted>
;; (and other references to TAG2 are replaced with TAG1)
@@ -1769,7 +1828,8 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(byte-compile-log-lap " %s %s\t-->\t%s %s" lap0 lap1 lap1 lap0)
(setcar rest lap1)
(setcar (cdr rest) lap0)
- (setq keep-going t))
+ (setq keep-going t
+ stack-adjust 0))
;;
;; varbind-X unbind-N --> discard unbind-(N-1)
;; save-excursion unbind-N --> unbind-(N-1)
@@ -1795,6 +1855,14 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
""))
(setq keep-going t))
;;
+ ;; stack-ref-N --> dup ; where N is TOS
+ ;;
+ ((and (eq (car lap0) 'byte-stack-ref)
+ (= (cdr lap0) (1- stack-depth)))
+ (setcar lap0 'byte-dup)
+ (setcdr lap0 nil)
+ (setq keep-going t))
+ ;;
;; goto*-X ... X: goto-Y --> goto*-Y
;; goto-X ... X: return --> return
;;
@@ -1871,20 +1939,22 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(cdr tmp))))
(setcdr lap1 (car (cdr tmp)))
(setq lap (delq lap0 lap))))
- (setq keep-going t))
+ (setq keep-going t
+ stack-adjust 0))
;;
;; X: varref-Y ... varset-Y goto-X -->
;; X: varref-Y Z: ... dup varset-Y goto-Z
;; (varset-X goto-BACK, BACK: varref-X --> copy the varref down.)
;; (This is so usual for while loops that it is worth handling).
;;
- ((and (eq (car lap1) 'byte-varset)
+ ((and (memq (car lap1) '(byte-varset byte-stack-set))
(eq (car lap2) 'byte-goto)
(not (memq (cdr lap2) rest)) ;Backwards jump
(eq (car (car (setq tmp (cdr (memq (cdr lap2) lap)))))
- 'byte-varref)
+ (if (eq (car lap1) 'byte-varset) 'byte-varref 'byte-stack-ref))
(eq (cdr (car tmp)) (cdr lap1))
- (not (memq (car (cdr lap1)) byte-boolean-vars)))
+ (not (and (eq (car lap1) 'byte-varref)
+ (memq (car (cdr lap1)) byte-boolean-vars))))
;;(byte-compile-log-lap " Pulled %s to end of loop" (car tmp))
(let ((newtag (byte-compile-make-tag)))
(byte-compile-log-lap
@@ -1941,10 +2011,15 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
byte-goto-if-not-nil
byte-goto byte-goto))))
)
- (setq keep-going t))
+ (setq keep-going t
+ stack-adjust (and (not (eq (car lap0) 'byte-goto)) -1)))
)
+
+ (setq stack-depth
+ (and stack-depth stack-adjust (+ stack-depth stack-adjust)))
(setq rest (cdr rest)))
)
+
;; Cleanup stage:
;; Rebuild byte-compile-constants / byte-compile-variables.
;; Simple optimizations that would inhibit other optimizations if they
@@ -1952,10 +2027,13 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
;; need to do more than once.
(setq byte-compile-constants nil
byte-compile-variables nil)
- (setq rest lap)
+ (setq rest lap
+ stack-depth initial-stack-depth)
+ (byte-compile-log-lap " ---- final pass")
(while rest
(setq lap0 (car rest)
lap1 (nth 1 rest))
+ (byte-opt-update-stack-params stack-adjust stack-depth lap0 rest lap)
(if (memq (car lap0) byte-constref-ops)
(if (or (eq (car lap0) 'byte-constant)
(eq (car lap0) 'byte-constant2))
@@ -2002,11 +2080,108 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
(byte-compile-log-lap " %s %s\t-->\t%s" lap0 lap1
(cons 'byte-unbind
(+ (cdr lap0) (cdr lap1))))
- (setq keep-going t)
(setq lap (delq lap0 lap))
(setcdr lap1 (+ (cdr lap1) (cdr lap0))))
+
+ ;;
+ ;; stack-set-M [discard/discardN ...] --> discardN-preserve-tos
+ ;; stack-set-M [discard/discardN ...] --> discardN
+ ;;
+ ((and (eq (car lap0) 'byte-stack-set)
+ (memq (car lap1) '(byte-discard byte-discardN))
+ (progn
+ ;; See if enough discard operations follow to expose or
+ ;; destroy the value stored by the stack-set.
+ (setq tmp (cdr rest))
+ (setq tmp2 (- stack-depth 2 (cdr lap0)))
+ (setq tmp3 0)
+ (while (memq (car (car tmp)) '(byte-discard byte-discardN))
+ (if (eq (car (car tmp)) 'byte-discard)
+ (setq tmp3 (1+ tmp3))
+ (setq tmp3 (+ tmp3 (cdr (car tmp)))))
+ (setq tmp (cdr tmp)))
+ (>= tmp3 tmp2)))
+ ;; Do the optimization
+ (setq lap (delq lap0 lap))
+ (cond ((= tmp2 tmp3)
+ ;; The value stored is the new TOS, so pop one more value
+ ;; (to get rid of the old value) using the TOS-preserving
+ ;; discard operator.
+ (setcar lap1 'byte-discardN-preserve-tos)
+ (setcdr lap1 (1+ tmp3)))
+ (t
+ ;; Otherwise, the value stored is lost, so just use a
+ ;; normal discard.
+ (setcar lap1 'byte-discardN)
+ (setcdr lap1 tmp3)))
+ (setcdr (cdr rest) tmp)
+ (setq stack-adjust 0)
+ (byte-compile-log-lap " %s [discard/discardN]...\t-->\t%s"
+ lap0 lap1))
+
+ ;;
+ ;; discard/discardN/discardN-preserve-tos-X discard/discardN-Y -->
+ ;; discardN-(X+Y)
+ ;;
+ ((and (memq (car lap0)
+ '(byte-discard
+ byte-discardN
+ byte-discardN-preserve-tos))
+ (memq (car lap1) '(byte-discard byte-discardN)))
+ (setq lap (delq lap0 lap))
+ (byte-compile-log-lap
+ " %s %s\t-->\t(discardN %s)"
+ lap0 lap1
+ (+ (if (eq (car lap0) 'byte-discard) 1 (cdr lap0))
+ (if (eq (car lap1) 'byte-discard) 1 (cdr lap1))))
+ (setcdr lap1 (+ (if (eq (car lap0) 'byte-discard) 1 (cdr lap0))
+ (if (eq (car lap1) 'byte-discard) 1 (cdr lap1))))
+ (setcar lap1 'byte-discardN)
+ (setq stack-adjust 0))
+
+ ;;
+ ;; discardN-preserve-tos-X discardN-preserve-tos-Y -->
+ ;; discardN-preserve-tos-(X+Y)
+ ;;
+ ((and (eq (car lap0) 'byte-discardN-preserve-tos)
+ (eq (car lap1) 'byte-discardN-preserve-tos))
+ (setq lap (delq lap0 lap))
+ (setcdr lap1 (+ (cdr lap0) (cdr lap1)))
+ (setq stack-adjust 0)
+ (byte-compile-log-lap " %s %s\t-->\t%s" lap0 lap1 (car rest)))
+
+ ;;
+ ;; discardN-preserve-tos return --> return
+ ;; dup return --> return
+ ;; stack-set-N return --> return ; where N is TOS-1
+ ;;
+ ((and (eq (car lap1) 'byte-return)
+ (or (memq (car lap0) '(byte-discardN-preserve-tos byte-dup))
+ (and (eq (car lap0) 'byte-stack-set)
+ (= (cdr lap0) (- stack-depth 2)))))
+ ;; the byte-code interpreter will pop the stack for us, so
+ ;; we can just leave stuff on it
+ (setq lap (delq lap0 lap))
+ (setq stack-adjust 0)
+ (byte-compile-log-lap " %s %s\t-->\t%s" lap0 lap1 lap1))
+
+ ;;
+ ;; dup stack-set-N return --> return ; where N is TOS
+ ;;
+ ((and (eq (car lap0) 'byte-dup)
+ (eq (car lap1) 'byte-stack-set)
+ (eq (car (car (cdr (cdr rest)))) 'byte-return)
+ (= (cdr lap1) (1- stack-depth)))
+ (setq lap (delq lap0 (delq lap1 lap)))
+ (setq rest (cdr rest))
+ (setq stack-adjust 0)
+ (byte-compile-log-lap " dup %s return\t-->\treturn" lap1))
)
+
+ (setq stack-depth
+ (and stack-depth stack-adjust (+ stack-depth stack-adjust)))
(setq rest (cdr rest)))
+
(setq byte-compile-maxdepth (+ byte-compile-maxdepth add-depth)))
lap)
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 8b47e0421e0..e1b5b402b28 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -118,12 +118,55 @@
;; Some versions of `file' can be customized to recognize that.
(require 'backquote)
+(require 'macroexp)
(eval-when-compile (require 'cl))
(or (fboundp 'defsubst)
;; This really ought to be loaded already!
(load "byte-run"))
+;; We want to do (require 'byte-lexbind) when compiling, to avoid compilation
+;; errors; however that file also wants to do (require 'bytecomp) for the
+;; same reason. Since we know it's OK to load byte-lexbind.el second, we
+;; have that file require a feature that's provided before at the beginning
+;; of this file, to avoid an infinite require loop.
+;; `eval-when-compile' is defined in byte-run.el, so it must come after the
+;; preceding load expression.
+(provide 'bytecomp-preload)
+(eval-when-compile (require 'byte-lexbind))
+
+;; The feature of compiling in a specific target Emacs version
+;; has been turned off because compile time options are a bad idea.
+(defmacro byte-compile-single-version () nil)
+(defmacro byte-compile-version-cond (cond) cond)
+
+;; The crud you see scattered through this file of the form
+;; (or (and (boundp 'epoch::version) epoch::version)
+;; (string-lessp emacs-version "19"))
+;; is because the Epoch folks couldn't be bothered to follow the
+;; normal emacs version numbering convention.
+
+;; (if (byte-compile-version-cond
+;; (or (and (boundp 'epoch::version) epoch::version)
+;; (string-lessp emacs-version "19")))
+;; (progn
+;; ;; emacs-18 compatibility.
+;; (defvar baud-rate (baud-rate)) ;Define baud-rate if it's undefined
+;;
+;; (if (byte-compile-single-version)
+;; (defmacro byte-code-function-p (x) "Emacs 18 doesn't have these." nil)
+;; (defun byte-code-function-p (x) "Emacs 18 doesn't have these." nil))
+;;
+;; (or (and (fboundp 'member)
+;; ;; avoid using someone else's possibly bogus definition of this.
+;; (subrp (symbol-function 'member)))
+;; (defun member (elt list)
+;; "like memq, but uses equal instead of eq. In v19, this is a subr."
+;; (while (and list (not (equal elt (car list))))
+;; (setq list (cdr list)))
+;; list))))
+
+
(defgroup bytecomp nil
"Emacs Lisp byte-compiler."
:group 'lisp)
@@ -400,7 +443,17 @@ specify different fields to sort on."
:type '(choice (const name) (const callers) (const calls)
(const calls+callers) (const nil)))
-(defvar byte-compile-debug nil)
+;(defvar byte-compile-debug nil)
+(defvar byte-compile-debug t)
+
+;; (defvar byte-compile-overwrite-file t
+;; "If nil, old .elc files are deleted before the new is saved, and .elc
+;; files will have the same modes as the corresponding .el file. Otherwise,
+;; existing .elc files will simply be overwritten, and the existing modes
+;; will not be changed. If this variable is nil, then an .elc file which
+;; is a symbolic link will be turned into a normal file, instead of the file
+;; which the link points to being overwritten.")
+
(defvar byte-compile-constants nil
"List of all constants encountered during compilation of this form.")
(defvar byte-compile-variables nil
@@ -420,9 +473,13 @@ This list lives partly on the stack.")
;; (byte-compiler-options . (lambda (&rest forms)
;; (apply 'byte-compiler-options-handler forms)))
(eval-when-compile . (lambda (&rest body)
- (list 'quote
- (byte-compile-eval (byte-compile-top-level
- (cons 'progn body))))))
+ (list
+ 'quote
+ (byte-compile-eval
+ (byte-compile-top-level
+ (macroexpand-all
+ (cons 'progn body)
+ byte-compile-initial-macro-environment))))))
(eval-and-compile . (lambda (&rest body)
(byte-compile-eval-before-compile (cons 'progn body))
(cons 'progn body))))
@@ -455,6 +512,14 @@ defined with incorrect args.")
Used for warnings about calling a function that is defined during compilation
but won't necessarily be defined when the compiled file is loaded.")
+;; Variables for lexical binding
+(defvar byte-compile-lexical-environment nil
+ "The current lexical environment.")
+(defvar byte-compile-current-heap-environment nil
+ "If non-nil, a descriptor for the current heap-allocated lexical environment.")
+(defvar byte-compile-current-num-closures 0
+ "The number of lexical closures that close over `byte-compile-current-heap-environment'.")
+
(defvar byte-compile-tag-number 0)
(defvar byte-compile-output nil
"Alist describing contents to put in byte code string.
@@ -500,11 +565,10 @@ Each element is (INDEX . VALUE)")
(put 'byte-stack+-info 'tmp-compile-time-value nil)))
-;; unused: 0-7
-
;; These opcodes are special in that they pack their argument into the
;; opcode word.
;;
+(byte-defop 0 1 byte-stack-ref "for stack reference")
(byte-defop 8 1 byte-varref "for variable reference")
(byte-defop 16 -1 byte-varset "for setting a variable")
(byte-defop 24 -1 byte-varbind "for binding a variable")
@@ -666,11 +730,28 @@ otherwise pop it")
(byte-defop 168 0 byte-integerp)
;; unused: 169-174
+
(byte-defop 175 nil byte-listN)
(byte-defop 176 nil byte-concatN)
(byte-defop 177 nil byte-insertN)
-;; unused: 178-191
+(byte-defop 178 -1 byte-stack-set) ; stack offset in following one byte
+(byte-defop 179 -1 byte-stack-set2) ; stack offset in following two bytes
+(byte-defop 180 1 byte-vec-ref) ; vector offset in following one byte
+(byte-defop 181 -1 byte-vec-set) ; vector offset in following one byte
+
+;; if (following one byte & 0x80) == 0
+;; discard (following one byte & 0x7F) stack entries
+;; else
+;; discard (following one byte & 0x7F) stack entries _underneath_ the top of stack
+;; (that is, if the operand = 0x83, ... X Y Z T => ... T)
+(byte-defop 182 nil byte-discardN)
+;; `byte-discardN-preserve-tos' is a pseudo-op that gets turned into
+;; `byte-discardN' with the high bit in the operand set (by
+;; `byte-compile-lapcode').
+(defconst byte-discardN-preserve-tos byte-discardN)
+
+;; unused: 182-191
(byte-defop 192 1 byte-constant "for reference to a constant")
;; codes 193-255 are consumed by byte-constant.
@@ -717,71 +798,108 @@ otherwise pop it")
;; front of the constants-vector than the constant-referencing instructions.
;; Also, this lets us notice references to free variables.
+(defmacro byte-compile-push-bytecodes (&rest args)
+ "Push BYTE... onto BYTES, and increment PC by the number of bytes pushed.
+ARGS is of the form (BYTE... BYTES PC), where BYTES and PC are variable names.
+BYTES and PC are updated after evaluating all the arguments."
+ (let ((byte-exprs (butlast args 2))
+ (bytes-var (car (last args 2)))
+ (pc-var (car (last args))))
+ `(setq ,bytes-var ,(if (null (cdr byte-exprs))
+ `(cons ,@byte-exprs ,bytes-var)
+ `(nconc (list ,@(reverse byte-exprs)) ,bytes-var))
+ ,pc-var (+ ,(length byte-exprs) ,pc-var))))
+
+(defmacro byte-compile-push-bytecode-const2 (opcode const2 bytes pc)
+ "Push OPCODE and the two-byte constant CONST2 onto BYTES, and add 3 to PC.
+CONST2 may be evaulated multiple times."
+ `(byte-compile-push-bytecodes ,opcode (logand ,const2 255) (lsh ,const2 -8)
+ ,bytes ,pc))
+
(defun byte-compile-lapcode (lap)
"Turns lapcode into bytecode. The lapcode is destroyed."
;; Lapcode modifications: changes the ID of a tag to be the tag's PC.
(let ((pc 0) ; Program counter
op off ; Operation & offset
+ opcode ; numeric value of OP
(bytes '()) ; Put the output bytes here
- (patchlist nil)) ; List of tags and goto's to patch
- (while lap
- (setq op (car (car lap))
- off (cdr (car lap)))
+ (patchlist nil)) ; List of gotos to patch
+ (dolist (lap-entry lap)
+ (setq op (car lap-entry)
+ off (cdr lap-entry))
(cond ((not (symbolp op))
(error "Non-symbolic opcode `%s'" op))
((eq op 'TAG)
- (setcar off pc)
- (setq patchlist (cons off patchlist)))
- ((memq op byte-goto-ops)
- (setq pc (+ pc 3))
- (setq bytes (cons (cons pc (cdr off))
- (cons nil
- (cons (symbol-value op) bytes))))
- (setq patchlist (cons bytes patchlist)))
+ (setcar off pc))
+ ((null op)
+ ;; a no-op added by `byte-compile-delay-out'
+ (unless (zerop off)
+ (error
+ "Placeholder added by `byte-compile-delay-out' not filled in.")
+ ))
(t
- (setq bytes
- (cond ((cond ((consp off)
- ;; Variable or constant reference
- (setq off (cdr off))
- (eq op 'byte-constant)))
- (cond ((< off byte-constant-limit)
- (setq pc (1+ pc))
- (cons (+ byte-constant off) bytes))
- (t
- (setq pc (+ 3 pc))
- (cons (lsh off -8)
- (cons (logand off 255)
- (cons byte-constant2 bytes))))))
- ((<= byte-listN (symbol-value op))
- (setq pc (+ 2 pc))
- (cons off (cons (symbol-value op) bytes)))
- ((< off 6)
- (setq pc (1+ pc))
- (cons (+ (symbol-value op) off) bytes))
- ((< off 256)
- (setq pc (+ 2 pc))
- (cons off (cons (+ (symbol-value op) 6) bytes)))
- (t
- (setq pc (+ 3 pc))
- (cons (lsh off -8)
- (cons (logand off 255)
- (cons (+ (symbol-value op) 7)
- bytes))))))))
- (setq lap (cdr lap)))
+ (if (eq op 'byte-discardN-preserve-tos)
+ ;; byte-discardN-preserve-tos is a psuedo op, which is actually
+ ;; the same as byte-discardN with a modified argument
+ (setq opcode byte-discardN)
+ (setq opcode (symbol-value op)))
+ (cond ((memq op byte-goto-ops)
+ ;; goto
+ (byte-compile-push-bytecodes opcode nil (cdr off) bytes pc)
+ (push bytes patchlist))
+ ((and (consp off)
+ ;; Variable or constant reference
+ (progn (setq off (cdr off))
+ (eq op 'byte-constant)))
+ ;; constant ref
+ (if (< off byte-constant-limit)
+ (byte-compile-push-bytecodes (+ byte-constant off)
+ bytes pc)
+ (byte-compile-push-bytecode-const2 byte-constant2 off
+ bytes pc)))
+ ((and (= opcode byte-stack-set)
+ (> off 255))
+ ;; Use the two-byte version of byte-stack-set if the
+ ;; offset is too large for the normal version.
+ (byte-compile-push-bytecode-const2 byte-stack-set2 off
+ bytes pc))
+ ((and (>= opcode byte-listN)
+ (< opcode byte-discardN))
+ ;; These insns all put their operand into one extra byte.
+ (byte-compile-push-bytecodes opcode off bytes pc))
+ ((= opcode byte-discardN)
+ ;; byte-discardN is wierd in that it encodes a flag in the
+ ;; top bit of its one-byte argument. If the argument is
+ ;; too large to fit in 7 bits, the opcode can be repeated.
+ (let ((flag (if (eq op 'byte-discardN-preserve-tos) #x80 0)))
+ (while (> off #x7f)
+ (byte-compile-push-bytecodes opcode (logior #x7f flag) bytes pc)
+ (setq off (- off #x7f)))
+ (byte-compile-push-bytecodes opcode (logior off flag) bytes pc)))
+ ((null off)
+ ;; opcode that doesn't use OFF
+ (byte-compile-push-bytecodes opcode bytes pc))
+ ;; The following three cases are for the special
+ ;; insns that encode their operand into 0, 1, or 2
+ ;; extra bytes depending on its magnitude.
+ ((< off 6)
+ (byte-compile-push-bytecodes (+ opcode off) bytes pc))
+ ((< off 256)
+ (byte-compile-push-bytecodes (+ opcode 6) off bytes pc))
+ (t
+ (byte-compile-push-bytecode-const2 (+ opcode 7) off
+ bytes pc))))))
;;(if (not (= pc (length bytes)))
;; (error "Compiler error: pc mismatch - %s %s" pc (length bytes)))
- ;; Patch PC into jumps
- (let (bytes)
- (while patchlist
- (setq bytes (car patchlist))
- (cond ((atom (car bytes))) ; Tag
- (t ; Absolute jump
- (setq pc (car (cdr (car bytes)))) ; Pick PC from tag
- (setcar (cdr bytes) (logand pc 255))
- (setcar bytes (lsh pc -8))
- ;; FIXME: Replace this by some workaround.
- (if (> (car bytes) 255) (error "Bytecode overflow"))))
- (setq patchlist (cdr patchlist))))
+
+ ;; Patch tag PCs into absolute jumps
+ (dolist (bytes-tail patchlist)
+ (setq pc (caar bytes-tail)) ; Pick PC from goto's tag
+ (setcar (cdr bytes-tail) (logand pc 255))
+ (setcar bytes-tail (lsh pc -8))
+ ;; FIXME: Replace this by some workaround.
+ (if (> (car bytes) 255) (error "Bytecode overflow")))
+
(apply 'unibyte-string (nreverse bytes))))
@@ -2086,18 +2204,16 @@ list that represents a doc string reference.
(defun byte-compile-file-form (form)
(let ((byte-compile-current-form nil) ; close over this for warnings.
bytecomp-handler)
- (cond
- ((not (consp form))
- (byte-compile-keep-pending form))
- ((and (symbolp (car form))
- (setq bytecomp-handler (get (car form) 'byte-hunk-handler)))
- (cond ((setq form (funcall bytecomp-handler form))
- (byte-compile-flush-pending)
- (byte-compile-output-file-form form))))
- ((eq form (setq form (macroexpand form byte-compile-macro-environment)))
- (byte-compile-keep-pending form))
- (t
- (byte-compile-file-form form)))))
+ (setq form (macroexpand-all form byte-compile-macro-environment))
+ (cond ((not (consp form))
+ (byte-compile-keep-pending form))
+ ((and (symbolp (car form))
+ (setq bytecomp-handler (get (car form) 'byte-hunk-handler)))
+ (cond ((setq form (funcall bytecomp-handler form))
+ (byte-compile-flush-pending)
+ (byte-compile-output-file-form form))))
+ (t
+ (byte-compile-keep-pending form)))))
;; Functions and variables with doc strings must be output separately,
;; so make-docfile can recognise them. Most other things can be output
@@ -2109,8 +2225,7 @@ list that represents a doc string reference.
(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
- (macroexpand form byte-compile-macro-environment))
+ (byte-compile-file-form form)
;; Return nil so the form is not output twice.
nil)
@@ -2431,6 +2546,12 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(if macro
(setq fun (cdr fun)))
(cond ((eq (car-safe fun) 'lambda)
+ ;; expand macros
+ (setq fun
+ (macroexpand-all fun
+ byte-compile-initial-macro-environment))
+ ;; get rid of the `function' quote added by the `lambda' macro
+ (setq fun (cadr fun))
(setq fun (if macro
(cons 'macro (byte-compile-lambda fun))
(byte-compile-lambda fun)))
@@ -2518,6 +2639,8 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(setq list (cdr list)))))
+(autoload 'byte-compile-make-lambda-lexenv "byte-lexbind")
+
;; Byte-compile a lambda-expression and return a valid function.
;; The value is usually a compiled function but may be the original
;; lambda-expression.
@@ -2574,20 +2697,43 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(byte-compile-warn "malformed interactive spec: %s"
(prin1-to-string bytecomp-int)))))
;; Process the body.
- (let ((compiled (byte-compile-top-level
- (cons 'progn bytecomp-body) nil 'lambda)))
+ (let* ((byte-compile-lexical-environment
+ ;; If doing lexical binding, push a new lexical environment
+ ;; containing the args and any closed-over variables.
+ (and lexical-binding
+ (byte-compile-make-lambda-lexenv
+ fun
+ byte-compile-lexical-environment)))
+ (is-closure
+ ;; This is true if we should be making a closure instead of
+ ;; a simple lambda (because some variables from the
+ ;; containing lexical environment are closed over).
+ (and lexical-binding
+ (byte-compile-closure-initial-lexenv-p
+ byte-compile-lexical-environment)))
+ (byte-compile-current-heap-environment nil)
+ (byte-compile-current-num-closures 0)
+ (compiled
+ (byte-compile-top-level (cons 'progn bytecomp-body) nil 'lambda)))
;; Build the actual byte-coded function.
(if (eq 'byte-code (car-safe compiled))
- (apply 'make-byte-code
- (append (list bytecomp-arglist)
- ;; byte-string, constants-vector, stack depth
- (cdr compiled)
- ;; optionally, the doc string.
- (if (or bytecomp-doc bytecomp-int)
- (list bytecomp-doc))
- ;; optionally, the interactive spec.
- (if bytecomp-int
- (list (nth 1 bytecomp-int)))))
+ (let ((code
+ (apply 'make-byte-code
+ (append (list bytecomp-arglist)
+ ;; byte-string, constants-vector, stack depth
+ (cdr compiled)
+ ;; optionally, the doc string.
+ (if (or bytecomp-doc bytecomp-int
+ lexical-binding)
+ (list bytecomp-doc))
+ ;; optionally, the interactive spec.
+ (if (or bytecomp-int lexical-binding)
+ (list (nth 1 bytecomp-int)))
+ (if lexical-binding
+ '(t))))))
+ (if is-closure
+ (cons 'closure code)
+ code))
(setq compiled
(nconc (if bytecomp-int (list bytecomp-int))
(cond ((eq (car-safe compiled) 'progn) (cdr compiled))
@@ -2598,6 +2744,26 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(bytecomp-body (list nil))))
compiled))))))
+(defun byte-compile-closure-code-p (code)
+ (eq (car-safe code) 'closure))
+
+(defun byte-compile-make-closure (code)
+ ;; A real closure requires that the constant be curried with an
+ ;; environment vector to make a closure object.
+ (if for-effect
+ (setq for-effect nil)
+ (byte-compile-push-constant 'curry)
+ (byte-compile-push-constant code)
+ (byte-compile-lexical-variable-ref byte-compile-current-heap-environment)
+ (byte-compile-out 'byte-call 2)))
+
+(defun byte-compile-closure (form &optional add-lambda)
+ (let ((code (byte-compile-lambda form add-lambda)))
+ (if (byte-compile-closure-code-p code)
+ (byte-compile-make-closure code)
+ ;; A simple lambda is just a constant
+ (byte-compile-constant code))))
+
(defun byte-compile-constants-vector ()
;; Builds the constants-vector from the current variables and constants.
;; This modifies the constants from (const . nil) to (const . offset).
@@ -2642,17 +2808,51 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(byte-compile-depth 0)
(byte-compile-maxdepth 0)
(byte-compile-output nil))
- (if (memq byte-optimize '(t source))
- (setq form (byte-optimize-form form for-effect)))
- (while (and (eq (car-safe form) 'progn) (null (cdr (cdr form))))
- (setq form (nth 1 form)))
- (if (and (eq 'byte-code (car-safe form))
- (not (memq byte-optimize '(t byte)))
- (stringp (nth 1 form)) (vectorp (nth 2 form))
- (natnump (nth 3 form)))
- form
- (byte-compile-form form for-effect)
- (byte-compile-out-toplevel for-effect output-type))))
+ (if (memq byte-optimize '(t source))
+ (setq form (byte-optimize-form form for-effect)))
+ (while (and (eq (car-safe form) 'progn) (null (cdr (cdr form))))
+ (setq form (nth 1 form)))
+ (if (and (eq 'byte-code (car-safe form))
+ (not (memq byte-optimize '(t byte)))
+ (stringp (nth 1 form)) (vectorp (nth 2 form))
+ (natnump (nth 3 form)))
+ form
+ ;; Set up things for a lexically-bound function
+ (when (and lexical-binding (eq output-type 'lambda))
+ ;; See how many arguments there are, and set the current stack depth
+ ;; accordingly
+ (dolist (var byte-compile-lexical-environment)
+ (when (byte-compile-lexvar-on-stack-p var)
+ (setq byte-compile-depth (1+ byte-compile-depth))))
+ ;; If there are args, output a tag to record the initial
+ ;; stack-depth for the optimizer
+ (when (> byte-compile-depth 0)
+ (byte-compile-out-tag (byte-compile-make-tag)))
+ ;; If this is the top-level of a lexically bound lambda expression,
+ ;; perhaps some parameters on stack need to be copied into a heap
+ ;; environment, so check for them, and do so if necessary.
+ (let ((lforminfo (byte-compile-make-lforminfo)))
+ ;; Add any lexical variable that's on the stack to the analysis set.
+ (dolist (var byte-compile-lexical-environment)
+ (when (byte-compile-lexvar-on-stack-p var)
+ (byte-compile-lforminfo-add-var lforminfo (car var) t)))
+ ;; Analyze the body
+ (unless (null (byte-compile-lforminfo-vars lforminfo))
+ (byte-compile-lforminfo-analyze lforminfo form nil nil))
+ ;; If the analysis revealed some argument need to be in a heap
+ ;; environment (because they're closed over by an embedded
+ ;; lambda), put them there.
+ (setq byte-compile-lexical-environment
+ (nconc (byte-compile-maybe-push-heap-environment lforminfo)
+ byte-compile-lexical-environment))
+ (dolist (arginfo (byte-compile-lforminfo-vars lforminfo))
+ (when (byte-compile-lvarinfo-closed-over-p arginfo)
+ (byte-compile-bind (car arginfo)
+ byte-compile-lexical-environment
+ lforminfo)))))
+ ;; Now compile FORM
+ (byte-compile-form form for-effect)
+ (byte-compile-out-toplevel for-effect output-type))))
(defun byte-compile-out-toplevel (&optional for-effect output-type)
(if for-effect
@@ -2774,7 +2974,6 @@ If FORM is a lambda or a macro, byte-compile it as a function."
;; (Use byte-compile-form-do-effect to reset the for-effect flag too.)
;;
(defun byte-compile-form (form &optional for-effect)
- (setq form (macroexpand form byte-compile-macro-environment))
(cond ((not (consp form))
(cond ((or (not (symbolp form)) (byte-compile-const-symbol-p form))
(when (symbolp form)
@@ -2784,7 +2983,8 @@ If FORM is a lambda or a macro, byte-compile it as a function."
(when (symbolp form)
(byte-compile-set-symbol-position form))
(setq for-effect nil))
- (t (byte-compile-variable-ref 'byte-varref form))))
+ (t
+ (byte-compile-variable-ref form))))
((symbolp (car form))
(let* ((bytecomp-fn (car form))
(bytecomp-handler (get bytecomp-fn 'byte-compile)))
@@ -2835,44 +3035,98 @@ That command is designed for interactive use only" bytecomp-fn))
(mapc 'byte-compile-form (cdr form)) ; wasteful, but faster.
(byte-compile-out 'byte-call (length (cdr form))))
-(defun byte-compile-variable-ref (base-op bytecomp-var)
- (when (symbolp bytecomp-var)
- (byte-compile-set-symbol-position bytecomp-var))
- (if (or (not (symbolp bytecomp-var))
- (byte-compile-const-symbol-p bytecomp-var
- (not (eq base-op 'byte-varref))))
- (if (byte-compile-warning-enabled-p 'constants)
- (byte-compile-warn
- (cond ((eq base-op 'byte-varbind) "attempt to let-bind %s `%s'")
- ((eq base-op 'byte-varset) "variable assignment to %s `%s'")
- (t "variable reference to %s `%s'"))
- (if (symbolp bytecomp-var) "constant" "nonvariable")
- (prin1-to-string bytecomp-var)))
- (and (get bytecomp-var 'byte-obsolete-variable)
- (not (memq bytecomp-var byte-compile-not-obsolete-vars))
- (byte-compile-warn-obsolete bytecomp-var))
- (if (eq base-op 'byte-varbind)
- (push bytecomp-var byte-compile-bound-variables)
- (or (not (byte-compile-warning-enabled-p 'free-vars))
- (boundp bytecomp-var)
- (memq bytecomp-var byte-compile-bound-variables)
- (if (eq base-op 'byte-varset)
- (or (memq bytecomp-var byte-compile-free-assignments)
- (progn
- (byte-compile-warn "assignment to free variable `%s'"
- bytecomp-var)
- (push bytecomp-var byte-compile-free-assignments)))
- (or (memq bytecomp-var byte-compile-free-references)
- (progn
- (byte-compile-warn "reference to free variable `%s'"
- bytecomp-var)
- (push bytecomp-var byte-compile-free-references)))))))
- (let ((tmp (assq bytecomp-var byte-compile-variables)))
+(defun byte-compile-check-variable (var &optional binding)
+ "Do various error checks before a use of the variable VAR.
+If BINDING is non-nil, VAR is being bound."
+ (when (symbolp var)
+ (byte-compile-set-symbol-position var))
+ (cond ((or (not (symbolp var)) (byte-compile-const-symbol-p var))
+ (when (byte-compile-warning-enabled-p 'constants)
+ (byte-compile-warn (if binding
+ "attempt to let-bind %s `%s`"
+ "variable reference to %s `%s'")
+ (if (symbolp var) "constant" "nonvariable")
+ (prin1-to-string var))))
+ ((and (get var 'byte-obsolete-variable)
+ (not (memq var byte-compile-not-obsolete-vars)))
+ (byte-compile-warn-obsolete var))))
+
+(defsubst byte-compile-dynamic-variable-op (base-op var)
+ (let ((tmp (assq var byte-compile-variables)))
(unless tmp
- (setq tmp (list bytecomp-var))
+ (setq tmp (list var))
(push tmp byte-compile-variables))
(byte-compile-out base-op tmp)))
+(defun byte-compile-dynamic-variable-bind (var)
+ "Generate code to bind the lexical variable VAR to the top-of-stack value."
+ (byte-compile-check-variable var t)
+ (when (byte-compile-warning-enabled-p 'free-vars)
+ (push var byte-compile-bound-variables))
+ (byte-compile-dynamic-variable-op 'byte-varbind var))
+
+;; This is used when it's know that VAR _definitely_ has a lexical
+;; binding, and no error-checking should be done.
+(defun byte-compile-lexical-variable-ref (var)
+ "Generate code to push the value of the lexical variable VAR on the stack."
+ (let ((binding (assq var byte-compile-lexical-environment)))
+ (when (null binding)
+ (error "Lexical binding not found for `%s'" var))
+ (if (byte-compile-lexvar-on-stack-p binding)
+ ;; On the stack
+ (byte-compile-stack-ref (byte-compile-lexvar-offset binding))
+ ;; In a heap environment vector; first push the vector on the stack
+ (byte-compile-lexical-variable-ref
+ (byte-compile-lexvar-environment binding))
+ ;; Now get the value from it
+ (byte-compile-out 'byte-vec-ref (byte-compile-lexvar-offset binding)))))
+
+(defun byte-compile-variable-ref (var)
+ "Generate code to push the value of the variable VAR on the stack."
+ (byte-compile-check-variable var)
+ (let ((lex-binding (assq var byte-compile-lexical-environment)))
+ (if lex-binding
+ ;; VAR is lexically bound
+ (if (byte-compile-lexvar-on-stack-p lex-binding)
+ ;; On the stack
+ (byte-compile-stack-ref (byte-compile-lexvar-offset lex-binding))
+ ;; In a heap environment vector
+ (byte-compile-lexical-variable-ref
+ (byte-compile-lexvar-environment lex-binding))
+ (byte-compile-out 'byte-vec-ref
+ (byte-compile-lexvar-offset lex-binding)))
+ ;; VAR is dynamically bound
+ (unless (or (not (byte-compile-warning-enabled-p 'free-vars))
+ (boundp var)
+ (memq var byte-compile-bound-variables)
+ (memq var byte-compile-free-references))
+ (byte-compile-warn "reference to free variable `%s'" var)
+ (push var byte-compile-free-references))
+ (byte-compile-dynamic-variable-op 'byte-varref var))))
+
+(defun byte-compile-variable-set (var)
+ "Generate code to set the variable VAR from the top-of-stack value."
+ (byte-compile-check-variable var)
+ (let ((lex-binding (assq var byte-compile-lexical-environment)))
+ (if lex-binding
+ ;; VAR is lexically bound
+ (if (byte-compile-lexvar-on-stack-p lex-binding)
+ ;; On the stack
+ (byte-compile-stack-set (byte-compile-lexvar-offset lex-binding))
+ ;; In a heap environment vector
+ (byte-compile-lexical-variable-ref
+ (byte-compile-lexvar-environment lex-binding))
+ (byte-compile-out 'byte-vec-set
+ (byte-compile-lexvar-offset lex-binding)))
+ ;; VAR is dynamically bound
+ (unless (or (not (byte-compile-warning-enabled-p 'free-vars))
+ (boundp var)
+ (memq var byte-compile-bound-variables)
+ (memq var byte-compile-free-assignments))
+ (byte-compile-warn "assignment to free variable `%s'" var)
+ (push var byte-compile-free-assignments))
+ (byte-compile-dynamic-variable-op 'byte-varset var))))
+
(defmacro byte-compile-get-constant (const)
`(or (if (stringp ,const)
;; In a string constant, treat properties as significant.
@@ -2899,6 +3153,25 @@ That command is designed for interactive use only" bytecomp-fn))
(let ((for-effect nil))
(inline (byte-compile-constant const))))
+(defun byte-compile-push-unknown-constant (&optional id)
+ "Generate code to push a `constant' who's value isn't known yet.
+A tag is returned which may then later be passed to
+`byte-compile-resolve-unknown-constant' to finalize the value.
+The optional argument ID is a tag returned by an earlier call to
+`byte-compile-push-unknown-constant', in which case the same constant is
+pushed again."
+ (unless id
+ (setq id (list (make-symbol "unknown")))
+ (push id byte-compile-constants))
+ (byte-compile-out 'byte-constant id)
+ id)
+
+(defun byte-compile-resolve-unknown-constant (id value)
+ "Give an `unknown constant' a value.
+ID is the tag returned by `byte-compile-push-unknown-constant'. and VALUE
+is the value it should have."
+ (setcar id value))
+
;; Compile those primitive ordinary functions
;; which have special byte codes just for speed.
@@ -3102,8 +3375,39 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
(defun byte-compile-noop (form)
(byte-compile-constant nil))
-(defun byte-compile-discard ()
- (byte-compile-out 'byte-discard 0))
+(defun byte-compile-discard (&optional num preserve-tos)
+ "Output byte codes to discard the NUM entries at the top of the stack (NUM defaults to 1).
+If PRESERVE-TOS is non-nil, preserve the top-of-stack value, as if it were
+popped before discarding the num values, and then pushed back again after
+discarding."
+ (if (and (null num) (not preserve-tos))
+ ;; common case
+ (byte-compile-out 'byte-discard)
+ ;; general case
+ (unless num
+ (setq num 1))
+ (when (and preserve-tos (> num 0))
+ ;; Preserve the top-of-stack value by writing it directly to the stack
+ ;; location which will be at the top-of-stack after popping.
+ (byte-compile-stack-set (1- (- byte-compile-depth num)))
+ ;; Now we actually discard one less value, since we want to keep
+ ;; the eventual TOS
+ (setq num (1- num)))
+ (while (> num 0)
+ (byte-compile-out 'byte-discard)
+ (setq num (1- num)))))
+
+(defun byte-compile-stack-ref (stack-pos)
+ "Output byte codes to push the value at position STACK-POS in the stack, on the top of the stack."
+ (if (= byte-compile-depth (1+ stack-pos))
+ ;; A simple optimization
+ (byte-compile-out 'byte-dup)
+ ;; normal case
+ (byte-compile-out 'byte-stack-ref stack-pos)))
+
+(defun byte-compile-stack-set (stack-pos)
+ "Output byte codes to store the top-of-stack value at position STACK-POS in the stack."
+ (byte-compile-out 'byte-stack-set stack-pos))
;; Compile a function that accepts one or more args and is right-associative.
@@ -3262,40 +3566,14 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
the syntax (function (lambda (...) ...)) instead.")))))
(byte-compile-two-args form))
-(defun byte-compile-funarg (form)
- ;; (mapcar '(lambda (x) ..) ..) ==> (mapcar (function (lambda (x) ..)) ..)
- ;; for cases where it's guaranteed that first arg will be used as a lambda.
- (byte-compile-normal-call
- (let ((fn (nth 1 form)))
- (if (and (eq (car-safe fn) 'quote)
- (eq (car-safe (nth 1 fn)) 'lambda))
- (cons (car form)
- (cons (cons 'function (cdr fn))
- (cdr (cdr form))))
- form))))
-
-(defun byte-compile-funarg-2 (form)
- ;; (sort ... '(lambda (x) ..)) ==> (sort ... (function (lambda (x) ..)))
- ;; for cases where it's guaranteed that second arg will be used as a lambda.
- (byte-compile-normal-call
- (let ((fn (nth 2 form)))
- (if (and (eq (car-safe fn) 'quote)
- (eq (car-safe (nth 1 fn)) 'lambda))
- (cons (car form)
- (cons (nth 1 form)
- (cons (cons 'function (cdr fn))
- (cdr (cdr (cdr form))))))
- form))))
-
;; (function foo) must compile like 'foo, not like (symbol-function 'foo).
;; Otherwise it will be incompatible with the interpreter,
;; and (funcall (function foo)) will lose with autoloads.
(defun byte-compile-function-form (form)
- (byte-compile-constant
- (cond ((symbolp (nth 1 form))
- (nth 1 form))
- ((byte-compile-lambda (nth 1 form))))))
+ (if (symbolp (nth 1 form))
+ (byte-compile-constant (nth 1 form))
+ (byte-compile-closure (nth 1 form))))
(defun byte-compile-indent-to (form)
(let ((len (length form)))
@@ -3339,7 +3617,7 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
(byte-compile-form (car (cdr bytecomp-args)))
(or for-effect (cdr (cdr bytecomp-args))
(byte-compile-out 'byte-dup 0))
- (byte-compile-variable-ref 'byte-varset (car bytecomp-args))
+ (byte-compile-variable-set (car bytecomp-args))
(setq bytecomp-args (cdr (cdr bytecomp-args))))
;; (setq), with no arguments.
(byte-compile-form nil for-effect))
@@ -3405,16 +3683,6 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
(byte-defop-compiler-1 or)
(byte-defop-compiler-1 while)
(byte-defop-compiler-1 funcall)
-(byte-defop-compiler-1 apply byte-compile-funarg)
-(byte-defop-compiler-1 mapcar byte-compile-funarg)
-(byte-defop-compiler-1 mapatoms byte-compile-funarg)
-(byte-defop-compiler-1 mapconcat byte-compile-funarg)
-(byte-defop-compiler-1 mapc byte-compile-funarg)
-(byte-defop-compiler-1 maphash byte-compile-funarg)
-(byte-defop-compiler-1 map-char-table byte-compile-funarg)
-(byte-defop-compiler-1 map-char-table byte-compile-funarg-2)
-;; map-charset-chars should be funarg but has optional third arg
-(byte-defop-compiler-1 sort byte-compile-funarg-2)
(byte-defop-compiler-1 let)
(byte-defop-compiler-1 let*)
@@ -3596,7 +3864,14 @@ that suppresses all warnings during execution of BODY."
(defun byte-compile-while (form)
(let ((endtag (byte-compile-make-tag))
- (looptag (byte-compile-make-tag)))
+ (looptag (byte-compile-make-tag))
+ ;; Heap environments can't be shared between a loop and its
+ ;; enclosing environment (because any lexical variables bound
+ ;; inside the loop should have an independent value for each
+ ;; iteration). Setting `byte-compile-current-num-closures' to
+ ;; an invalid value causes the code that tries to merge
+ ;; environments to not do so.
+ (byte-compile-current-num-closures -1))
(byte-compile-out-tag looptag)
(byte-compile-form (car (cdr form)))
(byte-compile-goto-if nil for-effect endtag)
@@ -3609,34 +3884,116 @@ that suppresses all warnings during execution of BODY."
(mapc 'byte-compile-form (cdr form))
(byte-compile-out 'byte-call (length (cdr (cdr form)))))
+
+;; let binding
+
+;; All other lexical-binding functions are guarded by a non-nil return
+;; value from `byte-compile-compute-lforminfo', so they needn't be
+;; autoloaded.
+(autoload 'byte-compile-compute-lforminfo "byte-lexbind")
+
+(defun byte-compile-push-binding-init (clause init-lexenv lforminfo)
+ "Emit byte-codes to push the initialization value for CLAUSE on the stack.
+INIT-LEXENV is the lexical environment created for initializations
+already done for this form.
+LFORMINFO should be information about lexical variables being bound.
+Return INIT-LEXENV updated to include the newest initialization, or nil
+if LFORMINFO is nil (meaning all bindings are dynamic)."
+ (let* ((var (if (consp clause) (car clause) clause))
+ (vinfo
+ (and lforminfo (assq var (byte-compile-lforminfo-vars lforminfo))))
+ (unused (and vinfo (zerop (cadr vinfo)))))
+ (unless (and unused (symbolp clause))
+ (when (and lforminfo (not unused))
+ ;; We record the stack position even of dynamic bindings and
+ ;; variables in non-stack lexical environments; we'll put
+ ;; them in the proper place below.
+ (push (byte-compile-make-lexvar var byte-compile-depth) init-lexenv))
+ (if (consp clause)
+ (byte-compile-form (cadr clause) unused)
+ (byte-compile-push-constant nil))))
+ init-lexenv)
(defun byte-compile-let (form)
- ;; First compute the binding values in the old scope.
- (let ((varlist (car (cdr form))))
- (dolist (var varlist)
- (if (consp var)
- (byte-compile-form (car (cdr var)))
- (byte-compile-push-constant nil))))
- (let ((byte-compile-bound-variables byte-compile-bound-variables) ;new scope
- (varlist (reverse (car (cdr form)))))
- (dolist (var varlist)
- (byte-compile-variable-ref 'byte-varbind
- (if (consp var) (car var) var)))
- (byte-compile-body-do-effect (cdr (cdr form)))
- (byte-compile-out 'byte-unbind (length (car (cdr form))))))
+ "Generate code for the `let' form FORM."
+ (let ((clauses (cadr form))
+ (lforminfo (and lexical-binding (byte-compile-compute-lforminfo form)))
+ (init-lexenv nil)
+ ;; bind these to restrict the scope of any changes
+ (byte-compile-current-heap-environment
+ byte-compile-current-heap-environment)
+ (byte-compile-current-num-closures byte-compile-current-num-closures))
+ (when (and lforminfo (byte-compile-non-stack-bindings-p clauses lforminfo))
+ ;; Some of the variables we're binding are lexical variables on
+ ;; the stack, but not all. As much as we can, rearrange the list
+ ;; so that non-stack lexical variables and dynamically bound
+ ;; variables come last, which allows slightly more optimal
+ ;; byte-code for binding them.
+ (setq clauses (byte-compile-rearrange-let-clauses clauses lforminfo)))
+ ;; If necessary, create a new heap environment to hold some of the
+ ;; variables bound here.
+ (when lforminfo
+ (setq init-lexenv (byte-compile-maybe-push-heap-environment lforminfo)))
+ ;; First compute the binding values in the old scope.
+ (dolist (clause clauses)
+ (setq init-lexenv
+ (byte-compile-push-binding-init clause init-lexenv lforminfo)))
+ ;; Now do the bindings, execute the body, and undo the bindings
+ (let ((byte-compile-bound-variables byte-compile-bound-variables)
+ (byte-compile-lexical-environment byte-compile-lexical-environment)
+ (preserve-body-value (not for-effect)))
+ (dolist (clause (reverse clauses))
+ (let ((var (if (consp clause) (car clause) clause)))
+ (cond ((null lforminfo)
+ ;; If there are no lexical bindings, we can do things simply.
+ (byte-compile-dynamic-variable-bind var))
+ ((byte-compile-bind var init-lexenv lforminfo)
+ (pop init-lexenv)))))
+ ;; Emit the body
+ (byte-compile-body-do-effect (cdr (cdr form)))
+ ;; Unbind the variables
+ (if lforminfo
+ ;; Unbind both lexical and dynamic variables
+ (byte-compile-unbind clauses init-lexenv lforminfo preserve-body-value)
+ ;; Unbind dynamic variables
+ (byte-compile-out 'byte-unbind (length clauses))))))
(defun byte-compile-let* (form)
- (let ((byte-compile-bound-variables byte-compile-bound-variables) ;new scope
- (varlist (copy-sequence (car (cdr form)))))
- (dolist (var varlist)
- (if (atom var)
- (byte-compile-push-constant nil)
- (byte-compile-form (car (cdr var)))
- (setq var (car var)))
- (byte-compile-variable-ref 'byte-varbind var))
+ "Generate code for the `let*' form FORM."
+ (let ((clauses (cadr form))
+ (lforminfo (and lexical-binding (byte-compile-compute-lforminfo form)))
+ (init-lexenv nil)
+ (preserve-body-value (not for-effect))
+ ;; bind these to restrict the scope of any changes
+ (byte-compile-bound-variables byte-compile-bound-variables)
+ (byte-compile-lexical-environment byte-compile-lexical-environment)
+ (byte-compile-current-heap-environment
+ byte-compile-current-heap-environment)
+ (byte-compile-current-num-closures byte-compile-current-num-closures))
+ ;; If necessary, create a new heap environment to hold some of the
+ ;; variables bound here.
+ (when lforminfo
+ (setq init-lexenv (byte-compile-maybe-push-heap-environment lforminfo)))
+ ;; Bind the variables
+ (dolist (clause clauses)
+ (setq init-lexenv
+ (byte-compile-push-binding-init clause init-lexenv lforminfo))
+ (let ((var (if (consp clause) (car clause) clause)))
+ (cond ((null lforminfo)
+ ;; If there are no lexical bindings, we can do things simply.
+ (byte-compile-dynamic-variable-bind var))
+ ((byte-compile-bind var init-lexenv lforminfo)
+ (pop init-lexenv)))))
+ ;; Emit the body
(byte-compile-body-do-effect (cdr (cdr form)))
- (byte-compile-out 'byte-unbind (length (car (cdr form))))))
+ ;; Unbind the variables
+ (if lforminfo
+ ;; Unbind both lexical and dynamic variables
+ (byte-compile-unbind clauses init-lexenv lforminfo preserve-body-value)
+ ;; Unbind dynamic variables
+ (byte-compile-out 'byte-unbind (length clauses)))))
+
(byte-defop-compiler-1 /= byte-compile-negated)
(byte-defop-compiler-1 atom byte-compile-negated)
@@ -3659,6 +4016,7 @@ that suppresses all warnings during execution of BODY."
"Compiler error: `%s' has no `byte-compile-negated-op' property"
(car form)))
(cdr form))))
+
;;; other tricky macro-like special-forms
@@ -3779,28 +4137,28 @@ that suppresses all warnings during execution of BODY."
(byte-compile-set-symbol-position (car form))
(byte-compile-set-symbol-position 'defun)
(error "defun name must be a symbol, not %s" (car form)))
- ;; We prefer to generate a defalias form so it will record the function
- ;; definition just like interpreting a defun.
- (byte-compile-form
- (list 'defalias
- (list 'quote (nth 1 form))
- (byte-compile-byte-code-maker
- (byte-compile-lambda (cdr (cdr form)) t)))
- t)
- (byte-compile-constant (nth 1 form)))
+ (let ((for-effect nil))
+ (byte-compile-push-constant 'defalias)
+ (byte-compile-push-constant (nth 1 form))
+ (byte-compile-closure (cdr (cdr form)) t))
+ (byte-compile-out 'byte-call 2))
(defun byte-compile-defmacro (form)
;; This is not used for file-level defmacros with doc strings.
- (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)))))
+ ;; 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))
+ (if (not (byte-compile-closure-code-p code))
+ ;; simple lambda
+ (byte-compile-push-constant (cons 'macro code))
+ (byte-compile-push-constant 'macro)
+ (byte-compile-make-closure code)
+ (byte-compile-out 'byte-cons))
+ (byte-compile-out 'byte-fset)
+ (byte-compile-discard))
+ (byte-compile-constant (nth 1 form)))
(defun byte-compile-defvar (form)
;; This is not used for file-level defvar/consts with doc strings.
@@ -3826,7 +4184,7 @@ that suppresses all warnings during execution of BODY."
;; Put the defined variable in this library's load-history entry
;; just as a real defvar would, but only in top-level forms.
(when (and (cddr form) (null byte-compile-current-form))
- `(push ',var current-load-list))
+ `(setq current-load-list (cons ',var current-load-list)))
(when (> (length form) 3)
(when (and string (not (stringp string)))
(byte-compile-warn "third arg to `%s %s' is not a string: %s"
@@ -3948,23 +4306,74 @@ that suppresses all warnings during execution of BODY."
(setq byte-compile-depth (and (not (eq opcode 'byte-goto))
(1- byte-compile-depth))))
-(defun byte-compile-out (opcode offset)
- (push (cons opcode offset) byte-compile-output)
- (cond ((eq opcode 'byte-call)
- (setq byte-compile-depth (- byte-compile-depth offset)))
- ((eq opcode 'byte-return)
- ;; This is actually an unnecessary case, because there should be
- ;; no more opcodes behind byte-return.
- (setq byte-compile-depth nil))
- (t
- (setq byte-compile-depth (+ byte-compile-depth
- (or (aref byte-stack+-info
- (symbol-value opcode))
- (- (1- offset))))
- byte-compile-maxdepth (max byte-compile-depth
- byte-compile-maxdepth))))
- ;;(if (< byte-compile-depth 0) (error "Compiler error: stack underflow"))
- )
+(defun byte-compile-stack-adjustment (op operand)
+ "Return the amount by which an operation adjusts the stack.
+OP and OPERAND are as passed to `byte-compile-out'."
+ (if (memq op '(byte-call byte-discardN byte-discardN-preserve-tos))
+ ;; For calls, OPERAND is the number of args, so we pop OPERAND + 1
+ ;; elements, and the push the result, for a total of -OPERAND.
+ ;; For discardN*, of course, we just pop OPERAND elements.
+ (- operand)
+ (or (aref byte-stack+-info (symbol-value op))
+ ;; Ops with a nil entry in `byte-stack+-info' are byte-codes
+ ;; that take OPERAND values off the stack and push a result, for
+ ;; a total of 1 - OPERAND
+ (- 1 operand))))
+
+(defun byte-compile-out (op &optional operand)
+ (push (cons op operand) byte-compile-output)
+ (if (eq op 'byte-return)
+ ;; This is actually an unnecessary case, because there should be no
+ ;; more ops behind byte-return.
+ (setq byte-compile-depth nil)
+ (setq byte-compile-depth
+ (+ byte-compile-depth (byte-compile-stack-adjustment op operand)))
+ (setq byte-compile-maxdepth (max byte-compile-depth byte-compile-maxdepth))
+ ;;(if (< byte-compile-depth 0) (error "Compiler error: stack underflow"))
+ ))
+
+(defun byte-compile-delay-out (&optional stack-used stack-adjust)
+ "Add a placeholder to the output, which can be used to later add byte-codes.
+Return a position tag that can be passed to `byte-compile-delayed-out'
+to add the delayed byte-codes. STACK-USED is the maximum amount of
+stack-spaced used by the delayed byte-codes (defaulting to 0), and
+STACK-ADJUST is the amount by which the later-added code will adjust the
+stack (defaulting to 0); the byte-codes added later _must_ adjust the
+stack by this amount! If STACK-ADJUST is 0, then it's not necessary to
+actually add anything later; the effect as if nothing was added at all."
+ ;; We just add a no-op to `byte-compile-output', and return a pointer to
+ ;; the tail of the list; `byte-compile-delayed-out' uses list surgery
+ ;; to add the byte-codes.
+ (when stack-used
+ (setq byte-compile-maxdepth
+ (max byte-compile-depth (+ byte-compile-depth (or stack-used 0)))))
+ (when stack-adjust
+ (setq byte-compile-depth
+ (+ byte-compile-depth stack-adjust)))
+ (push (cons nil (or stack-adjust 0)) byte-compile-output))
+
+(defun byte-compile-delayed-out (position op &optional operand)
+ "Add at POSITION the byte-operation OP, with optional numeric arg OPERAND.
+POSITION should a position returned by `byte-compile-delay-out'.
+Return a new position, which can be used to add further operations."
+ (unless (null (caar position))
+ (error "Bad POSITION arg to `byte-compile-delayed-out'"))
+ ;; This is kind of like `byte-compile-out', but we splice into the list
+ ;; where POSITION is. We don't bother updating `byte-compile-maxdepth'
+ ;; because that was already done by `byte-compile-delay-out', but we do
+ ;; update the relative operand stored in the no-op marker currently at
+ ;; POSITION; since we insert before that marker, this means that if the
+ ;; caller doesn't insert a sequence of byte-codes that matches the expected
+ ;; operand passed to `byte-compile-delay-out', then the nop will still have
+ ;; a non-zero operand when `byte-compile-lapcode' is called, which will
+ ;; cause an error to be signaled.
+
+ ;; Adjust the cumulative stack-adjustment stored in the cdr of the no-op
+ (setcdr (car position)
+ (- (cdar position) (byte-compile-stack-adjustment op operand)))
+ ;; Add the new operation onto the list tail at POSITION
+ (setcdr position (cons (cons op operand) (cdr position)))
+ position)
;;; call tree stuff
diff --git a/lisp/emacs-lisp/disass.el b/lisp/emacs-lisp/disass.el
index 9899e991e3f..18aa5fde0c8 100644
--- a/lisp/emacs-lisp/disass.el
+++ b/lisp/emacs-lisp/disass.el
@@ -73,19 +73,22 @@ redefine OBJECT if it is a symbol."
(let ((macro 'nil)
(name 'nil)
(doc 'nil)
+ (lexical-binding nil)
args)
(while (symbolp obj)
(setq name obj
obj (symbol-function obj)))
(if (subrp obj)
(error "Can't disassemble #<subr %s>" name))
- (if (and (listp obj) (eq (car obj) 'autoload))
- (progn
- (load (nth 1 obj))
- (setq obj (symbol-function name))))
+ (when (and (listp obj) (eq (car obj) 'autoload))
+ (load (nth 1 obj))
+ (setq obj (symbol-function name)))
(if (eq (car-safe obj) 'macro) ;handle macros
(setq macro t
obj (cdr obj)))
+ (when (and (listp obj) (eq (car obj) 'closure))
+ (setq lexical-binding t)
+ (setq obj (cddr obj)))
(if (and (listp obj) (eq (car obj) 'byte-code))
(setq obj (list 'lambda nil obj)))
(if (and (listp obj) (not (eq (car obj) 'lambda)))
@@ -216,7 +219,9 @@ OBJ should be a call to BYTE-CODE generated by the byte compiler."
(cond ((memq op byte-goto-ops)
(insert (int-to-string (nth 1 arg))))
((memq op '(byte-call byte-unbind
- byte-listN byte-concatN byte-insertN))
+ byte-listN byte-concatN byte-insertN
+ byte-stack-ref byte-stack-set byte-stack-set2
+ byte-discardN byte-discardN-preserve-tos))
(insert (int-to-string arg)))
((memq op '(byte-varref byte-varset byte-varbind))
(prin1 (car arg) (current-buffer)))
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index e4330e43fc9..b4ac0eebf6d 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -702,7 +702,15 @@ If CHAR is not a character, return nil."
(defun eval-last-sexp-1 (eval-last-sexp-arg-internal)
"Evaluate sexp before point; print value in minibuffer.
With argument, print output into current buffer."
- (let ((standard-output (if eval-last-sexp-arg-internal (current-buffer) t)))
+ (let ((standard-output (if eval-last-sexp-arg-internal (current-buffer) t))
+ ;; preserve the current lexical environment
+ (internal-interpreter-environment internal-interpreter-environment))
+ ;; Setup the lexical environment if lexical-binding is enabled.
+ ;; Note that `internal-interpreter-environment' _can't_ be both
+ ;; assigned and let-bound above -- it's treated specially (and
+ ;; oddly) by the interpreter!
+ (when lexical-binding
+ (setq internal-interpreter-environment '(t)))
(eval-last-sexp-print-value (eval (preceding-sexp)))))