aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVibhav Pant2017-02-24 20:50:51 +0530
committerVibhav Pant2017-02-24 20:50:51 +0530
commitca611bda9cd462aa6d92cdaad1db9783afb27e8e (patch)
tree8cb39b139a6dca4ac349a3cd16e3026c19d4abfd
parent91932fff1ded8ed3b4d39dd06891f26960153b9e (diff)
downloademacs-ca611bda9cd462aa6d92cdaad1db9783afb27e8e.tar.gz
emacs-ca611bda9cd462aa6d92cdaad1db9783afb27e8e.zip
Optimize tail recursive calls while byte compiling.feature/byte-tail-recursion
* lisp/emacs-lisp/byte-opt.el (byte-optimize-stack-adjustment) (byte-optimize-conv-return-goto), (byte-optimize-copy-ops), (byte-optimize-called-function), (byte-optimize-lapcode-tail-recursion): New functions. * lisp/emacs-lisp/bytecomp.el: Add variables b-c-current-{defun, arglist}. (byte-compile-file-form-defmumble), (byte-compile): Set them. (byte-compile-out-toplevel): Use byte-optimize-lapcode-tail-recursion.
-rw-r--r--lisp/emacs-lisp/byte-opt.el91
-rw-r--r--lisp/emacs-lisp/bytecomp.el29
2 files changed, 112 insertions, 8 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 004f2e28653..a38571a245a 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -2148,6 +2148,97 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
2148 (setq byte-compile-maxdepth (+ byte-compile-maxdepth add-depth))) 2148 (setq byte-compile-maxdepth (+ byte-compile-maxdepth add-depth)))
2149 lap) 2149 lap)
2150 2150
2151;; Tail recursion optimization
2152
2153(defun byte-optimize-stack-adjustment (op)
2154 (and (not (eq (car op) 'TAG))
2155 (byte-compile-stack-adjustment
2156 (car op)
2157 (if (consp (cdr op)) (nth 1 op) (cdr op)))))
2158
2159(defun byte-optimize-conv-return-goto (lap n)
2160 (let ((arglist (reverse byte-compile-current-arglist))
2161 (args-copied 0)
2162 args current-arg-lapcode op current-arg)
2163 (while (/= args-copied (length arglist))
2164 (cl-decf n)
2165 (cl-multiple-value-setq (current-arg-lapcode n)
2166 (byte-optimize-copy-ops lap n -1 nil))
2167 (setq current-arg (assq (nth args-copied arglist)
2168 byte-compile-variables))
2169 (cl-assert current-arg)
2170 (push `(,@current-arg-lapcode (byte-varset ,@current-arg))
2171 args)
2172 (cl-incf args-copied))
2173 (apply #'append args)))
2174
2175;; recursively copy ops from lap until depth is met
2176(defun byte-optimize-copy-ops (lap n depth ops)
2177 (let* ((op (nth n lap))
2178 (depth-op (byte-optimize-stack-adjustment op))
2179 (new-depth (and depth-op (+ depth depth-op))))
2180 (push op ops)
2181 (if (zerop new-depth)
2182 (cl-values ops n)
2183 (byte-optimize-copy-ops lap (1- n) (or new-depth depth) ops))))
2184
2185(defun byte-optimize-called-function (lap n)
2186 "Return:
2187The function name being called at N in LAP
2188The index from where the call lapcode starts \(ie, where
2189\(byte-constant <func-name>) is\).
2190
2191N should point to a `byte-call' op in LAP."
2192 (let* ((op (nth n lap))
2193 (depth (byte-compile-stack-adjustment (car op) (cdr op))))
2194 (cl-assert (eq (car op) 'byte-call))
2195 (while (/= depth 0)
2196 (setq op (nth (cl-decf n) lap))
2197 (cl-incf depth (or (byte-optimize-stack-adjustment op) 0)))
2198 ;; we should be at (byte-constant . <func-name>)
2199 (setq op (nth (cl-decf n) lap))
2200 (cl-assert (eq (car op) 'byte-constant))
2201 (cl-values (cadr op) n)))
2202
2203(defun byte-optimize-lapcode-tail-recursion (lap)
2204 (let ((n (1- (length lap)))
2205 (func-start-tag (nth 0 lap))
2206 op)
2207 (unless (eq (car func-start-tag) 'TAG)
2208 (push (setq func-start-tag (byte-compile-make-tag)) lap)
2209 (setcdr (cdr func-start-tag) 0)
2210 (cl-incf n))
2211 (while (>= n 0)
2212 (setq op (nth n lap))
2213 (when (eq (car op) 'byte-return)
2214 ;; `byte-optimize-lapcode' merges redundant tags,
2215 ;; so we only need to subtract once.
2216 (let* ((call-op-n (if (eq (car (nth (1- n) lap)) 'TAG)
2217 (- n 2)
2218 (1- n))) ;; index of the potential `byte-call' op
2219 (op-call (nth call-op-n lap)) ;; the op at call-op-n
2220 func-name ;; name of the function being called
2221 func-call-start-n) ;; from where the actual call lapcode start
2222 (when (and (eq (car op-call) 'byte-call) ;; this is a tail call
2223 (progn
2224 (cl-multiple-value-setq (func-name func-call-start-n)
2225 (byte-optimize-called-function lap call-op-n))
2226 ;; this is a (tail) recursive call
2227 (eq byte-compile-current-defun func-name))
2228 (not (or (memq '&optional byte-compile-current-arglist)
2229 (memq '&rest byte-compile-current-arglist))))
2230 ;; "Lift" the calling lapcode out of LAP, and replace it with
2231 ;; our new tail call code.
2232 (setq lap (append
2233 (cl-subseq lap 0 func-call-start-n)
2234 (byte-optimize-conv-return-goto lap call-op-n)
2235 `((byte-unbind-all)
2236 (byte-goto . ,func-start-tag))
2237 (cl-subseq lap (1+ n)))
2238 n (length lap)))))
2239 (cl-decf n))
2240 lap))
2241
2151(provide 'byte-opt) 2242(provide 'byte-opt)
2152 2243
2153 2244
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 25513bd0248..efe86404fcf 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -197,6 +197,7 @@ adds `c' to it; otherwise adds `.elc'."
197;; that doesn't define this function, so this seems to be a reasonable 197;; that doesn't define this function, so this seems to be a reasonable
198;; thing to do. 198;; thing to do.
199(autoload 'byte-decompile-bytecode "byte-opt") 199(autoload 'byte-decompile-bytecode "byte-opt")
200(autoload 'byte-optimize-lapcode-tail-recursion "byte-opt")
200 201
201(defcustom byte-compile-verbose 202(defcustom byte-compile-verbose
202 (and (not noninteractive) (> baud-rate search-slow-speed)) 203 (and (not noninteractive) (> baud-rate search-slow-speed))
@@ -1000,6 +1001,8 @@ Each function's symbol gets added to `byte-compile-noruntime-functions'."
1000(defvar byte-compile-current-file nil) 1001(defvar byte-compile-current-file nil)
1001(defvar byte-compile-current-group nil) 1002(defvar byte-compile-current-group nil)
1002(defvar byte-compile-current-buffer nil) 1003(defvar byte-compile-current-buffer nil)
1004(defvar byte-compile-current-defun nil)
1005(defvar byte-compile-current-arglist nil)
1003 1006
1004;; Log something that isn't a warning. 1007;; Log something that isn't a warning.
1005(defmacro byte-compile-log (format-string &rest args) 1008(defmacro byte-compile-log (format-string &rest args)
@@ -2538,7 +2541,9 @@ not to take responsibility for the actual compilation of the code."
2538 ;; Tell the caller that we didn't compile it yet. 2541 ;; Tell the caller that we didn't compile it yet.
2539 nil) 2542 nil)
2540 2543
2541 (let* ((code (byte-compile-lambda (cons arglist body) t))) 2544 (let* ((byte-compile-current-defun name)
2545 (byte-compile-current-arglist arglist)
2546 (code (byte-compile-lambda (cons arglist body) t)))
2542 (if this-one 2547 (if this-one
2543 ;; A definition in b-c-initial-m-e should always take precedence 2548 ;; A definition in b-c-initial-m-e should always take precedence
2544 ;; during compilation, so don't let it be redefined. (Bug#8647) 2549 ;; during compilation, so don't let it be redefined. (Bug#8647)
@@ -2668,11 +2673,13 @@ If FORM is a lambda or a macro, byte-compile it as a function."
2668 (setq fun (byte-compile--reify-function fun))) 2673 (setq fun (byte-compile--reify-function fun)))
2669 ;; Expand macros. 2674 ;; Expand macros.
2670 (setq fun (byte-compile-preprocess fun)) 2675 (setq fun (byte-compile-preprocess fun))
2671 (setq fun (byte-compile-top-level fun nil 'eval)) 2676 (let ((byte-compile-current-defun (and (symbolp form) form))
2672 (if macro (push 'macro fun)) 2677 (byte-compile-current-arglist (nth 1 (cadr fun))))
2673 (if (symbolp form) 2678 (setq fun (byte-compile-top-level fun nil 'eval))
2674 (fset form fun) 2679 (if macro (push 'macro fun))
2675 fun))))))) 2680 (if (symbolp form)
2681 (fset form fun)
2682 fun))))))))
2676 2683
2677(defun byte-compile-sexp (sexp) 2684(defun byte-compile-sexp (sexp)
2678 "Compile and return SEXP." 2685 "Compile and return SEXP."
@@ -2923,9 +2930,15 @@ for symbols generated by the byte compiler itself."
2923 (caar tmp)))))) 2930 (caar tmp))))))
2924 (byte-compile-out 'byte-return 0) 2931 (byte-compile-out 'byte-return 0)
2925 (setq byte-compile-output (nreverse byte-compile-output)) 2932 (setq byte-compile-output (nreverse byte-compile-output))
2926 (if (memq byte-optimize '(t byte)) 2933 (when (memq byte-optimize '(t byte))
2934 (setq byte-compile-output
2935 (byte-optimize-lapcode byte-compile-output))
2936 ;; Do tail recursion optimization after `byte-optimize-lapcode',
2937 ;; since the lapcode now contains more than a single `byte-return',
2938 ;; allowing us to optimize multiple tail recursive calls
2939 (when byte-compile-current-defun
2927 (setq byte-compile-output 2940 (setq byte-compile-output
2928 (byte-optimize-lapcode byte-compile-output))) 2941 (byte-optimize-lapcode-tail-recursion byte-compile-output))))
2929 2942
2930 ;; Decompile trivial functions: 2943 ;; Decompile trivial functions:
2931 ;; only constants and variables, or a single funcall except in lambdas. 2944 ;; only constants and variables, or a single funcall except in lambdas.