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 (&reg1, &reg2, 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