aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Tromey2018-01-20 12:25:26 -0700
committerTom Tromey2018-01-22 22:11:26 -0700
commit916094a84f0ab31be31aa6c3632f14176b4e882a (patch)
tree5d14b3b849b7b63f19577bd45bbbd85cdba0b702
parenta6b4b9b4af5405b62cbd59f5ce23ca0fe0027ac7 (diff)
downloademacs-feature/byte-unwind-protect.tar.gz
emacs-feature/byte-unwind-protect.zip
Add new bytecodes for unwind-protectfeature/byte-unwind-protect
* lisp/emacs-lisp/byte-opt.el (disassemble-offset): Handle byte-pushunwindprotect. * lisp/emacs-lisp/bytecomp.el (byte-pushunwindprotect) (byte-endunwindprotect): New bytecodes. (byte-goto-ops): Add byte-pushunwindprotect. (byte-compile-unwind-protect): Emit new bytecodes. (byte-compile-goto): Handle byte-pushunwindprotect. * lisp/emacs-lisp/cconv.el (cconv-convert): Don't special-case unwind-protect when byte-compile--use-old-handlers. (cconv-analyze-form): Likewise. * src/bytecode.c (Bpushunwindprotect, Bendunwindprotect): New bytecodes. (exec_byte_code): Implement new bytecodes. * test/src/bytecode-tests.el: New file.
-rw-r--r--lisp/emacs-lisp/byte-opt.el3
-rw-r--r--lisp/emacs-lisp/bytecomp.el66
-rw-r--r--lisp/emacs-lisp/cconv.el7
-rw-r--r--src/bytecode.c43
-rw-r--r--test/src/bytecode-tests.el58
5 files changed, 152 insertions, 25 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index e5e5f4ee590..5292deda6ea 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -1324,7 +1324,8 @@
1324 (<= bytedecomp-op byte-goto-if-not-nil-else-pop)) 1324 (<= bytedecomp-op byte-goto-if-not-nil-else-pop))
1325 (memq bytedecomp-op (eval-when-compile 1325 (memq bytedecomp-op (eval-when-compile
1326 (list byte-stack-set2 byte-pushcatch 1326 (list byte-stack-set2 byte-pushcatch
1327 byte-pushconditioncase)))) 1327 byte-pushconditioncase
1328 byte-pushunwindprotect))))
1328 ;; Offset in next 2 bytes. 1329 ;; Offset in next 2 bytes.
1329 (setq bytedecomp-ptr (1+ bytedecomp-ptr)) 1330 (setq bytedecomp-ptr (1+ bytedecomp-ptr))
1330 (+ (aref bytes bytedecomp-ptr) 1331 (+ (aref bytes bytedecomp-ptr)
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index a64c88c4f0d..5e04a620f33 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -603,8 +603,12 @@ Each element is (INDEX . VALUE)")
603(byte-defop 48 0 byte-pophandler) 603(byte-defop 48 0 byte-pophandler)
604(byte-defop 50 -1 byte-pushcatch) 604(byte-defop 50 -1 byte-pushcatch)
605(byte-defop 49 -1 byte-pushconditioncase) 605(byte-defop 49 -1 byte-pushconditioncase)
606;; New (in Emacs 27.1) bytecode for efficient handling of
607;; unwind-protect.
608(byte-defop 51 0 byte-pushunwindprotect)
609(byte-defop 52 -1 byte-endunwindprotect)
606 610
607;; unused: 51-55 611;; unused: 53-55
608 612
609(byte-defop 56 -1 byte-nth) 613(byte-defop 56 -1 byte-nth)
610(byte-defop 57 0 byte-symbolp) 614(byte-defop 57 0 byte-symbolp)
@@ -781,7 +785,8 @@ the value maps to, if any.")
781(defconst byte-goto-ops '(byte-goto byte-goto-if-nil byte-goto-if-not-nil 785(defconst byte-goto-ops '(byte-goto byte-goto-if-nil byte-goto-if-not-nil
782 byte-goto-if-nil-else-pop 786 byte-goto-if-nil-else-pop
783 byte-goto-if-not-nil-else-pop 787 byte-goto-if-not-nil-else-pop
784 byte-pushcatch byte-pushconditioncase) 788 byte-pushcatch byte-pushconditioncase
789 byte-pushunwindprotect)
785 "List of byte-codes whose offset is a pc.") 790 "List of byte-codes whose offset is a pc.")
786 791
787(defconst byte-goto-always-pop-ops '(byte-goto-if-nil byte-goto-if-not-nil)) 792(defconst byte-goto-always-pop-ops '(byte-goto-if-nil byte-goto-if-not-nil))
@@ -4459,18 +4464,33 @@ binding slots have been popped."
4459 (byte-compile-out 'byte-catch 0))) 4464 (byte-compile-out 'byte-catch 0)))
4460 4465
4461(defun byte-compile-unwind-protect (form) 4466(defun byte-compile-unwind-protect (form)
4462 (pcase (cddr form) 4467 (if (not byte-compile--use-old-handlers)
4463 (`(:fun-body ,f) 4468 (let ((except-tag (byte-compile-make-tag)))
4464 (byte-compile-form 4469 ;; If the goto is called, we'll have 2 extra items on the
4465 (if byte-compile--use-old-handlers `(list (list 'funcall ,f)) f))) 4470 ;; stack.
4466 (handlers 4471 (byte-compile-goto 'byte-pushunwindprotect except-tag)
4467 (if byte-compile--use-old-handlers 4472 (byte-compile-form (cadr form) nil)
4468 (byte-compile-push-constant 4473 (byte-compile-out 'byte-pophandler)
4469 (byte-compile-top-level-body handlers t)) 4474 ;; The value of the body is on the stack; now push a flag so
4470 (byte-compile-form `#'(lambda () ,@handlers))))) 4475 ;; that the coming endunwindprotect instruction knows what to
4471 (byte-compile-out 'byte-unwind-protect 0) 4476 ;; do.
4472 (byte-compile-form-do-effect (car (cdr form))) 4477 (byte-compile-push-constant nil)
4473 (byte-compile-out 'byte-unbind 1)) 4478 ;; The unwind forms.
4479 (byte-compile-out-tag except-tag)
4480 (byte-compile-body (cddr form) t)
4481 (byte-compile-out 'byte-endunwindprotect))
4482 (pcase (cddr form)
4483 (`(:fun-body ,f)
4484 (byte-compile-form
4485 (if byte-compile--use-old-handlers `(list (list 'funcall ,f)) f)))
4486 (handlers
4487 (if byte-compile--use-old-handlers
4488 (byte-compile-push-constant
4489 (byte-compile-top-level-body handlers t))
4490 (byte-compile-form `#'(lambda () ,@handlers)))))
4491 (byte-compile-out 'byte-unwind-protect 0)
4492 (byte-compile-form-do-effect (car (cdr form)))
4493 (byte-compile-out 'byte-unbind 1)))
4474 4494
4475(defun byte-compile-condition-case (form) 4495(defun byte-compile-condition-case (form)
4476 (if byte-compile--use-old-handlers 4496 (if byte-compile--use-old-handlers
@@ -4810,11 +4830,19 @@ binding slots have been popped."
4810 4830
4811(defun byte-compile-goto (opcode tag) 4831(defun byte-compile-goto (opcode tag)
4812 (push (cons opcode tag) byte-compile-output) 4832 (push (cons opcode tag) byte-compile-output)
4813 (setcdr (cdr tag) (if (memq opcode byte-goto-always-pop-ops) 4833 (setcdr (cdr tag)
4814 (1- byte-compile-depth) 4834 (cond
4815 byte-compile-depth)) 4835 ((memq opcode byte-goto-always-pop-ops)
4816 (setq byte-compile-depth (and (not (eq opcode 'byte-goto)) 4836 (1- byte-compile-depth))
4817 (1- byte-compile-depth)))) 4837 ((eq opcode 'byte-pushunwindprotect)
4838 (+ 2 byte-compile-depth))
4839 (t byte-compile-depth)))
4840 (setq byte-compile-depth
4841 (cond
4842 ((eq opcode 'byte-goto) nil)
4843 ((eq opcode 'byte-pushunwindprotect)
4844 byte-compile-depth)
4845 (t (1- byte-compile-depth)))))
4818 4846
4819(defun byte-compile-stack-adjustment (op operand) 4847(defun byte-compile-stack-adjustment (op operand)
4820 "Return the amount by which an operation adjusts the stack. 4848 "Return the amount by which an operation adjusts the stack.
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index 02fe794467b..925292483fd 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -87,7 +87,6 @@
87;; command-history). 87;; command-history).
88;; - canonize code in macro-expand so we don't have to handle (let (var) body) 88;; - canonize code in macro-expand so we don't have to handle (let (var) body)
89;; and other oddities. 89;; and other oddities.
90;; - new byte codes for unwind-protect so that closures aren't needed at all.
91;; - a reference to a var that is known statically to always hold a constant 90;; - a reference to a var that is known statically to always hold a constant
92;; should be turned into a byte-constant rather than a byte-stack-ref. 91;; should be turned into a byte-constant rather than a byte-stack-ref.
93;; Hmm... right, that's called constant propagation and could be done here, 92;; Hmm... right, that's called constant propagation and could be done here,
@@ -487,7 +486,8 @@ places where they originally did not directly appear."
487 handlers)))) 486 handlers))))
488 487
489 (`(,(and head (or (and `catch (guard byte-compile--use-old-handlers)) 488 (`(,(and head (or (and `catch (guard byte-compile--use-old-handlers))
490 `unwind-protect)) 489 (and `unwind-protect
490 (guard byte-compile--use-old-handlers))))
491 ,form . ,body) 491 ,form . ,body)
492 `(,head ,(cconv-convert form env extend) 492 `(,head ,(cconv-convert form env extend)
493 :fun-body ,(cconv--convert-function () body env form))) 493 :fun-body ,(cconv--convert-function () body env form)))
@@ -728,9 +728,8 @@ and updates the data stored in ENV."
728 (if var (cconv--analyze-use (cons (list var) (cdr varstruct)) 728 (if var (cconv--analyze-use (cons (list var) (cdr varstruct))
729 form "variable")))) 729 form "variable"))))
730 730
731 ;; FIXME: The bytecode for unwind-protect forces us to wrap the unwind.
732 (`(,(or (and `catch (guard byte-compile--use-old-handlers)) 731 (`(,(or (and `catch (guard byte-compile--use-old-handlers))
733 `unwind-protect) 732 (and `unwind-protect (guard byte-compile--use-old-handlers)))
734 ,form . ,body) 733 ,form . ,body)
735 (cconv-analyze-form form env) 734 (cconv-analyze-form form env)
736 (cconv--analyze-function () body env form)) 735 (cconv--analyze-function () body env form))
diff --git a/src/bytecode.c b/src/bytecode.c
index 55b193ffb2f..62ba2ca69d0 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -132,6 +132,8 @@ DEFINE (Bunbind7, 057) \
132DEFINE (Bpophandler, 060) \ 132DEFINE (Bpophandler, 060) \
133DEFINE (Bpushconditioncase, 061) \ 133DEFINE (Bpushconditioncase, 061) \
134DEFINE (Bpushcatch, 062) \ 134DEFINE (Bpushcatch, 062) \
135DEFINE (Bpushunwindprotect, 063) \
136DEFINE (Bendunwindprotect, 064) \
135 \ 137 \
136DEFINE (Bnth, 070) \ 138DEFINE (Bnth, 070) \
137DEFINE (Bsymbolp, 071) \ 139DEFINE (Bsymbolp, 071) \
@@ -770,6 +772,45 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
770 NEXT; 772 NEXT;
771 } 773 }
772 774
775 CASE (Bpushunwindprotect): /* New in 27.1. */
776 {
777 struct handler *c = push_handler (Qt, CATCHER_ALL);
778 c->bytecode_dest = FETCH2;
779 c->bytecode_top = top;
780
781 if (sys_setjmp (c->jmp))
782 {
783 struct handler *c = handlerlist;
784 top = c->bytecode_top;
785 op = c->bytecode_dest;
786 handlerlist = c->next;
787 /* Push the exception value, plus a flag indicating
788 that re-throwing is necessary. This will be used
789 by Bendunwindprotect. */
790 PUSH (c->val);
791 PUSH (Qt);
792 goto op_branch;
793 }
794
795 NEXT;
796 }
797 CASE (Bendunwindprotect): /* New in 27.1. */
798 {
799 Lisp_Object flag = POP;
800
801 if (!NILP (flag))
802 {
803 Lisp_Object err = POP;
804
805 if (EQ (XCAR (err), Qsignal))
806 Fsignal (XCAR (XCDR (err)), XCDR (XCDR (err)));
807 else
808 Fthrow (XCAR (XCDR (err)), XCDR (XCDR (err)));
809 }
810
811 NEXT;
812 }
813
773 CASE (Bpushcatch): /* New in 24.4. */ 814 CASE (Bpushcatch): /* New in 24.4. */
774 type = CATCHER; 815 type = CATCHER;
775 goto pushhandler; 816 goto pushhandler;
@@ -798,7 +839,7 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth,
798 handlerlist = handlerlist->next; 839 handlerlist = handlerlist->next;
799 NEXT; 840 NEXT;
800 841
801 CASE (Bunwind_protect): /* FIXME: avoid closure for lexbind. */ 842 CASE (Bunwind_protect): /* Obsolete since 27.1. */
802 { 843 {
803 Lisp_Object handler = POP; 844 Lisp_Object handler = POP;
804 /* Support for a function here is new in 24.4. */ 845 /* Support for a function here is new in 24.4. */
diff --git a/test/src/bytecode-tests.el b/test/src/bytecode-tests.el
new file mode 100644
index 00000000000..51cbfe7576c
--- /dev/null
+++ b/test/src/bytecode-tests.el
@@ -0,0 +1,58 @@
1;;; bytecode-tests.el --- unit tests for src/bytecode.c -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2018 Free Software Foundation, Inc.
4
5;; This file is part of GNU Emacs.
6
7;; GNU Emacs is free software: you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; GNU Emacs is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22;; Unit tests for src/bytecode.c.
23
24;;; Code:
25
26(require 'ert)
27
28(defun bctest-throw-something ()
29 (throw 'something 23))
30
31(defun bctest-signal ()
32 (signal 'error 23))
33
34(ert-deftest bctest-unwind-protect-signal ()
35 (let ((val nil))
36 (should-error (unwind-protect
37 (bctest-signal)
38 (setq val t)))
39 (should val)))
40
41(ert-deftest bctest-unwind-protect-throw ()
42 (let ((val nil))
43 (should (eq (catch 'something
44 (unwind-protect
45 (bctest-throw-something)
46 (setq val t))
47 'fail)
48 23))
49 (should val)))
50
51(ert-deftest bctest-unwind-protect-fallthrough ()
52 (let ((val nil))
53 (unwind-protect
54 (setq val 'x)
55 (setq val t))
56 (should val)))
57
58;;; bytecode-tests.el ends here