From: "Andrew Crabtree" Newsgroups: comp.os.msdos.djgpp Subject: Re: New exception & assembly language Date: Wed, 20 May 1998 16:23:15 -0700 Organization: Hewlett-Packard, Roseville Lines: 70 Message-ID: <6jvol4$499$1@rosenews.rose.hp.com> References: <35620fc6 DOT 4685212 AT news1 DOT bway DOT net> NNTP-Posting-Host: ros51675cra.rose.hp.com To: djgpp AT delorie DOT com DJ-Gateway: from newsgroup comp.os.msdos.djgpp Precedence: bulk Josh Rubin wrote in message <35620fc6 DOT 4685212 AT news1 DOT bway DOT net>... >Suppose an assembly language routine calls a C++ function. >Does the new exception handling mechanism require that >the assembly language routine do anything special for >stack unwinding in case an exception is thrown? This might help. Its from the file except.c in gcc/cp directory Andy /* ====================================================================== Briefly the algorithm works like this: When a constructor or start of a try block is encountered, push_eh_entry (&eh_stack) is called. Push_eh_entry () creates a new entry in the unwind protection stack and returns a label to output to start the protection for that block. When a destructor or end try block is encountered, pop_eh_entry (&eh_stack) is called. Pop_eh_entry () returns the eh_entry it created when push_eh_entry () was called. The eh_entry structure contains three things at this point. The start protect label, the end protect label, and the exception handler label. The end protect label should be output before the call to the destructor (if any). If it was a destructor, then its parse tree is stored in the finalization variable in the eh_entry structure. Otherwise the finalization variable is set to NULL to reflect the fact that it is the end of a try block. Next, this modified eh_entry node is enqueued in the finalizations queue by calling enqueue_eh_entry (&queue,entry). +---------------------------------------------------------------+ |XXX: Will need modification to deal with partially | | constructed arrays of objects | | | | Basically, this consists of keeping track of how many | | of the objects have been constructed already (this | | should be in a register though, so that shouldn't be a | | problem. | +---------------------------------------------------------------+ When a catch block is encountered, there is a lot of work to be done. Since we don't want to generate the catch block inline with the regular flow of the function, we need to have some way of doing so. Luckily, we can use sequences to defer the catch sections. When the start of a catch block is encountered, we start the sequence. After the catch block is generated, we end the sequence. Next we must insure that when the catch block is executed, all finalizations for the matching try block have been completed. If any of those finalizations throw an exception, we must call terminate according to the ARM (section r.15.6.1). What this means is that we need to dequeue and emit finalizations for each entry in the eh_queue until we get to an entry with a NULL finalization field. For any of the finalization entries, if it is not a call to terminate (), we must protect it by giving it another start label, end label, and exception handler label, setting its finalization tree to be a call to terminate (), and enqueue'ing this new eh_entry to be output at an outer level. Finally, after all that is done, we can get around to outputting the catch block which basically wraps all the "catch (...) {...}" statements in a big if/then/else construct that matches the correct block to call.