aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVibhav Pant2017-01-15 01:26:04 +0530
committerVibhav Pant2017-01-15 01:26:04 +0530
commit88549ec38e9bb30e338a9985d0de4e6263b40fb7 (patch)
tree4ee41982939210a6f8a6d74fd539b528805b7b04
parent877c525f4b98bc785f1bb0b50d70f72d09c80eb2 (diff)
downloademacs-88549ec38e9bb30e338a9985d0de4e6263b40fb7.tar.gz
emacs-88549ec38e9bb30e338a9985d0de4e6263b40fb7.zip
Add new 'switch' byte-code.
'switch' takes two arguments from the stack: the variable to test, and a jump table (implemented as a hash-table with the appropriate :test function). By looking up the value of the variable in the hash table, the interpreter can jump to the label pointed to by the value, if any. This implementation can only be used for `cond' forms of the type `(cond ((test x 'foo) 'bar) ...)`, such that the function `test` and variable `x` is same for all clauses. * lisp/emacs-lisp/bytecomp.el: * Add (byte-compile-cond-valid-obj2-p), (byte-compile-cond-vars), (byte-compile-cond-jump-table-info), (byte-compile-jump-table-add-tag), (byte-compile-cond-jump-table), byte-compile-jump-tables. * Add defcustom `byte-compile-cond-use-jump-table'. * (byte-compile-cond): Use them. * (byte-compile-lapcode): Patch tags present in jump tables, if any. * lisp/emacs-lisp//byte-opt.el: (byte-optimize-lapcode): Add checks to some peephole optimizations to prevent them from messing up any code involving `byte-switch`. * src/bytecode.c: (exec_byte_code): Add bytecode Bswitch.
-rw-r--r--lisp/emacs-lisp/byte-opt.el14
-rw-r--r--lisp/emacs-lisp/bytecomp.el174
-rw-r--r--src/bytecode.c16
3 files changed, 166 insertions, 38 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 13f885448ae..9412ce3b26d 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -185,6 +185,7 @@
185(require 'bytecomp) 185(require 'bytecomp)
186(eval-when-compile (require 'cl-lib)) 186(eval-when-compile (require 'cl-lib))
187(require 'macroexp) 187(require 'macroexp)
188(require 'subr-x)
188 189
189(defun byte-compile-log-lap-1 (format &rest args) 190(defun byte-compile-log-lap-1 (format &rest args)
190 ;; Newer byte codes for stack-ref make the slot 0 non-nil again. 191 ;; Newer byte codes for stack-ref make the slot 0 non-nil again.
@@ -1728,7 +1729,11 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
1728 ;; unused-TAG: --> <deleted> 1729 ;; unused-TAG: --> <deleted>
1729 ;; 1730 ;;
1730 ((and (eq 'TAG (car lap0)) 1731 ((and (eq 'TAG (car lap0))
1731 (not (rassq lap0 lap))) 1732 (not (rassq lap0 lap))
1733 (= (length (cl-loop for table in byte-compile-jump-tables
1734 when (member lap0 (hash-table-values table))
1735 collect t))
1736 0))
1732 (and (memq byte-optimize-log '(t byte)) 1737 (and (memq byte-optimize-log '(t byte))
1733 (byte-compile-log " unused tag %d removed" (nth 1 lap0))) 1738 (byte-compile-log " unused tag %d removed" (nth 1 lap0)))
1734 (setq lap (delq lap0 lap) 1739 (setq lap (delq lap0 lap)
@@ -1736,9 +1741,12 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance."
1736 ;; 1741 ;;
1737 ;; goto ... --> goto <delete until TAG or end> 1742 ;; goto ... --> goto <delete until TAG or end>
1738 ;; return ... --> return <delete until TAG or end> 1743 ;; return ... --> return <delete until TAG or end>
1739 ;; 1744 ;; (unless a jump-table is being used, where deleting may affect
1745 ;; other valid case bodies)
1746 ;;
1740 ((and (memq (car lap0) '(byte-goto byte-return)) 1747 ((and (memq (car lap0) '(byte-goto byte-return))
1741 (not (memq (car lap1) '(TAG nil)))) 1748 (not (memq (car lap1) '(TAG nil)))
1749 (not byte-compile-jump-tables))
1742 (setq tmp rest) 1750 (setq tmp rest)
1743 (let ((i 0) 1751 (let ((i 0)
1744 (opt-p (memq byte-optimize-log '(t lap))) 1752 (opt-p (memq byte-optimize-log '(t lap)))
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 63be7e208b3..fe91fecd355 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -223,6 +223,11 @@ This includes variable references and calls to functions such as `car'."
223 :group 'bytecomp 223 :group 'bytecomp
224 :type 'boolean) 224 :type 'boolean)
225 225
226(defcustom byte-compile-cond-use-jump-table t
227 "Compile `cond' clauses to a jump table implementation (using a hash-table)."
228 :group 'bytecomp
229 :type 'boolean)
230
226(defvar byte-compile-dynamic nil 231(defvar byte-compile-dynamic nil
227 "If non-nil, compile function bodies so they load lazily. 232 "If non-nil, compile function bodies so they load lazily.
228They are hidden in comments in the compiled file, 233They are hidden in comments in the compiled file,
@@ -412,6 +417,8 @@ specify different fields to sort on."
412 (const calls+callers) (const nil))) 417 (const calls+callers) (const nil)))
413 418
414(defvar byte-compile-debug nil) 419(defvar byte-compile-debug nil)
420(defvar byte-compile-jump-tables nil
421 "List of all jump tables used during compilation of this form.")
415(defvar byte-compile-constants nil 422(defvar byte-compile-constants nil
416 "List of all constants encountered during compilation of this form.") 423 "List of all constants encountered during compilation of this form.")
417(defvar byte-compile-variables nil 424(defvar byte-compile-variables nil
@@ -747,6 +754,8 @@ otherwise pop it")
747;; `byte-compile-lapcode'). 754;; `byte-compile-lapcode').
748(defconst byte-discardN-preserve-tos byte-discardN) 755(defconst byte-discardN-preserve-tos byte-discardN)
749 756
757(byte-defop 183 -2 byte-switch)
758
750;; unused: 182-191 759;; unused: 182-191
751 760
752(byte-defop 192 1 byte-constant "for reference to a constant") 761(byte-defop 192 1 byte-constant "for reference to a constant")
@@ -823,7 +832,7 @@ CONST2 may be evaluated multiple times."
823 op off ; Operation & offset 832 op off ; Operation & offset
824 opcode ; numeric value of OP 833 opcode ; numeric value of OP
825 (bytes '()) ; Put the output bytes here 834 (bytes '()) ; Put the output bytes here
826 (patchlist nil)) ; List of gotos to patch 835 (patchlist nil)) ; List of gotos to patch
827 (dolist (lap-entry lap) 836 (dolist (lap-entry lap)
828 (setq op (car lap-entry) 837 (setq op (car lap-entry)
829 off (cdr lap-entry)) 838 off (cdr lap-entry))
@@ -905,6 +914,11 @@ CONST2 may be evaluated multiple times."
905 ;; FIXME: Replace this by some workaround. 914 ;; FIXME: Replace this by some workaround.
906 (if (> (car bytes-tail) 255) (error "Bytecode overflow"))) 915 (if (> (car bytes-tail) 255) (error "Bytecode overflow")))
907 916
917 (dolist (hash-table byte-compile-jump-tables)
918 (cl-loop for k being the hash-keys of hash-table do
919 (let ((tag (cdr (gethash k hash-table))))
920 (setq pc (car tag))
921 (puthash k (cons (logand pc 255) (lsh pc -8)) hash-table))))
908 (apply 'unibyte-string (nreverse bytes)))) 922 (apply 'unibyte-string (nreverse bytes))))
909 923
910 924
@@ -1954,7 +1968,8 @@ With argument ARG, insert value in current buffer after the form."
1954;; (edebug-all-defs nil) 1968;; (edebug-all-defs nil)
1955;; (edebug-all-forms nil) 1969;; (edebug-all-forms nil)
1956 ;; Simulate entry to byte-compile-top-level 1970 ;; Simulate entry to byte-compile-top-level
1957 (byte-compile-constants nil) 1971 (byte-compile-jump-tables nil)
1972 (byte-compile-constants nil)
1958 (byte-compile-variables nil) 1973 (byte-compile-variables nil)
1959 (byte-compile-tag-number 0) 1974 (byte-compile-tag-number 0)
1960 (byte-compile-depth 0) 1975 (byte-compile-depth 0)
@@ -2250,7 +2265,8 @@ list that represents a doc string reference.
2250 byte-compile-variables nil 2265 byte-compile-variables nil
2251 byte-compile-depth 0 2266 byte-compile-depth 0
2252 byte-compile-maxdepth 0 2267 byte-compile-maxdepth 0
2253 byte-compile-output nil)))) 2268 byte-compile-output nil
2269 byte-compile-jump-tables nil))))
2254 2270
2255(defvar byte-compile-force-lexical-warnings nil) 2271(defvar byte-compile-force-lexical-warnings nil)
2256 2272
@@ -2862,7 +2878,8 @@ for symbols generated by the byte compiler itself."
2862 (byte-compile-maxdepth 0) 2878 (byte-compile-maxdepth 0)
2863 (byte-compile--lexical-environment lexenv) 2879 (byte-compile--lexical-environment lexenv)
2864 (byte-compile-reserved-constants (or reserved-csts 0)) 2880 (byte-compile-reserved-constants (or reserved-csts 0))
2865 (byte-compile-output nil)) 2881 (byte-compile-output nil)
2882 (byte-compile-jump-tables nil))
2866 (if (memq byte-optimize '(t source)) 2883 (if (memq byte-optimize '(t source))
2867 (setq form (byte-optimize-form form byte-compile--for-effect))) 2884 (setq form (byte-optimize-form form byte-compile--for-effect)))
2868 (while (and (eq (car-safe form) 'progn) (null (cdr (cdr form)))) 2885 (while (and (eq (car-safe form) 'progn) (null (cdr (cdr form))))
@@ -3951,37 +3968,124 @@ that suppresses all warnings during execution of BODY."
3951 (byte-compile-out-tag donetag)))) 3968 (byte-compile-out-tag donetag))))
3952 (setq byte-compile--for-effect nil)) 3969 (setq byte-compile--for-effect nil))
3953 3970
3971(defun byte-compile-cond-valid-obj2-p (obj)
3972 (cond
3973 ((symbolp obj) (keywordp obj))
3974 ((consp obj) (eq (car obj) 'quote))
3975 (t t)))
3976
3977(defun byte-compile-cond-vars (obj1 obj2)
3978 (or
3979 (and (symbolp obj1) (byte-compile-cond-valid-obj2-p obj2) (cons obj1 obj2))
3980 (and (symbolp obj2) (byte-compile-cond-valid-obj2-p obj1) (cons obj2 obj1))))
3981
3982(defun byte-compile-cond-jump-table-info (clauses)
3983 (let ((cases '())
3984 (ok t)
3985 prev-var prev-test)
3986 (and (catch 'break
3987 (dolist (clause (cdr clauses) ok)
3988 (let* ((condition (car clause))
3989 (test (car-safe condition))
3990 (vars (when (consp condition)
3991 (byte-compile-cond-vars (cadr condition) (cl-caddr condition))))
3992 (obj1 (car-safe vars))
3993 (obj2 (cdr-safe vars))
3994 (body (cdr-safe clause)))
3995 (unless prev-var
3996 (setq prev-var obj1))
3997 (unless prev-test
3998 (setq prev-test test))
3999 (if (and obj1 (memq test '(eq eql equal))
4000 (consp condition)
4001 (eq test prev-test)
4002 (eq obj1 prev-var))
4003 (push (list obj2 body) cases)
4004 (if (eq condition t)
4005 (progn (push (list 'default body) cases)
4006 (throw 'break t))
4007 (setq ok nil)
4008 (throw 'break nil))))))
4009 (list (cons prev-test prev-var) (nreverse cases)))))
4010
4011(defun byte-compile-jump-table-add-tag (value tag jump-table)
4012 (setcdr (cdr tag) byte-compile-depth)
4013 (puthash value tag jump-table))
4014
4015(defun byte-compile-cond-jump-table (clauses)
4016 (let* ((table-info (byte-compile-cond-jump-table-info clauses))
4017 (test (caar table-info))
4018 (var (cdar table-info))
4019 (cases (cadr table-info))
4020 jump-table test-obj body tag donetag finaltag finalcase)
4021 (when (and cases (not (= (length cases) 1)))
4022 (setq jump-table (make-hash-table :test test :size (length cases))
4023 donetag (byte-compile-make-tag))
4024 (byte-compile-variable-ref var)
4025 (byte-compile-push-constant jump-table)
4026 (byte-compile-out 'byte-switch)
4027
4028 (when (assq 'default cases)
4029 (setq finalcase (cadr (assq 'default cases))
4030 finaltag (byte-compile-make-tag))
4031 (setq cases (butlast cases 1))
4032 (let ((byte-compile-depth byte-compile-depth))
4033 (byte-compile-goto 'byte-goto finaltag)))
4034
4035 (dolist (case cases)
4036 (setq tag (byte-compile-make-tag)
4037 test-obj (nth 0 case)
4038 body (nth 1 case))
4039 (byte-compile-out-tag tag)
4040 (byte-compile-jump-table-add-tag test-obj tag jump-table)
4041
4042 (let ((byte-compile-depth byte-compile-depth))
4043 (byte-compile-maybe-guarded `(,test ,var ,test-obj)
4044 (byte-compile-body body byte-compile--for-effect))
4045 (byte-compile-goto 'byte-goto donetag))
4046 (setcdr (cdr donetag) nil))
4047
4048 (if finalcase
4049 (progn (byte-compile-out-tag finaltag)
4050 (byte-compile-body-do-effect finalcase))
4051 (byte-compile-push-constant nil))
4052 (byte-compile-out-tag donetag)
4053 (push jump-table byte-compile-jump-tables))))
4054
3954(defun byte-compile-cond (clauses) 4055(defun byte-compile-cond (clauses)
3955 (let ((donetag (byte-compile-make-tag)) 4056 (or (and byte-compile-cond-use-jump-table (byte-compile-cond-jump-table clauses))
3956 nexttag clause) 4057 (let ((donetag (byte-compile-make-tag))
3957 (while (setq clauses (cdr clauses)) 4058 nexttag clause)
3958 (setq clause (car clauses)) 4059 (while (setq clauses (cdr clauses))
3959 (cond ((or (eq (car clause) t) 4060 (setq clause (car clauses))
3960 (and (eq (car-safe (car clause)) 'quote) 4061 (cond ((or (eq (car clause) t)
3961 (car-safe (cdr-safe (car clause))))) 4062 (and (eq (car-safe (car clause)) 'quote)
3962 ;; Unconditional clause 4063 (car-safe (cdr-safe (car clause)))))
3963 (setq clause (cons t clause) 4064 ;; Unconditional clause
3964 clauses nil)) 4065 (setq clause (cons t clause)
3965 ((cdr clauses) 4066 clauses nil))
3966 (byte-compile-form (car clause)) 4067 ((cdr clauses)
3967 (if (null (cdr clause)) 4068 (byte-compile-form (car clause))
3968 ;; First clause is a singleton. 4069 ;; (message "out %s" donetag)
3969 (byte-compile-goto-if t byte-compile--for-effect donetag) 4070 (if (null (cdr clause))
3970 (setq nexttag (byte-compile-make-tag)) 4071 ;; First clause is a singleton.
3971 (byte-compile-goto 'byte-goto-if-nil nexttag) 4072 (byte-compile-goto-if t byte-compile--for-effect donetag)
3972 (byte-compile-maybe-guarded (car clause) 4073 ;; (message "inside %s" donetag)
3973 (byte-compile-body (cdr clause) byte-compile--for-effect)) 4074 (setq nexttag (byte-compile-make-tag))
3974 (byte-compile-goto 'byte-goto donetag) 4075 (byte-compile-goto 'byte-goto-if-nil nexttag)
3975 (byte-compile-out-tag nexttag))))) 4076 (byte-compile-maybe-guarded (car clause)
3976 ;; Last clause 4077 (byte-compile-body (cdr clause) byte-compile--for-effect))
3977 (let ((guard (car clause))) 4078 (byte-compile-goto 'byte-goto donetag)
3978 (and (cdr clause) (not (eq guard t)) 4079 (byte-compile-out-tag nexttag)))))
3979 (progn (byte-compile-form guard) 4080 ;; Last clause
3980 (byte-compile-goto-if nil byte-compile--for-effect donetag) 4081 (let ((guard (car clause)))
3981 (setq clause (cdr clause)))) 4082 (and (cdr clause) (not (eq guard t))
3982 (byte-compile-maybe-guarded guard 4083 (progn (byte-compile-form guard)
3983 (byte-compile-body-do-effect clause))) 4084 (byte-compile-goto-if nil byte-compile--for-effect donetag)
3984 (byte-compile-out-tag donetag))) 4085 (setq clause (cdr clause))))
4086 (byte-compile-maybe-guarded guard
4087 (byte-compile-body-do-effect clause)))
4088 (byte-compile-out-tag donetag))))
3985 4089
3986(defun byte-compile-and (form) 4090(defun byte-compile-and (form)
3987 (let ((failtag (byte-compile-make-tag)) 4091 (let ((failtag (byte-compile-make-tag))
@@ -4528,7 +4632,7 @@ binding slots have been popped."
4528 (and byte-compile-depth 4632 (and byte-compile-depth
4529 (not (= (cdr (cdr tag)) byte-compile-depth)) 4633 (not (= (cdr (cdr tag)) byte-compile-depth))
4530 (error "Compiler bug: depth conflict at tag %d" (car (cdr tag)))) 4634 (error "Compiler bug: depth conflict at tag %d" (car (cdr tag))))
4531 (setq byte-compile-depth (cdr (cdr tag)))) 4635 (setq byte-compile-depth (cdr (cdr tag))))
4532 (setcdr (cdr tag) byte-compile-depth))) 4636 (setcdr (cdr tag) byte-compile-depth)))
4533 4637
4534(defun byte-compile-goto (opcode tag) 4638(defun byte-compile-goto (opcode tag)
diff --git a/src/bytecode.c b/src/bytecode.c
index a64bc171d14..1695af9cb02 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -267,6 +267,8 @@ DEFINE (Bstack_set, 0262) \
267DEFINE (Bstack_set2, 0263) \ 267DEFINE (Bstack_set2, 0263) \
268DEFINE (BdiscardN, 0266) \ 268DEFINE (BdiscardN, 0266) \
269 \ 269 \
270DEFINE (Bswitch, 0267) \
271 \
270DEFINE (Bconstant, 0300) 272DEFINE (Bconstant, 0300)
271 273
272enum byte_code_op 274enum byte_code_op
@@ -1411,6 +1413,20 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
1411 DISCARD (op); 1413 DISCARD (op);
1412 NEXT; 1414 NEXT;
1413 1415
1416 CASE (Bswitch):
1417 {
1418 Lisp_Object jmp_table = POP;
1419 Lisp_Object v1 = POP;
1420 Lisp_Object dest = Fgethash(v1, jmp_table, Qnil);
1421 if (!NILP(dest)) {
1422 int car = XINT(XCAR(dest));
1423 int cdr = XINT(XCDR(dest));
1424 op = car + (cdr << 8); /* Simulate FETCH2 */
1425 goto op_branch;
1426 }
1427 }
1428 NEXT;
1429
1414 CASE_DEFAULT 1430 CASE_DEFAULT
1415 CASE (Bconstant): 1431 CASE (Bconstant):
1416 if (BYTE_CODE_SAFE 1432 if (BYTE_CODE_SAFE