+ ;; be saved on the stack: the block in edx-tn, start in ebx-tn, and
+ ;; count in ecx-tn.
+
+ (inst jmp (make-ea-for-object-slot block unwind-block-entry-pc-slot 0)))
+
+
+;;;; Win32 non-local exit noise
+
+#!+win32
+(define-assembly-routine (unwind
+ (:return-style :none)
+ (:policy :fast-safe))
+ ((:arg block (any-reg descriptor-reg) eax-offset)
+ (:arg start (any-reg descriptor-reg) ebx-offset)
+ (:arg count (any-reg descriptor-reg) ecx-offset))
+ (declare (ignore start count))
+
+ (let ((error (generate-error-code nil 'invalid-unwind-error)))
+ (inst test block block) ; check for NULL pointer
+ (inst jmp :z error))
+
+ ;; Save all our registers, as we're about to clobber them.
+ (inst pusha)
+
+ ;; Find the SEH frame surrounding our target.
+ (loadw ecx-tn block unwind-block-next-seh-frame-slot)
+
+ ;; This section copied from VOP CALL-OUT.
+ ;; Setup the NPX for C; all the FP registers need to be
+ ;; empty; pop them all.
+ (dotimes (i 8)
+ (inst fstp fr0-tn))
+
+ ;; I'm unlikely to ever forget this again.
+ (inst cld)
+
+ ;; Set up a bogus stack frame for RtlUnwind to pick its return
+ ;; address from. (Yes, this is how RtlUnwind works.)
+ (inst push (make-fixup 'win32-unwind-tail :assembly-routine))
+ (inst push ebp-tn)
+ (inst mov ebp-tn esp-tn)
+
+ ;; Actually call out for the unwind.
+ (inst push 0)
+ (inst push 0)
+ (inst push 0)
+ (inst push ecx-tn)
+ (inst call (make-fixup "RtlUnwind@16" :foreign)))
+
+;; We want no VOP for this one and for it to only happen on Win32
+;; targets. Hence the following disaster.
+#!+#.(cl:if (cl:member sb-assembling cl:*features*) win32 '(or))
+(define-assembly-routine
+ (win32-unwind-tail (:return-style :none))
+ ((:temp block unsigned-reg eax-offset))
+
+ ;; The unwind returns here. Had to use a VOP for this because
+ ;; PUSH won't accept a label as an argument.
+
+ ;; Clean up the bogus stack frame we pushed for the unwind.
+ (inst pop ebp-tn)
+ (inst pop esi-tn) ;; Random scratch register.
+
+ ;; This section based on VOP CALL-OUT.
+ ;; Restore the NPX for lisp; ensure no regs are empty
+ (dotimes (i 8)
+ (inst fldz))
+
+ ;; Restore our regs.
+ (inst popa)
+
+ ;; By now we've unwound all the UWP frames required, so we
+ ;; just jump to our target block.
+ (loadw ebp-tn block unwind-block-current-cont-slot)
+
+ ;; Nlx-entry expects the arg start in ebx-tn and the arg count
+ ;; in ecx-tn. Fortunately, that's where they are already.
+ (inst jmp (make-ea-for-object-slot block unwind-block-entry-pc-slot 0)))
+
+
+;;;; Win32 UWP block SEH interface.
+
+;; We want no VOP for this one and for it to only happen on Win32
+;; targets. Hence the following disaster.
+#!+#.(cl:if (cl:member sb-assembling cl:*features*) win32 '(or))
+(define-assembly-routine
+ (uwp-seh-handler (:return-style :none))
+ ((:temp block unsigned-reg eax-offset))
+
+ ;; We get called for any exception which happens within our
+ ;; dynamic contour that isn't handled below us, and for
+ ;; unwinding.
+
+ ;; For the exceptions we just return ExceptionContinueSearch.
+
+ ;; Find the exception record.
+ (inst mov eax-tn (make-ea :dword :base esp-tn :disp 4))
+
+ ;; Check unwind flags.
+ (inst test (make-ea :byte :base eax-tn :disp 4) 6) ; EH_UNWINDING | EH_EXIT_UNWIND
+
+ ;; To see if we're unwinding or not.
+ (inst jmp :nz UNWINDING)
+
+ ;; We're not unwinding, so we're not interested.
+ (inst mov eax-tn 1) ;; exception-continue-search
+ (inst ret)
+
+ ;; For the unwinds we establish a basic environment as per
+ ;; call_into_lisp, but without the extra SEH frame (the theory
+ ;; being that we're already in a Lisp SEH context), and invoke
+ ;; our UWP block to unwind itself.