;;; 005-sbcl-nlx.txt On the nature and implementation of non-local exits at the IR2 and assembly-routine level. Alastair Bridgewater, January 2007 [ Discuss the basic IR1 conversions for nlx-introducing forms, add ascii-art diagrams for heap blocks and stack behaviors, etc. ] The Common Lisp standard defines 3 basic forms of non-local exit. These forms are BLOCK/RETURN/RETURN-FROM, CATCH/THROW, and TAGBODY/GO. The standard also defines a form called UNWIND-PROTECT which is used to ensure that some cleanup forms are run when control flow leaves a "protected" form either normally or by way of a non-local exit. The standard definition for non-local exits involves the concept of an "exit point", which is effectively the destination of a non-local control transfer. These exit points are represented at the machine level (IR2 and lower) as UNWIND BLOCKs and CATCH BLOCKs. Win32 has what is called Structured Exception Handling, or SEH. SEH is a combination of HANDLER-BIND for OS and machine-level faults (exceptions) and UNWIND-PROTECT. This being provided by the host system, we must honor its conventions, which are fortunately compatible with our own. [ More detail on SEH here. ] The IR2 magic supporting the creation of unwind-blocks and catch-blocks is EMIT-NLX-START, in src/compiler/ir2tran.lisp. It causes a certain amount of dynamic state to be stored, and creates the appropriate block for the NLX. For a catch, the function uses the VOP MAKE-CATCH-BLOCK, which does all the dirty work of setting up the contents of the catch block and setting it up as *CURRENT-CATCH-BLOCK*. For a block or a tagbody, the function uses the VOP MAKE-UNWIND-BLOCK which does the minimum setup needed to create an unwind target, and stores a pointer to the block where the matching GO/RETURN/RETURN-FROM can find it. For an unwind-protect, things proceed the same as for a block or tagbody except that instead of storing the unwind-block for later use, the function uses the VOP SET-UNWIND-PROTECT to install it as the current innermost unwind-protect. The entry points for an nlx are handled by the (%NLX-ENTRY IR2-CONVERT) optimizer. This function uses one of the VOPs NLX-ENTRY or NLX-ENTRY-MULTIPLE to deal with results passed to a catch, tagbody, or block, and the VOP UWP-ENTRY to deal with the results passed to an unwind-protect. It then restores the dynamic state saved by EMIT-NLX-START. ________________________________________________________________ throw assembly-routine, VOP target start count Arguments: TARGET is the tag for a CATCH BLOCK to throw to. START is the location on the control stack for the values to return from the CATCH BLOCK. COUNT is the number of values to return from the CATCH BLOCK. Description: THROW searches through the chain of CATCH BLOCKs stored in *CURRENT-CATCH-BLOCK* for one which has a TAG value equal to TARGET. If it doesn't find one, it invokes an error trap (UNSEEN-THROW-TAG-ERROR). If it does find one, it invokes UNWIND, passing the CATCH BLOCK, START, and COUNT. ________________________________________________________________ unwind (:translate %continue-unwind) assembly-routine, VOP block start count Arguments: BLOCK is an UNWIND BLOCK to invoke. START is the location on the control stack for the values to return from the UNWIND BLOCK. COUNT is the number of values to return from the UNWIND BLOCK. Description: UNWIND first checks to see if BLOCK is a NULL pointer. If so, it invokes an error trap (INVALID-UNWIND-ERROR). UNWIND next checks to see if there is an UNWIND-PROTECT set in place since BLOCK was created. If there is, it invokes the UNWIND-BLOCK for the UNWIND-PROTECT, passing BLOCK, START, and COUNT. If there is no UNWIND-PROTECT set, UNWIND invokes BLOCK, passing START and COUNT. When an UNWIND-PROTECT finishes executing its cleanup forms, it invokes UNWIND again, as %CONTINUE-UNWIND, passing the same BLOCK, START, and COUNT arguments that it received, thus continuing the unwind process. Notes: On Win32, UNWIND doesn't translate %CONTINUE-UNWIND, and just invokes RtlUnwind() to clear each unwind-protect (both lisp and alien) from the SEH chain, then invokes BLOCK as usual. ________________________________________________________________ uwp-seh-handler assembly-routine Description: This is the Win32-specific SEH frame handler for unwinds. It checks to see if it is being called for an unwind or for an exception. If it is being called for an exception, it declines to handle it. If it is being called for an unwind, it sets up a dummy stack frame, finds the UNWIND BLOCK that it is embedded in, removes it from the chain pointed to by *CURRENT-UNWIND-PROTECT-BLOCK*, and invokes it, passing the frame pointer for the dummy stack frame as START, and dummy values for BLOCK and COUNT. The cleanup will either call %CONTINUE-UNWIND (see below) or perform an NLX itself. ________________________________________________________________ continue-unwind (:translate %continue-unwind) assembly-routine, VOP block start count Args: BLOCK and COUNT are for minimal impact compared with the non-win32 way of doing things. They are ignored. START contains the frame pointer for the dummy stack frame established in UWP-SEH-HANDLER. Description: CONTINUE-UNWIND is called, on Win32 only, at the end of the cleanup forms for an UNWIND-PROTECT. It recovers the dummy frame set up by UWP-SEH-HANDLER and returns to UWP-SEH-HANDLER's caller (presumably RtlUnwind). ________________________________________________________________ unwind-block primitive-type current-uwp current-cont current-code entry-pc next-seh-frame seh-frame-handler Fields: CURRENT-UWP is the value of *CURRENT-UNWIND-PROTECT-BLOCK* when the UNWIND-BLOCK was created. CURRENT-CONT (current context) is the value of the control stack frame pointer when the UNWINDBLOCK was created. CURRENT-CODE is only on non-x86, non-x86-64 systems, and is the containing function for the UNWIND BLOCK. ENTRY-PC is the program counter value at which to resume execution when the UNWIND BLOCK is invoked. NEXT-SEH-FRAME is only on win32 systems, and is the value of the system current SEH frame pointer when the UNWIND BLOCK was created. SEH-FRAME-HANDLER is only on win32 systems, and is only used for UNWIND-PROTECT blocks. For these blocks, it contains a pointer to the assembly-routine UWP-SEH-HANDLER. Description: An UNWIND BLOCK is the system representation of what the spec calls an "exit point". ________________________________________________________________ catch-block primitive-type current-uwp current-cont current-code entry-pc next-seh-frame seh-frame-handler tag previous-catch size Fields: CURRENT-UWP, CURRENT-CONT, CURRENT-CODE, ENTRY-PC, NEXT-SEH-FRAME, SEH-FRAME-HANDLER: See the definition of UNWIND-BLOCK. TAG: The TAG used to establish this CATCH BLOCK. PREVIOUS-CATCH: The value of *CURRENT-CATCH-BLOCK* when this CATCH BLOCK was created. SIZE: Unknown, appears to be unused. Description: A CATCH BLOCK is a superset of an UNWIND BLOCK, used to hold the additional data (catch tag and chain pointer) for the CATCH special form. ________________________________________________________________ make-unwind-block VOP tn Args: TN is a TN for the area within the current stack frame in which to build the unwind block. Description: MAKE-UNWIND-BLOCK sets up the minimum amount of information in an UNWIND BLOCK required for it to be able to serve as an exit point (the CURRENT-UWP, CURRENT-CONT, ENTRY-PC slots, and optionally the CURRENT-CODE or NEXT-SEH-FRAME slots). ________________________________________________________________ make-catch-block VOP tn tag Args: TN is a TN for the area within the current stack frame in which to build the CATCH BLOCK. TAG is the tag value associated with the CATCH. Description: MAKE-CATCH-BLOCK does essentially the same as MAKE-UNWIND-BLOCK, but also sets the TAG and PREVIOUS-CATCH slots and points *CURRENT-CATCH-BLOCK* at the new CATCH BLOCK. ________________________________________________________________ set-unwind-protect VOP tn Args: TN is a TN for the area within the current stack frame in which the UNWIND BLOCK was built. Description: SET-UNWIND-PROTECT stores a pointer to the area indicated to by TN in *CURRENT-UNWIND-PROTECT-BLOCK*. Notes: On Win32, also sets the SEH-FRAME-HANDLER slot in the UNWIND BLOCK, and sets the system top SEH frame pointer to point to the SEH frame structure embedded in the UNWIND BLOCK. ________________________________________________________________ unlink-catch-block (:translate %catch-breakup) VOP Description: Unlinks the topmost CATCH BLOCK from *CURRENT-CATCH-BLOCK*. ________________________________________________________________ unlink-unwind-protect (:translate %unwind-protect-breakup) VOP Description: Unlinks the topmost UNWIND BLOCK from *CURRENT-UNWIND-PROTECT-BLOCK*. Notes: On Win32, also sets the current SEH frame pointer to the value stored in the UNWIND BLOCK being unlinked. ;;; EOF