except.c revision 50397
150397Sobrien/* Implements exception handling. 250397Sobrien Copyright (C) 1989, 92-97, 1998 Free Software Foundation, Inc. 350397Sobrien Contributed by Mike Stump <mrs@cygnus.com>. 450397Sobrien 550397SobrienThis file is part of GNU CC. 650397Sobrien 750397SobrienGNU CC is free software; you can redistribute it and/or modify 850397Sobrienit under the terms of the GNU General Public License as published by 950397Sobrienthe Free Software Foundation; either version 2, or (at your option) 1050397Sobrienany later version. 1150397Sobrien 1250397SobrienGNU CC is distributed in the hope that it will be useful, 1350397Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of 1450397SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1550397SobrienGNU General Public License for more details. 1650397Sobrien 1750397SobrienYou should have received a copy of the GNU General Public License 1850397Sobrienalong with GNU CC; see the file COPYING. If not, write to 1950397Sobrienthe Free Software Foundation, 59 Temple Place - Suite 330, 2050397SobrienBoston, MA 02111-1307, USA. */ 2150397Sobrien 2250397Sobrien 2350397Sobrien/* An exception is an event that can be signaled from within a 2450397Sobrien function. This event can then be "caught" or "trapped" by the 2550397Sobrien callers of this function. This potentially allows program flow to 2650397Sobrien be transferred to any arbitrary code associated with a function call 2750397Sobrien several levels up the stack. 2850397Sobrien 2950397Sobrien The intended use for this mechanism is for signaling "exceptional 3050397Sobrien events" in an out-of-band fashion, hence its name. The C++ language 3150397Sobrien (and many other OO-styled or functional languages) practically 3250397Sobrien requires such a mechanism, as otherwise it becomes very difficult 3350397Sobrien or even impossible to signal failure conditions in complex 3450397Sobrien situations. The traditional C++ example is when an error occurs in 3550397Sobrien the process of constructing an object; without such a mechanism, it 3650397Sobrien is impossible to signal that the error occurs without adding global 3750397Sobrien state variables and error checks around every object construction. 3850397Sobrien 3950397Sobrien The act of causing this event to occur is referred to as "throwing 4050397Sobrien an exception". (Alternate terms include "raising an exception" or 4150397Sobrien "signaling an exception".) The term "throw" is used because control 4250397Sobrien is returned to the callers of the function that is signaling the 4350397Sobrien exception, and thus there is the concept of "throwing" the 4450397Sobrien exception up the call stack. 4550397Sobrien 4650397Sobrien There are two major codegen options for exception handling. The 4750397Sobrien flag -fsjlj-exceptions can be used to select the setjmp/longjmp 4850397Sobrien approach, which is the default. -fno-sjlj-exceptions can be used to 4950397Sobrien get the PC range table approach. While this is a compile time 5050397Sobrien flag, an entire application must be compiled with the same codegen 5150397Sobrien option. The first is a PC range table approach, the second is a 5250397Sobrien setjmp/longjmp based scheme. We will first discuss the PC range 5350397Sobrien table approach, after that, we will discuss the setjmp/longjmp 5450397Sobrien based approach. 5550397Sobrien 5650397Sobrien It is appropriate to speak of the "context of a throw". This 5750397Sobrien context refers to the address where the exception is thrown from, 5850397Sobrien and is used to determine which exception region will handle the 5950397Sobrien exception. 6050397Sobrien 6150397Sobrien Regions of code within a function can be marked such that if it 6250397Sobrien contains the context of a throw, control will be passed to a 6350397Sobrien designated "exception handler". These areas are known as "exception 6450397Sobrien regions". Exception regions cannot overlap, but they can be nested 6550397Sobrien to any arbitrary depth. Also, exception regions cannot cross 6650397Sobrien function boundaries. 6750397Sobrien 6850397Sobrien Exception handlers can either be specified by the user (which we 6950397Sobrien will call a "user-defined handler") or generated by the compiler 7050397Sobrien (which we will designate as a "cleanup"). Cleanups are used to 7150397Sobrien perform tasks such as destruction of objects allocated on the 7250397Sobrien stack. 7350397Sobrien 7450397Sobrien In the current implementation, cleanups are handled by allocating an 7550397Sobrien exception region for the area that the cleanup is designated for, 7650397Sobrien and the handler for the region performs the cleanup and then 7750397Sobrien rethrows the exception to the outer exception region. From the 7850397Sobrien standpoint of the current implementation, there is little 7950397Sobrien distinction made between a cleanup and a user-defined handler, and 8050397Sobrien the phrase "exception handler" can be used to refer to either one 8150397Sobrien equally well. (The section "Future Directions" below discusses how 8250397Sobrien this will change). 8350397Sobrien 8450397Sobrien Each object file that is compiled with exception handling contains 8550397Sobrien a static array of exception handlers named __EXCEPTION_TABLE__. 8650397Sobrien Each entry contains the starting and ending addresses of the 8750397Sobrien exception region, and the address of the handler designated for 8850397Sobrien that region. 8950397Sobrien 9050397Sobrien If the target does not use the DWARF 2 frame unwind information, at 9150397Sobrien program startup each object file invokes a function named 9250397Sobrien __register_exceptions with the address of its local 9350397Sobrien __EXCEPTION_TABLE__. __register_exceptions is defined in libgcc2.c, and 9450397Sobrien is responsible for recording all of the exception regions into one list 9550397Sobrien (which is kept in a static variable named exception_table_list). 9650397Sobrien 9750397Sobrien On targets that support crtstuff.c, the unwind information 9850397Sobrien is stored in a section named .eh_frame and the information for the 9950397Sobrien entire shared object or program is registered with a call to 10050397Sobrien __register_frame_info. On other targets, the information for each 10150397Sobrien translation unit is registered from the file generated by collect2. 10250397Sobrien __register_frame_info is defined in frame.c, and is responsible for 10350397Sobrien recording all of the unwind regions into one list (which is kept in a 10450397Sobrien static variable named unwind_table_list). 10550397Sobrien 10650397Sobrien The function __throw is actually responsible for doing the 10750397Sobrien throw. On machines that have unwind info support, __throw is generated 10850397Sobrien by code in libgcc2.c, otherwise __throw is generated on a 10950397Sobrien per-object-file basis for each source file compiled with 11050397Sobrien -fexceptions by the C++ frontend. Before __throw is invoked, 11150397Sobrien the current context of the throw needs to be placed in the global 11250397Sobrien variable __eh_pc. 11350397Sobrien 11450397Sobrien __throw attempts to find the appropriate exception handler for the 11550397Sobrien PC value stored in __eh_pc by calling __find_first_exception_table_match 11650397Sobrien (which is defined in libgcc2.c). If __find_first_exception_table_match 11750397Sobrien finds a relevant handler, __throw transfers control directly to it. 11850397Sobrien 11950397Sobrien If a handler for the context being thrown from can't be found, __throw 12050397Sobrien walks (see Walking the stack below) the stack up the dynamic call chain to 12150397Sobrien continue searching for an appropriate exception handler based upon the 12250397Sobrien caller of the function it last sought a exception handler for. It stops 12350397Sobrien then either an exception handler is found, or when the top of the 12450397Sobrien call chain is reached. 12550397Sobrien 12650397Sobrien If no handler is found, an external library function named 12750397Sobrien __terminate is called. If a handler is found, then we restart 12850397Sobrien our search for a handler at the end of the call chain, and repeat 12950397Sobrien the search process, but instead of just walking up the call chain, 13050397Sobrien we unwind the call chain as we walk up it. 13150397Sobrien 13250397Sobrien Internal implementation details: 13350397Sobrien 13450397Sobrien To associate a user-defined handler with a block of statements, the 13550397Sobrien function expand_start_try_stmts is used to mark the start of the 13650397Sobrien block of statements with which the handler is to be associated 13750397Sobrien (which is known as a "try block"). All statements that appear 13850397Sobrien afterwards will be associated with the try block. 13950397Sobrien 14050397Sobrien A call to expand_start_all_catch marks the end of the try block, 14150397Sobrien and also marks the start of the "catch block" (the user-defined 14250397Sobrien handler) associated with the try block. 14350397Sobrien 14450397Sobrien This user-defined handler will be invoked for *every* exception 14550397Sobrien thrown with the context of the try block. It is up to the handler 14650397Sobrien to decide whether or not it wishes to handle any given exception, 14750397Sobrien as there is currently no mechanism in this implementation for doing 14850397Sobrien this. (There are plans for conditionally processing an exception 14950397Sobrien based on its "type", which will provide a language-independent 15050397Sobrien mechanism). 15150397Sobrien 15250397Sobrien If the handler chooses not to process the exception (perhaps by 15350397Sobrien looking at an "exception type" or some other additional data 15450397Sobrien supplied with the exception), it can fall through to the end of the 15550397Sobrien handler. expand_end_all_catch and expand_leftover_cleanups 15650397Sobrien add additional code to the end of each handler to take care of 15750397Sobrien rethrowing to the outer exception handler. 15850397Sobrien 15950397Sobrien The handler also has the option to continue with "normal flow of 16050397Sobrien code", or in other words to resume executing at the statement 16150397Sobrien immediately after the end of the exception region. The variable 16250397Sobrien caught_return_label_stack contains a stack of labels, and jumping 16350397Sobrien to the topmost entry's label via expand_goto will resume normal 16450397Sobrien flow to the statement immediately after the end of the exception 16550397Sobrien region. If the handler falls through to the end, the exception will 16650397Sobrien be rethrown to the outer exception region. 16750397Sobrien 16850397Sobrien The instructions for the catch block are kept as a separate 16950397Sobrien sequence, and will be emitted at the end of the function along with 17050397Sobrien the handlers specified via expand_eh_region_end. The end of the 17150397Sobrien catch block is marked with expand_end_all_catch. 17250397Sobrien 17350397Sobrien Any data associated with the exception must currently be handled by 17450397Sobrien some external mechanism maintained in the frontend. For example, 17550397Sobrien the C++ exception mechanism passes an arbitrary value along with 17650397Sobrien the exception, and this is handled in the C++ frontend by using a 17750397Sobrien global variable to hold the value. (This will be changing in the 17850397Sobrien future.) 17950397Sobrien 18050397Sobrien The mechanism in C++ for handling data associated with the 18150397Sobrien exception is clearly not thread-safe. For a thread-based 18250397Sobrien environment, another mechanism must be used (possibly using a 18350397Sobrien per-thread allocation mechanism if the size of the area that needs 18450397Sobrien to be allocated isn't known at compile time.) 18550397Sobrien 18650397Sobrien Internally-generated exception regions (cleanups) are marked by 18750397Sobrien calling expand_eh_region_start to mark the start of the region, 18850397Sobrien and expand_eh_region_end (handler) is used to both designate the 18950397Sobrien end of the region and to associate a specified handler/cleanup with 19050397Sobrien the region. The rtl code in HANDLER will be invoked whenever an 19150397Sobrien exception occurs in the region between the calls to 19250397Sobrien expand_eh_region_start and expand_eh_region_end. After HANDLER is 19350397Sobrien executed, additional code is emitted to handle rethrowing the 19450397Sobrien exception to the outer exception handler. The code for HANDLER will 19550397Sobrien be emitted at the end of the function. 19650397Sobrien 19750397Sobrien TARGET_EXPRs can also be used to designate exception regions. A 19850397Sobrien TARGET_EXPR gives an unwind-protect style interface commonly used 19950397Sobrien in functional languages such as LISP. The associated expression is 20050397Sobrien evaluated, and whether or not it (or any of the functions that it 20150397Sobrien calls) throws an exception, the protect expression is always 20250397Sobrien invoked. This implementation takes care of the details of 20350397Sobrien associating an exception table entry with the expression and 20450397Sobrien generating the necessary code (it actually emits the protect 20550397Sobrien expression twice, once for normal flow and once for the exception 20650397Sobrien case). As for the other handlers, the code for the exception case 20750397Sobrien will be emitted at the end of the function. 20850397Sobrien 20950397Sobrien Cleanups can also be specified by using add_partial_entry (handler) 21050397Sobrien and end_protect_partials. add_partial_entry creates the start of 21150397Sobrien a new exception region; HANDLER will be invoked if an exception is 21250397Sobrien thrown with the context of the region between the calls to 21350397Sobrien add_partial_entry and end_protect_partials. end_protect_partials is 21450397Sobrien used to mark the end of these regions. add_partial_entry can be 21550397Sobrien called as many times as needed before calling end_protect_partials. 21650397Sobrien However, end_protect_partials should only be invoked once for each 21750397Sobrien group of calls to add_partial_entry as the entries are queued 21850397Sobrien and all of the outstanding entries are processed simultaneously 21950397Sobrien when end_protect_partials is invoked. Similarly to the other 22050397Sobrien handlers, the code for HANDLER will be emitted at the end of the 22150397Sobrien function. 22250397Sobrien 22350397Sobrien The generated RTL for an exception region includes 22450397Sobrien NOTE_INSN_EH_REGION_BEG and NOTE_INSN_EH_REGION_END notes that mark 22550397Sobrien the start and end of the exception region. A unique label is also 22650397Sobrien generated at the start of the exception region, which is available 22750397Sobrien by looking at the ehstack variable. The topmost entry corresponds 22850397Sobrien to the current region. 22950397Sobrien 23050397Sobrien In the current implementation, an exception can only be thrown from 23150397Sobrien a function call (since the mechanism used to actually throw an 23250397Sobrien exception involves calling __throw). If an exception region is 23350397Sobrien created but no function calls occur within that region, the region 23450397Sobrien can be safely optimized away (along with its exception handlers) 23550397Sobrien since no exceptions can ever be caught in that region. This 23650397Sobrien optimization is performed unless -fasynchronous-exceptions is 23750397Sobrien given. If the user wishes to throw from a signal handler, or other 23850397Sobrien asynchronous place, -fasynchronous-exceptions should be used when 23950397Sobrien compiling for maximally correct code, at the cost of additional 24050397Sobrien exception regions. Using -fasynchronous-exceptions only produces 24150397Sobrien code that is reasonably safe in such situations, but a correct 24250397Sobrien program cannot rely upon this working. It can be used in failsafe 24350397Sobrien code, where trying to continue on, and proceeding with potentially 24450397Sobrien incorrect results is better than halting the program. 24550397Sobrien 24650397Sobrien 24750397Sobrien Walking the stack: 24850397Sobrien 24950397Sobrien The stack is walked by starting with a pointer to the current 25050397Sobrien frame, and finding the pointer to the callers frame. The unwind info 25150397Sobrien tells __throw how to find it. 25250397Sobrien 25350397Sobrien Unwinding the stack: 25450397Sobrien 25550397Sobrien When we use the term unwinding the stack, we mean undoing the 25650397Sobrien effects of the function prologue in a controlled fashion so that we 25750397Sobrien still have the flow of control. Otherwise, we could just return 25850397Sobrien (jump to the normal end of function epilogue). 25950397Sobrien 26050397Sobrien This is done in __throw in libgcc2.c when we know that a handler exists 26150397Sobrien in a frame higher up the call stack than its immediate caller. 26250397Sobrien 26350397Sobrien To unwind, we find the unwind data associated with the frame, if any. 26450397Sobrien If we don't find any, we call the library routine __terminate. If we do 26550397Sobrien find it, we use the information to copy the saved register values from 26650397Sobrien that frame into the register save area in the frame for __throw, return 26750397Sobrien into a stub which updates the stack pointer, and jump to the handler. 26850397Sobrien The normal function epilogue for __throw handles restoring the saved 26950397Sobrien values into registers. 27050397Sobrien 27150397Sobrien When unwinding, we use this method if we know it will 27250397Sobrien work (if DWARF2_UNWIND_INFO is defined). Otherwise, we know that 27350397Sobrien an inline unwinder will have been emitted for any function that 27450397Sobrien __unwind_function cannot unwind. The inline unwinder appears as a 27550397Sobrien normal exception handler for the entire function, for any function 27650397Sobrien that we know cannot be unwound by __unwind_function. We inform the 27750397Sobrien compiler of whether a function can be unwound with 27850397Sobrien __unwind_function by having DOESNT_NEED_UNWINDER evaluate to true 27950397Sobrien when the unwinder isn't needed. __unwind_function is used as an 28050397Sobrien action of last resort. If no other method can be used for 28150397Sobrien unwinding, __unwind_function is used. If it cannot unwind, it 28250397Sobrien should call __terminate. 28350397Sobrien 28450397Sobrien By default, if the target-specific backend doesn't supply a definition 28550397Sobrien for __unwind_function and doesn't support DWARF2_UNWIND_INFO, inlined 28650397Sobrien unwinders will be used instead. The main tradeoff here is in text space 28750397Sobrien utilization. Obviously, if inline unwinders have to be generated 28850397Sobrien repeatedly, this uses much more space than if a single routine is used. 28950397Sobrien 29050397Sobrien However, it is simply not possible on some platforms to write a 29150397Sobrien generalized routine for doing stack unwinding without having some 29250397Sobrien form of additional data associated with each function. The current 29350397Sobrien implementation can encode this data in the form of additional 29450397Sobrien machine instructions or as static data in tabular form. The later 29550397Sobrien is called the unwind data. 29650397Sobrien 29750397Sobrien The backend macro DOESNT_NEED_UNWINDER is used to conditionalize whether 29850397Sobrien or not per-function unwinders are needed. If DOESNT_NEED_UNWINDER is 29950397Sobrien defined and has a non-zero value, a per-function unwinder is not emitted 30050397Sobrien for the current function. If the static unwind data is supported, then 30150397Sobrien a per-function unwinder is not emitted. 30250397Sobrien 30350397Sobrien On some platforms it is possible that neither __unwind_function 30450397Sobrien nor inlined unwinders are available. For these platforms it is not 30550397Sobrien possible to throw through a function call, and abort will be 30650397Sobrien invoked instead of performing the throw. 30750397Sobrien 30850397Sobrien The reason the unwind data may be needed is that on some platforms 30950397Sobrien the order and types of data stored on the stack can vary depending 31050397Sobrien on the type of function, its arguments and returned values, and the 31150397Sobrien compilation options used (optimization versus non-optimization, 31250397Sobrien -fomit-frame-pointer, processor variations, etc). 31350397Sobrien 31450397Sobrien Unfortunately, this also means that throwing through functions that 31550397Sobrien aren't compiled with exception handling support will still not be 31650397Sobrien possible on some platforms. This problem is currently being 31750397Sobrien investigated, but no solutions have been found that do not imply 31850397Sobrien some unacceptable performance penalties. 31950397Sobrien 32050397Sobrien Future directions: 32150397Sobrien 32250397Sobrien Currently __throw makes no differentiation between cleanups and 32350397Sobrien user-defined exception regions. While this makes the implementation 32450397Sobrien simple, it also implies that it is impossible to determine if a 32550397Sobrien user-defined exception handler exists for a given exception without 32650397Sobrien completely unwinding the stack in the process. This is undesirable 32750397Sobrien from the standpoint of debugging, as ideally it would be possible 32850397Sobrien to trap unhandled exceptions in the debugger before the process of 32950397Sobrien unwinding has even started. 33050397Sobrien 33150397Sobrien This problem can be solved by marking user-defined handlers in a 33250397Sobrien special way (probably by adding additional bits to exception_table_list). 33350397Sobrien A two-pass scheme could then be used by __throw to iterate 33450397Sobrien through the table. The first pass would search for a relevant 33550397Sobrien user-defined handler for the current context of the throw, and if 33650397Sobrien one is found, the second pass would then invoke all needed cleanups 33750397Sobrien before jumping to the user-defined handler. 33850397Sobrien 33950397Sobrien Many languages (including C++ and Ada) make execution of a 34050397Sobrien user-defined handler conditional on the "type" of the exception 34150397Sobrien thrown. (The type of the exception is actually the type of the data 34250397Sobrien that is thrown with the exception.) It will thus be necessary for 34350397Sobrien __throw to be able to determine if a given user-defined 34450397Sobrien exception handler will actually be executed, given the type of 34550397Sobrien exception. 34650397Sobrien 34750397Sobrien One scheme is to add additional information to exception_table_list 34850397Sobrien as to the types of exceptions accepted by each handler. __throw 34950397Sobrien can do the type comparisons and then determine if the handler is 35050397Sobrien actually going to be executed. 35150397Sobrien 35250397Sobrien There is currently no significant level of debugging support 35350397Sobrien available, other than to place a breakpoint on __throw. While 35450397Sobrien this is sufficient in most cases, it would be helpful to be able to 35550397Sobrien know where a given exception was going to be thrown to before it is 35650397Sobrien actually thrown, and to be able to choose between stopping before 35750397Sobrien every exception region (including cleanups), or just user-defined 35850397Sobrien exception regions. This should be possible to do in the two-pass 35950397Sobrien scheme by adding additional labels to __throw for appropriate 36050397Sobrien breakpoints, and additional debugger commands could be added to 36150397Sobrien query various state variables to determine what actions are to be 36250397Sobrien performed next. 36350397Sobrien 36450397Sobrien Another major problem that is being worked on is the issue with stack 36550397Sobrien unwinding on various platforms. Currently the only platforms that have 36650397Sobrien support for the generation of a generic unwinder are the SPARC and MIPS. 36750397Sobrien All other ports require per-function unwinders, which produce large 36850397Sobrien amounts of code bloat. 36950397Sobrien 37050397Sobrien For setjmp/longjmp based exception handling, some of the details 37150397Sobrien are as above, but there are some additional details. This section 37250397Sobrien discusses the details. 37350397Sobrien 37450397Sobrien We don't use NOTE_INSN_EH_REGION_{BEG,END} pairs. We don't 37550397Sobrien optimize EH regions yet. We don't have to worry about machine 37650397Sobrien specific issues with unwinding the stack, as we rely upon longjmp 37750397Sobrien for all the machine specific details. There is no variable context 37850397Sobrien of a throw, just the one implied by the dynamic handler stack 37950397Sobrien pointed to by the dynamic handler chain. There is no exception 38050397Sobrien table, and no calls to __register_exceptions. __sjthrow is used 38150397Sobrien instead of __throw, and it works by using the dynamic handler 38250397Sobrien chain, and longjmp. -fasynchronous-exceptions has no effect, as 38350397Sobrien the elimination of trivial exception regions is not yet performed. 38450397Sobrien 38550397Sobrien A frontend can set protect_cleanup_actions_with_terminate when all 38650397Sobrien the cleanup actions should be protected with an EH region that 38750397Sobrien calls terminate when an unhandled exception is throw. C++ does 38850397Sobrien this, Ada does not. */ 38950397Sobrien 39050397Sobrien 39150397Sobrien#include "config.h" 39250397Sobrien#include "defaults.h" 39350397Sobrien#include "eh-common.h" 39450397Sobrien#include "system.h" 39550397Sobrien#include "rtl.h" 39650397Sobrien#include "tree.h" 39750397Sobrien#include "flags.h" 39850397Sobrien#include "except.h" 39950397Sobrien#include "function.h" 40050397Sobrien#include "insn-flags.h" 40150397Sobrien#include "expr.h" 40250397Sobrien#include "insn-codes.h" 40350397Sobrien#include "regs.h" 40450397Sobrien#include "hard-reg-set.h" 40550397Sobrien#include "insn-config.h" 40650397Sobrien#include "recog.h" 40750397Sobrien#include "output.h" 40850397Sobrien#include "toplev.h" 40950397Sobrien 41050397Sobrien/* One to use setjmp/longjmp method of generating code for exception 41150397Sobrien handling. */ 41250397Sobrien 41350397Sobrienint exceptions_via_longjmp = 2; 41450397Sobrien 41550397Sobrien/* One to enable asynchronous exception support. */ 41650397Sobrien 41750397Sobrienint asynchronous_exceptions = 0; 41850397Sobrien 41950397Sobrien/* One to protect cleanup actions with a handler that calls 42050397Sobrien __terminate, zero otherwise. */ 42150397Sobrien 42250397Sobrienint protect_cleanup_actions_with_terminate; 42350397Sobrien 42450397Sobrien/* A list of labels used for exception handlers. Created by 42550397Sobrien find_exception_handler_labels for the optimization passes. */ 42650397Sobrien 42750397Sobrienrtx exception_handler_labels; 42850397Sobrien 42950397Sobrien/* The EH context. Nonzero if the function has already 43050397Sobrien fetched a pointer to the EH context for exception handling. */ 43150397Sobrien 43250397Sobrienrtx current_function_ehc; 43350397Sobrien 43450397Sobrien/* A stack used for keeping track of the currently active exception 43550397Sobrien handling region. As each exception region is started, an entry 43650397Sobrien describing the region is pushed onto this stack. The current 43750397Sobrien region can be found by looking at the top of the stack, and as we 43850397Sobrien exit regions, the corresponding entries are popped. 43950397Sobrien 44050397Sobrien Entries cannot overlap; they can be nested. So there is only one 44150397Sobrien entry at most that corresponds to the current instruction, and that 44250397Sobrien is the entry on the top of the stack. */ 44350397Sobrien 44450397Sobrienstatic struct eh_stack ehstack; 44550397Sobrien 44650397Sobrien 44750397Sobrien/* This stack is used to represent what the current eh region is 44850397Sobrien for the catch blocks beings processed */ 44950397Sobrien 45050397Sobrienstatic struct eh_stack catchstack; 45150397Sobrien 45250397Sobrien/* A queue used for tracking which exception regions have closed but 45350397Sobrien whose handlers have not yet been expanded. Regions are emitted in 45450397Sobrien groups in an attempt to improve paging performance. 45550397Sobrien 45650397Sobrien As we exit a region, we enqueue a new entry. The entries are then 45750397Sobrien dequeued during expand_leftover_cleanups and expand_start_all_catch, 45850397Sobrien 45950397Sobrien We should redo things so that we either take RTL for the handler, 46050397Sobrien or we expand the handler expressed as a tree immediately at region 46150397Sobrien end time. */ 46250397Sobrien 46350397Sobrienstatic struct eh_queue ehqueue; 46450397Sobrien 46550397Sobrien/* Insns for all of the exception handlers for the current function. 46650397Sobrien They are currently emitted by the frontend code. */ 46750397Sobrien 46850397Sobrienrtx catch_clauses; 46950397Sobrien 47050397Sobrien/* A TREE_CHAINed list of handlers for regions that are not yet 47150397Sobrien closed. The TREE_VALUE of each entry contains the handler for the 47250397Sobrien corresponding entry on the ehstack. */ 47350397Sobrien 47450397Sobrienstatic tree protect_list; 47550397Sobrien 47650397Sobrien/* Stacks to keep track of various labels. */ 47750397Sobrien 47850397Sobrien/* Keeps track of the label to resume to should one want to resume 47950397Sobrien normal control flow out of a handler (instead of, say, returning to 48050397Sobrien the caller of the current function or exiting the program). */ 48150397Sobrien 48250397Sobrienstruct label_node *caught_return_label_stack = NULL; 48350397Sobrien 48450397Sobrien/* Keeps track of the label used as the context of a throw to rethrow an 48550397Sobrien exception to the outer exception region. */ 48650397Sobrien 48750397Sobrienstruct label_node *outer_context_label_stack = NULL; 48850397Sobrien 48950397Sobrien/* A random data area for the front end's own use. */ 49050397Sobrien 49150397Sobrienstruct label_node *false_label_stack = NULL; 49250397Sobrien 49350397Sobrienstatic void push_eh_entry PROTO((struct eh_stack *)); 49450397Sobrienstatic struct eh_entry * pop_eh_entry PROTO((struct eh_stack *)); 49550397Sobrienstatic void enqueue_eh_entry PROTO((struct eh_queue *, struct eh_entry *)); 49650397Sobrienstatic struct eh_entry * dequeue_eh_entry PROTO((struct eh_queue *)); 49750397Sobrienstatic rtx call_get_eh_context PROTO((void)); 49850397Sobrienstatic void start_dynamic_cleanup PROTO((tree, tree)); 49950397Sobrienstatic void start_dynamic_handler PROTO((void)); 50050397Sobrienstatic void expand_rethrow PROTO((rtx)); 50150397Sobrienstatic void output_exception_table_entry PROTO((FILE *, int)); 50250397Sobrienstatic int can_throw PROTO((rtx)); 50350397Sobrienstatic rtx scan_region PROTO((rtx, int, int *)); 50450397Sobrienstatic void eh_regs PROTO((rtx *, rtx *, int)); 50550397Sobrienstatic void set_insn_eh_region PROTO((rtx *, int)); 50650397Sobrien#ifdef DONT_USE_BUILTIN_SETJMP 50750397Sobrienstatic void jumpif_rtx PROTO((rtx, rtx)); 50850397Sobrien#endif 50950397Sobrien 51050397Sobrien 51150397Sobrienrtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx)); 51250397Sobrien 51350397Sobrien/* Various support routines to manipulate the various data structures 51450397Sobrien used by the exception handling code. */ 51550397Sobrien 51650397Sobrien/* Push a label entry onto the given STACK. */ 51750397Sobrien 51850397Sobrienvoid 51950397Sobrienpush_label_entry (stack, rlabel, tlabel) 52050397Sobrien struct label_node **stack; 52150397Sobrien rtx rlabel; 52250397Sobrien tree tlabel; 52350397Sobrien{ 52450397Sobrien struct label_node *newnode 52550397Sobrien = (struct label_node *) xmalloc (sizeof (struct label_node)); 52650397Sobrien 52750397Sobrien if (rlabel) 52850397Sobrien newnode->u.rlabel = rlabel; 52950397Sobrien else 53050397Sobrien newnode->u.tlabel = tlabel; 53150397Sobrien newnode->chain = *stack; 53250397Sobrien *stack = newnode; 53350397Sobrien} 53450397Sobrien 53550397Sobrien/* Pop a label entry from the given STACK. */ 53650397Sobrien 53750397Sobrienrtx 53850397Sobrienpop_label_entry (stack) 53950397Sobrien struct label_node **stack; 54050397Sobrien{ 54150397Sobrien rtx label; 54250397Sobrien struct label_node *tempnode; 54350397Sobrien 54450397Sobrien if (! *stack) 54550397Sobrien return NULL_RTX; 54650397Sobrien 54750397Sobrien tempnode = *stack; 54850397Sobrien label = tempnode->u.rlabel; 54950397Sobrien *stack = (*stack)->chain; 55050397Sobrien free (tempnode); 55150397Sobrien 55250397Sobrien return label; 55350397Sobrien} 55450397Sobrien 55550397Sobrien/* Return the top element of the given STACK. */ 55650397Sobrien 55750397Sobrientree 55850397Sobrientop_label_entry (stack) 55950397Sobrien struct label_node **stack; 56050397Sobrien{ 56150397Sobrien if (! *stack) 56250397Sobrien return NULL_TREE; 56350397Sobrien 56450397Sobrien return (*stack)->u.tlabel; 56550397Sobrien} 56650397Sobrien 56750397Sobrien/* get an exception label. These must be on the permanent obstack */ 56850397Sobrien 56950397Sobrienrtx 57050397Sobriengen_exception_label () 57150397Sobrien{ 57250397Sobrien rtx lab; 57350397Sobrien 57450397Sobrien push_obstacks_nochange (); 57550397Sobrien end_temporary_allocation (); 57650397Sobrien lab = gen_label_rtx (); 57750397Sobrien pop_obstacks (); 57850397Sobrien return lab; 57950397Sobrien} 58050397Sobrien 58150397Sobrien/* Push a new eh_node entry onto STACK. */ 58250397Sobrien 58350397Sobrienstatic void 58450397Sobrienpush_eh_entry (stack) 58550397Sobrien struct eh_stack *stack; 58650397Sobrien{ 58750397Sobrien struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); 58850397Sobrien struct eh_entry *entry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry)); 58950397Sobrien 59050397Sobrien entry->outer_context = gen_label_rtx (); 59150397Sobrien entry->finalization = NULL_TREE; 59250397Sobrien entry->label_used = 0; 59350397Sobrien entry->exception_handler_label = gen_exception_label (); 59450397Sobrien 59550397Sobrien node->entry = entry; 59650397Sobrien node->chain = stack->top; 59750397Sobrien stack->top = node; 59850397Sobrien} 59950397Sobrien 60050397Sobrien/* push an existing entry onto a stack. */ 60150397Sobrienstatic void 60250397Sobrienpush_entry (stack, entry) 60350397Sobrien struct eh_stack *stack; 60450397Sobrien struct eh_entry *entry; 60550397Sobrien{ 60650397Sobrien struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); 60750397Sobrien node->entry = entry; 60850397Sobrien node->chain = stack->top; 60950397Sobrien stack->top = node; 61050397Sobrien} 61150397Sobrien 61250397Sobrien/* Pop an entry from the given STACK. */ 61350397Sobrien 61450397Sobrienstatic struct eh_entry * 61550397Sobrienpop_eh_entry (stack) 61650397Sobrien struct eh_stack *stack; 61750397Sobrien{ 61850397Sobrien struct eh_node *tempnode; 61950397Sobrien struct eh_entry *tempentry; 62050397Sobrien 62150397Sobrien tempnode = stack->top; 62250397Sobrien tempentry = tempnode->entry; 62350397Sobrien stack->top = stack->top->chain; 62450397Sobrien free (tempnode); 62550397Sobrien 62650397Sobrien return tempentry; 62750397Sobrien} 62850397Sobrien 62950397Sobrien/* Enqueue an ENTRY onto the given QUEUE. */ 63050397Sobrien 63150397Sobrienstatic void 63250397Sobrienenqueue_eh_entry (queue, entry) 63350397Sobrien struct eh_queue *queue; 63450397Sobrien struct eh_entry *entry; 63550397Sobrien{ 63650397Sobrien struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node)); 63750397Sobrien 63850397Sobrien node->entry = entry; 63950397Sobrien node->chain = NULL; 64050397Sobrien 64150397Sobrien if (queue->head == NULL) 64250397Sobrien { 64350397Sobrien queue->head = node; 64450397Sobrien } 64550397Sobrien else 64650397Sobrien { 64750397Sobrien queue->tail->chain = node; 64850397Sobrien } 64950397Sobrien queue->tail = node; 65050397Sobrien} 65150397Sobrien 65250397Sobrien/* Dequeue an entry from the given QUEUE. */ 65350397Sobrien 65450397Sobrienstatic struct eh_entry * 65550397Sobriendequeue_eh_entry (queue) 65650397Sobrien struct eh_queue *queue; 65750397Sobrien{ 65850397Sobrien struct eh_node *tempnode; 65950397Sobrien struct eh_entry *tempentry; 66050397Sobrien 66150397Sobrien if (queue->head == NULL) 66250397Sobrien return NULL; 66350397Sobrien 66450397Sobrien tempnode = queue->head; 66550397Sobrien queue->head = queue->head->chain; 66650397Sobrien 66750397Sobrien tempentry = tempnode->entry; 66850397Sobrien free (tempnode); 66950397Sobrien 67050397Sobrien return tempentry; 67150397Sobrien} 67250397Sobrien 67350397Sobrienstatic void 67450397Sobrienreceive_exception_label (handler_label) 67550397Sobrien rtx handler_label; 67650397Sobrien{ 67750397Sobrien emit_label (handler_label); 67850397Sobrien 67950397Sobrien#ifdef HAVE_exception_receiver 68050397Sobrien if (! exceptions_via_longjmp) 68150397Sobrien if (HAVE_exception_receiver) 68250397Sobrien emit_insn (gen_exception_receiver ()); 68350397Sobrien#endif 68450397Sobrien 68550397Sobrien#ifdef HAVE_nonlocal_goto_receiver 68650397Sobrien if (! exceptions_via_longjmp) 68750397Sobrien if (HAVE_nonlocal_goto_receiver) 68850397Sobrien emit_insn (gen_nonlocal_goto_receiver ()); 68950397Sobrien#endif 69050397Sobrien} 69150397Sobrien 69250397Sobrien 69350397Sobrienstruct func_eh_entry 69450397Sobrien{ 69550397Sobrien int range_number; /* EH region number from EH NOTE insn's */ 69650397Sobrien struct handler_info *handlers; 69750397Sobrien}; 69850397Sobrien 69950397Sobrien 70050397Sobrien/* table of function eh regions */ 70150397Sobrienstatic struct func_eh_entry *function_eh_regions = NULL; 70250397Sobrienstatic int num_func_eh_entries = 0; 70350397Sobrienstatic int current_func_eh_entry = 0; 70450397Sobrien 70550397Sobrien#define SIZE_FUNC_EH(X) (sizeof (struct func_eh_entry) * X) 70650397Sobrien 70750397Sobrien/* Add a new eh_entry for this function, and base it off of the information 70850397Sobrien in the EH_ENTRY parameter. A NULL parameter is invalid. The number 70950397Sobrien returned is an number which uniquely identifies this exception range. */ 71050397Sobrien 71150397Sobrienint 71250397Sobriennew_eh_region_entry (note_eh_region) 71350397Sobrien int note_eh_region; 71450397Sobrien{ 71550397Sobrien if (current_func_eh_entry == num_func_eh_entries) 71650397Sobrien { 71750397Sobrien if (num_func_eh_entries == 0) 71850397Sobrien { 71950397Sobrien function_eh_regions = 72050397Sobrien (struct func_eh_entry *) malloc (SIZE_FUNC_EH (50)); 72150397Sobrien num_func_eh_entries = 50; 72250397Sobrien } 72350397Sobrien else 72450397Sobrien { 72550397Sobrien num_func_eh_entries = num_func_eh_entries * 3 / 2; 72650397Sobrien function_eh_regions = (struct func_eh_entry *) 72750397Sobrien realloc (function_eh_regions, SIZE_FUNC_EH (num_func_eh_entries)); 72850397Sobrien } 72950397Sobrien } 73050397Sobrien function_eh_regions[current_func_eh_entry].range_number = note_eh_region; 73150397Sobrien function_eh_regions[current_func_eh_entry].handlers = NULL; 73250397Sobrien 73350397Sobrien return current_func_eh_entry++; 73450397Sobrien} 73550397Sobrien 73650397Sobrien/* Add new handler information to an exception range. The first parameter 73750397Sobrien specifies the range number (returned from new_eh_entry()). The second 73850397Sobrien parameter specifies the handler. By default the handler is inserted at 73950397Sobrien the end of the list. A handler list may contain only ONE NULL_TREE 74050397Sobrien typeinfo entry. Regardless where it is positioned, a NULL_TREE entry 74150397Sobrien is always output as the LAST handler in the exception table for a region. */ 74250397Sobrien 74350397Sobrienvoid 74450397Sobrienadd_new_handler (region, newhandler) 74550397Sobrien int region; 74650397Sobrien struct handler_info *newhandler; 74750397Sobrien{ 74850397Sobrien struct handler_info *last; 74950397Sobrien 75050397Sobrien newhandler->next = NULL; 75150397Sobrien last = function_eh_regions[region].handlers; 75250397Sobrien if (last == NULL) 75350397Sobrien function_eh_regions[region].handlers = newhandler; 75450397Sobrien else 75550397Sobrien { 75650397Sobrien for ( ; last->next != NULL; last = last->next) 75750397Sobrien ; 75850397Sobrien last->next = newhandler; 75950397Sobrien } 76050397Sobrien} 76150397Sobrien 76250397Sobrien/* Remove a handler label. The handler label is being deleted, so all 76350397Sobrien regions which reference this handler should have it removed from their 76450397Sobrien list of possible handlers. Any region which has the final handler 76550397Sobrien removed can be deleted. */ 76650397Sobrien 76750397Sobrienvoid remove_handler (removing_label) 76850397Sobrien rtx removing_label; 76950397Sobrien{ 77050397Sobrien struct handler_info *handler, *last; 77150397Sobrien int x; 77250397Sobrien for (x = 0 ; x < current_func_eh_entry; ++x) 77350397Sobrien { 77450397Sobrien last = NULL; 77550397Sobrien handler = function_eh_regions[x].handlers; 77650397Sobrien for ( ; handler; last = handler, handler = handler->next) 77750397Sobrien if (handler->handler_label == removing_label) 77850397Sobrien { 77950397Sobrien if (last) 78050397Sobrien { 78150397Sobrien last->next = handler->next; 78250397Sobrien handler = last; 78350397Sobrien } 78450397Sobrien else 78550397Sobrien function_eh_regions[x].handlers = handler->next; 78650397Sobrien } 78750397Sobrien } 78850397Sobrien} 78950397Sobrien 79050397Sobrien/* This function will return a malloc'd pointer to an array of 79150397Sobrien void pointer representing the runtime match values that 79250397Sobrien currently exist in all regions. */ 79350397Sobrien 79450397Sobrienint 79550397Sobrienfind_all_handler_type_matches (array) 79650397Sobrien void ***array; 79750397Sobrien{ 79850397Sobrien struct handler_info *handler, *last; 79950397Sobrien int x,y; 80050397Sobrien void *val; 80150397Sobrien void **ptr; 80250397Sobrien int max_ptr; 80350397Sobrien int n_ptr = 0; 80450397Sobrien 80550397Sobrien *array = NULL; 80650397Sobrien 80750397Sobrien if (!doing_eh (0) || ! flag_new_exceptions) 80850397Sobrien return 0; 80950397Sobrien 81050397Sobrien max_ptr = 100; 81150397Sobrien ptr = (void **)malloc (max_ptr * sizeof (void *)); 81250397Sobrien 81350397Sobrien if (ptr == NULL) 81450397Sobrien return 0; 81550397Sobrien 81650397Sobrien for (x = 0 ; x < current_func_eh_entry; x++) 81750397Sobrien { 81850397Sobrien last = NULL; 81950397Sobrien handler = function_eh_regions[x].handlers; 82050397Sobrien for ( ; handler; last = handler, handler = handler->next) 82150397Sobrien { 82250397Sobrien val = handler->type_info; 82350397Sobrien if (val != NULL && val != CATCH_ALL_TYPE) 82450397Sobrien { 82550397Sobrien /* See if this match value has already been found. */ 82650397Sobrien for (y = 0; y < n_ptr; y++) 82750397Sobrien if (ptr[y] == val) 82850397Sobrien break; 82950397Sobrien 83050397Sobrien /* If we break early, we already found this value. */ 83150397Sobrien if (y < n_ptr) 83250397Sobrien continue; 83350397Sobrien 83450397Sobrien /* Do we need to allocate more space? */ 83550397Sobrien if (n_ptr >= max_ptr) 83650397Sobrien { 83750397Sobrien max_ptr += max_ptr / 2; 83850397Sobrien ptr = (void **)realloc (ptr, max_ptr * sizeof (void *)); 83950397Sobrien if (ptr == NULL) 84050397Sobrien return 0; 84150397Sobrien } 84250397Sobrien ptr[n_ptr] = val; 84350397Sobrien n_ptr++; 84450397Sobrien } 84550397Sobrien } 84650397Sobrien } 84750397Sobrien *array = ptr; 84850397Sobrien return n_ptr; 84950397Sobrien} 85050397Sobrien 85150397Sobrien/* Create a new handler structure initialized with the handler label and 85250397Sobrien typeinfo fields passed in. */ 85350397Sobrien 85450397Sobrienstruct handler_info * 85550397Sobrienget_new_handler (handler, typeinfo) 85650397Sobrien rtx handler; 85750397Sobrien void *typeinfo; 85850397Sobrien{ 85950397Sobrien struct handler_info* ptr; 86050397Sobrien ptr = (struct handler_info *) malloc (sizeof (struct handler_info)); 86150397Sobrien ptr->handler_label = handler; 86250397Sobrien ptr->type_info = typeinfo; 86350397Sobrien ptr->next = NULL; 86450397Sobrien 86550397Sobrien return ptr; 86650397Sobrien} 86750397Sobrien 86850397Sobrien 86950397Sobrien 87050397Sobrien/* Find the index in function_eh_regions associated with a NOTE region. If 87150397Sobrien the region cannot be found, a -1 is returned. This should never happen! */ 87250397Sobrien 87350397Sobrienint 87450397Sobrienfind_func_region (insn_region) 87550397Sobrien int insn_region; 87650397Sobrien{ 87750397Sobrien int x; 87850397Sobrien for (x = 0; x < current_func_eh_entry; x++) 87950397Sobrien if (function_eh_regions[x].range_number == insn_region) 88050397Sobrien return x; 88150397Sobrien 88250397Sobrien return -1; 88350397Sobrien} 88450397Sobrien 88550397Sobrien/* Get a pointer to the first handler in an exception region's list. */ 88650397Sobrien 88750397Sobrienstruct handler_info * 88850397Sobrienget_first_handler (region) 88950397Sobrien int region; 89050397Sobrien{ 89150397Sobrien return function_eh_regions[find_func_region (region)].handlers; 89250397Sobrien} 89350397Sobrien 89450397Sobrien/* Clean out the function_eh_region table and free all memory */ 89550397Sobrien 89650397Sobrienstatic void 89750397Sobrienclear_function_eh_region () 89850397Sobrien{ 89950397Sobrien int x; 90050397Sobrien struct handler_info *ptr, *next; 90150397Sobrien for (x = 0; x < current_func_eh_entry; x++) 90250397Sobrien for (ptr = function_eh_regions[x].handlers; ptr != NULL; ptr = next) 90350397Sobrien { 90450397Sobrien next = ptr->next; 90550397Sobrien free (ptr); 90650397Sobrien } 90750397Sobrien free (function_eh_regions); 90850397Sobrien num_func_eh_entries = 0; 90950397Sobrien current_func_eh_entry = 0; 91050397Sobrien} 91150397Sobrien 91250397Sobrien/* Make a duplicate of an exception region by copying all the handlers 91350397Sobrien for an exception region. Return the new handler index. */ 91450397Sobrien 91550397Sobrienint 91650397Sobrienduplicate_handlers (old_note_eh_region, new_note_eh_region) 91750397Sobrien int old_note_eh_region, new_note_eh_region; 91850397Sobrien{ 91950397Sobrien struct handler_info *ptr, *new_ptr; 92050397Sobrien int new_region, region; 92150397Sobrien 92250397Sobrien region = find_func_region (old_note_eh_region); 92350397Sobrien if (region == -1) 92450397Sobrien error ("Cannot duplicate non-existant exception region."); 92550397Sobrien 92650397Sobrien if (find_func_region (new_note_eh_region) != -1) 92750397Sobrien error ("Cannot duplicate EH region because new note region already exists"); 92850397Sobrien 92950397Sobrien new_region = new_eh_region_entry (new_note_eh_region); 93050397Sobrien ptr = function_eh_regions[region].handlers; 93150397Sobrien 93250397Sobrien for ( ; ptr; ptr = ptr->next) 93350397Sobrien { 93450397Sobrien new_ptr = get_new_handler (ptr->handler_label, ptr->type_info); 93550397Sobrien add_new_handler (new_region, new_ptr); 93650397Sobrien } 93750397Sobrien 93850397Sobrien return new_region; 93950397Sobrien} 94050397Sobrien 94150397Sobrien 94250397Sobrien/* Routine to see if exception handling is turned on. 94350397Sobrien DO_WARN is non-zero if we want to inform the user that exception 94450397Sobrien handling is turned off. 94550397Sobrien 94650397Sobrien This is used to ensure that -fexceptions has been specified if the 94750397Sobrien compiler tries to use any exception-specific functions. */ 94850397Sobrien 94950397Sobrienint 95050397Sobriendoing_eh (do_warn) 95150397Sobrien int do_warn; 95250397Sobrien{ 95350397Sobrien if (! flag_exceptions) 95450397Sobrien { 95550397Sobrien static int warned = 0; 95650397Sobrien if (! warned && do_warn) 95750397Sobrien { 95850397Sobrien error ("exception handling disabled, use -fexceptions to enable"); 95950397Sobrien warned = 1; 96050397Sobrien } 96150397Sobrien return 0; 96250397Sobrien } 96350397Sobrien return 1; 96450397Sobrien} 96550397Sobrien 96650397Sobrien/* Given a return address in ADDR, determine the address we should use 96750397Sobrien to find the corresponding EH region. */ 96850397Sobrien 96950397Sobrienrtx 97050397Sobrieneh_outer_context (addr) 97150397Sobrien rtx addr; 97250397Sobrien{ 97350397Sobrien /* First mask out any unwanted bits. */ 97450397Sobrien#ifdef MASK_RETURN_ADDR 97550397Sobrien expand_and (addr, MASK_RETURN_ADDR, addr); 97650397Sobrien#endif 97750397Sobrien 97850397Sobrien /* Then adjust to find the real return address. */ 97950397Sobrien#if defined (RETURN_ADDR_OFFSET) 98050397Sobrien addr = plus_constant (addr, RETURN_ADDR_OFFSET); 98150397Sobrien#endif 98250397Sobrien 98350397Sobrien return addr; 98450397Sobrien} 98550397Sobrien 98650397Sobrien/* Start a new exception region for a region of code that has a 98750397Sobrien cleanup action and push the HANDLER for the region onto 98850397Sobrien protect_list. All of the regions created with add_partial_entry 98950397Sobrien will be ended when end_protect_partials is invoked. */ 99050397Sobrien 99150397Sobrienvoid 99250397Sobrienadd_partial_entry (handler) 99350397Sobrien tree handler; 99450397Sobrien{ 99550397Sobrien expand_eh_region_start (); 99650397Sobrien 99750397Sobrien /* Make sure the entry is on the correct obstack. */ 99850397Sobrien push_obstacks_nochange (); 99950397Sobrien resume_temporary_allocation (); 100050397Sobrien 100150397Sobrien /* Because this is a cleanup action, we may have to protect the handler 100250397Sobrien with __terminate. */ 100350397Sobrien handler = protect_with_terminate (handler); 100450397Sobrien 100550397Sobrien protect_list = tree_cons (NULL_TREE, handler, protect_list); 100650397Sobrien pop_obstacks (); 100750397Sobrien} 100850397Sobrien 100950397Sobrien/* Emit code to get EH context to current function. */ 101050397Sobrien 101150397Sobrienstatic rtx 101250397Sobriencall_get_eh_context () 101350397Sobrien{ 101450397Sobrien static tree fn; 101550397Sobrien tree expr; 101650397Sobrien 101750397Sobrien if (fn == NULL_TREE) 101850397Sobrien { 101950397Sobrien tree fntype; 102050397Sobrien fn = get_identifier ("__get_eh_context"); 102150397Sobrien push_obstacks_nochange (); 102250397Sobrien end_temporary_allocation (); 102350397Sobrien fntype = build_pointer_type (build_pointer_type 102450397Sobrien (build_pointer_type (void_type_node))); 102550397Sobrien fntype = build_function_type (fntype, NULL_TREE); 102650397Sobrien fn = build_decl (FUNCTION_DECL, fn, fntype); 102750397Sobrien DECL_EXTERNAL (fn) = 1; 102850397Sobrien TREE_PUBLIC (fn) = 1; 102950397Sobrien DECL_ARTIFICIAL (fn) = 1; 103050397Sobrien TREE_READONLY (fn) = 1; 103150397Sobrien make_decl_rtl (fn, NULL_PTR, 1); 103250397Sobrien assemble_external (fn); 103350397Sobrien pop_obstacks (); 103450397Sobrien } 103550397Sobrien 103650397Sobrien expr = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (fn)), fn); 103750397Sobrien expr = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (fn)), 103850397Sobrien expr, NULL_TREE, NULL_TREE); 103950397Sobrien TREE_SIDE_EFFECTS (expr) = 1; 104050397Sobrien 104150397Sobrien return copy_to_reg (expand_expr (expr, NULL_RTX, VOIDmode, 0)); 104250397Sobrien} 104350397Sobrien 104450397Sobrien/* Get a reference to the EH context. 104550397Sobrien We will only generate a register for the current function EH context here, 104650397Sobrien and emit a USE insn to mark that this is a EH context register. 104750397Sobrien 104850397Sobrien Later, emit_eh_context will emit needed call to __get_eh_context 104950397Sobrien in libgcc2, and copy the value to the register we have generated. */ 105050397Sobrien 105150397Sobrienrtx 105250397Sobrienget_eh_context () 105350397Sobrien{ 105450397Sobrien if (current_function_ehc == 0) 105550397Sobrien { 105650397Sobrien rtx insn; 105750397Sobrien 105850397Sobrien current_function_ehc = gen_reg_rtx (Pmode); 105950397Sobrien 106050397Sobrien insn = gen_rtx_USE (GET_MODE (current_function_ehc), 106150397Sobrien current_function_ehc); 106250397Sobrien insn = emit_insn_before (insn, get_first_nonparm_insn ()); 106350397Sobrien 106450397Sobrien REG_NOTES (insn) 106550397Sobrien = gen_rtx_EXPR_LIST (REG_EH_CONTEXT, current_function_ehc, 106650397Sobrien REG_NOTES (insn)); 106750397Sobrien } 106850397Sobrien return current_function_ehc; 106950397Sobrien} 107050397Sobrien 107150397Sobrien/* Get a reference to the dynamic handler chain. It points to the 107250397Sobrien pointer to the next element in the dynamic handler chain. It ends 107350397Sobrien when there are no more elements in the dynamic handler chain, when 107450397Sobrien the value is &top_elt from libgcc2.c. Immediately after the 107550397Sobrien pointer, is an area suitable for setjmp/longjmp when 107650397Sobrien DONT_USE_BUILTIN_SETJMP is defined, and an area suitable for 107750397Sobrien __builtin_setjmp/__builtin_longjmp when DONT_USE_BUILTIN_SETJMP 107850397Sobrien isn't defined. */ 107950397Sobrien 108050397Sobrienrtx 108150397Sobrienget_dynamic_handler_chain () 108250397Sobrien{ 108350397Sobrien rtx ehc, dhc, result; 108450397Sobrien 108550397Sobrien ehc = get_eh_context (); 108650397Sobrien 108750397Sobrien /* This is the offset of dynamic_handler_chain in the eh_context struct 108850397Sobrien declared in eh-common.h. If its location is change, change this offset */ 108950397Sobrien dhc = plus_constant (ehc, POINTER_SIZE / BITS_PER_UNIT); 109050397Sobrien 109150397Sobrien result = copy_to_reg (dhc); 109250397Sobrien 109350397Sobrien /* We don't want a copy of the dcc, but rather, the single dcc. */ 109450397Sobrien return gen_rtx_MEM (Pmode, result); 109550397Sobrien} 109650397Sobrien 109750397Sobrien/* Get a reference to the dynamic cleanup chain. It points to the 109850397Sobrien pointer to the next element in the dynamic cleanup chain. 109950397Sobrien Immediately after the pointer, are two Pmode variables, one for a 110050397Sobrien pointer to a function that performs the cleanup action, and the 110150397Sobrien second, the argument to pass to that function. */ 110250397Sobrien 110350397Sobrienrtx 110450397Sobrienget_dynamic_cleanup_chain () 110550397Sobrien{ 110650397Sobrien rtx dhc, dcc, result; 110750397Sobrien 110850397Sobrien dhc = get_dynamic_handler_chain (); 110950397Sobrien dcc = plus_constant (dhc, POINTER_SIZE / BITS_PER_UNIT); 111050397Sobrien 111150397Sobrien result = copy_to_reg (dcc); 111250397Sobrien 111350397Sobrien /* We don't want a copy of the dcc, but rather, the single dcc. */ 111450397Sobrien return gen_rtx_MEM (Pmode, result); 111550397Sobrien} 111650397Sobrien 111750397Sobrien#ifdef DONT_USE_BUILTIN_SETJMP 111850397Sobrien/* Generate code to evaluate X and jump to LABEL if the value is nonzero. 111950397Sobrien LABEL is an rtx of code CODE_LABEL, in this function. */ 112050397Sobrien 112150397Sobrienstatic void 112250397Sobrienjumpif_rtx (x, label) 112350397Sobrien rtx x; 112450397Sobrien rtx label; 112550397Sobrien{ 112650397Sobrien jumpif (make_tree (type_for_mode (GET_MODE (x), 0), x), label); 112750397Sobrien} 112850397Sobrien#endif 112950397Sobrien 113050397Sobrien/* Start a dynamic cleanup on the EH runtime dynamic cleanup stack. 113150397Sobrien We just need to create an element for the cleanup list, and push it 113250397Sobrien into the chain. 113350397Sobrien 113450397Sobrien A dynamic cleanup is a cleanup action implied by the presence of an 113550397Sobrien element on the EH runtime dynamic cleanup stack that is to be 113650397Sobrien performed when an exception is thrown. The cleanup action is 113750397Sobrien performed by __sjthrow when an exception is thrown. Only certain 113850397Sobrien actions can be optimized into dynamic cleanup actions. For the 113950397Sobrien restrictions on what actions can be performed using this routine, 114050397Sobrien see expand_eh_region_start_tree. */ 114150397Sobrien 114250397Sobrienstatic void 114350397Sobrienstart_dynamic_cleanup (func, arg) 114450397Sobrien tree func; 114550397Sobrien tree arg; 114650397Sobrien{ 114750397Sobrien rtx dcc; 114850397Sobrien rtx new_func, new_arg; 114950397Sobrien rtx x, buf; 115050397Sobrien int size; 115150397Sobrien 115250397Sobrien /* We allocate enough room for a pointer to the function, and 115350397Sobrien one argument. */ 115450397Sobrien size = 2; 115550397Sobrien 115650397Sobrien /* XXX, FIXME: The stack space allocated this way is too long lived, 115750397Sobrien but there is no allocation routine that allocates at the level of 115850397Sobrien the last binding contour. */ 115950397Sobrien buf = assign_stack_local (BLKmode, 116050397Sobrien GET_MODE_SIZE (Pmode)*(size+1), 116150397Sobrien 0); 116250397Sobrien 116350397Sobrien buf = change_address (buf, Pmode, NULL_RTX); 116450397Sobrien 116550397Sobrien /* Store dcc into the first word of the newly allocated buffer. */ 116650397Sobrien 116750397Sobrien dcc = get_dynamic_cleanup_chain (); 116850397Sobrien emit_move_insn (buf, dcc); 116950397Sobrien 117050397Sobrien /* Store func and arg into the cleanup list element. */ 117150397Sobrien 117250397Sobrien new_func = gen_rtx_MEM (Pmode, plus_constant (XEXP (buf, 0), 117350397Sobrien GET_MODE_SIZE (Pmode))); 117450397Sobrien new_arg = gen_rtx_MEM (Pmode, plus_constant (XEXP (buf, 0), 117550397Sobrien GET_MODE_SIZE (Pmode)*2)); 117650397Sobrien x = expand_expr (func, new_func, Pmode, 0); 117750397Sobrien if (x != new_func) 117850397Sobrien emit_move_insn (new_func, x); 117950397Sobrien 118050397Sobrien x = expand_expr (arg, new_arg, Pmode, 0); 118150397Sobrien if (x != new_arg) 118250397Sobrien emit_move_insn (new_arg, x); 118350397Sobrien 118450397Sobrien /* Update the cleanup chain. */ 118550397Sobrien 118650397Sobrien emit_move_insn (dcc, XEXP (buf, 0)); 118750397Sobrien} 118850397Sobrien 118950397Sobrien/* Emit RTL to start a dynamic handler on the EH runtime dynamic 119050397Sobrien handler stack. This should only be used by expand_eh_region_start 119150397Sobrien or expand_eh_region_start_tree. */ 119250397Sobrien 119350397Sobrienstatic void 119450397Sobrienstart_dynamic_handler () 119550397Sobrien{ 119650397Sobrien rtx dhc, dcc; 119750397Sobrien rtx x, arg, buf; 119850397Sobrien int size; 119950397Sobrien 120050397Sobrien#ifndef DONT_USE_BUILTIN_SETJMP 120150397Sobrien /* The number of Pmode words for the setjmp buffer, when using the 120250397Sobrien builtin setjmp/longjmp, see expand_builtin, case 120350397Sobrien BUILT_IN_LONGJMP. */ 120450397Sobrien size = 5; 120550397Sobrien#else 120650397Sobrien#ifdef JMP_BUF_SIZE 120750397Sobrien size = JMP_BUF_SIZE; 120850397Sobrien#else 120950397Sobrien /* Should be large enough for most systems, if it is not, 121050397Sobrien JMP_BUF_SIZE should be defined with the proper value. It will 121150397Sobrien also tend to be larger than necessary for most systems, a more 121250397Sobrien optimal port will define JMP_BUF_SIZE. */ 121350397Sobrien size = FIRST_PSEUDO_REGISTER+2; 121450397Sobrien#endif 121550397Sobrien#endif 121650397Sobrien /* XXX, FIXME: The stack space allocated this way is too long lived, 121750397Sobrien but there is no allocation routine that allocates at the level of 121850397Sobrien the last binding contour. */ 121950397Sobrien arg = assign_stack_local (BLKmode, 122050397Sobrien GET_MODE_SIZE (Pmode)*(size+1), 122150397Sobrien 0); 122250397Sobrien 122350397Sobrien arg = change_address (arg, Pmode, NULL_RTX); 122450397Sobrien 122550397Sobrien /* Store dhc into the first word of the newly allocated buffer. */ 122650397Sobrien 122750397Sobrien dhc = get_dynamic_handler_chain (); 122850397Sobrien dcc = gen_rtx_MEM (Pmode, plus_constant (XEXP (arg, 0), 122950397Sobrien GET_MODE_SIZE (Pmode))); 123050397Sobrien emit_move_insn (arg, dhc); 123150397Sobrien 123250397Sobrien /* Zero out the start of the cleanup chain. */ 123350397Sobrien emit_move_insn (dcc, const0_rtx); 123450397Sobrien 123550397Sobrien /* The jmpbuf starts two words into the area allocated. */ 123650397Sobrien buf = plus_constant (XEXP (arg, 0), GET_MODE_SIZE (Pmode)*2); 123750397Sobrien 123850397Sobrien#ifdef DONT_USE_BUILTIN_SETJMP 123950397Sobrien x = emit_library_call_value (setjmp_libfunc, NULL_RTX, 1, SImode, 1, 124050397Sobrien buf, Pmode); 124150397Sobrien /* If we come back here for a catch, transfer control to the handler. */ 124250397Sobrien jumpif_rtx (x, ehstack.top->entry->exception_handler_label); 124350397Sobrien#else 124450397Sobrien { 124550397Sobrien /* A label to continue execution for the no exception case. */ 124650397Sobrien rtx noex = gen_label_rtx(); 124750397Sobrien x = expand_builtin_setjmp (buf, NULL_RTX, noex, 124850397Sobrien ehstack.top->entry->exception_handler_label); 124950397Sobrien emit_label (noex); 125050397Sobrien } 125150397Sobrien#endif 125250397Sobrien 125350397Sobrien /* We are committed to this, so update the handler chain. */ 125450397Sobrien 125550397Sobrien emit_move_insn (dhc, XEXP (arg, 0)); 125650397Sobrien} 125750397Sobrien 125850397Sobrien/* Start an exception handling region for the given cleanup action. 125950397Sobrien All instructions emitted after this point are considered to be part 126050397Sobrien of the region until expand_eh_region_end is invoked. CLEANUP is 126150397Sobrien the cleanup action to perform. The return value is true if the 126250397Sobrien exception region was optimized away. If that case, 126350397Sobrien expand_eh_region_end does not need to be called for this cleanup, 126450397Sobrien nor should it be. 126550397Sobrien 126650397Sobrien This routine notices one particular common case in C++ code 126750397Sobrien generation, and optimizes it so as to not need the exception 126850397Sobrien region. It works by creating a dynamic cleanup action, instead of 126950397Sobrien a using an exception region. */ 127050397Sobrien 127150397Sobrienint 127250397Sobrienexpand_eh_region_start_tree (decl, cleanup) 127350397Sobrien tree decl; 127450397Sobrien tree cleanup; 127550397Sobrien{ 127650397Sobrien /* This is the old code. */ 127750397Sobrien if (! doing_eh (0)) 127850397Sobrien return 0; 127950397Sobrien 128050397Sobrien /* The optimization only applies to actions protected with 128150397Sobrien terminate, and only applies if we are using the setjmp/longjmp 128250397Sobrien codegen method. */ 128350397Sobrien if (exceptions_via_longjmp 128450397Sobrien && protect_cleanup_actions_with_terminate) 128550397Sobrien { 128650397Sobrien tree func, arg; 128750397Sobrien tree args; 128850397Sobrien 128950397Sobrien /* Ignore any UNSAVE_EXPR. */ 129050397Sobrien if (TREE_CODE (cleanup) == UNSAVE_EXPR) 129150397Sobrien cleanup = TREE_OPERAND (cleanup, 0); 129250397Sobrien 129350397Sobrien /* Further, it only applies if the action is a call, if there 129450397Sobrien are 2 arguments, and if the second argument is 2. */ 129550397Sobrien 129650397Sobrien if (TREE_CODE (cleanup) == CALL_EXPR 129750397Sobrien && (args = TREE_OPERAND (cleanup, 1)) 129850397Sobrien && (func = TREE_OPERAND (cleanup, 0)) 129950397Sobrien && (arg = TREE_VALUE (args)) 130050397Sobrien && (args = TREE_CHAIN (args)) 130150397Sobrien 130250397Sobrien /* is the second argument 2? */ 130350397Sobrien && TREE_CODE (TREE_VALUE (args)) == INTEGER_CST 130450397Sobrien && TREE_INT_CST_LOW (TREE_VALUE (args)) == 2 130550397Sobrien && TREE_INT_CST_HIGH (TREE_VALUE (args)) == 0 130650397Sobrien 130750397Sobrien /* Make sure there are no other arguments. */ 130850397Sobrien && TREE_CHAIN (args) == NULL_TREE) 130950397Sobrien { 131050397Sobrien /* Arrange for returns and gotos to pop the entry we make on the 131150397Sobrien dynamic cleanup stack. */ 131250397Sobrien expand_dcc_cleanup (decl); 131350397Sobrien start_dynamic_cleanup (func, arg); 131450397Sobrien return 1; 131550397Sobrien } 131650397Sobrien } 131750397Sobrien 131850397Sobrien expand_eh_region_start_for_decl (decl); 131950397Sobrien ehstack.top->entry->finalization = cleanup; 132050397Sobrien 132150397Sobrien return 0; 132250397Sobrien} 132350397Sobrien 132450397Sobrien/* Just like expand_eh_region_start, except if a cleanup action is 132550397Sobrien entered on the cleanup chain, the TREE_PURPOSE of the element put 132650397Sobrien on the chain is DECL. DECL should be the associated VAR_DECL, if 132750397Sobrien any, otherwise it should be NULL_TREE. */ 132850397Sobrien 132950397Sobrienvoid 133050397Sobrienexpand_eh_region_start_for_decl (decl) 133150397Sobrien tree decl; 133250397Sobrien{ 133350397Sobrien rtx note; 133450397Sobrien 133550397Sobrien /* This is the old code. */ 133650397Sobrien if (! doing_eh (0)) 133750397Sobrien return; 133850397Sobrien 133950397Sobrien if (exceptions_via_longjmp) 134050397Sobrien { 134150397Sobrien /* We need a new block to record the start and end of the 134250397Sobrien dynamic handler chain. We could always do this, but we 134350397Sobrien really want to permit jumping into such a block, and we want 134450397Sobrien to avoid any errors or performance impact in the SJ EH code 134550397Sobrien for now. */ 134650397Sobrien expand_start_bindings (0); 134750397Sobrien 134850397Sobrien /* But we don't need or want a new temporary level. */ 134950397Sobrien pop_temp_slots (); 135050397Sobrien 135150397Sobrien /* Mark this block as created by expand_eh_region_start. This 135250397Sobrien is so that we can pop the block with expand_end_bindings 135350397Sobrien automatically. */ 135450397Sobrien mark_block_as_eh_region (); 135550397Sobrien 135650397Sobrien /* Arrange for returns and gotos to pop the entry we make on the 135750397Sobrien dynamic handler stack. */ 135850397Sobrien expand_dhc_cleanup (decl); 135950397Sobrien } 136050397Sobrien 136150397Sobrien push_eh_entry (&ehstack); 136250397Sobrien note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG); 136350397Sobrien NOTE_BLOCK_NUMBER (note) 136450397Sobrien = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label); 136550397Sobrien if (exceptions_via_longjmp) 136650397Sobrien start_dynamic_handler (); 136750397Sobrien} 136850397Sobrien 136950397Sobrien/* Start an exception handling region. All instructions emitted after 137050397Sobrien this point are considered to be part of the region until 137150397Sobrien expand_eh_region_end is invoked. */ 137250397Sobrien 137350397Sobrienvoid 137450397Sobrienexpand_eh_region_start () 137550397Sobrien{ 137650397Sobrien expand_eh_region_start_for_decl (NULL_TREE); 137750397Sobrien} 137850397Sobrien 137950397Sobrien/* End an exception handling region. The information about the region 138050397Sobrien is found on the top of ehstack. 138150397Sobrien 138250397Sobrien HANDLER is either the cleanup for the exception region, or if we're 138350397Sobrien marking the end of a try block, HANDLER is integer_zero_node. 138450397Sobrien 138550397Sobrien HANDLER will be transformed to rtl when expand_leftover_cleanups 138650397Sobrien is invoked. */ 138750397Sobrien 138850397Sobrienvoid 138950397Sobrienexpand_eh_region_end (handler) 139050397Sobrien tree handler; 139150397Sobrien{ 139250397Sobrien struct eh_entry *entry; 139350397Sobrien rtx note; 139450397Sobrien 139550397Sobrien if (! doing_eh (0)) 139650397Sobrien return; 139750397Sobrien 139850397Sobrien entry = pop_eh_entry (&ehstack); 139950397Sobrien 140050397Sobrien note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END); 140150397Sobrien NOTE_BLOCK_NUMBER (note) 140250397Sobrien = CODE_LABEL_NUMBER (entry->exception_handler_label); 140350397Sobrien if (exceptions_via_longjmp == 0 140450397Sobrien /* We share outer_context between regions; only emit it once. */ 140550397Sobrien && INSN_UID (entry->outer_context) == 0) 140650397Sobrien { 140750397Sobrien rtx label; 140850397Sobrien 140950397Sobrien label = gen_label_rtx (); 141050397Sobrien emit_jump (label); 141150397Sobrien 141250397Sobrien /* Emit a label marking the end of this exception region that 141350397Sobrien is used for rethrowing into the outer context. */ 141450397Sobrien emit_label (entry->outer_context); 141550397Sobrien expand_internal_throw (); 141650397Sobrien 141750397Sobrien emit_label (label); 141850397Sobrien } 141950397Sobrien 142050397Sobrien entry->finalization = handler; 142150397Sobrien 142250397Sobrien /* create region entry in final exception table */ 142350397Sobrien new_eh_region_entry (NOTE_BLOCK_NUMBER (note)); 142450397Sobrien 142550397Sobrien enqueue_eh_entry (&ehqueue, entry); 142650397Sobrien 142750397Sobrien /* If we have already started ending the bindings, don't recurse. 142850397Sobrien This only happens when exceptions_via_longjmp is true. */ 142950397Sobrien if (is_eh_region ()) 143050397Sobrien { 143150397Sobrien /* Because we don't need or want a new temporary level and 143250397Sobrien because we didn't create one in expand_eh_region_start, 143350397Sobrien create a fake one now to avoid removing one in 143450397Sobrien expand_end_bindings. */ 143550397Sobrien push_temp_slots (); 143650397Sobrien 143750397Sobrien mark_block_as_not_eh_region (); 143850397Sobrien 143950397Sobrien /* Maybe do this to prevent jumping in and so on... */ 144050397Sobrien expand_end_bindings (NULL_TREE, 0, 0); 144150397Sobrien } 144250397Sobrien} 144350397Sobrien 144450397Sobrien/* End the EH region for a goto fixup. We only need them in the region-based 144550397Sobrien EH scheme. */ 144650397Sobrien 144750397Sobrienvoid 144850397Sobrienexpand_fixup_region_start () 144950397Sobrien{ 145050397Sobrien if (! doing_eh (0) || exceptions_via_longjmp) 145150397Sobrien return; 145250397Sobrien 145350397Sobrien expand_eh_region_start (); 145450397Sobrien} 145550397Sobrien 145650397Sobrien/* End the EH region for a goto fixup. CLEANUP is the cleanup we just 145750397Sobrien expanded; to avoid running it twice if it throws, we look through the 145850397Sobrien ehqueue for a matching region and rethrow from its outer_context. */ 145950397Sobrien 146050397Sobrienvoid 146150397Sobrienexpand_fixup_region_end (cleanup) 146250397Sobrien tree cleanup; 146350397Sobrien{ 146450397Sobrien struct eh_node *node; 146550397Sobrien int dont_issue; 146650397Sobrien 146750397Sobrien if (! doing_eh (0) || exceptions_via_longjmp) 146850397Sobrien return; 146950397Sobrien 147050397Sobrien for (node = ehstack.top; node && node->entry->finalization != cleanup; ) 147150397Sobrien node = node->chain; 147250397Sobrien if (node == 0) 147350397Sobrien for (node = ehqueue.head; node && node->entry->finalization != cleanup; ) 147450397Sobrien node = node->chain; 147550397Sobrien if (node == 0) 147650397Sobrien abort (); 147750397Sobrien 147850397Sobrien /* If the outer context label has not been issued yet, we don't want 147950397Sobrien to issue it as a part of this region, unless this is the 148050397Sobrien correct region for the outer context. If we did, then the label for 148150397Sobrien the outer context will be WITHIN the begin/end labels, 148250397Sobrien and we could get an infinte loop when it tried to rethrow, or just 148350397Sobrien generally incorrect execution following a throw. */ 148450397Sobrien 148550397Sobrien dont_issue = ((INSN_UID (node->entry->outer_context) == 0) 148650397Sobrien && (ehstack.top->entry != node->entry)); 148750397Sobrien 148850397Sobrien ehstack.top->entry->outer_context = node->entry->outer_context; 148950397Sobrien 149050397Sobrien /* Since we are rethrowing to the OUTER region, we know we don't need 149150397Sobrien a jump around sequence for this region, so we'll pretend the outer 149250397Sobrien context label has been issued by setting INSN_UID to 1, then clearing 149350397Sobrien it again afterwards. */ 149450397Sobrien 149550397Sobrien if (dont_issue) 149650397Sobrien INSN_UID (node->entry->outer_context) = 1; 149750397Sobrien 149850397Sobrien /* Just rethrow. size_zero_node is just a NOP. */ 149950397Sobrien expand_eh_region_end (size_zero_node); 150050397Sobrien 150150397Sobrien if (dont_issue) 150250397Sobrien INSN_UID (node->entry->outer_context) = 0; 150350397Sobrien} 150450397Sobrien 150550397Sobrien/* If we are using the setjmp/longjmp EH codegen method, we emit a 150650397Sobrien call to __sjthrow. 150750397Sobrien 150850397Sobrien Otherwise, we emit a call to __throw and note that we threw 150950397Sobrien something, so we know we need to generate the necessary code for 151050397Sobrien __throw. 151150397Sobrien 151250397Sobrien Before invoking throw, the __eh_pc variable must have been set up 151350397Sobrien to contain the PC being thrown from. This address is used by 151450397Sobrien __throw to determine which exception region (if any) is 151550397Sobrien responsible for handling the exception. */ 151650397Sobrien 151750397Sobrienvoid 151850397Sobrienemit_throw () 151950397Sobrien{ 152050397Sobrien if (exceptions_via_longjmp) 152150397Sobrien { 152250397Sobrien emit_library_call (sjthrow_libfunc, 0, VOIDmode, 0); 152350397Sobrien } 152450397Sobrien else 152550397Sobrien { 152650397Sobrien#ifdef JUMP_TO_THROW 152750397Sobrien emit_indirect_jump (throw_libfunc); 152850397Sobrien#else 152950397Sobrien emit_library_call (throw_libfunc, 0, VOIDmode, 0); 153050397Sobrien#endif 153150397Sobrien } 153250397Sobrien emit_barrier (); 153350397Sobrien} 153450397Sobrien 153550397Sobrien/* Throw the current exception. If appropriate, this is done by jumping 153650397Sobrien to the next handler. */ 153750397Sobrien 153850397Sobrienvoid 153950397Sobrienexpand_internal_throw () 154050397Sobrien{ 154150397Sobrien emit_throw (); 154250397Sobrien} 154350397Sobrien 154450397Sobrien/* Called from expand_exception_blocks and expand_end_catch_block to 154550397Sobrien emit any pending handlers/cleanups queued from expand_eh_region_end. */ 154650397Sobrien 154750397Sobrienvoid 154850397Sobrienexpand_leftover_cleanups () 154950397Sobrien{ 155050397Sobrien struct eh_entry *entry; 155150397Sobrien 155250397Sobrien while ((entry = dequeue_eh_entry (&ehqueue)) != 0) 155350397Sobrien { 155450397Sobrien rtx prev; 155550397Sobrien 155650397Sobrien /* A leftover try block. Shouldn't be one here. */ 155750397Sobrien if (entry->finalization == integer_zero_node) 155850397Sobrien abort (); 155950397Sobrien 156050397Sobrien /* Output the label for the start of the exception handler. */ 156150397Sobrien 156250397Sobrien receive_exception_label (entry->exception_handler_label); 156350397Sobrien 156450397Sobrien /* register a handler for this cleanup region */ 156550397Sobrien add_new_handler ( 156650397Sobrien find_func_region (CODE_LABEL_NUMBER (entry->exception_handler_label)), 156750397Sobrien get_new_handler (entry->exception_handler_label, NULL)); 156850397Sobrien 156950397Sobrien /* And now generate the insns for the handler. */ 157050397Sobrien expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); 157150397Sobrien 157250397Sobrien prev = get_last_insn (); 157350397Sobrien if (prev == NULL || GET_CODE (prev) != BARRIER) 157450397Sobrien /* Emit code to throw to the outer context if we fall off 157550397Sobrien the end of the handler. */ 157650397Sobrien expand_rethrow (entry->outer_context); 157750397Sobrien 157850397Sobrien do_pending_stack_adjust (); 157950397Sobrien free (entry); 158050397Sobrien } 158150397Sobrien} 158250397Sobrien 158350397Sobrien/* Called at the start of a block of try statements. */ 158450397Sobrienvoid 158550397Sobrienexpand_start_try_stmts () 158650397Sobrien{ 158750397Sobrien if (! doing_eh (1)) 158850397Sobrien return; 158950397Sobrien 159050397Sobrien expand_eh_region_start (); 159150397Sobrien} 159250397Sobrien 159350397Sobrien/* Called to begin a catch clause. The parameter is the object which 159450397Sobrien will be passed to the runtime type check routine. */ 159550397Sobrienvoid 159650397Sobrienstart_catch_handler (rtime) 159750397Sobrien tree rtime; 159850397Sobrien{ 159950397Sobrien rtx handler_label; 160050397Sobrien int insn_region_num; 160150397Sobrien int eh_region_entry; 160250397Sobrien 160350397Sobrien if (! doing_eh (1)) 160450397Sobrien return; 160550397Sobrien 160650397Sobrien handler_label = catchstack.top->entry->exception_handler_label; 160750397Sobrien insn_region_num = CODE_LABEL_NUMBER (handler_label); 160850397Sobrien eh_region_entry = find_func_region (insn_region_num); 160950397Sobrien 161050397Sobrien /* If we've already issued this label, pick a new one */ 161150397Sobrien if (catchstack.top->entry->label_used) 161250397Sobrien handler_label = gen_exception_label (); 161350397Sobrien else 161450397Sobrien catchstack.top->entry->label_used = 1; 161550397Sobrien 161650397Sobrien receive_exception_label (handler_label); 161750397Sobrien 161850397Sobrien add_new_handler (eh_region_entry, get_new_handler (handler_label, rtime)); 161950397Sobrien} 162050397Sobrien 162150397Sobrien/* Generate RTL for the start of a group of catch clauses. 162250397Sobrien 162350397Sobrien It is responsible for starting a new instruction sequence for the 162450397Sobrien instructions in the catch block, and expanding the handlers for the 162550397Sobrien internally-generated exception regions nested within the try block 162650397Sobrien corresponding to this catch block. */ 162750397Sobrien 162850397Sobrienvoid 162950397Sobrienexpand_start_all_catch () 163050397Sobrien{ 163150397Sobrien struct eh_entry *entry; 163250397Sobrien tree label; 163350397Sobrien rtx outer_context; 163450397Sobrien 163550397Sobrien if (! doing_eh (1)) 163650397Sobrien return; 163750397Sobrien 163850397Sobrien outer_context = ehstack.top->entry->outer_context; 163950397Sobrien 164050397Sobrien /* End the try block. */ 164150397Sobrien expand_eh_region_end (integer_zero_node); 164250397Sobrien 164350397Sobrien emit_line_note (input_filename, lineno); 164450397Sobrien label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); 164550397Sobrien 164650397Sobrien /* The label for the exception handling block that we will save. 164750397Sobrien This is Lresume in the documentation. */ 164850397Sobrien expand_label (label); 164950397Sobrien 165050397Sobrien /* Push the label that points to where normal flow is resumed onto 165150397Sobrien the top of the label stack. */ 165250397Sobrien push_label_entry (&caught_return_label_stack, NULL_RTX, label); 165350397Sobrien 165450397Sobrien /* Start a new sequence for all the catch blocks. We will add this 165550397Sobrien to the global sequence catch_clauses when we have completed all 165650397Sobrien the handlers in this handler-seq. */ 165750397Sobrien start_sequence (); 165850397Sobrien 165950397Sobrien entry = dequeue_eh_entry (&ehqueue); 166050397Sobrien for ( ; entry->finalization != integer_zero_node; 166150397Sobrien entry = dequeue_eh_entry (&ehqueue)) 166250397Sobrien { 166350397Sobrien rtx prev; 166450397Sobrien 166550397Sobrien /* Emit the label for the cleanup handler for this region, and 166650397Sobrien expand the code for the handler. 166750397Sobrien 166850397Sobrien Note that a catch region is handled as a side-effect here; 166950397Sobrien for a try block, entry->finalization will contain 167050397Sobrien integer_zero_node, so no code will be generated in the 167150397Sobrien expand_expr call below. But, the label for the handler will 167250397Sobrien still be emitted, so any code emitted after this point will 167350397Sobrien end up being the handler. */ 167450397Sobrien 167550397Sobrien receive_exception_label (entry->exception_handler_label); 167650397Sobrien 167750397Sobrien /* register a handler for this cleanup region */ 167850397Sobrien add_new_handler ( 167950397Sobrien find_func_region (CODE_LABEL_NUMBER (entry->exception_handler_label)), 168050397Sobrien get_new_handler (entry->exception_handler_label, NULL)); 168150397Sobrien 168250397Sobrien /* And now generate the insns for the cleanup handler. */ 168350397Sobrien expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); 168450397Sobrien 168550397Sobrien prev = get_last_insn (); 168650397Sobrien if (prev == NULL || GET_CODE (prev) != BARRIER) 168750397Sobrien /* Code to throw out to outer context when we fall off end 168850397Sobrien of the handler. We can't do this here for catch blocks, 168950397Sobrien so it's done in expand_end_all_catch instead. */ 169050397Sobrien expand_rethrow (entry->outer_context); 169150397Sobrien 169250397Sobrien do_pending_stack_adjust (); 169350397Sobrien free (entry); 169450397Sobrien } 169550397Sobrien 169650397Sobrien /* At this point, all the cleanups are done, and the ehqueue now has 169750397Sobrien the current exception region at its head. We dequeue it, and put it 169850397Sobrien on the catch stack. */ 169950397Sobrien 170050397Sobrien push_entry (&catchstack, entry); 170150397Sobrien 170250397Sobrien /* If we are not doing setjmp/longjmp EH, because we are reordered 170350397Sobrien out of line, we arrange to rethrow in the outer context. We need to 170450397Sobrien do this because we are not physically within the region, if any, that 170550397Sobrien logically contains this catch block. */ 170650397Sobrien if (! exceptions_via_longjmp) 170750397Sobrien { 170850397Sobrien expand_eh_region_start (); 170950397Sobrien ehstack.top->entry->outer_context = outer_context; 171050397Sobrien } 171150397Sobrien 171250397Sobrien /* We also have to start the handler if we aren't using the new model. */ 171350397Sobrien if (! flag_new_exceptions) 171450397Sobrien start_catch_handler (NULL); 171550397Sobrien} 171650397Sobrien 171750397Sobrien/* Finish up the catch block. At this point all the insns for the 171850397Sobrien catch clauses have already been generated, so we only have to add 171950397Sobrien them to the catch_clauses list. We also want to make sure that if 172050397Sobrien we fall off the end of the catch clauses that we rethrow to the 172150397Sobrien outer EH region. */ 172250397Sobrien 172350397Sobrienvoid 172450397Sobrienexpand_end_all_catch () 172550397Sobrien{ 172650397Sobrien rtx new_catch_clause, outer_context = NULL_RTX; 172750397Sobrien struct eh_entry *entry; 172850397Sobrien 172950397Sobrien if (! doing_eh (1)) 173050397Sobrien return; 173150397Sobrien 173250397Sobrien /* Dequeue the current catch clause region. */ 173350397Sobrien entry = pop_eh_entry (&catchstack); 173450397Sobrien free (entry); 173550397Sobrien 173650397Sobrien if (! exceptions_via_longjmp) 173750397Sobrien { 173850397Sobrien outer_context = ehstack.top->entry->outer_context; 173950397Sobrien 174050397Sobrien /* Finish the rethrow region. size_zero_node is just a NOP. */ 174150397Sobrien expand_eh_region_end (size_zero_node); 174250397Sobrien } 174350397Sobrien 174450397Sobrien /* Code to throw out to outer context, if we fall off end of catch 174550397Sobrien handlers. This is rethrow (Lresume, same id, same obj) in the 174650397Sobrien documentation. We use Lresume because we know that it will throw 174750397Sobrien to the correct context. 174850397Sobrien 174950397Sobrien In other words, if the catch handler doesn't exit or return, we 175050397Sobrien do a "throw" (using the address of Lresume as the point being 175150397Sobrien thrown from) so that the outer EH region can then try to process 175250397Sobrien the exception. */ 175350397Sobrien expand_rethrow (outer_context); 175450397Sobrien 175550397Sobrien /* Now we have the complete catch sequence. */ 175650397Sobrien new_catch_clause = get_insns (); 175750397Sobrien end_sequence (); 175850397Sobrien 175950397Sobrien /* This level of catch blocks is done, so set up the successful 176050397Sobrien catch jump label for the next layer of catch blocks. */ 176150397Sobrien pop_label_entry (&caught_return_label_stack); 176250397Sobrien pop_label_entry (&outer_context_label_stack); 176350397Sobrien 176450397Sobrien /* Add the new sequence of catches to the main one for this function. */ 176550397Sobrien push_to_sequence (catch_clauses); 176650397Sobrien emit_insns (new_catch_clause); 176750397Sobrien catch_clauses = get_insns (); 176850397Sobrien end_sequence (); 176950397Sobrien 177050397Sobrien /* Here we fall through into the continuation code. */ 177150397Sobrien} 177250397Sobrien 177350397Sobrien/* Rethrow from the outer context LABEL. */ 177450397Sobrien 177550397Sobrienstatic void 177650397Sobrienexpand_rethrow (label) 177750397Sobrien rtx label; 177850397Sobrien{ 177950397Sobrien if (exceptions_via_longjmp) 178050397Sobrien emit_throw (); 178150397Sobrien else 178250397Sobrien emit_jump (label); 178350397Sobrien} 178450397Sobrien 178550397Sobrien/* End all the pending exception regions on protect_list. The handlers 178650397Sobrien will be emitted when expand_leftover_cleanups is invoked. */ 178750397Sobrien 178850397Sobrienvoid 178950397Sobrienend_protect_partials () 179050397Sobrien{ 179150397Sobrien while (protect_list) 179250397Sobrien { 179350397Sobrien expand_eh_region_end (TREE_VALUE (protect_list)); 179450397Sobrien protect_list = TREE_CHAIN (protect_list); 179550397Sobrien } 179650397Sobrien} 179750397Sobrien 179850397Sobrien/* Arrange for __terminate to be called if there is an unhandled throw 179950397Sobrien from within E. */ 180050397Sobrien 180150397Sobrientree 180250397Sobrienprotect_with_terminate (e) 180350397Sobrien tree e; 180450397Sobrien{ 180550397Sobrien /* We only need to do this when using setjmp/longjmp EH and the 180650397Sobrien language requires it, as otherwise we protect all of the handlers 180750397Sobrien at once, if we need to. */ 180850397Sobrien if (exceptions_via_longjmp && protect_cleanup_actions_with_terminate) 180950397Sobrien { 181050397Sobrien tree handler, result; 181150397Sobrien 181250397Sobrien /* All cleanups must be on the function_obstack. */ 181350397Sobrien push_obstacks_nochange (); 181450397Sobrien resume_temporary_allocation (); 181550397Sobrien 181650397Sobrien handler = make_node (RTL_EXPR); 181750397Sobrien TREE_TYPE (handler) = void_type_node; 181850397Sobrien RTL_EXPR_RTL (handler) = const0_rtx; 181950397Sobrien TREE_SIDE_EFFECTS (handler) = 1; 182050397Sobrien start_sequence_for_rtl_expr (handler); 182150397Sobrien 182250397Sobrien emit_library_call (terminate_libfunc, 0, VOIDmode, 0); 182350397Sobrien emit_barrier (); 182450397Sobrien 182550397Sobrien RTL_EXPR_SEQUENCE (handler) = get_insns (); 182650397Sobrien end_sequence (); 182750397Sobrien 182850397Sobrien result = build (TRY_CATCH_EXPR, TREE_TYPE (e), e, handler); 182950397Sobrien TREE_SIDE_EFFECTS (result) = TREE_SIDE_EFFECTS (e); 183050397Sobrien TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (e); 183150397Sobrien TREE_READONLY (result) = TREE_READONLY (e); 183250397Sobrien 183350397Sobrien pop_obstacks (); 183450397Sobrien 183550397Sobrien e = result; 183650397Sobrien } 183750397Sobrien 183850397Sobrien return e; 183950397Sobrien} 184050397Sobrien 184150397Sobrien/* The exception table that we build that is used for looking up and 184250397Sobrien dispatching exceptions, the current number of entries, and its 184350397Sobrien maximum size before we have to extend it. 184450397Sobrien 184550397Sobrien The number in eh_table is the code label number of the exception 184650397Sobrien handler for the region. This is added by add_eh_table_entry and 184750397Sobrien used by output_exception_table_entry. */ 184850397Sobrien 184950397Sobrienstatic int *eh_table = NULL; 185050397Sobrienstatic int eh_table_size = 0; 185150397Sobrienstatic int eh_table_max_size = 0; 185250397Sobrien 185350397Sobrien/* Note the need for an exception table entry for region N. If we 185450397Sobrien don't need to output an explicit exception table, avoid all of the 185550397Sobrien extra work. 185650397Sobrien 185750397Sobrien Called from final_scan_insn when a NOTE_INSN_EH_REGION_BEG is seen. 185850397Sobrien (Or NOTE_INSN_EH_REGION_END sometimes) 185950397Sobrien N is the NOTE_BLOCK_NUMBER of the note, which comes from the code 186050397Sobrien label number of the exception handler for the region. */ 186150397Sobrien 186250397Sobrienvoid 186350397Sobrienadd_eh_table_entry (n) 186450397Sobrien int n; 186550397Sobrien{ 186650397Sobrien#ifndef OMIT_EH_TABLE 186750397Sobrien if (eh_table_size >= eh_table_max_size) 186850397Sobrien { 186950397Sobrien if (eh_table) 187050397Sobrien { 187150397Sobrien eh_table_max_size += eh_table_max_size>>1; 187250397Sobrien 187350397Sobrien if (eh_table_max_size < 0) 187450397Sobrien abort (); 187550397Sobrien 187650397Sobrien eh_table = (int *) xrealloc (eh_table, 187750397Sobrien eh_table_max_size * sizeof (int)); 187850397Sobrien } 187950397Sobrien else 188050397Sobrien { 188150397Sobrien eh_table_max_size = 252; 188250397Sobrien eh_table = (int *) xmalloc (eh_table_max_size * sizeof (int)); 188350397Sobrien } 188450397Sobrien } 188550397Sobrien eh_table[eh_table_size++] = n; 188650397Sobrien#endif 188750397Sobrien} 188850397Sobrien 188950397Sobrien/* Return a non-zero value if we need to output an exception table. 189050397Sobrien 189150397Sobrien On some platforms, we don't have to output a table explicitly. 189250397Sobrien This routine doesn't mean we don't have one. */ 189350397Sobrien 189450397Sobrienint 189550397Sobrienexception_table_p () 189650397Sobrien{ 189750397Sobrien if (eh_table) 189850397Sobrien return 1; 189950397Sobrien 190050397Sobrien return 0; 190150397Sobrien} 190250397Sobrien 190350397Sobrien/* Output the entry of the exception table corresponding to the 190450397Sobrien exception region numbered N to file FILE. 190550397Sobrien 190650397Sobrien N is the code label number corresponding to the handler of the 190750397Sobrien region. */ 190850397Sobrien 190950397Sobrienstatic void 191050397Sobrienoutput_exception_table_entry (file, n) 191150397Sobrien FILE *file; 191250397Sobrien int n; 191350397Sobrien{ 191450397Sobrien char buf[256]; 191550397Sobrien rtx sym; 191650397Sobrien struct handler_info *handler; 191750397Sobrien 191850397Sobrien handler = get_first_handler (n); 191950397Sobrien 192050397Sobrien for ( ; handler != NULL; handler = handler->next) 192150397Sobrien { 192250397Sobrien ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n); 192350397Sobrien sym = gen_rtx_SYMBOL_REF (Pmode, buf); 192450397Sobrien assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); 192550397Sobrien 192650397Sobrien ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n); 192750397Sobrien sym = gen_rtx_SYMBOL_REF (Pmode, buf); 192850397Sobrien assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1); 192950397Sobrien 193050397Sobrien assemble_integer (handler->handler_label, 193150397Sobrien POINTER_SIZE / BITS_PER_UNIT, 1); 193250397Sobrien 193350397Sobrien if (flag_new_exceptions) 193450397Sobrien { 193550397Sobrien if (handler->type_info == NULL) 193650397Sobrien assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); 193750397Sobrien else 193850397Sobrien if (handler->type_info == CATCH_ALL_TYPE) 193950397Sobrien assemble_integer (GEN_INT (CATCH_ALL_TYPE), 194050397Sobrien POINTER_SIZE / BITS_PER_UNIT, 1); 194150397Sobrien else 194250397Sobrien output_constant ((tree)(handler->type_info), 194350397Sobrien POINTER_SIZE / BITS_PER_UNIT); 194450397Sobrien } 194550397Sobrien putc ('\n', file); /* blank line */ 194650397Sobrien } 194750397Sobrien} 194850397Sobrien 194950397Sobrien/* Output the exception table if we have and need one. */ 195050397Sobrien 195150397Sobrienstatic short language_code = 0; 195250397Sobrienstatic short version_code = 0; 195350397Sobrien 195450397Sobrien/* This routine will set the language code for exceptions. */ 195550397Sobrienvoid set_exception_lang_code (code) 195650397Sobrien short code; 195750397Sobrien{ 195850397Sobrien language_code = code; 195950397Sobrien} 196050397Sobrien 196150397Sobrien/* This routine will set the language version code for exceptions. */ 196250397Sobrienvoid set_exception_version_code (code) 196350397Sobrien short code; 196450397Sobrien{ 196550397Sobrien version_code = code; 196650397Sobrien} 196750397Sobrien 196850397Sobrien 196950397Sobrienvoid 197050397Sobrienoutput_exception_table () 197150397Sobrien{ 197250397Sobrien int i; 197350397Sobrien extern FILE *asm_out_file; 197450397Sobrien 197550397Sobrien if (! doing_eh (0) || ! eh_table) 197650397Sobrien return; 197750397Sobrien 197850397Sobrien exception_section (); 197950397Sobrien 198050397Sobrien /* Beginning marker for table. */ 198150397Sobrien assemble_align (GET_MODE_ALIGNMENT (ptr_mode)); 198250397Sobrien assemble_label ("__EXCEPTION_TABLE__"); 198350397Sobrien 198450397Sobrien if (flag_new_exceptions) 198550397Sobrien { 198650397Sobrien assemble_integer (GEN_INT (NEW_EH_RUNTIME), 198750397Sobrien POINTER_SIZE / BITS_PER_UNIT, 1); 198850397Sobrien assemble_integer (GEN_INT (language_code), 2 , 1); 198950397Sobrien assemble_integer (GEN_INT (version_code), 2 , 1); 199050397Sobrien 199150397Sobrien /* Add enough padding to make sure table aligns on a pointer boundry. */ 199250397Sobrien i = GET_MODE_ALIGNMENT (ptr_mode) / BITS_PER_UNIT - 4; 199350397Sobrien for ( ; i < 0; i = i + GET_MODE_ALIGNMENT (ptr_mode) / BITS_PER_UNIT) 199450397Sobrien ; 199550397Sobrien if (i != 0) 199650397Sobrien assemble_integer (const0_rtx, i , 1); 199750397Sobrien } 199850397Sobrien 199950397Sobrien for (i = 0; i < eh_table_size; ++i) 200050397Sobrien output_exception_table_entry (asm_out_file, eh_table[i]); 200150397Sobrien 200250397Sobrien free (eh_table); 200350397Sobrien clear_function_eh_region (); 200450397Sobrien 200550397Sobrien /* Ending marker for table. */ 200650397Sobrien assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); 200750397Sobrien 200850397Sobrien /* for binary compatability, the old __throw checked the second 200950397Sobrien position for a -1, so we should output at least 2 -1's */ 201050397Sobrien if (! flag_new_exceptions) 201150397Sobrien assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1); 201250397Sobrien 201350397Sobrien putc ('\n', asm_out_file); /* blank line */ 201450397Sobrien} 201550397Sobrien 201650397Sobrien/* Emit code to get EH context. 201750397Sobrien 201850397Sobrien We have to scan thru the code to find possible EH context registers. 201950397Sobrien Inlined functions may use it too, and thus we'll have to be able 202050397Sobrien to change them too. 202150397Sobrien 202250397Sobrien This is done only if using exceptions_via_longjmp. */ 202350397Sobrien 202450397Sobrienvoid 202550397Sobrienemit_eh_context () 202650397Sobrien{ 202750397Sobrien rtx insn; 202850397Sobrien rtx ehc = 0; 202950397Sobrien 203050397Sobrien if (! doing_eh (0)) 203150397Sobrien return; 203250397Sobrien 203350397Sobrien for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 203450397Sobrien if (GET_CODE (insn) == INSN 203550397Sobrien && GET_CODE (PATTERN (insn)) == USE) 203650397Sobrien { 203750397Sobrien rtx reg = find_reg_note (insn, REG_EH_CONTEXT, 0); 203850397Sobrien if (reg) 203950397Sobrien { 204050397Sobrien rtx insns; 204150397Sobrien 204250397Sobrien start_sequence (); 204350397Sobrien 204450397Sobrien /* If this is the first use insn, emit the call here. This 204550397Sobrien will always be at the top of our function, because if 204650397Sobrien expand_inline_function notices a REG_EH_CONTEXT note, it 204750397Sobrien adds a use insn to this function as well. */ 204850397Sobrien if (ehc == 0) 204950397Sobrien ehc = call_get_eh_context (); 205050397Sobrien 205150397Sobrien emit_move_insn (XEXP (reg, 0), ehc); 205250397Sobrien insns = get_insns (); 205350397Sobrien end_sequence (); 205450397Sobrien 205550397Sobrien emit_insns_before (insns, insn); 205650397Sobrien } 205750397Sobrien } 205850397Sobrien} 205950397Sobrien 206050397Sobrien/* Scan the current insns and build a list of handler labels. The 206150397Sobrien resulting list is placed in the global variable exception_handler_labels. 206250397Sobrien 206350397Sobrien It is called after the last exception handling region is added to 206450397Sobrien the current function (when the rtl is almost all built for the 206550397Sobrien current function) and before the jump optimization pass. */ 206650397Sobrien 206750397Sobrienvoid 206850397Sobrienfind_exception_handler_labels () 206950397Sobrien{ 207050397Sobrien rtx insn; 207150397Sobrien 207250397Sobrien exception_handler_labels = NULL_RTX; 207350397Sobrien 207450397Sobrien /* If we aren't doing exception handling, there isn't much to check. */ 207550397Sobrien if (! doing_eh (0)) 207650397Sobrien return; 207750397Sobrien 207850397Sobrien /* For each start of a region, add its label to the list. */ 207950397Sobrien 208050397Sobrien for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 208150397Sobrien { 208250397Sobrien struct handler_info* ptr; 208350397Sobrien if (GET_CODE (insn) == NOTE 208450397Sobrien && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) 208550397Sobrien { 208650397Sobrien ptr = get_first_handler (NOTE_BLOCK_NUMBER (insn)); 208750397Sobrien for ( ; ptr; ptr = ptr->next) 208850397Sobrien { 208950397Sobrien /* make sure label isn't in the list already */ 209050397Sobrien rtx x; 209150397Sobrien for (x = exception_handler_labels; x; x = XEXP (x, 1)) 209250397Sobrien if (XEXP (x, 0) == ptr->handler_label) 209350397Sobrien break; 209450397Sobrien if (! x) 209550397Sobrien exception_handler_labels = gen_rtx_EXPR_LIST (VOIDmode, 209650397Sobrien ptr->handler_label, exception_handler_labels); 209750397Sobrien } 209850397Sobrien } 209950397Sobrien } 210050397Sobrien} 210150397Sobrien 210250397Sobrien/* Return a value of 1 if the parameter label number is an exception handler 210350397Sobrien label. Return 0 otherwise. */ 210450397Sobrien 210550397Sobrienint 210650397Sobrienis_exception_handler_label (lab) 210750397Sobrien int lab; 210850397Sobrien{ 210950397Sobrien rtx x; 211050397Sobrien for (x = exception_handler_labels ; x ; x = XEXP (x, 1)) 211150397Sobrien if (lab == CODE_LABEL_NUMBER (XEXP (x, 0))) 211250397Sobrien return 1; 211350397Sobrien return 0; 211450397Sobrien} 211550397Sobrien 211650397Sobrien/* Perform sanity checking on the exception_handler_labels list. 211750397Sobrien 211850397Sobrien Can be called after find_exception_handler_labels is called to 211950397Sobrien build the list of exception handlers for the current function and 212050397Sobrien before we finish processing the current function. */ 212150397Sobrien 212250397Sobrienvoid 212350397Sobriencheck_exception_handler_labels () 212450397Sobrien{ 212550397Sobrien rtx insn, insn2; 212650397Sobrien 212750397Sobrien /* If we aren't doing exception handling, there isn't much to check. */ 212850397Sobrien if (! doing_eh (0)) 212950397Sobrien return; 213050397Sobrien 213150397Sobrien /* Make sure there is no more than 1 copy of a label */ 213250397Sobrien for (insn = exception_handler_labels; insn; insn = XEXP (insn, 1)) 213350397Sobrien { 213450397Sobrien int count = 0; 213550397Sobrien for (insn2 = exception_handler_labels; insn2; insn2 = XEXP (insn2, 1)) 213650397Sobrien if (XEXP (insn, 0) == XEXP (insn2, 0)) 213750397Sobrien count++; 213850397Sobrien if (count != 1) 213950397Sobrien warning ("Counted %d copies of EH region %d in list.\n", count, 214050397Sobrien CODE_LABEL_NUMBER (insn)); 214150397Sobrien } 214250397Sobrien 214350397Sobrien} 214450397Sobrien 214550397Sobrien/* This group of functions initializes the exception handling data 214650397Sobrien structures at the start of the compilation, initializes the data 214750397Sobrien structures at the start of a function, and saves and restores the 214850397Sobrien exception handling data structures for the start/end of a nested 214950397Sobrien function. */ 215050397Sobrien 215150397Sobrien/* Toplevel initialization for EH things. */ 215250397Sobrien 215350397Sobrienvoid 215450397Sobrieninit_eh () 215550397Sobrien{ 215650397Sobrien} 215750397Sobrien 215850397Sobrien/* Initialize the per-function EH information. */ 215950397Sobrien 216050397Sobrienvoid 216150397Sobrieninit_eh_for_function () 216250397Sobrien{ 216350397Sobrien ehstack.top = 0; 216450397Sobrien catchstack.top = 0; 216550397Sobrien ehqueue.head = ehqueue.tail = 0; 216650397Sobrien catch_clauses = NULL_RTX; 216750397Sobrien false_label_stack = 0; 216850397Sobrien caught_return_label_stack = 0; 216950397Sobrien protect_list = NULL_TREE; 217050397Sobrien current_function_ehc = NULL_RTX; 217150397Sobrien} 217250397Sobrien 217350397Sobrien/* Save some of the per-function EH info into the save area denoted by 217450397Sobrien P. 217550397Sobrien 217650397Sobrien This is currently called from save_stmt_status. */ 217750397Sobrien 217850397Sobrienvoid 217950397Sobriensave_eh_status (p) 218050397Sobrien struct function *p; 218150397Sobrien{ 218250397Sobrien if (p == NULL) 218350397Sobrien abort (); 218450397Sobrien 218550397Sobrien p->ehstack = ehstack; 218650397Sobrien p->catchstack = catchstack; 218750397Sobrien p->ehqueue = ehqueue; 218850397Sobrien p->catch_clauses = catch_clauses; 218950397Sobrien p->false_label_stack = false_label_stack; 219050397Sobrien p->caught_return_label_stack = caught_return_label_stack; 219150397Sobrien p->protect_list = protect_list; 219250397Sobrien p->ehc = current_function_ehc; 219350397Sobrien 219450397Sobrien init_eh_for_function (); 219550397Sobrien} 219650397Sobrien 219750397Sobrien/* Restore the per-function EH info saved into the area denoted by P. 219850397Sobrien 219950397Sobrien This is currently called from restore_stmt_status. */ 220050397Sobrien 220150397Sobrienvoid 220250397Sobrienrestore_eh_status (p) 220350397Sobrien struct function *p; 220450397Sobrien{ 220550397Sobrien if (p == NULL) 220650397Sobrien abort (); 220750397Sobrien 220850397Sobrien protect_list = p->protect_list; 220950397Sobrien caught_return_label_stack = p->caught_return_label_stack; 221050397Sobrien false_label_stack = p->false_label_stack; 221150397Sobrien catch_clauses = p->catch_clauses; 221250397Sobrien ehqueue = p->ehqueue; 221350397Sobrien ehstack = p->ehstack; 221450397Sobrien catchstack = p->catchstack; 221550397Sobrien current_function_ehc = p->ehc; 221650397Sobrien} 221750397Sobrien 221850397Sobrien/* This section is for the exception handling specific optimization 221950397Sobrien pass. First are the internal routines, and then the main 222050397Sobrien optimization pass. */ 222150397Sobrien 222250397Sobrien/* Determine if the given INSN can throw an exception. */ 222350397Sobrien 222450397Sobrienstatic int 222550397Sobriencan_throw (insn) 222650397Sobrien rtx insn; 222750397Sobrien{ 222850397Sobrien /* Calls can always potentially throw exceptions. */ 222950397Sobrien if (GET_CODE (insn) == CALL_INSN) 223050397Sobrien return 1; 223150397Sobrien 223250397Sobrien if (asynchronous_exceptions) 223350397Sobrien { 223450397Sobrien /* If we wanted asynchronous exceptions, then everything but NOTEs 223550397Sobrien and CODE_LABELs could throw. */ 223650397Sobrien if (GET_CODE (insn) != NOTE && GET_CODE (insn) != CODE_LABEL) 223750397Sobrien return 1; 223850397Sobrien } 223950397Sobrien 224050397Sobrien return 0; 224150397Sobrien} 224250397Sobrien 224350397Sobrien/* Scan a exception region looking for the matching end and then 224450397Sobrien remove it if possible. INSN is the start of the region, N is the 224550397Sobrien region number, and DELETE_OUTER is to note if anything in this 224650397Sobrien region can throw. 224750397Sobrien 224850397Sobrien Regions are removed if they cannot possibly catch an exception. 224950397Sobrien This is determined by invoking can_throw on each insn within the 225050397Sobrien region; if can_throw returns true for any of the instructions, the 225150397Sobrien region can catch an exception, since there is an insn within the 225250397Sobrien region that is capable of throwing an exception. 225350397Sobrien 225450397Sobrien Returns the NOTE_INSN_EH_REGION_END corresponding to this region, or 225550397Sobrien calls abort if it can't find one. 225650397Sobrien 225750397Sobrien Can abort if INSN is not a NOTE_INSN_EH_REGION_BEGIN, or if N doesn't 225850397Sobrien correspond to the region number, or if DELETE_OUTER is NULL. */ 225950397Sobrien 226050397Sobrienstatic rtx 226150397Sobrienscan_region (insn, n, delete_outer) 226250397Sobrien rtx insn; 226350397Sobrien int n; 226450397Sobrien int *delete_outer; 226550397Sobrien{ 226650397Sobrien rtx start = insn; 226750397Sobrien 226850397Sobrien /* Assume we can delete the region. */ 226950397Sobrien int delete = 1; 227050397Sobrien 227150397Sobrien if (insn == NULL_RTX 227250397Sobrien || GET_CODE (insn) != NOTE 227350397Sobrien || NOTE_LINE_NUMBER (insn) != NOTE_INSN_EH_REGION_BEG 227450397Sobrien || NOTE_BLOCK_NUMBER (insn) != n 227550397Sobrien || delete_outer == NULL) 227650397Sobrien abort (); 227750397Sobrien 227850397Sobrien insn = NEXT_INSN (insn); 227950397Sobrien 228050397Sobrien /* Look for the matching end. */ 228150397Sobrien while (! (GET_CODE (insn) == NOTE 228250397Sobrien && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) 228350397Sobrien { 228450397Sobrien /* If anything can throw, we can't remove the region. */ 228550397Sobrien if (delete && can_throw (insn)) 228650397Sobrien { 228750397Sobrien delete = 0; 228850397Sobrien } 228950397Sobrien 229050397Sobrien /* Watch out for and handle nested regions. */ 229150397Sobrien if (GET_CODE (insn) == NOTE 229250397Sobrien && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) 229350397Sobrien { 229450397Sobrien insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &delete); 229550397Sobrien } 229650397Sobrien 229750397Sobrien insn = NEXT_INSN (insn); 229850397Sobrien } 229950397Sobrien 230050397Sobrien /* The _BEG/_END NOTEs must match and nest. */ 230150397Sobrien if (NOTE_BLOCK_NUMBER (insn) != n) 230250397Sobrien abort (); 230350397Sobrien 230450397Sobrien /* If anything in this exception region can throw, we can throw. */ 230550397Sobrien if (! delete) 230650397Sobrien *delete_outer = 0; 230750397Sobrien else 230850397Sobrien { 230950397Sobrien /* Delete the start and end of the region. */ 231050397Sobrien delete_insn (start); 231150397Sobrien delete_insn (insn); 231250397Sobrien 231350397Sobrien/* We no longer removed labels here, since flow will now remove any 231450397Sobrien handler which cannot be called any more. */ 231550397Sobrien 231650397Sobrien#if 0 231750397Sobrien /* Only do this part if we have built the exception handler 231850397Sobrien labels. */ 231950397Sobrien if (exception_handler_labels) 232050397Sobrien { 232150397Sobrien rtx x, *prev = &exception_handler_labels; 232250397Sobrien 232350397Sobrien /* Find it in the list of handlers. */ 232450397Sobrien for (x = exception_handler_labels; x; x = XEXP (x, 1)) 232550397Sobrien { 232650397Sobrien rtx label = XEXP (x, 0); 232750397Sobrien if (CODE_LABEL_NUMBER (label) == n) 232850397Sobrien { 232950397Sobrien /* If we are the last reference to the handler, 233050397Sobrien delete it. */ 233150397Sobrien if (--LABEL_NUSES (label) == 0) 233250397Sobrien delete_insn (label); 233350397Sobrien 233450397Sobrien if (optimize) 233550397Sobrien { 233650397Sobrien /* Remove it from the list of exception handler 233750397Sobrien labels, if we are optimizing. If we are not, then 233850397Sobrien leave it in the list, as we are not really going to 233950397Sobrien remove the region. */ 234050397Sobrien *prev = XEXP (x, 1); 234150397Sobrien XEXP (x, 1) = 0; 234250397Sobrien XEXP (x, 0) = 0; 234350397Sobrien } 234450397Sobrien 234550397Sobrien break; 234650397Sobrien } 234750397Sobrien prev = &XEXP (x, 1); 234850397Sobrien } 234950397Sobrien } 235050397Sobrien#endif 235150397Sobrien } 235250397Sobrien return insn; 235350397Sobrien} 235450397Sobrien 235550397Sobrien/* Perform various interesting optimizations for exception handling 235650397Sobrien code. 235750397Sobrien 235850397Sobrien We look for empty exception regions and make them go (away). The 235950397Sobrien jump optimization code will remove the handler if nothing else uses 236050397Sobrien it. */ 236150397Sobrien 236250397Sobrienvoid 236350397Sobrienexception_optimize () 236450397Sobrien{ 236550397Sobrien rtx insn; 236650397Sobrien int n; 236750397Sobrien 236850397Sobrien /* Remove empty regions. */ 236950397Sobrien for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 237050397Sobrien { 237150397Sobrien if (GET_CODE (insn) == NOTE 237250397Sobrien && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG) 237350397Sobrien { 237450397Sobrien /* Since scan_region will return the NOTE_INSN_EH_REGION_END 237550397Sobrien insn, we will indirectly skip through all the insns 237650397Sobrien inbetween. We are also guaranteed that the value of insn 237750397Sobrien returned will be valid, as otherwise scan_region won't 237850397Sobrien return. */ 237950397Sobrien insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &n); 238050397Sobrien } 238150397Sobrien } 238250397Sobrien} 238350397Sobrien 238450397Sobrien/* Various hooks for the DWARF 2 __throw routine. */ 238550397Sobrien 238650397Sobrien/* Do any necessary initialization to access arbitrary stack frames. 238750397Sobrien On the SPARC, this means flushing the register windows. */ 238850397Sobrien 238950397Sobrienvoid 239050397Sobrienexpand_builtin_unwind_init () 239150397Sobrien{ 239250397Sobrien /* Set this so all the registers get saved in our frame; we need to be 239350397Sobrien able to copy the saved values for any registers from frames we unwind. */ 239450397Sobrien current_function_has_nonlocal_label = 1; 239550397Sobrien 239650397Sobrien#ifdef SETUP_FRAME_ADDRESSES 239750397Sobrien SETUP_FRAME_ADDRESSES (); 239850397Sobrien#endif 239950397Sobrien} 240050397Sobrien 240150397Sobrien/* Given a value extracted from the return address register or stack slot, 240250397Sobrien return the actual address encoded in that value. */ 240350397Sobrien 240450397Sobrienrtx 240550397Sobrienexpand_builtin_extract_return_addr (addr_tree) 240650397Sobrien tree addr_tree; 240750397Sobrien{ 240850397Sobrien rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); 240950397Sobrien return eh_outer_context (addr); 241050397Sobrien} 241150397Sobrien 241250397Sobrien/* Given an actual address in addr_tree, do any necessary encoding 241350397Sobrien and return the value to be stored in the return address register or 241450397Sobrien stack slot so the epilogue will return to that address. */ 241550397Sobrien 241650397Sobrienrtx 241750397Sobrienexpand_builtin_frob_return_addr (addr_tree) 241850397Sobrien tree addr_tree; 241950397Sobrien{ 242050397Sobrien rtx addr = expand_expr (addr_tree, NULL_RTX, Pmode, 0); 242150397Sobrien#ifdef RETURN_ADDR_OFFSET 242250397Sobrien addr = plus_constant (addr, -RETURN_ADDR_OFFSET); 242350397Sobrien#endif 242450397Sobrien return addr; 242550397Sobrien} 242650397Sobrien 242750397Sobrien/* Given an actual address in addr_tree, set the return address register up 242850397Sobrien so the epilogue will return to that address. If the return address is 242950397Sobrien not in a register, do nothing. */ 243050397Sobrien 243150397Sobrienvoid 243250397Sobrienexpand_builtin_set_return_addr_reg (addr_tree) 243350397Sobrien tree addr_tree; 243450397Sobrien{ 243550397Sobrien rtx tmp; 243650397Sobrien rtx ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, 243750397Sobrien 0, hard_frame_pointer_rtx); 243850397Sobrien 243950397Sobrien if (GET_CODE (ra) != REG || REGNO (ra) >= FIRST_PSEUDO_REGISTER) 244050397Sobrien return; 244150397Sobrien 244250397Sobrien tmp = force_operand (expand_builtin_frob_return_addr (addr_tree), ra); 244350397Sobrien if (tmp != ra) 244450397Sobrien emit_move_insn (ra, tmp); 244550397Sobrien} 244650397Sobrien 244750397Sobrien/* Choose two registers for communication between the main body of 244850397Sobrien __throw and the stub for adjusting the stack pointer. The first register 244950397Sobrien is used to pass the address of the exception handler; the second register 245050397Sobrien is used to pass the stack pointer offset. 245150397Sobrien 245250397Sobrien For register 1 we use the return value register for a void *. 245350397Sobrien For register 2 we use the static chain register if it exists and is 245450397Sobrien different from register 1, otherwise some arbitrary call-clobbered 245550397Sobrien register. */ 245650397Sobrien 245750397Sobrienstatic void 245850397Sobrieneh_regs (r1, r2, outgoing) 245950397Sobrien rtx *r1, *r2; 246050397Sobrien int outgoing; 246150397Sobrien{ 246250397Sobrien rtx reg1, reg2; 246350397Sobrien 246450397Sobrien#ifdef FUNCTION_OUTGOING_VALUE 246550397Sobrien if (outgoing) 246650397Sobrien reg1 = FUNCTION_OUTGOING_VALUE (build_pointer_type (void_type_node), 246750397Sobrien current_function_decl); 246850397Sobrien else 246950397Sobrien#endif 247050397Sobrien reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node), 247150397Sobrien current_function_decl); 247250397Sobrien 247350397Sobrien#ifdef STATIC_CHAIN_REGNUM 247450397Sobrien if (outgoing) 247550397Sobrien reg2 = static_chain_incoming_rtx; 247650397Sobrien else 247750397Sobrien reg2 = static_chain_rtx; 247850397Sobrien if (REGNO (reg2) == REGNO (reg1)) 247950397Sobrien#endif /* STATIC_CHAIN_REGNUM */ 248050397Sobrien reg2 = NULL_RTX; 248150397Sobrien 248250397Sobrien if (reg2 == NULL_RTX) 248350397Sobrien { 248450397Sobrien int i; 248550397Sobrien for (i = 0; i < FIRST_PSEUDO_REGISTER; ++i) 248650397Sobrien if (call_used_regs[i] && ! fixed_regs[i] && i != REGNO (reg1)) 248750397Sobrien { 248850397Sobrien reg2 = gen_rtx_REG (Pmode, i); 248950397Sobrien break; 249050397Sobrien } 249150397Sobrien 249250397Sobrien if (reg2 == NULL_RTX) 249350397Sobrien abort (); 249450397Sobrien } 249550397Sobrien 249650397Sobrien *r1 = reg1; 249750397Sobrien *r2 = reg2; 249850397Sobrien} 249950397Sobrien 250050397Sobrien 250150397Sobrien/* Retrieve the register which contains the pointer to the eh_context 250250397Sobrien structure set the __throw. */ 250350397Sobrien 250450397Sobrienrtx 250550397Sobrienget_reg_for_handler () 250650397Sobrien{ 250750397Sobrien rtx reg1; 250850397Sobrien reg1 = FUNCTION_VALUE (build_pointer_type (void_type_node), 250950397Sobrien current_function_decl); 251050397Sobrien return reg1; 251150397Sobrien} 251250397Sobrien 251350397Sobrien 251450397Sobrien/* Emit inside of __throw a stub which adjusts the stack pointer and jumps 251550397Sobrien to the exception handler. __throw will set up the necessary values 251650397Sobrien and then return to the stub. */ 251750397Sobrien 251850397Sobrienrtx 251950397Sobrienexpand_builtin_eh_stub_old () 252050397Sobrien{ 252150397Sobrien rtx stub_start = gen_label_rtx (); 252250397Sobrien rtx after_stub = gen_label_rtx (); 252350397Sobrien rtx handler, offset; 252450397Sobrien 252550397Sobrien emit_jump (after_stub); 252650397Sobrien emit_label (stub_start); 252750397Sobrien 252850397Sobrien eh_regs (&handler, &offset, 0); 252950397Sobrien 253050397Sobrien adjust_stack (offset); 253150397Sobrien emit_indirect_jump (handler); 253250397Sobrien emit_label (after_stub); 253350397Sobrien return gen_rtx_LABEL_REF (Pmode, stub_start); 253450397Sobrien} 253550397Sobrien 253650397Sobrienrtx 253750397Sobrienexpand_builtin_eh_stub () 253850397Sobrien{ 253950397Sobrien rtx stub_start = gen_label_rtx (); 254050397Sobrien rtx after_stub = gen_label_rtx (); 254150397Sobrien rtx handler, offset; 254250397Sobrien rtx temp; 254350397Sobrien 254450397Sobrien emit_jump (after_stub); 254550397Sobrien emit_label (stub_start); 254650397Sobrien 254750397Sobrien eh_regs (&handler, &offset, 0); 254850397Sobrien 254950397Sobrien adjust_stack (offset); 255050397Sobrien 255150397Sobrien /* Handler is in fact a pointer to the _eh_context structure, we need 255250397Sobrien to pick out the handler field (first element), and jump to there, 255350397Sobrien leaving the pointer to _eh_conext in the same hardware register. */ 255450397Sobrien 255550397Sobrien temp = gen_rtx_MEM (Pmode, handler); 255650397Sobrien MEM_IN_STRUCT_P (temp) = 1; 255750397Sobrien RTX_UNCHANGING_P (temp) = 1; 255850397Sobrien emit_move_insn (offset, temp); 255950397Sobrien emit_insn (gen_rtx_USE (Pmode, handler)); 256050397Sobrien 256150397Sobrien emit_indirect_jump (offset); 256250397Sobrien 256350397Sobrien emit_label (after_stub); 256450397Sobrien return gen_rtx_LABEL_REF (Pmode, stub_start); 256550397Sobrien} 256650397Sobrien 256750397Sobrien/* Set up the registers for passing the handler address and stack offset 256850397Sobrien to the stub above. */ 256950397Sobrien 257050397Sobrienvoid 257150397Sobrienexpand_builtin_set_eh_regs (handler, offset) 257250397Sobrien tree handler, offset; 257350397Sobrien{ 257450397Sobrien rtx reg1, reg2; 257550397Sobrien 257650397Sobrien eh_regs (®1, ®2, 1); 257750397Sobrien 257850397Sobrien store_expr (offset, reg2, 0); 257950397Sobrien store_expr (handler, reg1, 0); 258050397Sobrien 258150397Sobrien /* These will be used by the stub. */ 258250397Sobrien emit_insn (gen_rtx_USE (VOIDmode, reg1)); 258350397Sobrien emit_insn (gen_rtx_USE (VOIDmode, reg2)); 258450397Sobrien} 258550397Sobrien 258650397Sobrien 258750397Sobrien 258850397Sobrien/* This contains the code required to verify whether arbitrary instructions 258950397Sobrien are in the same exception region. */ 259050397Sobrien 259150397Sobrienstatic int *insn_eh_region = (int *)0; 259250397Sobrienstatic int maximum_uid; 259350397Sobrien 259450397Sobrienstatic void 259550397Sobrienset_insn_eh_region (first, region_num) 259650397Sobrien rtx *first; 259750397Sobrien int region_num; 259850397Sobrien{ 259950397Sobrien rtx insn; 260050397Sobrien int rnum; 260150397Sobrien 260250397Sobrien for (insn = *first; insn; insn = NEXT_INSN (insn)) 260350397Sobrien { 260450397Sobrien if ((GET_CODE (insn) == NOTE) && 260550397Sobrien (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)) 260650397Sobrien { 260750397Sobrien rnum = NOTE_BLOCK_NUMBER (insn); 260850397Sobrien insn_eh_region[INSN_UID (insn)] = rnum; 260950397Sobrien insn = NEXT_INSN (insn); 261050397Sobrien set_insn_eh_region (&insn, rnum); 261150397Sobrien /* Upon return, insn points to the EH_REGION_END of nested region */ 261250397Sobrien continue; 261350397Sobrien } 261450397Sobrien insn_eh_region[INSN_UID (insn)] = region_num; 261550397Sobrien if ((GET_CODE (insn) == NOTE) && 261650397Sobrien (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END)) 261750397Sobrien break; 261850397Sobrien } 261950397Sobrien *first = insn; 262050397Sobrien} 262150397Sobrien 262250397Sobrien/* Free the insn table, an make sure it cannot be used again. */ 262350397Sobrien 262450397Sobrienvoid 262550397Sobrienfree_insn_eh_region () 262650397Sobrien{ 262750397Sobrien if (!doing_eh (0)) 262850397Sobrien return; 262950397Sobrien 263050397Sobrien if (insn_eh_region) 263150397Sobrien { 263250397Sobrien free (insn_eh_region); 263350397Sobrien insn_eh_region = (int *)0; 263450397Sobrien } 263550397Sobrien} 263650397Sobrien 263750397Sobrien/* Initialize the table. max_uid must be calculated and handed into 263850397Sobrien this routine. If it is unavailable, passing a value of 0 will 263950397Sobrien cause this routine to calculate it as well. */ 264050397Sobrien 264150397Sobrienvoid 264250397Sobrieninit_insn_eh_region (first, max_uid) 264350397Sobrien rtx first; 264450397Sobrien int max_uid; 264550397Sobrien{ 264650397Sobrien rtx insn; 264750397Sobrien 264850397Sobrien if (!doing_eh (0)) 264950397Sobrien return; 265050397Sobrien 265150397Sobrien if (insn_eh_region) 265250397Sobrien free_insn_eh_region(); 265350397Sobrien 265450397Sobrien if (max_uid == 0) 265550397Sobrien for (insn = first; insn; insn = NEXT_INSN (insn)) 265650397Sobrien if (INSN_UID (insn) > max_uid) /* find largest UID */ 265750397Sobrien max_uid = INSN_UID (insn); 265850397Sobrien 265950397Sobrien maximum_uid = max_uid; 266050397Sobrien insn_eh_region = (int *) malloc ((max_uid + 1) * sizeof (int)); 266150397Sobrien insn = first; 266250397Sobrien set_insn_eh_region (&insn, 0); 266350397Sobrien} 266450397Sobrien 266550397Sobrien 266650397Sobrien/* Check whether 2 instructions are within the same region. */ 266750397Sobrien 266850397Sobrienint 266950397Sobrienin_same_eh_region (insn1, insn2) 267050397Sobrien rtx insn1, insn2; 267150397Sobrien{ 267250397Sobrien int ret, uid1, uid2; 267350397Sobrien 267450397Sobrien /* If no exceptions, instructions are always in same region. */ 267550397Sobrien if (!doing_eh (0)) 267650397Sobrien return 1; 267750397Sobrien 267850397Sobrien /* If the table isn't allocated, assume the worst. */ 267950397Sobrien if (!insn_eh_region) 268050397Sobrien return 0; 268150397Sobrien 268250397Sobrien uid1 = INSN_UID (insn1); 268350397Sobrien uid2 = INSN_UID (insn2); 268450397Sobrien 268550397Sobrien /* if instructions have been allocated beyond the end, either 268650397Sobrien the table is out of date, or this is a late addition, or 268750397Sobrien something... Assume the worst. */ 268850397Sobrien if (uid1 > maximum_uid || uid2 > maximum_uid) 268950397Sobrien return 0; 269050397Sobrien 269150397Sobrien ret = (insn_eh_region[uid1] == insn_eh_region[uid2]); 269250397Sobrien return ret; 269350397Sobrien} 269450397Sobrien 2695