diff options
| author | Vibhav Pant | 2017-01-15 01:26:04 +0530 |
|---|---|---|
| committer | Vibhav Pant | 2017-01-15 01:26:04 +0530 |
| commit | 88549ec38e9bb30e338a9985d0de4e6263b40fb7 (patch) | |
| tree | 4ee41982939210a6f8a6d74fd539b528805b7b04 | |
| parent | 877c525f4b98bc785f1bb0b50d70f72d09c80eb2 (diff) | |
| download | emacs-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.el | 14 | ||||
| -rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 174 | ||||
| -rw-r--r-- | src/bytecode.c | 16 |
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. |
| 228 | They are hidden in comments in the compiled file, | 233 | They 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) \ | |||
| 267 | DEFINE (Bstack_set2, 0263) \ | 267 | DEFINE (Bstack_set2, 0263) \ |
| 268 | DEFINE (BdiscardN, 0266) \ | 268 | DEFINE (BdiscardN, 0266) \ |
| 269 | \ | 269 | \ |
| 270 | DEFINE (Bswitch, 0267) \ | ||
| 271 | \ | ||
| 270 | DEFINE (Bconstant, 0300) | 272 | DEFINE (Bconstant, 0300) |
| 271 | 273 | ||
| 272 | enum byte_code_op | 274 | enum 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 |