diff lisp/bytecomp.el @ 4677:8f1ee2d15784

Support full Common Lisp multiple values in C. lisp/ChangeLog 2009-08-11 Aidan Kehoe <kehoea@parhasard.net> * bytecomp.el : Update this file to support full C-level multiple values. This involves: -- Four new bytecodes, and special compiler functions to compile multiple-value-call, multiple-value-list-internal, values, values-list, and, since it now needs to pass back multiple values and is a special form, throw. -- There's a new compiler variable, byte-compile-checks-on-load, which is a list of forms that are evaluated at the very start of a file, with an error thrown if any of them give nil. -- The header is now inserted *after* compilation, giving a chance for the compilation process to influence what those checks are. There is still a check done before compilation for non-ASCII characters, to try to turn off dynamic docstrings if appopriate, in `byte-compile-maybe-reset-coding'. Space is reserved for checks; comments describing the version of the byte compiler generating the file are inserted if space remains for them. * bytecomp.el (byte-compile-version): Update this, we're a newer version of the byte compiler. * byte-optimize.el (byte-optimize-funcall): Correct a comment. * bytecomp.el (byte-compile-lapcode): Discard the arg with byte-multiple-value-call. * bytecomp.el (byte-compile-checks-and-comments-space): New variable, describe how many octets to reserve for checks at the start of byte-compiled files. * cl-compat.el: Remove the fake multiple-value implementation. Have the functions that use it use the real multiple-value implementation instead. * cl-macs.el (cl-block-wrapper, cl-block-throw): Revise the byte-compile properties of these symbols to work now we've made throw into a special form; keep the byte-compile properties as anonymous lambdas, since we don't have docstrings for them. * cl-macs.el (multiple-value-bind, multiple-value-setq) (multiple-value-list, nth-value): Update these functions to work with the C support for multiple values. * cl-macs.el (values): Modify the setf handler for this to call #'multiple-value-list-internal appropriately. * cl-macs.el (cl-setf-do-store): If the store form is a cons, treat it specially as wrapping the store value. * cl.el (cl-block-wrapper): Make this an alias of #'and, not #'identity, since it needs to pass back multiple values. * cl.el (multiple-value-apply): We no longer support this, mark it obsolete. * lisp-mode.el (eval-interactive-verbose): Remove a useless space in the docstring. * lisp-mode.el (eval-interactive): Update this function and its docstring. It now passes back a list, basically wrapping any eval calls with multiple-value-list. This allows multiple values to be printed by default in *scratch*. * lisp-mode.el (prin1-list-as-multiple-values): New function, printing a list as multiple values in the manner of Bruno Haible's clisp, separating each entry with " ;\n". * lisp-mode.el (eval-last-sexp): Call #'prin1-list-as-multiple-values on the return value of #'eval-interactive. * lisp-mode.el (eval-defun): Call #'prin1-list-as-multiple-values on the return value of #'eval-interactive. * mouse.el (mouse-eval-sexp): Deal with lists corresponding to multiple values from #'eval-interactive. Call #'cl-prettyprint, which is always available, instead of sometimes calling #'pprint and sometimes falling back to prin1. * obsolete.el (obsolete-throw): New function, called from eval.c when #'funcall encounters an attempt to call #'throw (now a special form) as a function. Only needed for compatibility with 21.4 byte-code. man/ChangeLog addition: 2009-08-11 Aidan Kehoe <kehoea@parhasard.net> * cl.texi (Organization): Remove references to the obsolete multiple-value emulating code. src/ChangeLog addition: 2009-08-11 Aidan Kehoe <kehoea@parhasard.net> * bytecode.c (enum Opcode /* Byte codes */): Add four new bytecodes, to deal with multiple values. (POP_WITH_MULTIPLE_VALUES): New macro. (POP): Modify this macro to ignore multiple values. (DISCARD_PRESERVING_MULTIPLE_VALUES): New macro. (DISCARD): Modify this macro to ignore multiple values. (TOP_WITH_MULTIPLE_VALUES): New macro. (TOP_ADDRESS): New macro. (TOP): Modify this macro to ignore multiple values. (TOP_LVALUE): New macro. (Bcall): Ignore multiple values where appropriate. (Breturn): Pass back multiple values. (Bdup): Preserve multiple values. Use TOP_LVALUE with most bytecodes that assign anything to anything. (Bbind_multiple_value_limits, Bmultiple_value_call, Bmultiple_value_list_internal, Bthrow): Implement the new bytecodes. (Bgotoifnilelsepop, Bgotoifnonnilelsepop, BRgotoifnilelsepop, BRgotoifnonnilelsepop): Discard any multiple values. * callint.c (Fcall_interactively): Ignore multiple values when calling #'eval, in two places. * device-x.c (x_IO_error_handler): * macros.c (pop_kbd_macro_event): * eval.c (Fsignal): * eval.c (flagged_a_squirmer): Call throw_or_bomb_out, not Fthrow, now that the latter is a special form. * eval.c: Make Qthrow, Qobsolete_throw available as symbols. Provide multiple_value_current_limit, multiple-values-limit (the latter as specified by Common Lisp. * eval.c (For): Ignore multiple values when comparing with Qnil, but pass any multiple values back for the last arg. * eval.c (Fand): Ditto. * eval.c (Fif): Ignore multiple values when examining the result of the condition. * eval.c (Fcond): Ignore multiple values when comparing what the clauses give, but pass them back if a clause gave non-nil. * eval.c (Fprog2): Never pass back multiple values. * eval.c (FletX, Flet): Ignore multiple when evaluating what exactly symbols should be bound to. * eval.c (Fwhile): Ignore multiple values when evaluating the test. * eval.c (Fsetq, Fdefvar, Fdefconst): Ignore multiple values. * eval.c (Fthrow): Declare this as a special form; ignore multiple values for TAG, preserve them for VALUE. * eval.c (throw_or_bomb_out): Make this available to other files, now Fthrow is a special form. * eval.c (Feval): Ignore multiple values when calling a compiled function, a non-special-form subr, or a lambda expression. * eval.c (Ffuncall): If we attempt to call #'throw (now a special form) as a function, don't error, call #'obsolete-throw instead. * eval.c (make_multiple_value, multiple_value_aset) (multiple_value_aref, print_multiple_value, mark_multiple_value) (size_multiple_value): Implement the multiple_value type. Add a long comment describing our implementation. * eval.c (bind_multiple_value_limits): New function, used by the bytecode and by #'multiple-value-call, #'multiple-value-list-internal. * eval.c (multiple_value_call): New function, used by the bytecode and #'multiple-value-call. * eval.c (Fmultiple_value_call): New special form. * eval.c (multiple_value_list_internal): New function, used by the byte code and #'multiple-value-list-internal. * eval.c (Fmultiple_value_list_internal, Fmultiple_value_prog1): New special forms. * eval.c (Fvalues, Fvalues_list): New Lisp functions. * eval.c (values2): New function, for C code returning multiple values. * eval.c (syms_of_eval): Make our new Lisp functions and symbols available. * eval.c (multiple-values-limit): Make this available to Lisp. * event-msw.c (dde_eval_string): * event-stream.c (execute_help_form): * glade.c (connector): * glyphs-widget.c (glyph_instantiator_to_glyph): * glyphs.c (evaluate_xpm_color_symbols): * gui-x.c (wv_set_evalable_slot, button_item_to_widget_value): * gui.c (gui_item_value, gui_item_display_flush_left): * lread.c (check_if_suppressed): * menubar-gtk.c (menu_convert, menu_descriptor_to_widget_1): * menubar-msw.c (populate_menu_add_item): * print.c (Fwith_output_to_temp_buffer): * symbols.c (Fsetq_default): Ignore multiple values when calling Feval. * symeval.h: Add the header declarations necessary for the multiple-values implementation. * inline.c: #include symeval.h, now that it has some inline functions. * lisp.h: Update Fthrow's declaration. Make throw_or_bomb_out available to all files. * lrecord.h (enum lrecord_type): Add the multiple_value type here.
author Aidan Kehoe <kehoea@parhasard.net>
date Sun, 16 Aug 2009 20:55:49 +0100
parents 7757334005ae
children 0cc9d22c3732
line wrap: on
line diff
--- a/lisp/bytecomp.el	Sun Aug 16 14:58:57 2009 +0100
+++ b/lisp/bytecomp.el	Sun Aug 16 20:55:49 2009 +0100
@@ -10,7 +10,7 @@
 ;;	Richard Stallman <rms@gnu.org>
 ;; Keywords: internal lisp
 
-(defconst byte-compile-version "2.27 XEmacs; 2000-09-12.")
+(defconst byte-compile-version "2.28 XEmacs; 2009-08-09.")
 
 ;; This file is part of XEmacs.
 
@@ -215,7 +215,7 @@
     (load-library "bytecomp-runtime"))
 
 (eval-when-compile
-  (defvar byte-compile-single-version nil
+  (defvar byte-compile-single-version t
     "If this is true, the choice of emacs version (v19 or v20) byte-codes will
 be hard-coded into bytecomp when it compiles itself.  If the compiler itself
 is compiled with optimization, this causes a speedup.")
@@ -304,6 +304,10 @@
   "This is completely ignored.  It is only around for backwards
 compatibility.")
 
+(defvar byte-compile-checks-on-load '((featurep 'xemacs))
+  "A list of expressions to check when first loading a file. 
+Emacs will throw an error if any of them fail; checks will be made in
+reverse order.")
 
 ;; FSF enables byte-compile-dynamic-docstrings but not byte-compile-dynamic
 ;; by default.  This would be a reasonable conservative approach except
@@ -440,7 +444,7 @@
 on the specbind stack.  The cdr of each cell is an integer bitmask.")
 
 (defvar byte-compile-force-escape-quoted nil
-  "If non-nil, `byte-compile-insert-header' always adds a coding cookie.
+  "If t, `byte-compile-maybe-reset-coding' always chooses `escape-quoted'
 
 This is for situations where the byte compiler output file needs to be
 able to encode character values above ?\\xFF, but this cannot be
@@ -733,7 +737,10 @@
 (byte-defop 176 nil byte-concatN)
 (byte-defop 177 nil byte-insertN)
 
-;; unused: 178-181
+(byte-defop 178  1 byte-bind-multiple-value-limits)
+(byte-defop 179 -3 byte-multiple-value-list-internal)
+(byte-defop 180  0 byte-multiple-value-call)
+(byte-defop 181 -1 byte-throw)
 
 ;; these ops are new to v20
 (byte-defop 182 -1 byte-member)
@@ -833,6 +840,10 @@
 			       (<= (symbol-value op) byte-insertN))
 			  (setq pc (+ 2 pc))
 			  (cons off (cons (symbol-value op) bytes)))
+			 ((= byte-multiple-value-call (symbol-value op))
+			  (setq pc (1+ pc))
+			  ;; Ignore off. 
+			  (cons (symbol-value op) bytes))
 			 ((< off 6)
 			  (setq pc (1+ pc))
 			  (cons (+ (symbol-value op) off) bytes))
@@ -1386,6 +1397,8 @@
 	(byte-optimize byte-optimize)
 	(byte-compile-emacs19-compatibility
 	 byte-compile-emacs19-compatibility)
+	(byte-compile-checks-on-load
+	 byte-compile-checks-on-load)
 	(byte-compile-dynamic byte-compile-dynamic)
 	(byte-compile-dynamic-docstrings
 	 byte-compile-dynamic-docstrings)
@@ -1718,9 +1731,7 @@
 	;;				     byte-compile-warning-types
 	;;				   byte-compile-warnings))
         (byte-compile-force-escape-quoted byte-compile-force-escape-quoted)
-        (byte-compile-using-dynamic nil)
-        (byte-compile-using-escape-quoted nil)
-	)
+        (byte-compile-using-dynamic nil))
     (byte-compile-close-variables
      (save-excursion
        (setq byte-compile-outbuffer
@@ -1730,9 +1741,8 @@
        (setq case-fold-search nil)
        (and filename
 	    (not eval)
-	    (byte-compile-insert-header filename
-					byte-compile-inbuffer
-					byte-compile-outbuffer))
+	    (byte-compile-maybe-reset-coding byte-compile-inbuffer
+					     byte-compile-outbuffer))
        (setq byte-compile-using-dynamic
              (or (symbol-value-in-buffer 'byte-compile-dynamic
                                          byte-compile-inbuffer)
@@ -1763,6 +1773,8 @@
 
 	;; Compile pending forms at end of file.
 	(byte-compile-flush-pending)
+	(byte-compile-insert-header filename byte-compile-inbuffer
+				    byte-compile-outbuffer)
 	(byte-compile-warn-about-unresolved-functions)
 	;; Should we always do this?  When calling multiple files, it
 	;; would be useful to delay this warning until all have
@@ -1797,11 +1809,16 @@
       (kill-buffer byte-compile-outbuffer)
       nil)))
 
+(defvar byte-compile-checks-and-comments-space 475
+  "Number of octets of space for checks and comments; used by the dynamic
+docstrings code.")
+
 (defun byte-compile-insert-header (filename byte-compile-inbuffer
-					    byte-compile-outbuffer)
+				   byte-compile-outbuffer)
   (set-buffer byte-compile-inbuffer)
-  (let ((dynamic-docstrings byte-compile-dynamic-docstrings))
+  (let (checks-string comments)
     (set-buffer byte-compile-outbuffer)
+    (delete-region 1 (1+ byte-compile-checks-and-comments-space))
     (goto-char 1)
     ;;
     ;; The magic number of .elc files is ";ELC", or 0x3B454C43.  After that is
@@ -1817,62 +1834,56 @@
     (insert
      ";ELC"
      (if (byte-compile-version-cond byte-compile-emacs19-compatibility) 19 20)
-     "\000\000\000\n"
-     )
-    (insert ";;; compiled by "
-	    (or (and (boundp 'user-mail-address) user-mail-address)
-		(concat (user-login-name) "@" (system-name)))
-	    " on "
-	    (current-time-string) "\n;;; from file " filename "\n")
-    (insert ";;; emacs version " emacs-version ".\n")
-    (insert ";;; bytecomp version " byte-compile-version "\n;;; "
-     (cond
-       ((eq byte-optimize 'source) "source-level optimization only")
-       ((eq byte-optimize 'byte) "byte-level optimization only")
-       (byte-optimize "optimization is on")
-       (t "optimization is off"))
-     (if (byte-compile-version-cond byte-compile-emacs19-compatibility)
-	 "; compiled with Emacs 19 compatibility.\n"
-       ".\n"))
-   (if (not (byte-compile-version-cond byte-compile-emacs19-compatibility))
-       (insert ";;; this file uses opcodes which do not exist in Emacs 19.\n"
-	       ;; Have to check if emacs-version is bound so that this works
-	       ;; in files loaded early in loadup.el.
-	       "\n(if (and (boundp 'emacs-version)\n"
-	       "\t (or (and (boundp 'epoch::version) epoch::version)\n"
-	       "\t     (string-lessp emacs-version \"20\")))\n"
-	       "    (error \"`"
-	       ;; prin1-to-string is used to quote backslashes.
-	       (substring (prin1-to-string (file-name-nondirectory filename))
-			  1 -1)
-	       "' was compiled for Emacs 20\"))\n\n"))
-   (insert "(or (boundp 'current-load-list) (setq current-load-list nil))\n"
-	   "\n")
-   (if (and (byte-compile-version-cond byte-compile-emacs19-compatibility)
-	    dynamic-docstrings)
-       (insert ";;; this file uses opcodes which do not exist prior to\n"
-	       ";;; XEmacs 19.14/GNU Emacs 19.29 or later."
-	       ;; Have to check if emacs-version is bound so that this works
-	       ;; in files loaded early in loadup.el.
-	       "\n(if (and (boundp 'emacs-version)\n"
-	       "\t (or (and (boundp 'epoch::version) epoch::version)\n"
-	       "\t     (and (not (string-match \"XEmacs\" emacs-version))\n"
-	       "\t          (string-lessp emacs-version \"19.29\"))\n"
-	       "\t     (string-lessp emacs-version \"19.14\")))\n"
-	       "    (error \"`"
-	       ;; prin1-to-string is used to quote backslashes.
-	       (substring (prin1-to-string (file-name-nondirectory filename))
-			  1 -1)
-	       "' was compiled for XEmacs 19.14/Emacs 19.29 or later\"))\n\n"
-	       )
-      ))
-
-  ;; back in the inbuffer; determine and set the coding system for the .elc
-  ;; file if under Mule.  If there are any extended characters in the
-  ;; input file, use `escape-quoted' to make sure that both binary and
-  ;; extended characters are output properly and distinguished properly.
-  ;; Otherwise, use `raw-text' for maximum portability with non-Mule
-  ;; Emacsen.
+     "\000\000\000\n")
+    (when (not (eq (find-coding-system 'raw-text-unix)
+		   (find-coding-system buffer-file-coding-system)))
+      (insert (format ";;;###coding system: %s\n"
+		      (coding-system-name buffer-file-coding-system))))
+    (insert (format
+	     "\n(or %s\n    (error \"Loading this file requires: %s\"))\n"
+	     (setq checks-string
+		   (let ((print-readably t))
+		     (prin1-to-string (if (> (length 
+					      byte-compile-checks-on-load)
+					     1)
+					  (cons 'and
+						(reverse
+						 byte-compile-checks-on-load))
+					(car byte-compile-checks-on-load)))))
+	     checks-string))
+    (setq comments 
+	  (with-string-as-buffer-contents ""
+	    (insert "\n;;; compiled by "
+		    (or (and (boundp 'user-mail-address) user-mail-address)
+			(concat (user-login-name) "@" (system-name)))
+		    " on "
+		    (current-time-string) "\n;;; from file " filename "\n")
+	    (insert ";;; emacs version " emacs-version ".\n")
+	    (insert ";;; bytecomp version " byte-compile-version "\n;;; "
+		    (cond
+		     ((eq byte-optimize 'source)
+		      "source-level optimization only")
+		     ((eq byte-optimize 'byte) "byte-level optimization only")
+		     (byte-optimize "optimization is on")
+		     (t "optimization is off"))
+		    "\n")))
+
+    ;; We won't trip this unless the byte-compiler changes, in which case
+    ;; it's just a matter of upping the space. 
+    (assert (natnump (- (1+ byte-compile-checks-and-comments-space) (point)))
+	    t "Not enough space for the feature checks!")
+
+    (if (natnump (- (1+ byte-compile-checks-and-comments-space)
+		    (+ (point) (length comments))))
+	(insert comments))
+    (insert-char ?\  (- (1+ byte-compile-checks-and-comments-space)
+			(point)))))
+
+(defun byte-compile-maybe-reset-coding (byte-compile-inbuffer
+					byte-compile-outbuffer)
+  ;; We also reserve some space for the feature checks:
+  (goto-char 1)
+  (insert-char ?\  byte-compile-checks-and-comments-space)
   (if (or (featurep '(not mule)) ;; Don't scan buffer if we are not muleized
           (and
 	   (not byte-compile-force-escape-quoted)
@@ -1885,7 +1896,8 @@
 	     ;; not true of ordinary comments.
 	     (let ((non-latin-1-re
 		    (concat "[^\000-\377]" 
-			    #r"\|\\u[0-9a-fA-F]\{4,4\}\|\\U[0-9a-fA-F]\{8,8\}"))
+			    #r"\|\\u[0-9a-fA-F]\{4,4\}\|\\U[0-9a-fA-F]"
+			    "\\{8,8\\}"))
 		   (case-fold-search nil))
 	       (catch 'need-to-escape-quote
 		 (while (re-search-forward non-latin-1-re nil t)
@@ -1894,19 +1906,12 @@
 		   (forward-line 1))
 		 t)))))
       (setq buffer-file-coding-system 'raw-text-unix)
-    (insert "(or (featurep 'mule) (error \"Loading this file requires Mule support\"))
-;;;###coding system: escape-quoted\n")
     (setq buffer-file-coding-system 'escape-quoted)
-    ;; #### Lazy loading not yet implemented for MULE files
-    ;; mrb - Fix this someday.
+    (pushnew '(featurep 'mule) byte-compile-checks-on-load)
     (save-excursion
       (set-buffer byte-compile-inbuffer)
       (setq byte-compile-dynamic nil
-	    byte-compile-dynamic-docstrings nil))
-    ;;(external-debugging-output (prin1-to-string (buffer-local-variables))))
-    )
-  )
-
+	    byte-compile-dynamic-docstrings nil))))
 
 (defun byte-compile-output-file-form (form)
   ;; writes the given form to the output buffer, being careful of docstrings
@@ -3084,6 +3089,11 @@
 (byte-defop-compiler (% byte-rem)	2)
 (byte-defop-compiler aset		3)
 
+(byte-defop-compiler-1 bind-multiple-value-limits)
+(byte-defop-compiler multiple-value-list-internal)
+(byte-defop-compiler-1 multiple-value-call)
+(byte-defop-compiler throw)
+
 (byte-defop-compiler-rmsfun member	2)
 (byte-defop-compiler-rmsfun assq	2)
 
@@ -3102,11 +3112,14 @@
 ;;(byte-defop-compiler (mod byte-rem) 2)
 
 
-(defun byte-compile-subr-wrong-args (form n)
+(defun byte-compile-warn-wrong-args (form n)
   (when (memq 'subr-callargs byte-compile-warnings)
     (byte-compile-warn "%s called with %d arg%s, but requires %s"
 		       (car form) (length (cdr form))
-		       (if (= 1 (length (cdr form))) "" "s") n))
+		       (if (= 1 (length (cdr form))) "" "s") n)))
+
+(defun byte-compile-subr-wrong-args (form n)
+  (byte-compile-warn-wrong-args form n)
   ;; get run-time wrong-number-of-args error.
   (byte-compile-normal-call form))
 
@@ -3641,6 +3654,9 @@
 (byte-defop-compiler-1 inline byte-compile-progn)
 (byte-defop-compiler-1 progn)
 (byte-defop-compiler-1 prog1)
+(byte-defop-compiler-1 multiple-value-prog1)
+(byte-defop-compiler-1 values)
+(byte-defop-compiler-1 values-list)
 (byte-defop-compiler-1 prog2)
 (byte-defop-compiler-1 if)
 (byte-defop-compiler-1 cond)
@@ -3660,13 +3676,36 @@
 
 (defun byte-compile-prog1 (form)
   (setq form (cdr form))
+  ;; #'prog1 never returns multiple values:
+  (byte-compile-form-do-effect (list 'values (pop form)))
+  (byte-compile-body form t))
+
+(defun byte-compile-multiple-value-prog1 (form)
+  (setq form (cdr form))
   (byte-compile-form-do-effect (pop form))
   (byte-compile-body form t))
 
+(defun byte-compile-values (form)
+  (if (and (= 2 (length form))
+           (byte-compile-constp (second form)))
+      (byte-compile-form-do-effect (second form))
+    (byte-compile-normal-call form)))
+
+(defun byte-compile-values-list (form)
+  (if (and (= 2 (length form))
+           (or (null (second form))
+               (and (consp (second form))
+                    (eq (car (second form))
+                        'quote)
+                    (not (symbolp (car-safe (cdr (second form))))))))
+      (byte-compile-form-do-effect (car-safe (cdr (second form))))
+    (byte-compile-normal-call form)))
+
 (defun byte-compile-prog2 (form)
   (setq form (cdr form))
   (byte-compile-form (pop form) t)
-  (byte-compile-form-do-effect (pop form))
+  ;; #'prog2 never returns multiple values:
+  (byte-compile-form-do-effect (list 'values (pop form)))
   (byte-compile-body form t))
 
 (defmacro byte-compile-goto-if (cond discard tag)
@@ -3952,6 +3991,65 @@
   (byte-compile-body (cdr (cdr form)))
   (byte-compile-out 'byte-temp-output-buffer-show 0))
 
+(defun byte-compile-multiple-value-call (form)
+  (if (< (length form) 2)
+      (progn
+        (byte-compile-warn-wrong-args form 1)
+        (byte-compile-normal-call
+         `(signal 'wrong-number-of-arguments '(,(car form)
+                                               ,(length (cdr form))))))
+    (setq form (cdr form))
+    (byte-compile-form (car form))
+    (byte-compile-push-constant 0)
+    (byte-compile-variable-ref 'byte-varref 'multiple-values-limit)
+    ;; bind-multiple-value-limits leaves two existing values on the stack,
+    ;; and pushes a new value, the specpdl_depth() at the time it was
+    ;; called.
+    (byte-compile-out 'byte-bind-multiple-value-limits 0)
+    (mapcar 'byte-compile-form (cdr form))
+    ;; Most of the other code puts this sort of value in the program stream,
+    ;; not pushing it on the stack.
+    (byte-compile-push-constant (+ 3 (length form)))
+    (byte-compile-out 'byte-multiple-value-call (+ 3 (length form)))
+    (pushnew '(subrp (symbol-function 'multiple-value-call))
+             byte-compile-checks-on-load
+             :test #'equal)))
+
+(defun byte-compile-multiple-value-list-internal (form)
+  (if (/= 4 (length form))
+      (progn
+        (byte-compile-warn-wrong-args form 3)
+        (byte-compile-normal-call
+         `(signal 'wrong-number-of-arguments '(,(car form)
+                                               ,(length (cdr form))))))
+    (byte-compile-form (nth 1 form))
+    (byte-compile-form (nth 2 form))
+    (byte-compile-out 'byte-bind-multiple-value-limits 0)
+    (byte-compile-form (nth 3 form))
+    (byte-compile-out (get (car form) 'byte-opcode) 0)
+    (pushnew '(subrp (symbol-function 'multiple-value-call))
+             byte-compile-checks-on-load
+             :test #'equal)))
+
+(defun byte-compile-throw (form)
+  ;; We can't use byte-compile-two-args for throw because in the event that
+  ;; the form does not have two args, it tries to #'funcall it expecting a
+  ;; runtime wrong-number-of-arguments error. Now that #'throw is a special
+  ;; form, it provokes an invalid-function error instead (or at least it
+  ;; should; there's a kludge around for the moment in eval.c that avoids
+  ;; that, but this file should not assume that that will always be there).
+  (if (/= 2 (length (cdr form)))
+      (progn
+        (byte-compile-warn-wrong-args form 2)
+        (byte-compile-normal-call
+         `(signal 'wrong-number-of-arguments '(,(car form)
+                                               ,(length (cdr form))))))
+    (byte-compile-form (nth 1 form))  ;; Push the arguments
+    (byte-compile-form (nth 2 form))
+    (byte-compile-out (get (car form) 'byte-opcode) 0)
+    (pushnew '(null (function-max-args 'throw))
+             byte-compile-checks-on-load
+             :test #'equal)))
 
 ;;; top-level forms elsewhere
 
@@ -4115,6 +4213,8 @@
      ;; This is actually an unnecessary case, because there should be
      ;; no more opcodes behind byte-return.
      (setq byte-compile-depth nil))
+    (byte-multiple-value-call
+     (setq byte-compile-depth (- byte-compile-depth offset)))
     (t
      (setq byte-compile-depth (+ byte-compile-depth
 				 (or (aref byte-stack+-info