aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorMattias EngdegÄrd2019-06-07 17:04:10 +0200
committerMattias EngdegÄrd2019-06-19 11:22:21 +0200
commitd3a7f3e6cd0124e62ed2b5ffc87eee57fee39a9a (patch)
tree865b52b1a0a33438b35a766de7198cea8aea3488 /lisp
parent14a81524c27ab54850e0fda736e4ee0c92e447b5 (diff)
downloademacs-d3a7f3e6cd0124e62ed2b5ffc87eee57fee39a9a.tar.gz
emacs-d3a7f3e6cd0124e62ed2b5ffc87eee57fee39a9a.zip
Compile any subsequence of `cond' clauses to switch (bug#36139)
A single `cond' form can how be compiled to any number of switch ops, optionally interspersed with non-switch conditions. Previously, switch ops would only be used for whole `cond' forms containing no other tests. * lisp/emacs-lisp/bytecomp.el (byte-compile--cond-vars): Rename from `byte-compile-cond-vars'. (byte-compile--default-val): Remove. (byte-compile--cond-switch-prefix): Replace `byte-compile-cond-jump-table-info'; now also returns trailing non-switch clauses. (byte-compile-cond-jump-table): New arguments; no longer compiles the default case. (byte-compile-cond): Look for and compile switches at any place in the list of clauses. * test/lisp/emacs-lisp/bytecomp-tests.el (byte-opt-testsuite-arith-data): Add test expression.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/emacs-lisp/bytecomp.el342
1 files changed, 162 insertions, 180 deletions
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index 3a23543f6a7..c01c74a4569 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -4122,7 +4122,7 @@ that suppresses all warnings during execution of BODY."
4122 (byte-compile-out-tag donetag)))) 4122 (byte-compile-out-tag donetag))))
4123 (setq byte-compile--for-effect nil)) 4123 (setq byte-compile--for-effect nil))
4124 4124
4125(defun byte-compile-cond-vars (obj1 obj2) 4125(defun byte-compile--cond-vars (obj1 obj2)
4126 ;; We make sure that of OBJ1 and OBJ2, one of them is a symbol, 4126 ;; We make sure that of OBJ1 and OBJ2, one of them is a symbol,
4127 ;; and the other is a constant expression whose value can be 4127 ;; and the other is a constant expression whose value can be
4128 ;; compared with `eq' (with `macroexp-const-p'). 4128 ;; compared with `eq' (with `macroexp-const-p').
@@ -4130,193 +4130,175 @@ that suppresses all warnings during execution of BODY."
4130 (and (symbolp obj1) (macroexp-const-p obj2) (cons obj1 (eval obj2))) 4130 (and (symbolp obj1) (macroexp-const-p obj2) (cons obj1 (eval obj2)))
4131 (and (symbolp obj2) (macroexp-const-p obj1) (cons obj2 (eval obj1))))) 4131 (and (symbolp obj2) (macroexp-const-p obj1) (cons obj2 (eval obj1)))))
4132 4132
4133(defconst byte-compile--default-val (cons nil nil) "A unique object.")
4134
4135(defun byte-compile--common-test (test-1 test-2) 4133(defun byte-compile--common-test (test-1 test-2)
4136 "Most specific common test of `eq', `eql' and `equal'" 4134 "Most specific common test of `eq', `eql' and `equal'"
4137 (cond ((or (eq test-1 'equal) (eq test-2 'equal)) 'equal) 4135 (cond ((or (eq test-1 'equal) (eq test-2 'equal)) 'equal)
4138 ((or (eq test-1 'eql) (eq test-2 'eql)) 'eql) 4136 ((or (eq test-1 'eql) (eq test-2 'eql)) 'eql)
4139 (t 'eq))) 4137 (t 'eq)))
4140 4138
4141(defun byte-compile-cond-jump-table-info (clauses) 4139(defun byte-compile--cond-switch-prefix (clauses)
4142 "If CLAUSES is a `cond' form where: 4140 "Find a switch corresponding to a prefix of CLAUSES, or nil if none.
4143The condition for each clause is of the form (TEST VAR VALUE). 4141Return (TAIL VAR TEST CASES), where:
4144VAR is a variable. 4142 TAIL is the remaining part of CLAUSES after the switch, including
4145TEST and VAR are the same throughout all conditions. 4143 any default clause,
4146VALUE satisfies `macroexp-const-p'. 4144 VAR is the variable being switched on,
4147 4145 TEST is the equality test (`eq', `eql' or `equal'),
4148Return a list of the form ((TEST . VAR) ((VALUES BODY) ...))" 4146 CASES is a list of (VALUES . BODY) where VALUES is a list of values
4149 (let ((cases '()) 4147 corresponding to BODY (always non-empty)."
4150 (ok t) 4148 (let ((cases nil) ; Reversed list of (VALUES BODY).
4151 (all-keys nil) 4149 (keys nil) ; Switch keys seen so far.
4152 (prev-test 'eq) 4150 (switch-var nil)
4153 prev-var) 4151 (switch-test 'eq))
4154 (and (catch 'break 4152 (while (pcase (car clauses)
4155 (dolist (clause (cdr clauses) ok) 4153 (`((,fn ,expr1 ,expr2) . ,body)
4156 (let* ((condition (car clause)) 4154 (let* ((vars (byte-compile--cond-vars expr1 expr2))
4157 (test (car-safe condition)) 4155 (var (car vars))
4158 (vars (when (consp condition) 4156 (value (cdr vars)))
4159 (byte-compile-cond-vars (cadr condition) (cl-caddr condition)))) 4157 (and var (or (eq var switch-var) (not switch-var))
4160 (obj1 (car-safe vars)) 4158 (cond
4161 (obj2 (cdr-safe vars)) 4159 ((memq fn '(eq eql equal))
4162 (body (cdr-safe clause))) 4160 (setq switch-var var)
4163 (unless prev-var 4161 (setq switch-test
4164 (setq prev-var obj1)) 4162 (byte-compile--common-test switch-test fn))
4165 (cond 4163 (unless (member value keys)
4166 ((and obj1 (memq test '(eq eql equal)) 4164 (push value keys)
4167 (eq obj1 prev-var)) 4165 (push (cons (list value) (or body '(t))) cases))
4168 (setq prev-test (byte-compile--common-test prev-test test)) 4166 t)
4169 ;; Discard values already tested for. 4167 ((and (memq fn '(memq memql member))
4170 (unless (member obj2 all-keys) 4168 (listp value)
4171 (push obj2 all-keys) 4169 ;; Require a non-empty body, since the member
4172 (push (list (list obj2) body) cases))) 4170 ;; function value depends on the switch
4173 4171 ;; argument.
4174 ((and obj1 (memq test '(memq memql member)) 4172 body)
4175 (eq obj1 prev-var) 4173 (setq switch-var var)
4176 (listp obj2) 4174 (setq switch-test
4177 ;; Require a non-empty body, since the member function 4175 (byte-compile--common-test
4178 ;; value depends on the switch argument. 4176 switch-test (cdr (assq fn '((memq . eq)
4179 body) 4177 (memql . eql)
4180 (setq prev-test 4178 (member . equal))))))
4181 (byte-compile--common-test 4179 (let ((vals nil))
4182 prev-test (cdr (assq test '((memq . eq) 4180 (dolist (elem value)
4183 (memql . eql) 4181 (unless (funcall fn elem keys)
4184 (member . equal)))))) 4182 (push elem vals)))
4185 (let ((vals nil)) 4183 (when vals
4186 ;; Discard values already tested for. 4184 (setq keys (append vals keys))
4187 (dolist (elem obj2) 4185 (push (cons (nreverse vals) body) cases)))
4188 (unless (funcall test elem all-keys) 4186 t))))))
4189 (push elem vals))) 4187 (setq clauses (cdr clauses)))
4190 (when vals 4188 ;; Assume that a single switch is cheaper than two or more discrete
4191 (setq all-keys (append vals all-keys)) 4189 ;; compare clauses. This could be tuned, possibly taking into
4192 (push (list vals body) cases)))) 4190 ;; account the total number of values involved.
4193 4191 (and (> (length cases) 1)
4194 ((and (macroexp-const-p condition) condition) 4192 (list clauses switch-var switch-test (nreverse cases)))))
4195 (push (list byte-compile--default-val 4193
4196 (or body `(,condition))) 4194(defun byte-compile-cond-jump-table (switch donetag)
4197 cases) 4195 "Generate code for SWITCH, ending at DONETAG."
4198 (throw 'break t)) 4196 (let* ((var (car switch))
4199 (t (setq ok nil) 4197 (test (nth 1 switch))
4200 (throw 'break nil)))))) 4198 (cases (nth 2 switch))
4201 (list (cons prev-test prev-var) (nreverse cases))))) 4199 jump-table test-objects body tag default-tag)
4202 4200 ;; TODO: Once :linear-search is implemented for `make-hash-table'
4203(defun byte-compile-cond-jump-table (clauses) 4201 ;; set it to `t' for cond forms with a small number of cases.
4204 (let* ((table-info (byte-compile-cond-jump-table-info clauses)) 4202 (let ((nvalues (apply #'+ (mapcar (lambda (case) (length (car case)))
4205 (test (caar table-info)) 4203 cases))))
4206 (var (cdar table-info)) 4204 (setq jump-table (make-hash-table
4207 (cases (cadr table-info)) 4205 :test test
4208 jump-table test-objects body tag donetag default-tag default-case) 4206 :purecopy t
4209 (when (and cases (not (= (length cases) 1))) 4207 :size nvalues)))
4210 ;; TODO: Once :linear-search is implemented for `make-hash-table' 4208 (setq default-tag (byte-compile-make-tag))
4211 ;; set it to `t' for cond forms with a small number of cases. 4209 ;; The structure of byte-switch code:
4212 (let ((nvalues (apply #'+ (mapcar (lambda (case) (length (car case))) 4210 ;;
4213 cases)))) 4211 ;; varref var
4214 (setq jump-table (make-hash-table 4212 ;; constant #s(hash-table purecopy t data (val1 (TAG1) val2 (TAG2)))
4215 :test test 4213 ;; switch
4216 :purecopy t 4214 ;; goto DEFAULT-TAG
4217 :size (if (assq byte-compile--default-val cases) 4215 ;; TAG1
4218 (1- nvalues) 4216 ;; <clause body>
4219 nvalues)))) 4217 ;; goto DONETAG
4220 (setq default-tag (byte-compile-make-tag)) 4218 ;; TAG2
4221 (setq donetag (byte-compile-make-tag)) 4219 ;; <clause body>
4222 ;; The structure of byte-switch code: 4220 ;; goto DONETAG
4223 ;; 4221 ;; DEFAULT-TAG
4224 ;; varref var 4222 ;; <body for remaining (non-switch) clauses>
4225 ;; constant #s(hash-table purecopy t data (val1 (TAG1) val2 (TAG2))) 4223 ;; DONETAG
4226 ;; switch 4224
4227 ;; goto DEFAULT-TAG 4225 (byte-compile-variable-ref var)
4228 ;; TAG1 4226 (byte-compile-push-constant jump-table)
4229 ;; <clause body> 4227 (byte-compile-out 'byte-switch)
4230 ;; goto DONETAG 4228
4231 ;; TAG2 4229 ;; When the opcode argument is `byte-goto', `byte-compile-goto' sets
4232 ;; <clause body> 4230 ;; `byte-compile-depth' to `nil'. However, we need `byte-compile-depth'
4233 ;; goto DONETAG 4231 ;; to be non-nil for generating tags for all cases. Since
4234 ;; DEFAULT-TAG 4232 ;; `byte-compile-depth' will increase by at most 1 after compiling
4235 ;; <body for `t' clause, if any (else `constant nil')> 4233 ;; all of the clause (which is further enforced by cl-assert below)
4236 ;; DONETAG 4234 ;; it should be safe to preserve its value.
4237 4235 (let ((byte-compile-depth byte-compile-depth))
4238 (byte-compile-variable-ref var) 4236 (byte-compile-goto 'byte-goto default-tag))
4239 (byte-compile-push-constant jump-table) 4237
4240 (byte-compile-out 'byte-switch) 4238 (dolist (case cases)
4241 4239 (setq tag (byte-compile-make-tag)
4242 ;; When the opcode argument is `byte-goto', `byte-compile-goto' sets 4240 test-objects (car case)
4243 ;; `byte-compile-depth' to `nil'. However, we need `byte-compile-depth' 4241 body (cdr case))
4244 ;; to be non-nil for generating tags for all cases. Since 4242 (byte-compile-out-tag tag)
4245 ;; `byte-compile-depth' will increase by at most 1 after compiling 4243 (dolist (value test-objects)
4246 ;; all of the clause (which is further enforced by cl-assert below) 4244 (puthash value tag jump-table))
4247 ;; it should be safe to preserve its value. 4245
4248 (let ((byte-compile-depth byte-compile-depth)) 4246 (let ((byte-compile-depth byte-compile-depth)
4249 (byte-compile-goto 'byte-goto default-tag)) 4247 (init-depth byte-compile-depth))
4250 4248 ;; Since `byte-compile-body' might increase `byte-compile-depth'
4251 (let ((default-match (assq byte-compile--default-val cases))) 4249 ;; by 1, not preserving its value will cause it to potentially
4252 (when default-match 4250 ;; increase by one for every clause body compiled, causing
4253 (setq default-case (cadr default-match) 4251 ;; depth/tag conflicts or violating asserts down the road.
4254 cases (butlast cases)))) 4252 ;; To make sure `byte-compile-body' itself doesn't violate this,
4255 4253 ;; we use `cl-assert'.
4256 (dolist (case cases) 4254 (byte-compile-body body byte-compile--for-effect)
4257 (setq tag (byte-compile-make-tag) 4255 (cl-assert (or (= byte-compile-depth init-depth)
4258 test-objects (nth 0 case) 4256 (= byte-compile-depth (1+ init-depth))))
4259 body (nth 1 case)) 4257 (byte-compile-goto 'byte-goto donetag)
4260 (byte-compile-out-tag tag) 4258 (setcdr (cdr donetag) nil)))
4261 (dolist (value test-objects) 4259
4262 (puthash value tag jump-table)) 4260 (byte-compile-out-tag default-tag)
4263 4261 (push jump-table byte-compile-jump-tables)))
4264 (let ((byte-compile-depth byte-compile-depth)
4265 (init-depth byte-compile-depth))
4266 ;; Since `byte-compile-body' might increase `byte-compile-depth'
4267 ;; by 1, not preserving its value will cause it to potentially
4268 ;; increase by one for every clause body compiled, causing
4269 ;; depth/tag conflicts or violating asserts down the road.
4270 ;; To make sure `byte-compile-body' itself doesn't violate this,
4271 ;; we use `cl-assert'.
4272 (if (null body)
4273 (byte-compile-form t byte-compile--for-effect)
4274 (byte-compile-body body byte-compile--for-effect))
4275 (cl-assert (or (= byte-compile-depth init-depth)
4276 (= byte-compile-depth (1+ init-depth))))
4277 (byte-compile-goto 'byte-goto donetag)
4278 (setcdr (cdr donetag) nil)))
4279
4280 (byte-compile-out-tag default-tag)
4281 (if default-case
4282 (byte-compile-body-do-effect default-case)
4283 (byte-compile-constant nil))
4284 (byte-compile-out-tag donetag)
4285 (push jump-table byte-compile-jump-tables))))
4286 4262
4287(defun byte-compile-cond (clauses) 4263(defun byte-compile-cond (clauses)
4288 (or (and byte-compile-cond-use-jump-table 4264 (let ((donetag (byte-compile-make-tag))
4289 (byte-compile-cond-jump-table clauses)) 4265 nexttag clause)
4290 (let ((donetag (byte-compile-make-tag)) 4266 (setq clauses (cdr clauses))
4291 nexttag clause) 4267 (while clauses
4292 (while (setq clauses (cdr clauses)) 4268 (let ((switch-prefix (and byte-compile-cond-use-jump-table
4293 (setq clause (car clauses)) 4269 (byte-compile--cond-switch-prefix clauses))))
4294 (cond ((or (eq (car clause) t) 4270 (if switch-prefix
4295 (and (eq (car-safe (car clause)) 'quote) 4271 (progn
4296 (car-safe (cdr-safe (car clause))))) 4272 (byte-compile-cond-jump-table (cdr switch-prefix) donetag)
4297 ;; Unconditional clause 4273 (setq clauses (car switch-prefix)))
4298 (setq clause (cons t clause) 4274 (setq clause (car clauses))
4299 clauses nil)) 4275 (cond ((or (eq (car clause) t)
4300 ((cdr clauses) 4276 (and (eq (car-safe (car clause)) 'quote)
4301 (byte-compile-form (car clause)) 4277 (car-safe (cdr-safe (car clause)))))
4302 (if (null (cdr clause)) 4278 ;; Unconditional clause
4303 ;; First clause is a singleton. 4279 (setq clause (cons t clause)
4304 (byte-compile-goto-if t byte-compile--for-effect donetag) 4280 clauses nil))
4305 (setq nexttag (byte-compile-make-tag)) 4281 ((cdr clauses)
4306 (byte-compile-goto 'byte-goto-if-nil nexttag) 4282 (byte-compile-form (car clause))
4307 (byte-compile-maybe-guarded (car clause) 4283 (if (null (cdr clause))
4308 (byte-compile-body (cdr clause) byte-compile--for-effect)) 4284 ;; First clause is a singleton.
4309 (byte-compile-goto 'byte-goto donetag) 4285 (byte-compile-goto-if t byte-compile--for-effect donetag)
4310 (byte-compile-out-tag nexttag))))) 4286 (setq nexttag (byte-compile-make-tag))
4311 ;; Last clause 4287 (byte-compile-goto 'byte-goto-if-nil nexttag)
4312 (let ((guard (car clause))) 4288 (byte-compile-maybe-guarded (car clause)
4313 (and (cdr clause) (not (eq guard t)) 4289 (byte-compile-body (cdr clause) byte-compile--for-effect))
4314 (progn (byte-compile-form guard) 4290 (byte-compile-goto 'byte-goto donetag)
4315 (byte-compile-goto-if nil byte-compile--for-effect donetag) 4291 (byte-compile-out-tag nexttag))))
4316 (setq clause (cdr clause)))) 4292 (setq clauses (cdr clauses)))))
4317 (byte-compile-maybe-guarded guard 4293 ;; Last clause
4318 (byte-compile-body-do-effect clause))) 4294 (let ((guard (car clause)))
4319 (byte-compile-out-tag donetag)))) 4295 (and (cdr clause) (not (eq guard t))
4296 (progn (byte-compile-form guard)
4297 (byte-compile-goto-if nil byte-compile--for-effect donetag)
4298 (setq clause (cdr clause))))
4299 (byte-compile-maybe-guarded guard
4300 (byte-compile-body-do-effect clause)))
4301 (byte-compile-out-tag donetag)))
4320 4302
4321(defun byte-compile-and (form) 4303(defun byte-compile-and (form)
4322 (let ((failtag (byte-compile-make-tag)) 4304 (let ((failtag (byte-compile-make-tag))