Misc fixes, and use lexical-binding in more files.
[bpt/emacs.git] / lisp / emacs-lisp / bytecomp.el
index 5e97517..c661e6b 100644 (file)
@@ -1,7 +1,6 @@
-;;; bytecomp.el --- compilation of Lisp code into byte code
+;;; bytecomp.el --- compilation of Lisp code into byte code -*- lexical-binding: t -*-
 
-;; Copyright (C) 1985, 1986, 1987, 1992, 1994, 1998, 2000, 2001, 2002,
-;;   2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;; Copyright (C) 1985-1987, 1992, 1994, 1998, 2000-2011
 ;;   Free Software Foundation, Inc.
 
 ;; Author: Jamie Zawinski <jwz@lucid.com>
 
 ;;; Code:
 
+;; FIXME: Use lexical-binding and get rid of the atrocious "bytecomp-"
+;; variable prefix.
+
 ;; ========================================================================
 ;; Entry points:
 ;;     byte-recompile-directory, byte-compile-file,
+;;      byte-recompile-file,
 ;;     batch-byte-compile, batch-byte-recompile-directory,
 ;;     byte-compile, compile-defun,
 ;;     display-call-tree
 
 (require 'backquote)
 (require 'macroexp)
+(require 'cconv)
 (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."
@@ -270,6 +238,7 @@ the functions you loaded will not be able to run.")
 
 (defvar byte-compile-disable-print-circle nil
   "If non-nil, disable `print-circle' on printing a byte-compiled code.")
+(make-obsolete-variable 'byte-compile-disable-print-circle nil "24.1")
 ;;;###autoload(put 'byte-compile-disable-print-circle 'safe-local-variable 'booleanp)
 
 (defcustom byte-compile-dynamic-docstrings t
@@ -290,10 +259,14 @@ This option is enabled by default because it reduces Emacs memory usage."
   :type 'boolean)
 ;;;###autoload(put 'byte-compile-dynamic-docstrings 'safe-local-variable 'booleanp)
 
+(defconst byte-compile-log-buffer "*Compile-Log*"
+  "Name of the byte-compiler's log buffer.")
+
 (defcustom byte-optimize-log nil
-  "If true, the byte-compiler will log its optimizations into *Compile-Log*.
+  "If non-nil, the byte-compiler will log its optimizations.
 If this is 'source, then only source-level optimizations will be logged.
-If it is 'byte, then only byte-level optimizations will be logged."
+If it is 'byte, then only byte-level optimizations will be logged.
+The information is logged to `byte-compile-log-buffer'."
   :group 'bytecomp
   :type '(choice (const :tag "none" nil)
                 (const :tag "all" t)
@@ -339,21 +312,12 @@ suppress.  For example, (not mapcar) will suppress warnings about mapcar."
                 (set :menu-tag "Some"
                       ,@(mapcar (lambda (x) `(const ,x))
                                 byte-compile-warning-types))))
-;;;###autoload(put 'byte-compile-warnings 'safe-local-variable 'byte-compile-warnings-safe-p)
 
 ;;;###autoload
-(defun byte-compile-warnings-safe-p (x)
-  "Return non-nil if X is valid as a value of `byte-compile-warnings'."
-  (or (booleanp x)
-      (and (listp x)
-           (if (eq (car x) 'not) (setq x (cdr x))
-             t)
-          (equal (mapcar
-                  (lambda (e)
-                    (when (memq e byte-compile-warning-types)
-                      e))
-                  x)
-                 x))))
+(put 'byte-compile-warnings 'safe-local-variable
+     (lambda (v)
+       (or (symbolp v)
+           (null (delq nil (mapcar (lambda (x) (not (symbolp x))) v))))))
 
 (defun byte-compile-warning-enabled-p (warning)
   "Return non-nil if WARNING is enabled, according to `byte-compile-warnings'."
@@ -443,23 +407,15 @@ 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 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.")
+(setq debug-on-error t)
 
 (defvar byte-compile-constants nil
   "List of all constants encountered during compilation of this form.")
 (defvar byte-compile-variables nil
   "List of all variables encountered during compilation of this form.")
 (defvar byte-compile-bound-variables nil
-  "List of variables bound in the context of the current form.
+  "List of dynamic variables bound in the context of the current form.
 This list lives partly on the stack.")
 (defvar byte-compile-const-variables nil
   "List of variables declared as constants during compilation of this file.")
@@ -472,14 +428,16 @@ This list lives partly on the stack.")
   '(
 ;;     (byte-compiler-options . (lambda (&rest forms)
 ;;                            (apply 'byte-compiler-options-handler forms)))
+    (declare-function . byte-compile-macroexpand-declare-function)
     (eval-when-compile . (lambda (&rest body)
                           (list
                            'quote
+                            ;; FIXME: is that right in lexbind code?
                            (byte-compile-eval
-                             (byte-compile-top-level
-                              (macroexpand-all
-                               (cons 'progn body)
-                               byte-compile-initial-macro-environment))))))
+                             (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))))
@@ -515,10 +473,6 @@ 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
@@ -638,7 +592,6 @@ Each element is (INDEX . VALUE)")
 (byte-defop 114  0 byte-save-current-buffer
   "To make a binding to record the current buffer")
 (byte-defop 115  0 byte-set-mark-OBSOLETE)
-(byte-defop 116  1 byte-interactive-p)
 
 ;; These ops are new to v19
 (byte-defop 117  0 byte-forward-char)
@@ -674,8 +627,6 @@ otherwise pop it")
 
 (byte-defop 138  0 byte-save-excursion
   "to make a binding to record the buffer, point and mark")
-(byte-defop 139  0 byte-save-window-excursion
-  "to make a binding to record entire window configuration")
 (byte-defop 140  0 byte-save-restriction
   "to make a binding to record the current buffer clipping restrictions")
 (byte-defop 141 -1 byte-catch
@@ -691,13 +642,13 @@ otherwise pop it")
 ;; Takes, on stack, the buffer name.
 ;; Binds standard-output and does some other things.
 ;; Returns with temp buffer on the stack in place of buffer name.
-(byte-defop 144  0 byte-temp-output-buffer-setup)
+;; (byte-defop 144  0 byte-temp-output-buffer-setup)
 
 ;; For exit from with-output-to-temp-buffer.
 ;; Expects the temp buffer on the stack underneath value to return.
 ;; Pops them both, then pushes the value back on.
 ;; Unbinds standard-output and makes the temp buffer visible.
-(byte-defop 145 -1 byte-temp-output-buffer-show)
+;; (byte-defop 145 -1 byte-temp-output-buffer-show)
 
 ;; these ops are new to v19
 
@@ -737,8 +688,6 @@ otherwise pop it")
 
 (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
@@ -806,9 +755,10 @@ BYTES and PC are updated after evaluating all the arguments."
        (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))))
+                           `(progn (assert (<= 0 ,(car 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.
@@ -827,68 +777,78 @@ CONST2 may be evaulated multiple times."
     (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))
-           ((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
-            (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))))))
+      (cond
+       ((not (symbolp op))
+        (error "Non-symbolic opcode `%s'" op))
+       ((eq op 'TAG)
+        (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 opcode
+              (if (eq op 'byte-discardN-preserve-tos)
+                  ;; byte-discardN-preserve-tos is a pseudo op, which
+                  ;; is actually the same as byte-discardN
+                  ;; with a modified argument.
+                  byte-discardN
+                (symbol-value op)))
+        (cond ((memq op byte-goto-ops)
+               ;; goto
+               (byte-compile-push-bytecodes opcode nil (cdr off) bytes pc)
+               (push bytes patchlist)) 
+              ((or (and (consp off)
+                        ;; Variable or constant reference
+                        (progn
+                          (setq off (cdr off))
+                          (eq op 'byte-constant)))
+                   (and (eq op 'byte-constant) ;; 'byte-closed-var
+                        (integerp off)))
+               ;; 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 weird 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))
+              ((and (eq opcode byte-stack-ref) (eq off 0))
+               ;; (stack-ref 0) is really just another name for `dup'.
+               (debug)                 ;FIXME: When would this happen?
+               (byte-compile-push-bytecodes byte-dup 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)))
 
@@ -1002,7 +962,7 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
 
 ;; Log something that isn't a warning.
 (defun byte-compile-log-1 (string)
-  (with-current-buffer "*Compile-Log*"
+  (with-current-buffer byte-compile-log-buffer
     (let ((inhibit-read-only t))
       (goto-char (point-max))
       (byte-compile-warning-prefix nil nil)
@@ -1104,19 +1064,19 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
 ;; This no-op function is used as the value of warning-series
 ;; to tell inner calls to displaying-byte-compile-warnings
 ;; not to bind warning-series.
-(defun byte-compile-warning-series (&rest ignore)
+(defun byte-compile-warning-series (&rest _ignore)
   nil)
 
 ;; (compile-mode) will cause this to be loaded.
 (declare-function compilation-forget-errors "compile" ())
 
-;; Log the start of a file in *Compile-Log*, and mark it as done.
+;; Log the start of a file in `byte-compile-log-buffer', and mark it as done.
 ;; Return the position of the start of the page in the log buffer.
 ;; But do nothing in batch mode.
 (defun byte-compile-log-file ()
   (and (not (equal byte-compile-current-file byte-compile-last-logged-file))
        (not noninteractive)
-       (with-current-buffer (get-buffer-create "*Compile-Log*")
+       (with-current-buffer (get-buffer-create byte-compile-log-buffer)
         (goto-char (point-max))
         (let* ((inhibit-read-only t)
                (dir (and byte-compile-current-file
@@ -1147,14 +1107,14 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
           (compilation-forget-errors)
           pt))))
 
-;; Log a message STRING in *Compile-Log*.
+;; Log a message STRING in `byte-compile-log-buffer'.
 ;; Also log the current function and file if not already done.
 (defun byte-compile-log-warning (string &optional fill level)
   (let ((warning-prefix-function 'byte-compile-warning-prefix)
        (warning-type-format "")
        (warning-fill-prefix (if fill "    "))
        (inhibit-read-only t))
-    (display-warning 'bytecomp string level "*Compile-Log*")))
+    (display-warning 'bytecomp string level byte-compile-log-buffer)))
 
 (defun byte-compile-warn (format &rest args)
   "Issue a byte compiler warning; use (format FORMAT ARGS...) for message."
@@ -1186,13 +1146,6 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
   (byte-compile-log-warning
    (error-message-string error-info)
    nil :error))
-
-;;; Used by make-obsolete.
-(defun byte-compile-obsolete (form)
-  (byte-compile-set-symbol-position (car form))
-  (byte-compile-warn-obsolete (car form))
-  (funcall (or (cadr (get (car form) 'byte-obsolete-info)) ; handler
-              'byte-compile-normal-call) form))
 \f
 ;;; sanity-checking arglists
 
@@ -1232,22 +1185,28 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
              (t fn)))))))
 
 (defun byte-compile-arglist-signature (arglist)
-  (let ((args 0)
-       opts
-       restp)
-    (while arglist
-      (cond ((eq (car arglist) '&optional)
-            (or opts (setq opts 0)))
-           ((eq (car arglist) '&rest)
-            (if (cdr arglist)
-                (setq restp t
-                      arglist nil)))
-           (t
-            (if opts
-                (setq opts (1+ opts))
+  (if (integerp arglist)
+      ;; New style byte-code arglist.
+      (cons (logand arglist 127)             ;Mandatory.
+            (if (zerop (logand arglist 128)) ;No &rest.
+                (lsh arglist -8)))           ;Nonrest.
+    ;; Old style byte-code, or interpreted function.
+    (let ((args 0)
+          opts
+          restp)
+      (while arglist
+        (cond ((eq (car arglist) '&optional)
+               (or opts (setq opts 0)))
+              ((eq (car arglist) '&rest)
+               (if (cdr arglist)
+                   (setq restp t
+                         arglist nil)))
+              (t
+               (if opts
+                   (setq opts (1+ opts))
                 (setq args (1+ args)))))
-      (setq arglist (cdr arglist)))
-    (cons args (if restp nil (if opts (+ args opts) args)))))
+        (setq arglist (cdr arglist)))
+      (cons args (if restp nil (if opts (+ args opts) args))))))
 
 
 (defun byte-compile-arglist-signatures-congruent-p (old new)
@@ -1374,50 +1333,54 @@ extra args."
 ;; Warn if the function or macro is being redefined with a different
 ;; number of arguments.
 (defun byte-compile-arglist-warn (form macrop)
-  (let ((old (byte-compile-fdefinition (nth 1 form) macrop)))
+  (let* ((name (nth 1 form))
+         (old (byte-compile-fdefinition name macrop)))
     (if (and old (not (eq old t)))
        (progn
          (and (eq 'macro (car-safe old))
               (eq 'lambda (car-safe (cdr-safe old)))
               (setq old (cdr old)))
          (let ((sig1 (byte-compile-arglist-signature
-                      (if (eq 'lambda (car-safe old))
-                          (nth 1 old)
-                        (if (byte-code-function-p old)
-                            (aref old 0)
-                          '(&rest def)))))
+                      (pcase old
+                         (`(lambda ,args . ,_) args)
+                         (`(closure ,_ ,_ ,args . ,_) args)
+                         ((pred byte-code-function-p) (aref old 0))
+                         (t '(&rest def)))))
                (sig2 (byte-compile-arglist-signature (nth 2 form))))
            (unless (byte-compile-arglist-signatures-congruent-p sig1 sig2)
-             (byte-compile-set-symbol-position (nth 1 form))
+             (byte-compile-set-symbol-position name)
              (byte-compile-warn
               "%s %s used to take %s %s, now takes %s"
               (if (eq (car form) 'defun) "function" "macro")
-              (nth 1 form)
+              name
               (byte-compile-arglist-signature-string sig1)
               (if (equal sig1 '(1 . 1)) "argument" "arguments")
               (byte-compile-arglist-signature-string sig2)))))
       ;; This is the first definition.  See if previous calls are compatible.
-      (let ((calls (assq (nth 1 form) byte-compile-unresolved-functions))
+      (let ((calls (assq name byte-compile-unresolved-functions))
            nums sig min max)
-       (if calls
-           (progn
-             (setq sig (byte-compile-arglist-signature (nth 2 form))
-                   nums (sort (copy-sequence (cdr calls)) (function <))
-                   min (car nums)
-                   max (car (nreverse nums)))
-             (when (or (< min (car sig))
-                     (and (cdr sig) (> max (cdr sig))))
-               (byte-compile-set-symbol-position (nth 1 form))
-               (byte-compile-warn
-                "%s being defined to take %s%s, but was previously called with %s"
-                (nth 1 form)
-                (byte-compile-arglist-signature-string sig)
-                (if (equal sig '(1 . 1)) " arg" " args")
-                (byte-compile-arglist-signature-string (cons min max))))
-
-             (setq byte-compile-unresolved-functions
-                   (delq calls byte-compile-unresolved-functions)))))
-      )))
+       (when calls
+          (when (and (symbolp name)
+                     (eq (get name 'byte-optimizer)
+                         'byte-compile-inline-expand))
+            (byte-compile-warn "defsubst `%s' was used before it was defined"
+                      name))
+          (setq sig (byte-compile-arglist-signature (nth 2 form))
+                nums (sort (copy-sequence (cdr calls)) (function <))
+                min (car nums)
+                max (car (nreverse nums)))
+          (when (or (< min (car sig))
+                    (and (cdr sig) (> max (cdr sig))))
+            (byte-compile-set-symbol-position name)
+            (byte-compile-warn
+             "%s being defined to take %s%s, but was previously called with %s"
+             name
+             (byte-compile-arglist-signature-string sig)
+             (if (equal sig '(1 . 1)) " arg" " args")
+             (byte-compile-arglist-signature-string (cons min max))))
+
+          (setq byte-compile-unresolved-functions
+                (delq calls byte-compile-unresolved-functions)))))))
 
 (defvar byte-compile-cl-functions nil
   "List of functions defined in CL.")
@@ -1453,14 +1416,7 @@ extra args."
                          ;; but such warnings are never useful,
                          ;; so don't warn about them.
                          macroexpand cl-macroexpand-all
-                         cl-compiling-file)))
-            ;; Avoid warnings for things which are safe because they
-            ;; have suitable compiler macros, but those aren't
-            ;; expanded at this stage.  There should probably be more
-            ;; here than caaar and friends.
-            (not (and (eq (get func 'byte-compile)
-                          'cl-byte-compile-compiler-macro)
-                      (string-match "\\`c[ad]+r\\'" (symbol-name func)))))
+                         cl-compiling-file))))
        (byte-compile-warn "function `%s' from cl package called at runtime"
                           func)))
   form)
@@ -1523,7 +1479,7 @@ symbol itself."
       (if any-value
          (or (memq symbol byte-compile-const-variables)
              ;; FIXME: We should provide a less intrusive way to find out
-             ;; is a variable is "constant".
+             ;; if a variable is "constant".
              (and (boundp symbol)
                   (condition-case nil
                       (progn (set symbol (symbol-value symbol)) nil)
@@ -1536,6 +1492,7 @@ symbol itself."
         ((byte-compile-const-symbol-p ,form))))
 
 (defmacro byte-compile-close-variables (&rest body)
+  (declare (debug t))
   (cons 'let
        (cons '(;;
                ;; Close over these variables to encapsulate the
@@ -1566,11 +1523,12 @@ symbol itself."
              body)))
 
 (defmacro displaying-byte-compile-warnings (&rest body)
+  (declare (debug t))
   `(let* ((--displaying-byte-compile-warnings-fn (lambda () ,@body))
          (warning-series-started
           (and (markerp warning-series)
                (eq (marker-buffer warning-series)
-                   (get-buffer "*Compile-Log*")))))
+                   (get-buffer byte-compile-log-buffer)))))
      (byte-compile-find-cl-functions)
      (if (or (eq warning-series 'byte-compile-warning-series)
             warning-series-started)
@@ -1632,7 +1590,7 @@ that already has a `.elc' file."
       nil
     (save-some-buffers)
     (force-mode-line-update))
-  (with-current-buffer (get-buffer-create "*Compile-Log*")
+  (with-current-buffer (get-buffer-create byte-compile-log-buffer)
     (setq default-directory (expand-file-name bytecomp-directory))
     ;; compilation-mode copies value of default-directory.
     (unless (eq major-mode 'compilation-mode)
@@ -1649,7 +1607,7 @@ that already has a `.elc' file."
         (setq bytecomp-directory (car bytecomp-directories))
         (message "Checking %s..." bytecomp-directory)
         (let ((bytecomp-files (directory-files bytecomp-directory))
-              bytecomp-source bytecomp-dest)
+              bytecomp-source)
           (dolist (bytecomp-file bytecomp-files)
             (setq bytecomp-source
                    (expand-file-name bytecomp-file bytecomp-directory))
@@ -1669,23 +1627,10 @@ that already has a `.elc' file."
                        (not (auto-save-file-name-p bytecomp-source))
                        (not (string-equal dir-locals-file
                                           (file-name-nondirectory
-                                           bytecomp-source)))
-                       (setq bytecomp-dest
-                              (byte-compile-dest-file bytecomp-source))
-                       (if (file-exists-p bytecomp-dest)
-                           ;; File was already compiled.
-                           (or bytecomp-force
-                                (file-newer-than-file-p bytecomp-source
-                                                        bytecomp-dest))
-                         ;; No compiled file exists yet.
-                         (and bytecomp-arg
-                              (or (eq 0 bytecomp-arg)
-                                  (y-or-n-p (concat "Compile "
-                                                     bytecomp-source "? "))))))
-                  (progn (if (and noninteractive (not byte-compile-verbose))
-                             (message "Compiling %s..." bytecomp-source))
-                         (let ((bytecomp-res (byte-compile-file
-                                               bytecomp-source)))
+                                           bytecomp-source))))
+                  (progn (let ((bytecomp-res (byte-recompile-file
+                                               bytecomp-source
+                                               bytecomp-force bytecomp-arg)))
                            (cond ((eq bytecomp-res 'no-byte-compile)
                                   (setq skip-count (1+ skip-count)))
                                  ((eq bytecomp-res t)
@@ -1710,9 +1655,63 @@ that already has a `.elc' file."
   "Non-nil to prevent byte-compiling of Emacs Lisp code.
 This is normally set in local file variables at the end of the elisp file:
 
-;; Local Variables:\n;; no-byte-compile: t\n;; End: ")
+\;; Local Variables:\n;; no-byte-compile: t\n;; End: ") ;Backslash for compile-main.
 ;;;###autoload(put 'no-byte-compile 'safe-local-variable 'booleanp)
 
+(defun byte-recompile-file (bytecomp-filename &optional bytecomp-force bytecomp-arg load)
+  "Recompile BYTECOMP-FILENAME file if it needs recompilation.
+This happens when its `.elc' file is older than itself.
+
+If the `.elc' file exists and is up-to-date, normally this
+function *does not* compile BYTECOMP-FILENAME. However, if the
+prefix argument BYTECOMP-FORCE is set, that means do compile
+BYTECOMP-FILENAME even if the destination already exists and is
+up-to-date.
+
+If the `.elc' file does not exist, normally this function *does
+not* compile BYTECOMP-FILENAME. If BYTECOMP-ARG is 0, that means
+compile the file even if it has never been compiled before.
+A nonzero BYTECOMP-ARG means ask the user.
+
+If LOAD is set, `load' the file after compiling.
+
+The value returned is the value returned by `byte-compile-file',
+or 'no-byte-compile if the file did not need recompilation."
+  (interactive
+      (let ((bytecomp-file buffer-file-name)
+        (bytecomp-file-name nil)
+        (bytecomp-file-dir nil))
+     (and bytecomp-file
+         (eq (cdr (assq 'major-mode (buffer-local-variables)))
+             'emacs-lisp-mode)
+         (setq bytecomp-file-name (file-name-nondirectory bytecomp-file)
+               bytecomp-file-dir (file-name-directory bytecomp-file)))
+     (list (read-file-name (if current-prefix-arg
+                              "Byte compile file: "
+                            "Byte recompile file: ")
+                          bytecomp-file-dir bytecomp-file-name nil)
+          current-prefix-arg)))
+  (let ((bytecomp-dest
+         (byte-compile-dest-file bytecomp-filename))
+        ;; Expand now so we get the current buffer's defaults
+        (bytecomp-filename (expand-file-name bytecomp-filename)))
+    (if (if (file-exists-p bytecomp-dest)
+            ;; File was already compiled
+            ;; Compile if forced to, or filename newer
+            (or bytecomp-force
+                (file-newer-than-file-p bytecomp-filename
+                                         bytecomp-dest))
+          (and bytecomp-arg
+               (or (eq 0 bytecomp-arg)
+                   (y-or-n-p (concat "Compile "
+                                     bytecomp-filename "? ")))))
+        (progn
+          (if (and noninteractive (not byte-compile-verbose))
+              (message "Compiling %s..." bytecomp-filename))
+          (byte-compile-file bytecomp-filename load))
+      (when load (load bytecomp-filename))
+      'no-byte-compile)))
+
 ;;;###autoload
 (defun byte-compile-file (bytecomp-filename &optional load)
   "Compile a file of Lisp code named BYTECOMP-FILENAME into a file of byte code.
@@ -1726,8 +1725,7 @@ The value is non-nil if there were no errors, nil if errors."
         (bytecomp-file-name nil)
         (bytecomp-file-dir nil))
      (and bytecomp-file
-         (eq (cdr (assq 'major-mode (buffer-local-variables)))
-             'emacs-lisp-mode)
+         (derived-mode-p 'emacs-lisp-mode)
          (setq bytecomp-file-name (file-name-nondirectory bytecomp-file)
                bytecomp-file-dir (file-name-directory bytecomp-file)))
      (list (read-file-name (if current-prefix-arg
@@ -1805,7 +1803,7 @@ The value is non-nil if there were no errors, nil if errors."
       ;; within byte-compile-from-buffer lingers in that buffer.
       (setq output-buffer
            (save-current-buffer
-             (byte-compile-from-buffer input-buffer bytecomp-filename)))
+             (byte-compile-from-buffer input-buffer)))
       (if byte-compiler-error-flag
          nil
        (when byte-compile-verbose
@@ -1882,9 +1880,11 @@ With argument ARG, insert value in current buffer after the form."
             (insert "\n"))
            ((message "%s" (prin1-to-string value)))))))
 
+;; Dynamically bound in byte-compile-from-buffer.
+;; NB also used in cl.el and cl-macs.el.
+(defvar bytecomp-outbuffer)
 
-(defun byte-compile-from-buffer (bytecomp-inbuffer &optional bytecomp-filename)
-  ;; Filename is used for the loading-into-Emacs-18 error message.
+(defun byte-compile-from-buffer (bytecomp-inbuffer)
   (let (bytecomp-outbuffer
        (byte-compile-current-buffer bytecomp-inbuffer)
        (byte-compile-read-position nil)
@@ -1921,8 +1921,9 @@ With argument ARG, insert value in current buffer after the form."
        (setq case-fold-search nil))
      (displaying-byte-compile-warnings
       (with-current-buffer bytecomp-inbuffer
-       (and bytecomp-filename
-            (byte-compile-insert-header bytecomp-filename bytecomp-outbuffer))
+       (and byte-compile-current-file
+            (byte-compile-insert-header byte-compile-current-file
+                                         bytecomp-outbuffer))
        (goto-char (point-min))
        ;; Should we always do this?  When calling multiple files, it
        ;; would be useful to delay this warning until all have been
@@ -1945,7 +1946,7 @@ With argument ARG, insert value in current buffer after the form."
               (byte-compile-warn "!! The file uses old-style backquotes !!
 This functionality has been obsolete for more than 10 years already
 and will be removed soon.  See (elisp)Backquote in the manual."))
-           (byte-compile-file-form form)))
+           (byte-compile-toplevel-file-form form)))
        ;; Compile pending forms at end of file.
        (byte-compile-flush-pending)
        ;; Make warnings about unresolved functions
@@ -1954,9 +1955,9 @@ and will be removed soon.  See (elisp)Backquote in the manual."))
        (byte-compile-warn-about-unresolved-functions))
       ;; Fix up the header at the front of the output
       ;; if the buffer contains multibyte characters.
-      (and bytecomp-filename
+      (and byte-compile-current-file
           (with-current-buffer bytecomp-outbuffer
-            (byte-compile-fix-header bytecomp-filename)))))
+            (byte-compile-fix-header byte-compile-current-file)))))
     bytecomp-outbuffer))
 
 (defun byte-compile-fix-header (filename)
@@ -2045,10 +2046,6 @@ Call from the source buffer."
        ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"
        ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n\n"))))
 
-;; Dynamically bound in byte-compile-from-buffer.
-;; NB also used in cl.el and cl-macs.el.
-(defvar bytecomp-outbuffer)
-
 (defun byte-compile-output-file-form (form)
   ;; writes the given form to the output buffer, being careful of docstrings
   ;; in defun, defmacro, defvar, defvaralias, defconst, autoload and
@@ -2056,8 +2053,8 @@ Call from the source buffer."
   ;; defalias calls are output directly by byte-compile-file-form-defmumble;
   ;; it does not pay to first build the defalias in defmumble and then parse
   ;; it here.
-  (if (and (memq (car-safe form) '(defun defmacro defvar defvaralias defconst autoload
-                                  custom-declare-variable))
+  (if (and (memq (car-safe form) '(defun defmacro defvar defvaralias defconst
+                                    autoload custom-declare-variable))
           (stringp (nth 3 form)))
       (byte-compile-output-docform nil nil '("\n(" 3 ")") form nil
                                   (memq (car form)
@@ -2075,6 +2072,7 @@ Call from the source buffer."
       nil)))
 
 (defvar print-gensym-alist)            ;Used before print-circle existed.
+(defvar for-effect)
 
 (defun byte-compile-output-docform (preface name info form specindex quoted)
   "Print a form with a doc string.  INFO is (prefix doc-index postfix).
@@ -2139,9 +2137,9 @@ list that represents a doc string reference.
                         ;; to objects already output
                         ;; (for instance, gensyms in the arg list).
                         (let (non-nil)
-                          (dotimes (i (length print-number-table))
-                            (if (aref print-number-table i)
-                                (setq non-nil t)))
+                          (when (hash-table-p print-number-table)
+                            (maphash (lambda (_k v) (if v (setq non-nil t)))
+                                     print-number-table))
                           (not non-nil)))
                    ;; Output the byte code and constants specially
                    ;; for lazy dynamic loading.
@@ -2197,13 +2195,19 @@ list that represents a doc string reference.
              byte-compile-maxdepth 0
              byte-compile-output nil))))
 
-(defun byte-compile-file-form (form)
-  (let ((byte-compile-current-form nil)        ; close over this for warnings.
-       bytecomp-handler)
+;; byte-hunk-handlers cannot call this!
+(defun byte-compile-toplevel-file-form (form)
+  (let ((byte-compile-current-form nil))       ; close over this for warnings.
     (setq form (macroexpand-all form byte-compile-macro-environment))
-    (cond ((not (consp form))
-          (byte-compile-keep-pending form))
-         ((and (symbolp (car form))
+    (if lexical-binding
+        (setq form (cconv-closure-convert form)))
+    (byte-compile-file-form form)))
+
+;; byte-hunk-handlers can call this.
+(defun byte-compile-file-form (form)
+  (let (bytecomp-handler)
+    (cond ((and (consp form)
+                (symbolp (car form))
                (setq bytecomp-handler (get (car form) 'byte-hunk-handler)))
           (cond ((setq form (funcall bytecomp-handler form))
                  (byte-compile-flush-pending)
@@ -2215,16 +2219,6 @@ list that represents a doc string reference.
 ;; so make-docfile can recognise them.  Most other things can be output
 ;; as byte-code.
 
-(put 'defsubst 'byte-hunk-handler 'byte-compile-file-form-defsubst)
-(defun byte-compile-file-form-defsubst (form)
-  (when (assq (nth 1 form) byte-compile-unresolved-functions)
-    (setq byte-compile-current-form (nth 1 form))
-    (byte-compile-warn "defsubst `%s' was used before it was defined"
-                      (nth 1 form)))
-  (byte-compile-file-form form)
-  ;; Return nil so the form is not output twice.
-  nil)
-
 (put 'autoload 'byte-hunk-handler 'byte-compile-file-form-autoload)
 (defun byte-compile-file-form-autoload (form)
   (and (let ((form form))
@@ -2399,8 +2393,8 @@ by side-effects."
     (if (byte-compile-warning-enabled-p 'redefine)
        (byte-compile-arglist-warn form macrop))
     (if byte-compile-verbose
-       ;; bytecomp-filename is from byte-compile-from-buffer.
-       (message "Compiling %s... (%s)" (or bytecomp-filename "") (nth 1 form)))
+       (message "Compiling %s... (%s)"
+                 (or byte-compile-current-file "") (nth 1 form)))
     (cond (bytecomp-that-one
           (if (and (byte-compile-warning-enabled-p 'redefine)
                    ;; don't warn when compiling the stubs in byte-run...
@@ -2547,12 +2541,14 @@ 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))
+            ;; Expand macros.
+             (setq fun
+                   (macroexpand-all fun
+                                    byte-compile-initial-macro-environment))
+             (if lexical-binding
+                 (setq fun (cconv-closure-convert fun)))
+            ;; Get rid of the `function' quote added by the `lambda' macro.
+            (if (eq (car-safe fun) 'function) (setq fun (cadr fun)))
             (setq fun (if macro
                           (cons 'macro (byte-compile-lambda fun))
                         (byte-compile-lambda fun)))
@@ -2577,6 +2573,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
    ;; b-c-lambda didn't produce a compiled-function, so it's either a trivial
    ;; function, or this is Emacs 18, or generate-emacs19-bytecodes is off.
    ((let (tmp)
+      ;; FIXME: can this happen?
       (if (and (setq tmp (assq 'byte-code (cdr-safe (cdr fun))))
               (null (cdr (memq tmp fun))))
          ;; Generate a make-byte-code call.
@@ -2601,7 +2598,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
        (list 'quote fun))))))
 
 ;; Turn a function into an ordinary lambda.  Needed for v18 files.
-(defun byte-compile-byte-code-unmake (function)
+(defun byte-compile-byte-code-unmake (function) ;FIXME: what is it?
   (if (consp function)
       function;;It already is a lambda.
     (setq function (append function nil)) ; turn it into a list
@@ -2640,7 +2637,43 @@ 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")
+(defun byte-compile-arglist-vars (arglist)
+  "Return a list of the variables in the lambda argument list ARGLIST."
+  (remq '&rest (remq '&optional arglist)))
+
+(defun byte-compile-make-lambda-lexenv (form)
+  "Return a new lexical environment for a lambda expression FORM."
+  ;; See if this is a closure or not
+  (let ((args (byte-compile-arglist-vars (cadr form))))
+    (let ((lexenv nil))
+      ;; Fill in the initial stack contents
+      (let ((stackpos 0))
+       ;; Add entries for each argument
+       (dolist (arg args)
+         (push (cons arg stackpos) lexenv)
+         (setq stackpos (1+ stackpos)))
+       ;; Return the new lexical environment
+       lexenv))))
+
+(defun byte-compile-make-args-desc (arglist)
+  (let ((mandatory 0)
+        nonrest (rest 0))
+    (while (and arglist (not (memq (car arglist) '(&optional &rest))))
+      (setq mandatory (1+ mandatory))
+      (setq arglist (cdr arglist)))
+    (setq nonrest mandatory)
+    (when (eq (car arglist) '&optional)
+      (setq arglist (cdr arglist))
+      (while (and arglist (not (eq (car arglist) '&rest)))
+        (setq nonrest (1+ nonrest))
+        (setq arglist (cdr arglist))))
+    (when arglist
+      (setq rest 1))
+    (if (> mandatory 127)
+        (byte-compile-report-error "Too many (>127) mandatory arguments")
+      (logior mandatory
+              (lsh nonrest 8)
+              (lsh rest 7)))))
 
 ;; Byte-compile a lambda-expression and return a valid function.
 ;; The value is usually a compiled function but may be the original
@@ -2649,7 +2682,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
 ;; of the list FUN and `byte-compile-set-symbol-position' is not called.
 ;; Use this feature to avoid calling `byte-compile-set-symbol-position'
 ;; for symbols generated by the byte compiler itself.
-(defun byte-compile-lambda (bytecomp-fun &optional add-lambda)
+(defun byte-compile-lambda (bytecomp-fun &optional add-lambda reserved-csts)
   (if add-lambda
       (setq bytecomp-fun (cons 'lambda bytecomp-fun))
     (unless (eq 'lambda (car-safe bytecomp-fun))
@@ -2658,17 +2691,16 @@ If FORM is a lambda or a macro, byte-compile it as a function."
   (byte-compile-check-lambda-list (nth 1 bytecomp-fun))
   (let* ((bytecomp-arglist (nth 1 bytecomp-fun))
         (byte-compile-bound-variables
-         (nconc (and (byte-compile-warning-enabled-p 'free-vars)
-                     (delq '&rest
-                           (delq '&optional (copy-sequence bytecomp-arglist))))
-                byte-compile-bound-variables))
+         (append (and (not lexical-binding)
+                       (byte-compile-arglist-vars bytecomp-arglist))
+                  byte-compile-bound-variables))
         (bytecomp-body (cdr (cdr bytecomp-fun)))
         (bytecomp-doc (if (stringp (car bytecomp-body))
-                 (prog1 (car bytecomp-body)
-                   ;; Discard the doc string
-                   ;; unless it is the last element of the body.
-                   (if (cdr bytecomp-body)
-                       (setq bytecomp-body (cdr bytecomp-body))))))
+                           (prog1 (car bytecomp-body)
+                             ;; Discard the doc string
+                             ;; unless it is the last element of the body.
+                             (if (cdr bytecomp-body)
+                                 (setq bytecomp-body (cdr bytecomp-body))))))
         (bytecomp-int (assq 'interactive bytecomp-body)))
     ;; Process the interactive spec.
     (when bytecomp-int
@@ -2684,57 +2716,52 @@ If FORM is a lambda or a macro, byte-compile it as a function."
             ;; compile it, because `call-interactively' looks at the
             ;; args of `list'.  Actually, compile it to get warnings,
             ;; but don't use the result.
-            (let ((form (nth 1 bytecomp-int)))
+            (let* ((form (nth 1 bytecomp-int))
+                    (newform (byte-compile-top-level form)))
               (while (memq (car-safe form) '(let let* progn save-excursion))
                 (while (consp (cdr form))
                   (setq form (cdr form)))
                 (setq form (car form)))
-              (if (eq (car-safe form) 'list)
-                  (byte-compile-top-level (nth 1 bytecomp-int))
-                (setq bytecomp-int (list 'interactive
-                                (byte-compile-top-level
-                                 (nth 1 bytecomp-int)))))))
+              (if (and (eq (car-safe form) 'list)
+                        ;; The spec is evaled in callint.c in dynamic-scoping
+                        ;; mode, so just leaving the form unchanged would mean
+                        ;; it won't be eval'd in the right mode.
+                        (not lexical-binding))
+                  nil
+                (setq bytecomp-int `(interactive ,newform)))))
            ((cdr bytecomp-int)
             (byte-compile-warn "malformed interactive spec: %s"
                                (prin1-to-string bytecomp-int)))))
     ;; Process the body.
-    (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)))
+    (let ((compiled
+           (byte-compile-top-level (cons 'progn bytecomp-body) nil 'lambda
+                                   ;; If doing lexical binding, push a new
+                                   ;; lexical environment containing just the
+                                   ;; args (since lambda expressions should be
+                                   ;; closed by now).
+                                   (and lexical-binding
+                                        (byte-compile-make-lambda-lexenv
+                                         bytecomp-fun))
+                                   reserved-csts)))
       ;; Build the actual byte-coded function.
       (if (eq 'byte-code (car-safe compiled))
-         (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))
+          (apply 'make-byte-code
+                 (if lexical-binding
+                     (byte-compile-make-args-desc bytecomp-arglist)
+                   bytecomp-arglist)
+                 (append
+                  ;; byte-string, constants-vector, stack depth
+                  (cdr compiled)
+                  ;; optionally, the doc string.
+                  (cond (lexical-binding
+                         (require 'help-fns)
+                         (list (help-add-fundoc-usage
+                                bytecomp-doc bytecomp-arglist)))
+                        ((or bytecomp-doc bytecomp-int)
+                         (list bytecomp-doc)))
+                  ;; optionally, the interactive spec.
+                  (if bytecomp-int
+                      (list (nth 1 bytecomp-int)))))
        (setq compiled
              (nconc (if bytecomp-int (list bytecomp-int))
                     (cond ((eq (car-safe compiled) 'progn) (cdr compiled))
@@ -2745,25 +2772,12 @@ 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))))
+    ;; A simple lambda is just a constant.
+    (byte-compile-constant code)))
+
+(defvar byte-compile-reserved-constants 0)
 
 (defun byte-compile-constants-vector ()
   ;; Builds the constants-vector from the current variables and constants.
@@ -2773,7 +2787,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
   ;;   Next up to byte-constant-limit are constants, still with one-byte codes.
   ;;   Next variables again, to get 2-byte codes for variable lookup.
   ;;   The rest of the constants and variables need 3-byte byte-codes.
-  (let* ((i -1)
+  (let* ((i (1- byte-compile-reserved-constants))
         (rest (nreverse byte-compile-variables)) ; nreverse because the first
         (other (nreverse byte-compile-constants)) ; vars often are used most.
         ret tmp
@@ -2784,11 +2798,15 @@ If FORM is a lambda or a macro, byte-compile it as a function."
         limit)
     (while (or rest other)
       (setq limit (car limits))
-      (while (and rest (not (eq i limit)))
-       (if (setq tmp (assq (car (car rest)) ret))
-           (setcdr (car rest) (cdr tmp))
+      (while (and rest (< i limit))
+       (cond
+        ((numberp (car rest))
+         (assert (< (car rest) byte-compile-reserved-constants)))
+        ((setq tmp (assq (car (car rest)) ret))
+         (setcdr (car rest) (cdr tmp)))
+        (t
          (setcdr (car rest) (setq i (1+ i)))
-         (setq ret (cons (car rest) ret)))
+         (setq ret (cons (car rest) ret))))
        (setq rest (cdr rest)))
       (setq limits (cdr limits)
            rest (prog1 other
@@ -2797,17 +2815,21 @@ If FORM is a lambda or a macro, byte-compile it as a function."
 
 ;; Given an expression FORM, compile it and return an equivalent byte-code
 ;; expression (a call to the function byte-code).
-(defun byte-compile-top-level (form &optional for-effect output-type)
+(defun byte-compile-top-level (form &optional for-effect-arg output-type
+                                    lexenv reserved-csts)
   ;; OUTPUT-TYPE advises about how form is expected to be used:
   ;;   'eval or nil    -> a single form,
   ;;   'progn or t     -> a list of forms,
   ;;   'lambda         -> body of a lambda,
   ;;   'file           -> used at file-level.
-  (let ((byte-compile-constants nil)
+  (let ((for-effect for-effect-arg)
+        (byte-compile-constants nil)
        (byte-compile-variables nil)
        (byte-compile-tag-number 0)
        (byte-compile-depth 0)
        (byte-compile-maxdepth 0)
+        (byte-compile-lexical-environment lexenv)
+        (byte-compile-reserved-constants (or reserved-csts 0))
        (byte-compile-output nil))
     (if (memq byte-optimize '(t source))
        (setq form (byte-optimize-form form for-effect)))
@@ -2818,45 +2840,21 @@ If FORM is a lambda or a macro, byte-compile it as a function."
             (stringp (nth 1 form)) (vectorp (nth 2 form))
             (natnump (nth 3 form)))
        form
-      ;; Set up things for a lexically-bound function
+      ;; 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))))
+       ;; accordingly.
+        (setq byte-compile-depth (length byte-compile-lexical-environment))
        ;; If there are args, output a tag to record the initial
-       ;; stack-depth for the optimizer
+       ;; 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)))))
+         (byte-compile-out-tag (byte-compile-make-tag))))
       ;; 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
+(defun byte-compile-out-toplevel (&optional for-effect-arg output-type)
+  (if for-effect-arg
       ;; The stack is empty. Push a value to be returned from (byte-code ..).
       (if (eq (car (car byte-compile-output)) 'byte-discard)
          (setq byte-compile-output (cdr byte-compile-output))
@@ -2875,7 +2873,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
   (setq byte-compile-output (nreverse byte-compile-output))
   (if (memq byte-optimize '(t byte))
       (setq byte-compile-output
-           (byte-optimize-lapcode byte-compile-output for-effect)))
+           (byte-optimize-lapcode byte-compile-output)))
 
   ;; Decompile trivial functions:
   ;; only constants and variables, or a single funcall except in lambdas.
@@ -2892,6 +2890,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
   ;;   progn   -> as <<same-as-eval>> or (progn <<same-as-eval>> atom)
   ;;   file    -> as progn, but takes both quotes and atoms, and longer forms.
   (let (rest
+        (for-effect for-effect-arg)
        (maycall (not (eq output-type 'lambda))) ; t if we may make a funcall.
        tmp body)
     (cond
@@ -2941,26 +2940,26 @@ If FORM is a lambda or a macro, byte-compile it as a function."
      ((car body)))))
 
 ;; Given BYTECOMP-BODY, compile it and return a new body.
-(defun byte-compile-top-level-body (bytecomp-body &optional for-effect)
+(defun byte-compile-top-level-body (bytecomp-body &optional for-effect-arg)
   (setq bytecomp-body
-       (byte-compile-top-level (cons 'progn bytecomp-body) for-effect t))
+       (byte-compile-top-level (cons 'progn bytecomp-body) for-effect-arg t))
   (cond ((eq (car-safe bytecomp-body) 'progn)
         (cdr bytecomp-body))
        (bytecomp-body
         (list bytecomp-body))))
 
-(put 'declare-function 'byte-hunk-handler 'byte-compile-declare-function)
-(defun byte-compile-declare-function (form)
-  (push (cons (nth 1 form)
-              (if (and (> (length form) 3)
-                       (listp (nth 3 form)))
-                  (list 'declared (nth 3 form))
+;; Special macro-expander used during byte-compilation.
+(defun byte-compile-macroexpand-declare-function (fn file &rest args)
+  (push (cons fn
+              (if (and (consp args) (listp (car args)))
+                  (list 'declared (car args))
                 t))                     ; arglist not specified
         byte-compile-function-environment)
   ;; We are stating that it _will_ be defined at runtime.
   (setq byte-compile-noruntime-functions
-        (delq (nth 1 form) byte-compile-noruntime-functions))
-  nil)
+        (delq fn byte-compile-noruntime-functions))
+  ;; Delegate the rest to the normal macro definition.
+  (macroexpand `(declare-function ,fn ,file ,@args)))
 
 \f
 ;; This is the recursive entry point for compiling each subform of an
@@ -2974,57 +2973,68 @@ If FORM is a lambda or a macro, byte-compile it as a function."
 ;; byte-compile-form, or take extreme care to handle for-effect correctly.
 ;; (Use byte-compile-form-do-effect to reset the for-effect flag too.)
 ;;
-(defun byte-compile-form (form &optional for-effect)
-  (cond ((not (consp form))
-        (cond ((or (not (symbolp form)) (byte-compile-const-symbol-p form))
-               (when (symbolp form)
-                 (byte-compile-set-symbol-position form))
-               (byte-compile-constant form))
-              ((and for-effect byte-compile-delete-errors)
-               (when (symbolp form)
-                 (byte-compile-set-symbol-position form))
-               (setq for-effect nil))
-              (t
-               (byte-compile-variable-ref form))))
-       ((symbolp (car form))
-        (let* ((bytecomp-fn (car form))
-               (bytecomp-handler (get bytecomp-fn 'byte-compile)))
-          (when (byte-compile-const-symbol-p bytecomp-fn)
-            (byte-compile-warn "`%s' called as a function" bytecomp-fn))
-          (and (byte-compile-warning-enabled-p 'interactive-only)
-               (memq bytecomp-fn byte-compile-interactive-only-functions)
-               (byte-compile-warn "`%s' used from Lisp code\n\
+(defun byte-compile-form (form &optional for-effect-arg)
+  (let ((for-effect for-effect-arg))
+    (cond
+     ((not (consp form))
+      (cond ((or (not (symbolp form)) (byte-compile-const-symbol-p form))
+             (when (symbolp form)
+               (byte-compile-set-symbol-position form))
+             (byte-compile-constant form))
+            ((and for-effect byte-compile-delete-errors)
+             (when (symbolp form)
+               (byte-compile-set-symbol-position form))
+             (setq for-effect nil))
+            (t
+             (byte-compile-variable-ref form))))
+     ((symbolp (car form))
+      (let* ((bytecomp-fn (car form))
+             (bytecomp-handler (get bytecomp-fn 'byte-compile)))
+        (when (byte-compile-const-symbol-p bytecomp-fn)
+          (byte-compile-warn "`%s' called as a function" bytecomp-fn))
+        (and (byte-compile-warning-enabled-p 'interactive-only)
+             (memq bytecomp-fn byte-compile-interactive-only-functions)
+             (byte-compile-warn "`%s' used from Lisp code\n\
 That command is designed for interactive use only" bytecomp-fn))
-          (when (byte-compile-warning-enabled-p 'callargs)
-            (if (memq bytecomp-fn
-                      '(custom-declare-group custom-declare-variable
-                                             custom-declare-face))
-                  (byte-compile-nogroup-warn form))
-            (byte-compile-callargs-warn form))
-          (if (and bytecomp-handler
-                    ;; Make sure that function exists.  This is important
-                    ;; for CL compiler macros since the symbol may be
-                    ;; `cl-byte-compile-compiler-macro' but if CL isn't
-                    ;; loaded, this function doesn't exist.
-                    (or (not (memq bytecomp-handler
-                                  '(cl-byte-compile-compiler-macro)))
-                        (functionp bytecomp-handler)))
-               (funcall bytecomp-handler form)
-            (byte-compile-normal-call form))
-          (if (byte-compile-warning-enabled-p 'cl-functions)
-              (byte-compile-cl-warn form))))
-       ((and (or (byte-code-function-p (car form))
-                 (eq (car-safe (car form)) 'lambda))
-             ;; if the form comes out the same way it went in, that's
-             ;; because it was malformed, and we couldn't unfold it.
-             (not (eq form (setq form (byte-compile-unfold-lambda form)))))
-        (byte-compile-form form for-effect)
-        (setq for-effect nil))
-       ((byte-compile-normal-call form)))
-  (if for-effect
-      (byte-compile-discard)))
+        (if (and (fboundp (car form))
+                 (eq (car-safe (symbol-function (car form))) 'macro))
+            (byte-compile-report-error
+             (format "Forgot to expand macro %s" (car form))))
+        (if (and bytecomp-handler
+                 ;; Make sure that function exists.  This is important
+                 ;; for CL compiler macros since the symbol may be
+                 ;; `cl-byte-compile-compiler-macro' but if CL isn't
+                 ;; loaded, this function doesn't exist.
+                 (and (not (eq bytecomp-handler
+                               ;; Already handled by macroexpand-all.
+                               'cl-byte-compile-compiler-macro))
+                      (functionp bytecomp-handler)))
+            (funcall bytecomp-handler form)
+          (byte-compile-normal-call form))
+        (if (byte-compile-warning-enabled-p 'cl-functions)
+            (byte-compile-cl-warn form))))
+     ((and (or (byte-code-function-p (car form))
+               (eq (car-safe (car form)) 'lambda))
+           ;; if the form comes out the same way it went in, that's
+           ;; because it was malformed, and we couldn't unfold it.
+           (not (eq form (setq form (byte-compile-unfold-lambda form)))))
+      (byte-compile-form form for-effect)
+      (setq for-effect nil))
+     ((byte-compile-normal-call form)))
+    (if for-effect
+        (byte-compile-discard))))
 
 (defun byte-compile-normal-call (form)
+  (when (and (byte-compile-warning-enabled-p 'callargs)
+             (symbolp (car form)))
+    (if (memq (car form)
+              '(custom-declare-group
+                ;; custom-declare-variable custom-declare-face
+                ))
+        (byte-compile-nogroup-warn form))
+    (when (get (car form) 'byte-obsolete-info)
+      (byte-compile-warn-obsolete (car form)))
+    (byte-compile-callargs-warn form))
   (if byte-compile-generate-call-tree
       (byte-compile-annotate-call-tree form))
   (when (and for-effect (eq (car form) 'mapcar)
@@ -3062,46 +3072,22 @@ If BINDING is non-nil, VAR is being bound."
 (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))
+  (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)))
+        (byte-compile-stack-ref (cdr 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)
+       (byte-compile-warn "reference to free variable `%S'" var)
        (push var byte-compile-free-references))
       (byte-compile-dynamic-variable-op 'byte-varref var))))
 
@@ -3111,14 +3097,7 @@ If BINDING is non-nil, VAR is being bound."
   (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)))
+        (byte-compile-stack-set (cdr lex-binding))
       ;; VAR is dynamically bound
       (unless (or (not (byte-compile-warning-enabled-p 'free-vars))
                  (boundp var)
@@ -3153,26 +3132,6 @@ If BINDING is non-nil, VAR is being bound."
 (defun byte-compile-push-constant (const)
   (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))
-
 \f
 ;; Compile those primitive ordinary functions
 ;; which have special byte codes just for speed.
@@ -3243,7 +3202,6 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
 (byte-defop-compiler bobp              0)
 (byte-defop-compiler current-buffer    0)
 ;;(byte-defop-compiler read-char       0) ;; obsolete
-(byte-defop-compiler interactive-p     0)
 (byte-defop-compiler widen             0)
 (byte-defop-compiler end-of-line    0-1)
 (byte-defop-compiler forward-char   0-1)
@@ -3373,7 +3331,7 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
          ((= len 4) (byte-compile-three-args form))
          (t (byte-compile-subr-wrong-args form "2-3")))))
 
-(defun byte-compile-noop (form)
+(defun byte-compile-noop (_form)
   (byte-compile-constant nil))
 
 (defun byte-compile-discard (&optional num preserve-tos)
@@ -3400,16 +3358,40 @@ discarding."
 
 (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)))
+  (let ((dist (- byte-compile-depth (1+ stack-pos))))
+    (if (zerop dist)
+        ;; A simple optimization
+        (byte-compile-out 'byte-dup)
+      ;; normal case
+      (byte-compile-out 'byte-stack-ref dist))))
 
 (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))
-
+  (byte-compile-out 'byte-stack-set (- byte-compile-depth (1+ stack-pos))))
+
+(byte-defop-compiler-1 internal-make-closure byte-compile-make-closure)
+(byte-defop-compiler-1 internal-get-closed-var byte-compile-get-closed-var)
+
+(defconst byte-compile--env-var (make-symbol "env"))
+
+(defun byte-compile-make-closure (form)
+  (if for-effect (setq for-effect nil)
+    (let* ((vars (nth 1 form))
+           (env (nth 2 form))
+           (body (nthcdr 3 form))
+           (fun
+            (byte-compile-lambda `(lambda ,vars . ,body) nil (length env))))
+      (assert (byte-code-function-p fun))
+      (byte-compile-form `(make-byte-code
+                           ',(aref fun 0) ',(aref fun 1)
+                           (vconcat (vector . ,env) ',(aref fun 2))
+                           ,@(nthcdr 3 (mapcar (lambda (x) `',x) fun)))))))
+    
+
+(defun byte-compile-get-closed-var (form)
+  (if for-effect (setq for-effect nil)
+    (byte-compile-out 'byte-constant ;; byte-closed-var
+                      (nth 1 form))))
 
 ;; Compile a function that accepts one or more args and is right-associative.
 ;; We do it by left-associativity so that the operations
@@ -3609,7 +3591,6 @@ discarding."
 (byte-defop-compiler-1 setq)
 (byte-defop-compiler-1 setq-default)
 (byte-defop-compiler-1 quote)
-(byte-defop-compiler-1 quote-form)
 
 (defun byte-compile-setq (form)
   (let ((bytecomp-args (cdr form)))
@@ -3653,18 +3634,14 @@ discarding."
 
 (defun byte-compile-quote (form)
   (byte-compile-constant (car (cdr form))))
-
-(defun byte-compile-quote-form (form)
-  (byte-compile-constant (byte-compile-top-level (nth 1 form))))
-
 \f
 ;;; control structures
 
-(defun byte-compile-body (bytecomp-body &optional for-effect)
+(defun byte-compile-body (bytecomp-body &optional for-effect-arg)
   (while (cdr bytecomp-body)
     (byte-compile-form (car bytecomp-body) t)
     (setq bytecomp-body (cdr bytecomp-body)))
-  (byte-compile-form (car bytecomp-body) for-effect))
+  (byte-compile-form (car bytecomp-body) for-effect-arg))
 
 (defsubst byte-compile-body-do-effect (bytecomp-body)
   (byte-compile-body bytecomp-body for-effect)
@@ -3685,7 +3662,7 @@ discarding."
 (byte-defop-compiler-1 while)
 (byte-defop-compiler-1 funcall)
 (byte-defop-compiler-1 let)
-(byte-defop-compiler-1 let*)
+(byte-defop-compiler-1 let* byte-compile-let)
 
 (defun byte-compile-progn (form)
   (byte-compile-body-do-effect (cdr form)))
@@ -3750,9 +3727,7 @@ that suppresses all warnings during execution of BODY."
                       ,condition (list 'boundp 'default-boundp)))
          ;; Maybe add to the bound list.
          (byte-compile-bound-variables
-          (if bound-list
-              (append bound-list byte-compile-bound-variables)
-            byte-compile-bound-variables)))
+           (append bound-list byte-compile-bound-variables)))
      (unwind-protect
         ;; If things not being bound at all is ok, so must them being obsolete.
         ;; Note that we add to the existing lists since Tramp (ab)uses
@@ -3865,14 +3840,7 @@ that suppresses all warnings during execution of BODY."
 
 (defun byte-compile-while (form)
   (let ((endtag (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))
+       (looptag (byte-compile-make-tag)))
     (byte-compile-out-tag looptag)
     (byte-compile-form (car (cdr form)))
     (byte-compile-goto-if nil for-effect endtag)
@@ -3888,111 +3856,115 @@ that suppresses all warnings during execution of BODY."
 \f
 ;; 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)
+(defun byte-compile-push-binding-init (clause)
   "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))
+Return the offset in the form (VAR . OFFSET)."
+  (let* ((var (if (consp clause) (car clause) clause)))
+    ;; 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.
+    (prog1 (cons var byte-compile-depth)
       (if (consp clause)
-         (byte-compile-form (cadr clause) unused)
-       (byte-compile-push-constant nil))))
-  init-lexenv)
+          (byte-compile-form (cadr clause))
+        (byte-compile-push-constant nil)))))
+
+(defun byte-compile-not-lexical-var-p (var)
+  (or (not (symbolp var))
+      (special-variable-p var)
+      (memq var byte-compile-bound-variables)
+      (memq var '(nil t))
+      (keywordp var)))
+
+(defun byte-compile-bind (var init-lexenv)
+  "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.
+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, to move them to TOS for
+  ;; dynamic binding.
+  (cond ((not (byte-compile-not-lexical-var-p var))
+         ;; VAR is a simple stack-allocated lexical variable
+         (push (assq var init-lexenv)
+               byte-compile-lexical-environment)
+         nil)
+        ((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)
+        (t
+         ;; 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)))
+
+(defun byte-compile-unbind (clauses init-lexenv
+                                   &optional 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.  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))
+    (dolist (clause clauses)
+      (unless (assq (if (consp clause) (car clause) clause)
+                    byte-compile-lexical-environment)
+        (setq num-dynamic-bindings (1+ num-dynamic-bindings))))
+    (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)))
 
 (defun byte-compile-let (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
+       (init-lexenv nil))
+    (when (eq (car form) 'let)
+      ;; First compute the binding values in the old scope.
+      (dolist (var clauses)
+        (push (byte-compile-push-binding-init var) init-lexenv)))
+    ;; New scope.
     (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)
-  "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)))
-    ;; 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-compile-lexical-environment byte-compile-lexical-environment))
+      ;; Bind the variables.
+      ;; For `let', do it in reverse order, because it makes no
+      ;; semantic difference, but it is a lot more efficient since the
+      ;; values are now in reverse order on the stack.
+      (dolist (var (if (eq (car form) 'let) (reverse clauses) clauses))
+        (unless (eq (car form) 'let)
+          (push (byte-compile-push-binding-init var) init-lexenv))
+        (let ((var (if (consp var) (car var) var)))
+          (cond ((null lexical-binding)
+                 ;; If there are no lexical bindings, we can do things simply.
+                 (byte-compile-dynamic-variable-bind var))
+                ((byte-compile-bind var init-lexenv)
+                 (pop init-lexenv)))))
+      ;; Emit the body.
+      (let ((init-stack-depth byte-compile-depth))
+        (byte-compile-body-do-effect (cdr (cdr form)))
+        ;; Unbind the variables.
+        (if lexical-binding
+            ;; Unbind both lexical and dynamic variables.
+            (progn
+              (assert (or (eq byte-compile-depth init-stack-depth)
+                          (eq byte-compile-depth (1+ init-stack-depth))))
+              (byte-compile-unbind clauses init-lexenv (> byte-compile-depth
+                                                          init-stack-depth)))
+          ;; Unbind dynamic variables.
+          (byte-compile-out 'byte-unbind (length clauses)))))))
 
 \f
 
@@ -4027,70 +3999,84 @@ if LFORMINFO is nil (meaning all bindings are dynamic)."
 (byte-defop-compiler-1 save-excursion)
 (byte-defop-compiler-1 save-current-buffer)
 (byte-defop-compiler-1 save-restriction)
-(byte-defop-compiler-1 save-window-excursion)
-(byte-defop-compiler-1 with-output-to-temp-buffer)
 (byte-defop-compiler-1 track-mouse)
 
 (defun byte-compile-catch (form)
   (byte-compile-form (car (cdr form)))
-  (byte-compile-push-constant
-    (byte-compile-top-level (cons 'progn (cdr (cdr form))) for-effect))
+  (pcase (cddr form)
+    (`(:fun-body ,f)
+     (byte-compile-form `(list 'funcall ,f)))
+    (body
+     (byte-compile-push-constant
+      (byte-compile-top-level (cons 'progn body) for-effect))))
   (byte-compile-out 'byte-catch 0))
 
 (defun byte-compile-unwind-protect (form)
-  (byte-compile-push-constant
-   (byte-compile-top-level-body (cdr (cdr form)) t))
+  (pcase (cddr form)
+    (`(:fun-body ,f)
+     (byte-compile-form `(list (list 'funcall ,f))))
+    (handlers
+     (byte-compile-push-constant
+      (byte-compile-top-level-body handlers t))))
   (byte-compile-out 'byte-unwind-protect 0)
   (byte-compile-form-do-effect (car (cdr form)))
   (byte-compile-out 'byte-unbind 1))
 
 (defun byte-compile-track-mouse (form)
   (byte-compile-form
-   `(funcall '(lambda nil
-               (track-mouse ,@(byte-compile-top-level-body (cdr form)))))))
+   (pcase form
+     (`(,_ :fun-body ,f) `(eval (list 'track-mouse (list 'funcall ,f))))
+     (_ `(eval '(track-mouse ,@(byte-compile-top-level-body (cdr form))))))))
 
 (defun byte-compile-condition-case (form)
   (let* ((var (nth 1 form))
-        (byte-compile-bound-variables
-         (if var (cons var byte-compile-bound-variables)
+        (fun-bodies (eq var :fun-body))
+         (byte-compile-bound-variables
+         (if (and var (not fun-bodies))
+              (cons var byte-compile-bound-variables)
            byte-compile-bound-variables)))
     (byte-compile-set-symbol-position 'condition-case)
     (unless (symbolp var)
       (byte-compile-warn
        "`%s' is not a variable-name or nil (in condition-case)" var))
+    (if fun-bodies (setq var (make-symbol "err")))
     (byte-compile-push-constant var)
-    (byte-compile-push-constant (byte-compile-top-level
-                                (nth 2 form) for-effect))
-    (let ((clauses (cdr (cdr (cdr form))))
-         compiled-clauses)
-      (while clauses
-       (let* ((clause (car clauses))
-               (condition (car clause)))
-          (cond ((not (or (symbolp condition)
-                         (and (listp condition)
-                              (let ((syms condition) (ok t))
-                                (while syms
-                                  (if (not (symbolp (car syms)))
-                                      (setq ok nil))
-                                  (setq syms (cdr syms)))
-                                ok))))
-                 (byte-compile-warn
-                   "`%s' is not a condition name or list of such (in condition-case)"
-                   (prin1-to-string condition)))
-;;                ((not (or (eq condition 't)
-;;                       (and (stringp (get condition 'error-message))
-;;                            (consp (get condition 'error-conditions)))))
-;;                 (byte-compile-warn
-;;                   "`%s' is not a known condition name (in condition-case)"
-;;                   condition))
-               )
-         (setq compiled-clauses
-               (cons (cons condition
-                           (byte-compile-top-level-body
-                            (cdr clause) for-effect))
-                     compiled-clauses)))
-       (setq clauses (cdr clauses)))
-      (byte-compile-push-constant (nreverse compiled-clauses)))
+    (if fun-bodies
+        (byte-compile-form `(list 'funcall ,(nth 2 form)))
+      (byte-compile-push-constant
+       (byte-compile-top-level (nth 2 form) for-effect)))
+    (let ((compiled-clauses
+           (mapcar
+            (lambda (clause)
+              (let ((condition (car clause)))
+                (cond ((not (or (symbolp condition)
+                                (and (listp condition)
+                                     (let ((ok t))
+                                       (dolist (sym condition)
+                                         (if (not (symbolp sym))
+                                             (setq ok nil)))
+                                       ok))))
+                       (byte-compile-warn
+                        "`%S' is not a condition name or list of such (in condition-case)"
+                        condition))
+                      ;; (not (or (eq condition 't)
+                      ;;         (and (stringp (get condition 'error-message))
+                      ;;              (consp (get condition
+                      ;;                          'error-conditions)))))
+                      ;; (byte-compile-warn
+                      ;;   "`%s' is not a known condition name
+                      ;;   (in condition-case)"
+                      ;;   condition))
+                      )
+                (if fun-bodies
+                    `(list ',condition (list 'funcall ,(cadr clause) ',var))
+                  (cons condition
+                        (byte-compile-top-level-body
+                         (cdr clause) for-effect)))))
+            (cdr (cdr (cdr form))))))
+      (if fun-bodies
+          (byte-compile-form `(list ,@compiled-clauses))
+        (byte-compile-push-constant compiled-clauses)))
     (byte-compile-out 'byte-condition-case 0)))
 
 
@@ -4111,17 +4097,6 @@ if LFORMINFO is nil (meaning all bindings are dynamic)."
   (byte-compile-out 'byte-save-current-buffer 0)
   (byte-compile-body-do-effect (cdr form))
   (byte-compile-out 'byte-unbind 1))
-
-(defun byte-compile-save-window-excursion (form)
-  (byte-compile-push-constant
-   (byte-compile-top-level-body (cdr form) for-effect))
-  (byte-compile-out 'byte-save-window-excursion 0))
-
-(defun byte-compile-with-output-to-temp-buffer (form)
-  (byte-compile-form (car (cdr form)))
-  (byte-compile-out 'byte-temp-output-buffer-setup 0)
-  (byte-compile-body (cdr (cdr form)))
-  (byte-compile-out 'byte-temp-output-buffer-show 0))
 \f
 ;;; top-level forms elsewhere
 
@@ -4146,20 +4121,16 @@ if LFORMINFO is nil (meaning all bindings are dynamic)."
 
 (defun byte-compile-defmacro (form)
   ;; This is not used for file-level defmacros with doc strings.
-  ;; FIXME handle decls, use defalias?
-  (let ((decls (byte-compile-defmacro-declaration form))
-       (code (byte-compile-lambda (cdr (cdr form)) t))
-       (for-effect nil))
-    (byte-compile-push-constant (nth 1 form))
-    (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)))
+  (byte-compile-body-do-effect
+   (let ((decls (byte-compile-defmacro-declaration form))
+         (code (byte-compile-byte-code-maker
+                (byte-compile-lambda (cdr (cdr form)) t))))
+     `((defalias ',(nth 1 form)
+         ,(if (eq (car-safe code) 'make-byte-code)
+              `(cons 'macro ,code)
+            `'(macro . ,(eval code))))
+       ,@decls
+       ',(nth 1 form)))))
 
 (defun byte-compile-defvar (form)
   ;; This is not used for file-level defvar/consts with doc strings.
@@ -4224,7 +4195,7 @@ if LFORMINFO is nil (meaning all bindings are dynamic)."
 
 ;; Lambdas in valid places are handled as special cases by various code.
 ;; The ones that remain are errors.
-(defun byte-compile-lambda-form (form)
+(defun byte-compile-lambda-form (_form)
   (byte-compile-set-symbol-position 'lambda)
   (error "`lambda' used as function name is invalid"))
 
@@ -4299,8 +4270,8 @@ if LFORMINFO is nil (meaning all bindings are dynamic)."
       (progn
        ;; ## remove this someday
        (and byte-compile-depth
-         (not (= (cdr (cdr tag)) byte-compile-depth))
-         (error "Compiler bug: depth conflict at tag %d" (car (cdr tag))))
+             (not (= (cdr (cdr tag)) byte-compile-depth))
+             (error "Compiler bug: depth conflict at tag %d" (car (cdr tag))))
        (setq byte-compile-depth (cdr (cdr tag))))
     (setcdr (cdr tag) byte-compile-depth)))
 
@@ -4631,7 +4602,14 @@ Use with caution."
         (setq f (car f))
         (if (string-match "elc\\'" f) (setq f (substring f 0 -1)))
         (when (and (file-readable-p f)
-                   (file-newer-than-file-p f emacs-file))
+                   (file-newer-than-file-p f emacs-file)
+                   ;; Don't reload the source version of the files below
+                   ;; because that causes subsequent byte-compilation to
+                   ;; be a lot slower and need a higher max-lisp-eval-depth,
+                   ;; so it can cause recompilation to fail.
+                   (not (member (file-name-nondirectory f)
+                                '("pcase.el" "bytecomp.el" "macroexp.el"
+                                  "cconv.el" "byte-opt.el"))))
           (message "Reloading stale %s" (file-name-nondirectory f))
           (condition-case nil
               (load f 'noerror nil 'nosuffix)
@@ -4717,5 +4695,4 @@ and corresponding effects."
 
 (run-hooks 'bytecomp-load-hook)
 
-;; arch-tag: 9c97b0f0-8745-4571-bfc3-8dceb677292a
 ;;; bytecomp.el ends here