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.
This commit is contained in:
Vibhav Pant 2017-04-20 20:59:15 +05:30
parent 4364a769b4
commit b389379c87

View file

@ -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."