summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVibhav Pant <vibhavp@gmail.com>2017-04-20 20:59:15 +0530
committerVibhav Pant <vibhavp@gmail.com>2017-04-20 21:23:08 +0530
commitb389379c87481b6bc647ceb4d323f861281cad72 (patch)
treeb944aefcc0452bb4dddb396a0f3af6ec46e0a72b
parent4364a769b489c044c4e9eeac6cfbabcc844ab332 (diff)
downloademacs-b389379c87481b6bc647ceb4d323f861281cad72.tar.gz
bytecomp: Don't inline functions that use byte-switch (Bug#26518)
* lisp/emacs-lisp/bytecomp.el (byte-compile-unfold-bcf): Don't inline FORM if the bytecode uses the byte-switch instruction. It is impossible to guess the correct stack depth while inlining such bytecode, resulting in faulty code.
-rw-r--r--lisp/emacs-lisp/bytecomp.el86
1 files changed, 46 insertions, 40 deletions
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index f0f938da43f..aba07102055 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -3204,47 +3204,53 @@ for symbols generated by the byte compiler itself."
(fmax2 (if (numberp fargs) (lsh fargs -7))) ;2*max+rest.
;; (fmin (if (numberp fargs) (logand fargs 127)))
(alen (length (cdr form)))
- (dynbinds ()))
+ (dynbinds ())
+ lap)
(fetch-bytecode fun)
- (mapc 'byte-compile-form (cdr form))
- (unless fmax2
- ;; Old-style byte-code.
- (cl-assert (listp fargs))
- (while fargs
- (pcase (car fargs)
- (`&optional (setq fargs (cdr fargs)))
- (`&rest (setq fmax2 (+ (* 2 (length dynbinds)) 1))
- (push (cadr fargs) dynbinds)
- (setq fargs nil))
- (_ (push (pop fargs) dynbinds))))
- (unless fmax2 (setq fmax2 (* 2 (length dynbinds)))))
- (cond
- ((<= (+ alen alen) fmax2)
- ;; Add missing &optional (or &rest) arguments.
- (dotimes (_ (- (/ (1+ fmax2) 2) alen))
- (byte-compile-push-constant nil)))
- ((zerop (logand fmax2 1))
- (byte-compile-report-error
- (format "Too many arguments for inlined function %S" form))
- (byte-compile-discard (- alen (/ fmax2 2))))
- (t
- ;; Turn &rest args into a list.
- (let ((n (- alen (/ (1- fmax2) 2))))
- (cl-assert (> n 0) nil "problem: fmax2=%S alen=%S n=%S" fmax2 alen n)
- (if (< n 5)
- (byte-compile-out
- (aref [byte-list1 byte-list2 byte-list3 byte-list4] (1- n))
- 0)
- (byte-compile-out 'byte-listN n)))))
- (mapc #'byte-compile-dynamic-variable-bind dynbinds)
- (byte-compile-inline-lapcode
- (byte-decompile-bytecode-1 (aref fun 1) (aref fun 2) t)
- (1+ start-depth))
- ;; Unbind dynamic variables.
- (when dynbinds
- (byte-compile-out 'byte-unbind (length dynbinds)))
- (cl-assert (eq byte-compile-depth (1+ start-depth))
- nil "Wrong depth start=%s end=%s" start-depth byte-compile-depth)))
+ (setq lap (byte-decompile-bytecode-1 (aref fun 1) (aref fun 2) t))
+ ;; optimized switch bytecode makes it impossible to guess the correct
+ ;; `byte-compile-depth', which can result in incorrect inlined code.
+ ;; therefore, we do not inline code that uses the `byte-switch'
+ ;; instruction.
+ (if (assq 'byte-switch lap)
+ (byte-compile-normal-call form)
+ (mapc 'byte-compile-form (cdr form))
+ (unless fmax2
+ ;; Old-style byte-code.
+ (cl-assert (listp fargs))
+ (while fargs
+ (pcase (car fargs)
+ (`&optional (setq fargs (cdr fargs)))
+ (`&rest (setq fmax2 (+ (* 2 (length dynbinds)) 1))
+ (push (cadr fargs) dynbinds)
+ (setq fargs nil))
+ (_ (push (pop fargs) dynbinds))))
+ (unless fmax2 (setq fmax2 (* 2 (length dynbinds)))))
+ (cond
+ ((<= (+ alen alen) fmax2)
+ ;; Add missing &optional (or &rest) arguments.
+ (dotimes (_ (- (/ (1+ fmax2) 2) alen))
+ (byte-compile-push-constant nil)))
+ ((zerop (logand fmax2 1))
+ (byte-compile-report-error
+ (format "Too many arguments for inlined function %S" form))
+ (byte-compile-discard (- alen (/ fmax2 2))))
+ (t
+ ;; Turn &rest args into a list.
+ (let ((n (- alen (/ (1- fmax2) 2))))
+ (cl-assert (> n 0) nil "problem: fmax2=%S alen=%S n=%S" fmax2 alen n)
+ (if (< n 5)
+ (byte-compile-out
+ (aref [byte-list1 byte-list2 byte-list3 byte-list4] (1- n))
+ 0)
+ (byte-compile-out 'byte-listN n)))))
+ (mapc #'byte-compile-dynamic-variable-bind dynbinds)
+ (byte-compile-inline-lapcode lap (1+ start-depth))
+ ;; Unbind dynamic variables.
+ (when dynbinds
+ (byte-compile-out 'byte-unbind (length dynbinds)))
+ (cl-assert (eq byte-compile-depth (1+ start-depth))
+ nil "Wrong depth start=%s end=%s" start-depth byte-compile-depth))))
(defun byte-compile-check-variable (var access-type)
"Do various error checks before a use of the variable VAR."