diff options
| author | Vibhav Pant | 2017-02-24 20:50:51 +0530 |
|---|---|---|
| committer | Vibhav Pant | 2017-02-24 20:50:51 +0530 |
| commit | ca611bda9cd462aa6d92cdaad1db9783afb27e8e (patch) | |
| tree | 8cb39b139a6dca4ac349a3cd16e3026c19d4abfd | |
| parent | 91932fff1ded8ed3b4d39dd06891f26960153b9e (diff) | |
| download | emacs-feature/byte-tail-recursion.tar.gz emacs-feature/byte-tail-recursion.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.el | 91 | ||||
| -rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 29 |
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: | ||
| 2187 | The function name being called at N in LAP | ||
| 2188 | The index from where the call lapcode starts \(ie, where | ||
| 2189 | \(byte-constant <func-name>) is\). | ||
| 2190 | |||
| 2191 | N 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. |