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:
parent
4364a769b4
commit
b389379c87
1 changed files with 46 additions and 40 deletions
|
@ -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."
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue