]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/byte-opt.el
Merge from trunk
[gnu-emacs] / lisp / emacs-lisp / byte-opt.el
index 0f4018dc8da059942a16fa5cd0f7ac7ebc3cff09..c9cc4618967d1ab36da954905a4c15fe26ffb998 100644 (file)
 (eval-when-compile (require 'cl))
 
 (defun byte-compile-log-lap-1 (format &rest args)
-  (if (aref byte-code-vector 0)
-      (error "The old version of the disassembler is loaded.  Reload new-bytecomp as well"))
+  ;; Newer byte codes for stack-ref make the slot 0 non-nil again.
+  ;; But the "old disassembler" is *really* ancient by now.
+  ;; (if (aref byte-code-vector 0)
+  ;;     (error "The old version of the disassembler is loaded.  Reload new-bytecomp as well"))
   (byte-compile-log-1
    (apply 'format format
      (let (c a)
 ;; are no collisions, and that byte-compile-tag-number is reasonable
 ;; after this is spliced in.  The provided list is destroyed.
 (defun byte-inline-lapcode (lap)
-  (setq byte-compile-output (nconc (nreverse lap) byte-compile-output)))
+  ;; "Replay" the operations: we used to just do
+  ;; (setq byte-compile-output (nconc (nreverse lap) byte-compile-output))
+  ;; but that fails to update byte-compile-depth, so we had to assume
+  ;; that `lap' ends up adding exactly 1 element to the stack.  This
+  ;; happens to be true for byte-code generated by bytecomp.el without
+  ;; lexical-binding, but it's not true in general, and it's not true for
+  ;; code output by bytecomp.el with lexical-binding.
+  (dolist (op lap)
+    (cond
+     ((eq (car op) 'TAG) (byte-compile-out-tag op))
+     ((memq (car op) byte-goto-ops) (byte-compile-goto (car op) (cdr op)))
+     (t (byte-compile-out (car op) (cdr op))))))
 
 (defun byte-compile-inline-expand (form)
   (let* ((name (car form))
                     (cdr (assq name byte-compile-function-environment)))))
       (if (and (consp fn) (eq (car fn) 'autoload))
          (error "File `%s' didn't define `%s'" (nth 1 fn) name))
-      (if (and (symbolp fn) (not (eq fn t)))
-         (byte-compile-inline-expand (cons fn (cdr form)))
-       (if (byte-code-function-p fn)
-           (let (string)
-             (fetch-bytecode fn)
-             (setq string (aref fn 1))
-             ;; Isn't it an error for `string' not to be unibyte??  --stef
-             (if (fboundp 'string-as-unibyte)
-                 (setq string (string-as-unibyte string)))
-             ;; `byte-compile-splice-in-already-compiled-code'
-             ;; takes care of inlining the body.
-             (cons `(lambda ,(aref fn 0)
-                      (byte-code ,string ,(aref fn 2) ,(aref fn 3)))
-                   (cdr form)))
-         (if (eq (car-safe fn) 'lambda)
-             (cons fn (cdr form))
-           ;; Give up on inlining.
-           form))))))
+      (cond
+       ((and (symbolp fn) (not (eq fn t))) ;A function alias.
+        (byte-compile-inline-expand (cons fn (cdr form))))
+       ((and (byte-code-function-p fn)
+             ;; FIXME: This works to inline old-style-byte-codes into
+             ;; old-style-byte-codes, but not mixed cases (not sure
+             ;; about new-style into new-style).
+             (not lexical-binding)
+             (not (and (>= (length fn) 7)
+                       (aref fn 6))))   ;6 = COMPILED_PUSH_ARGS
+        ;; (message "Inlining %S byte-code" name)
+        (fetch-bytecode fn)
+        (let ((string (aref fn 1)))
+          ;; Isn't it an error for `string' not to be unibyte??  --stef
+          (if (fboundp 'string-as-unibyte)
+              (setq string (string-as-unibyte string)))
+          ;; `byte-compile-splice-in-already-compiled-code'
+          ;; takes care of inlining the body.
+          (cons `(lambda ,(aref fn 0)
+                   (byte-code ,string ,(aref fn 2) ,(aref fn 3)))
+                (cdr form))))
+       ((eq (car-safe fn) 'lambda)
+        (macroexpand-all (cons fn (cdr form))
+                         byte-compile-macro-environment))
+       (t ;; Give up on inlining.
+        form)))))
 
 ;; ((lambda ...) ...)
 (defun byte-compile-unfold-lambda (form &optional name)
                              (prin1-to-string form))
           nil)
 
-         ((memq fn '(defun defmacro function
-                     condition-case save-window-excursion))
+         ((memq fn '(defun defmacro function condition-case))
           ;; These forms are compiled as constants or by breaking out
           ;; all the subexpressions and compiling them separately.
           form)
           ;; However, don't actually bother calling `ignore'.
           `(prog1 nil . ,(mapcar 'byte-optimize-form (cdr form))))
 
-         ;; If optimization is on, this is the only place that macros are
-         ;; expanded.  If optimization is off, then macroexpansion happens
-         ;; in byte-compile-form.  Otherwise, the macros are already expanded
-         ;; by the time that is reached.
-         ((not (eq form
-                   (setq form (macroexpand form
-                                           byte-compile-macro-environment))))
-          (byte-optimize-form form for-effect))
-
-         ;; Support compiler macros as in cl.el.
-         ((and (fboundp 'compiler-macroexpand)
-               (symbolp (car-safe form))
-               (get (car-safe form) 'cl-compiler-macro)
-               (not (eq form
-                        (with-no-warnings
-                         (setq form (compiler-macroexpand form))))))
-          (byte-optimize-form form for-effect))
-
          ((not (symbolp fn))
           (byte-compile-warn "`%s' is a malformed function"
                              (prin1-to-string fn))
   (if (not (memq byte-optimize '(t lap)))
       (byte-compile-normal-call form)
     (byte-inline-lapcode
-     (byte-decompile-bytecode-1 (nth 1 form) (nth 2 form) t))
-    (setq byte-compile-maxdepth (max (+ byte-compile-depth (nth 3 form))
-                                    byte-compile-maxdepth))
-    (setq byte-compile-depth (1+ byte-compile-depth))))
+     (byte-decompile-bytecode-1 (nth 1 form) (nth 2 form) t))))
 
 (put 'byte-code 'byte-compile 'byte-compile-splice-in-already-compiled-code)
 
 (defconst byte-constref-ops
   '(byte-constant byte-constant2 byte-varref byte-varset byte-varbind))
 
+;; Used and set dynamically in byte-decompile-bytecode-1.
+(defvar bytedecomp-op)
+(defvar bytedecomp-ptr)
+(defvar bytedecomp-bytes)
+
 ;; This function extracts the bitfields from variable-length opcodes.
 ;; Originally defined in disass.el (which no longer uses it.)
-
 (defun disassemble-offset ()
   "Don't call this!"
   ;; fetch and return the offset for the current opcode.
   ;; return nil if this opcode has no offset
-  ;; Used and set dynamically in byte-decompile-bytecode-1.
-  (defvar bytedecomp-op)
-  (defvar bytedecomp-ptr)
-  (defvar bytedecomp-bytes)
   (cond ((< bytedecomp-op byte-nth)
         (let ((tem (logand bytedecomp-op 7)))
           (setq bytedecomp-op (logand bytedecomp-op 248))
        ((>= bytedecomp-op byte-constant)
         (prog1 (- bytedecomp-op byte-constant) ;offset in opcode
           (setq bytedecomp-op byte-constant)))
-       ((and (>= bytedecomp-op byte-constant2)
-             (<= bytedecomp-op byte-goto-if-not-nil-else-pop))
+       ((or (and (>= bytedecomp-op byte-constant2)
+                  (<= bytedecomp-op byte-goto-if-not-nil-else-pop))
+             (= bytedecomp-op byte-stack-set2))
         ;; Offset in next 2 bytes.
         (setq bytedecomp-ptr (1+ bytedecomp-ptr))
         (+ (aref bytedecomp-bytes bytedecomp-ptr)
            (progn (setq bytedecomp-ptr (1+ bytedecomp-ptr))
                   (lsh (aref bytedecomp-bytes bytedecomp-ptr) 8))))
        ((and (>= bytedecomp-op byte-listN)
-             (<= bytedecomp-op byte-insertN))
+             (<= bytedecomp-op byte-discardN))
         (setq bytedecomp-ptr (1+ bytedecomp-ptr)) ;offset in next byte
         (aref bytedecomp-bytes bytedecomp-ptr))))
 
             (if (= bytedecomp-ptr (1- length))
                 (setq bytedecomp-op nil)
               (setq offset (or endtag (setq endtag (byte-compile-make-tag)))
-                    bytedecomp-op 'byte-goto))))
+                    bytedecomp-op 'byte-goto)))
+           ((eq bytedecomp-op 'byte-stack-set2)
+            (setq bytedecomp-op 'byte-stack-set))
+           ((and (eq bytedecomp-op 'byte-discardN) (>= offset #x80))
+            ;; The top bit of the operand for byte-discardN is a flag,
+            ;; saying whether the top-of-stack is preserved.  In
+            ;; lapcode, we represent this by using a different opcode
+            ;; (with the flag removed from the operand).
+            (setq bytedecomp-op 'byte-discardN-preserve-tos)
+            (setq offset (- offset #x80))))
       ;; lap = ( [ (pc . (op . arg)) ]* )
       (setq lap (cons (cons optr (cons bytedecomp-op (or offset 0)))
                      lap))
     byte-cdr-safe byte-cons byte-list1 byte-list2 byte-point byte-point-max
     byte-point-min byte-following-char byte-preceding-char
     byte-current-column byte-eolp byte-eobp byte-bolp byte-bobp
-    byte-current-buffer byte-interactive-p))
+    byte-current-buffer byte-stack-ref))
 
 (defconst byte-compile-side-effect-free-ops
   (nconc
@@ -1580,9 +1589,14 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
              ;; const/dup varbind-X varref-X --> const/dup varbind-X const/dup
              ;; The latter two can enable other optimizations.
              ;;
+              ;; For lexical variables, we could do the same
+              ;;   stack-set-X+1 stack-ref-X  -->  dup stack-set-X+2
+              ;; but this is a very minor gain, since dup is stack-ref-0,
+              ;; i.e. it's only better if X>5, and even then it comes
+              ;; at the cost cost of an extra stack slot.  Let's not bother.
              ((and (eq 'byte-varref (car lap2))
-                   (eq (cdr lap1) (cdr lap2))
-                   (memq (car lap1) '(byte-varset byte-varbind)))
+                    (eq (cdr lap1) (cdr lap2))
+                    (memq (car lap1) '(byte-varset byte-varbind)))
               (if (and (setq tmp (memq (car (cdr lap2)) byte-boolean-vars))
                        (not (eq (car lap0) 'byte-constant)))
                   nil
@@ -1611,14 +1625,17 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
              ;;
              ;; dup varset-X discard  -->  varset-X
              ;; dup varbind-X discard  -->  varbind-X
+              ;; dup stack-set-X discard  -->  stack-set-X-1
              ;; (the varbind variant can emerge from other optimizations)
              ;;
              ((and (eq 'byte-dup (car lap0))
                    (eq 'byte-discard (car lap2))
-                   (memq (car lap1) '(byte-varset byte-varbind)))
+                   (memq (car lap1) '(byte-varset byte-varbind
+                                       byte-stack-set)))
               (byte-compile-log-lap "  dup %s discard\t-->\t%s" lap1 lap1)
               (setq keep-going t
                     rest (cdr rest))
+               (if (eq 'byte-stack-set (car lap1)) (decf (cdr lap1)))
               (setq lap (delq lap0 (delq lap2 lap))))
              ;;
              ;; not goto-X-if-nil              -->  goto-X-if-non-nil
@@ -1673,30 +1690,34 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
                      (setq rest (cdr rest)
                            lap (delq lap0 (delq lap1 lap))))
                     (t
-                     (if (memq (car lap1) byte-goto-always-pop-ops)
-                         (progn
-                           (byte-compile-log-lap "  %s %s\t-->\t%s"
-                            lap0 lap1 (cons 'byte-goto (cdr lap1)))
-                           (setq lap (delq lap0 lap)))
-                       (byte-compile-log-lap "  %s %s\t-->\t%s" lap0 lap1
-                        (cons 'byte-goto (cdr lap1))))
+                     (byte-compile-log-lap "  %s %s\t-->\t%s"
+                                           lap0 lap1
+                                           (cons 'byte-goto (cdr lap1)))
+                     (when (memq (car lap1) byte-goto-always-pop-ops)
+                       (setq lap (delq lap0 lap)))
                      (setcar lap1 'byte-goto)))
               (setq keep-going t))
              ;;
              ;; varref-X varref-X  -->  varref-X dup
              ;; varref-X [dup ...] varref-X  -->  varref-X [dup ...] dup
+             ;; stackref-X [dup ...] stackref-X+N  -->  stackref-X [dup ...] dup
              ;; We don't optimize the const-X variations on this here,
              ;; because that would inhibit some goto optimizations; we
              ;; optimize the const-X case after all other optimizations.
              ;;
-             ((and (eq 'byte-varref (car lap0))
+             ((and (memq (car lap0) '(byte-varref byte-stack-ref))
                    (progn
                      (setq tmp (cdr rest))
+                      (setq tmp2 0)
                      (while (eq (car (car tmp)) 'byte-dup)
-                       (setq tmp (cdr tmp)))
+                       (setq tmp2 (1+ tmp2))
+                        (setq tmp (cdr tmp)))
                      t)
-                   (eq (cdr lap0) (cdr (car tmp)))
-                   (eq 'byte-varref (car (car tmp))))
+                   (eq (if (eq 'byte-stack-ref (car lap0))
+                            (+ tmp2 1 (cdr lap0))
+                          (cdr lap0))
+                        (cdr (car tmp)))
+                   (eq (car lap0) (car (car tmp))))
               (if (memq byte-optimize-log '(t byte))
                   (let ((str ""))
                     (setq tmp2 (cdr rest))
@@ -1883,6 +1904,11 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
              ;; X: varref-Y Z: ... dup varset-Y goto-Z
              ;; (varset-X goto-BACK, BACK: varref-X --> copy the varref down.)
              ;; (This is so usual for while loops that it is worth handling).
+              ;;
+              ;; Here again, we could do it for stack-ref/stack-set, but
+             ;; that's replacing a stack-ref-Y with a stack-ref-0, which
+              ;; is a very minor improvement (if any), at the cost of
+             ;; more stack use and more byte-code.  Let's not do it.
              ;;
              ((and (eq (car lap1) 'byte-varset)
                    (eq (car lap2) 'byte-goto)
@@ -1955,10 +1981,11 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
     ;; Rebuild byte-compile-constants / byte-compile-variables.
     ;; Simple optimizations that would inhibit other optimizations if they
     ;; were done in the optimizing loop, and optimizations which there is no
-    ;;  need to do more than once.
+    ;; need to do more than once.
     (setq byte-compile-constants nil
          byte-compile-variables nil)
     (setq rest lap)
+    (byte-compile-log-lap "  ---- final pass")
     (while rest
       (setq lap0 (car rest)
            lap1 (nth 1 rest))
@@ -2008,10 +2035,88 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
             (byte-compile-log-lap "  %s %s\t-->\t%s" lap0 lap1
                                   (cons 'byte-unbind
                                         (+ (cdr lap0) (cdr lap1))))
-            (setq keep-going t)
             (setq lap (delq lap0 lap))
             (setcdr lap1 (+ (cdr lap1) (cdr lap0))))
-           )
+           
+           ;;
+           ;; stack-set-M [discard/discardN ...]  -->  discardN-preserve-tos
+           ;; stack-set-M [discard/discardN ...]  -->  discardN
+           ;;
+           ((and (eq (car lap0) 'byte-stack-set)
+                 (memq (car lap1) '(byte-discard byte-discardN))
+                 (progn
+                   ;; See if enough discard operations follow to expose or
+                   ;; destroy the value stored by the stack-set.
+                   (setq tmp (cdr rest))
+                   (setq tmp2 (1- (cdr lap0)))
+                   (setq tmp3 0)
+                   (while (memq (car (car tmp)) '(byte-discard byte-discardN))
+                     (setq tmp3
+                            (+ tmp3 (if (eq (car (car tmp)) 'byte-discard)
+                                        1
+                                      (cdr (car tmp)))))
+                     (setq tmp (cdr tmp)))
+                   (>= tmp3 tmp2)))
+            ;; Do the optimization.
+            (setq lap (delq lap0 lap))
+             (setcar lap1
+                     (if (= tmp2 tmp3)
+                         ;; The value stored is the new TOS, so pop
+                         ;; one more value (to get rid of the old
+                         ;; value) using the TOS-preserving
+                         ;; discard operator.
+                         'byte-discardN-preserve-tos
+                       ;; Otherwise, the value stored is lost, so just use a
+                       ;; normal discard.
+                       'byte-discardN))
+             (setcdr lap1 (1+ tmp3))
+            (setcdr (cdr rest) tmp)
+            (byte-compile-log-lap "  %s [discard/discardN]...\t-->\t%s"
+                                  lap0 lap1))
+
+           ;;
+           ;; discard/discardN/discardN-preserve-tos-X discard/discardN-Y  -->
+           ;; discardN-(X+Y)
+           ;;
+           ((and (memq (car lap0)
+                       '(byte-discard
+                         byte-discardN
+                         byte-discardN-preserve-tos))
+                 (memq (car lap1) '(byte-discard byte-discardN)))
+            (setq lap (delq lap0 lap))
+            (byte-compile-log-lap
+             "  %s %s\t-->\t(discardN %s)"
+             lap0 lap1
+             (+ (if (eq (car lap0) 'byte-discard) 1 (cdr lap0))
+                (if (eq (car lap1) 'byte-discard) 1 (cdr lap1))))
+            (setcdr lap1 (+ (if (eq (car lap0) 'byte-discard) 1 (cdr lap0))
+                            (if (eq (car lap1) 'byte-discard) 1 (cdr lap1))))
+            (setcar lap1 'byte-discardN))
+
+           ;;
+           ;; discardN-preserve-tos-X discardN-preserve-tos-Y  -->
+           ;; discardN-preserve-tos-(X+Y)
+           ;;
+           ((and (eq (car lap0) 'byte-discardN-preserve-tos)
+                 (eq (car lap1) 'byte-discardN-preserve-tos))
+            (setq lap (delq lap0 lap))
+            (setcdr lap1 (+ (cdr lap0) (cdr lap1)))
+            (byte-compile-log-lap "  %s %s\t-->\t%s" lap0 lap1 (car rest)))
+
+           ;;
+           ;; discardN-preserve-tos return  -->  return
+           ;; dup return  -->  return
+           ;; stack-set-N return  -->  return     ; where N is TOS-1
+           ;;
+           ((and (eq (car lap1) 'byte-return)
+                 (or (memq (car lap0) '(byte-discardN-preserve-tos byte-dup))
+                     (and (eq (car lap0) 'byte-stack-set)
+                          (= (cdr lap0) 1))))
+            ;; The byte-code interpreter will pop the stack for us, so
+            ;; we can just leave stuff on it.
+            (setq lap (delq lap0 lap))
+            (byte-compile-log-lap "  %s %s\t-->\t%s" lap0 lap1 lap1))
+            )
       (setq rest (cdr rest)))
     (setq byte-compile-maxdepth (+ byte-compile-maxdepth add-depth)))
   lap)