aboutsummaryrefslogtreecommitdiff
path: root/gcc/except.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/except.c')
-rw-r--r--gcc/except.c1571
1 files changed, 0 insertions, 1571 deletions
diff --git a/gcc/except.c b/gcc/except.c
deleted file mode 100644
index 4d8d1bc9a29..00000000000
--- a/gcc/except.c
+++ /dev/null
@@ -1,1571 +0,0 @@
-/* Implements exception handling.
- Copyright (C) 1989, 92-95, 1996 Free Software Foundation, Inc.
- Contributed by Mike Stump <mrs@cygnus.com>.
-
-This file is part of GNU CC.
-
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
-
-
-/* An exception is an event that can be signaled from within a
- function. This event can then be "caught" or "trapped" by the
- callers of this function. This potentially allows program flow to
- be transferred to any arbitrary code assocated with a function call
- several levels up the stack.
-
- The intended use for this mechanism is for signaling "exceptional
- events" in an out-of-band fashion, hence its name. The C++ language
- (and many other OO-styled or functional languages) practically
- requires such a mechanism, as otherwise it becomes very difficult
- or even impossible to signal failure conditions in complex
- situations. The traditional C++ example is when an error occurs in
- the process of constructing an object; without such a mechanism, it
- is impossible to signal that the error occurs without adding global
- state variables and error checks around every object construction.
-
- The act of causing this event to occur is referred to as "throwing
- an exception". (Alternate terms include "raising an exception" or
- "signaling an exception".) The term "throw" is used because control
- is returned to the callers of the function that is signaling the
- exception, and thus there is the concept of "throwing" the
- exception up the call stack.
-
- It is appropriate to speak of the "context of a throw". This
- context refers to the address where the exception is thrown from,
- and is used to determine which exception region will handle the
- exception.
-
- Regions of code within a function can be marked such that if it
- contains the context of a throw, control will be passed to a
- designated "exception handler". These areas are known as "exception
- regions". Exception regions cannot overlap, but they can be nested
- to any arbitrary depth. Also, exception regions cannot cross
- function boundaries.
-
- Exception handlers can either be specified by the user (which we
- will call a "user-defined handler") or generated by the compiler
- (which we will designate as a "cleanup"). Cleanups are used to
- perform tasks such as destruction of objects allocated on the
- stack.
-
- In the current implementaion, cleanups are handled by allocating an
- exception region for the area that the cleanup is designated for,
- and the handler for the region performs the cleanup and then
- rethrows the exception to the outer exception region. From the
- standpoint of the current implementation, there is little
- distinction made between a cleanup and a user-defined handler, and
- the phrase "exception handler" can be used to refer to either one
- equally well. (The section "Future Directions" below discusses how
- this will change).
-
- Each object file that is compiled with exception handling contains
- a static array of exception handlers named __EXCEPTION_TABLE__.
- Each entry contains the starting and ending addresses of the
- exception region, and the address of the handler designated for
- that region.
-
- At program startup each object file invokes a function named
- __register_exceptions with the address of its local
- __EXCEPTION_TABLE__. __register_exceptions is defined in libgcc2.c,
- and is responsible for recording all of the exception regions into
- one list (which is kept in a static variable named exception_table_list).
-
- The function __throw () is actually responsible for doing the
- throw. In the C++ frontend, __throw () is generated on a
- per-object-file basis for each source file compiled with
- -fexceptions. Before __throw () is invoked, the current context
- of the throw needs to be placed in the global variable __eh_pc.
-
- __throw () attempts to find the appropriate exception handler for the
- PC value stored in __eh_pc by calling __find_first_exception_table_match
- (which is defined in libgcc2.c). If __find_first_exception_table_match
- finds a relevant handler, __throw jumps directly to it.
-
- If a handler for the context being thrown from can't be found,
- __throw is responsible for unwinding the stack, determining the
- address of the caller of the current function (which will be used
- as the new context to throw from), and then restarting the process
- of searching for a handler for the new context. __throw may also
- call abort () if it is unable to unwind the stack, and can also
- call an external library function named __terminate if it reaches
- the top of the stack without finding an appropriate handler. (By
- default __terminate () invokes abort (), but this behavior can be
- changed by the user to perform some sort of cleanup behavior before
- exiting).
-
- Internal implementation details:
-
- To associate a user-defined handler with a block of statements, the
- function expand_start_try_stmts () is used to mark the start of the
- block of statements with which the handler is to be associated
- (which is known as a "try block"). All statements that appear
- afterwards will be associated with the try block.
-
- A call to expand_start_all_catch () marks the end of the try block,
- and also marks the start of the "catch block" (the user-defined
- handler) associated with the try block.
-
- This user-defined handler will be invoked for *every* exception
- thrown with the context of the try block. It is up to the handler
- to decide whether or not it wishes to handle any given exception,
- as there is currently no mechanism in this implementation for doing
- this. (There are plans for conditionally processing an exception
- based on its "type", which will provide a language-independent
- mechanism).
-
- If the handler chooses not to process the exception (perhaps by
- looking at an "exception type" or some other additional data
- supplied with the exception), it can fall through to the end of the
- handler. expand_end_all_catch () and expand_leftover_cleanups ()
- add additional code to the end of each handler to take care of
- rethrowing to the outer exception handler.
-
- The handler also has the option to continue with "normal flow of
- code", or in other words to resume executing at the statement
- immediately after the end of the exception region. The variable
- caught_return_label_stack contains a stack of labels, and jumping
- to the topmost entry's label via expand_goto () will resume normal
- flow to the statement immediately after the end of the exception
- region. If the handler falls through to the end, the exception will
- be rethrown to the outer exception region.
-
- The instructions for the catch block are kept as a separate
- sequence, and will be emitted at the end of the function along with
- the handlers specified via expand_eh_region_end (). The end of the
- catch block is marked with expand_end_all_catch ().
-
- Any data associated with the exception must currently be handled by
- some external mechanism maintained in the frontend. For example,
- the C++ exception mechanism passes an arbitrary value along with
- the exception, and this is handled in the C++ frontend by using a
- global variable to hold the value. (This will be changing in the
- future.)
-
- The mechanism in C++ for handling data associated with the
- exception is clearly not thread-safe. For a thread-based
- environment, another mechanism must be used (possibly using a
- per-thread allocation mechanism if the size of the area that needs
- to be allocated isn't known at compile time.)
-
- Internally-generated exception regions (cleanups) are marked by
- calling expand_eh_region_start () to mark the start of the region,
- and expand_eh_region_end (handler) is used to both designate the
- end of the region and to associate a specified handler/cleanup with
- the region. The rtl code in HANDLER will be invoked whenever an
- exception occurs in the region between the calls to
- expand_eh_region_start and expand_eh_region_end. After HANDLER is
- executed, additional code is emitted to handle rethrowing the
- exception to the outer exception handler. The code for HANDLER will
- be emitted at the end of the function.
-
- TARGET_EXPRs can also be used to designate exception regions. A
- TARGET_EXPR gives an unwind-protect style interface commonly used
- in functional languages such as LISP. The associated expression is
- evaluated, and whether or not it (or any of the functions that it
- calls) throws an exception, the protect expression is always
- invoked. This implementation takes care of the details of
- associating an exception table entry with the expression and
- generating the necessary code (it actually emits the protect
- expression twice, once for normal flow and once for the exception
- case). As for the other handlers, the code for the exception case
- will be emitted at the end of the function.
-
- Cleanups can also be specified by using add_partial_entry (handler)
- and end_protect_partials (). add_partial_entry creates the start of
- a new exception region; HANDLER will be invoked if an exception is
- thrown with the context of the region between the calls to
- add_partial_entry and end_protect_partials. end_protect_partials is
- used to mark the end of these regions. add_partial_entry can be
- called as many times as needed before calling end_protect_partials.
- However, end_protect_partials should only be invoked once for each
- group of calls to add_partial_entry () as the entries are queued
- and all of the outstanding entries are processed simultaneously
- when end_protect_partials is invoked. Similarly to the other
- handlers, the code for HANDLER will be emitted at the end of the
- function.
-
- The generated RTL for an exception region includes
- NOTE_INSN_EH_REGION_BEG and NOTE_INSN_EH_REGION_END notes that mark
- the start and end of the exception region. A unique label is also
- generated at the start of the exception region, which is available
- by looking at the ehstack variable. The topmost entry corresponds
- to the current region.
-
- In the current implementation, an exception can only be thrown from
- a function call (since the mechanism used to actually throw an
- exception involves calling __throw). If an exception region is
- created but no function calls occur within that region, the region
- can be safely optimized away (along with its exception handlers)
- since no exceptions can ever be caught in that region.
-
- Unwinding the stack:
-
- The details of unwinding the stack to the next frame can be rather
- complex. While in many cases a generic __unwind_function () routine
- can be used by the generated exception handling code to do this, it
- is often necessary to generate inline code to do the unwinding.
-
- Whether or not these inlined unwinders are necessary is
- target-specific.
-
- By default, if the target-specific backend doesn't supply a
- definition for __unwind_function (), inlined unwinders will be used
- instead. The main tradeoff here is in text space utilization.
- Obviously, if inline unwinders have to be generated repeatedly,
- this uses much more space than if a single routine is used.
-
- However, it is simply not possible on some platforms to write a
- generalized routine for doing stack unwinding without having some
- form of additional data associated with each function. The current
- implementation encodes this data in the form of additional machine
- instructions. This is clearly not desirable, as it is extremely
- inefficient. The next implementation will provide a set of metadata
- for each function that will provide the needed information.
-
- The backend macro DOESNT_NEED_UNWINDER is used to conditionalize
- whether or not per-function unwinders are needed. If DOESNT_NEED_UNWINDER
- is defined and has a non-zero value, a per-function unwinder is
- not emitted for the current function.
-
- On some platforms it is possible that neither __unwind_function ()
- nor inlined unwinders are available. For these platforms it is not
- possible to throw through a function call, and abort () will be
- invoked instead of performing the throw.
-
- Future directions:
-
- Currently __throw () makes no differentiation between cleanups and
- user-defined exception regions. While this makes the implementation
- simple, it also implies that it is impossible to determine if a
- user-defined exception handler exists for a given exception without
- completely unwinding the stack in the process. This is undesirable
- from the standpoint of debugging, as ideally it would be possible
- to trap unhandled exceptions in the debugger before the process of
- unwinding has even started.
-
- This problem can be solved by marking user-defined handlers in a
- special way (probably by adding additional bits to exception_table_list).
- A two-pass scheme could then be used by __throw () to iterate
- through the table. The first pass would search for a relevant
- user-defined handler for the current context of the throw, and if
- one is found, the second pass would then invoke all needed cleanups
- before jumping to the user-defined handler.
-
- Many languages (including C++ and Ada) make execution of a
- user-defined handler conditional on the "type" of the exception
- thrown. (The type of the exception is actually the type of the data
- that is thrown with the exception.) It will thus be necessary for
- __throw () to be able to determine if a given user-defined
- exception handler will actually be executed, given the type of
- exception.
-
- One scheme is to add additional information to exception_table_list
- as to the types of exceptions accepted by each handler. __throw ()
- can do the type comparisons and then determine if the handler is
- actually going to be executed.
-
- There is currently no significant level of debugging support
- available, other than to place a breakpoint on __throw (). While
- this is sufficient in most cases, it would be helpful to be able to
- know where a given exception was going to be thrown to before it is
- actually thrown, and to be able to choose between stopping before
- every exception region (including cleanups), or just user-defined
- exception regions. This should be possible to do in the two-pass
- scheme by adding additional labels to __throw () for appropriate
- breakpoints, and additional debugger commands could be added to
- query various state variables to determine what actions are to be
- performed next.
-
- Another major problem that is being worked on is the issue with
- stack unwinding on various platforms. Currently the only platform
- that has support for __unwind_function () is the Sparc; all other
- ports require per-function unwinders, which causes large amounts of
- code bloat.
-
- Ideally it would be possible to store a small set of metadata with
- each function that would then make it possible to write a
- __unwind_function () for every platform. This would eliminate the
- need for per-function unwinders.
-
- The main reason the data is needed is that on some platforms the
- order and types of data stored on the stack can vary depending on
- the type of function, its arguments and returned values, and the
- compilation options used (optimization versus non-optimization,
- -fomit-frame-pointer, processor variations, etc).
-
- Unfortunately, this also means that throwing through functions that
- aren't compiled with exception handling support will still not be
- possible on some platforms. This problem is currently being
- investigated, but no solutions have been found that do not imply
- some unacceptable performance penalties. */
-
-
-#include "config.h"
-#include <stdio.h>
-#include "rtl.h"
-#include "tree.h"
-#include "flags.h"
-#include "except.h"
-#include "function.h"
-#include "insn-flags.h"
-#include "expr.h"
-#include "insn-codes.h"
-#include "regs.h"
-#include "hard-reg-set.h"
-#include "insn-config.h"
-#include "recog.h"
-#include "output.h"
-#include "assert.h"
-
-/* A list of labels used for exception handlers. Created by
- find_exception_handler_labels for the optimization passes. */
-
-rtx exception_handler_labels;
-
-/* Nonzero means that __throw was invoked.
-
- This is used by the C++ frontend to know if code needs to be emitted
- for __throw or not. */
-
-int throw_used;
-
-/* A stack used for keeping track of the currectly active exception
- handling region. As each exception region is started, an entry
- describing the region is pushed onto this stack. The current
- region can be found by looking at the top of the stack, and as we
- exit regions, the corresponding entries are popped.
-
- Entries cannot overlap; they must be nested. So there is only one
- entry at most that corresponds to the current instruction, and that
- is the entry on the top of the stack. */
-
-struct eh_stack ehstack;
-
-/* A queue used for tracking which exception regions have closed but
- whose handlers have not yet been expanded. Regions are emitted in
- groups in an attempt to improve paging performance.
-
- As we exit a region, we enqueue a new entry. The entries are then
- dequeued during expand_leftover_cleanups () and expand_start_all_catch (),
-
- We should redo things so that we either take RTL for the handler,
- or we expand the handler expressed as a tree immediately at region
- end time. */
-
-struct eh_queue ehqueue;
-
-/* Insns for all of the exception handlers for the current function.
- They are currently emitted by the frontend code. */
-
-rtx catch_clauses;
-
-/* A TREE_CHAINed list of handlers for regions that are not yet
- closed. The TREE_VALUE of each entry contains the handler for the
- corresponding entry on the ehstack. */
-
-static tree protect_list;
-
-/* Stacks to keep track of various labels. */
-
-/* Keeps track of the label to resume to should one want to resume
- normal control flow out of a handler (instead of, say, returning to
- the caller of the current function or exiting the program). Also
- used as the context of a throw to rethrow an exception to the outer
- exception region. */
-
-struct label_node *caught_return_label_stack = NULL;
-
-/* A random data area for the front end's own use. */
-
-struct label_node *false_label_stack = NULL;
-
-/* The rtx and the tree for the saved PC value. */
-
-rtx eh_saved_pc_rtx;
-tree eh_saved_pc;
-
-rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx));
-
-/* Various support routines to manipulate the various data structures
- used by the exception handling code. */
-
-/* Push a label entry onto the given STACK. */
-
-void
-push_label_entry (stack, rlabel, tlabel)
- struct label_node **stack;
- rtx rlabel;
- tree tlabel;
-{
- struct label_node *newnode
- = (struct label_node *) xmalloc (sizeof (struct label_node));
-
- if (rlabel)
- newnode->u.rlabel = rlabel;
- else
- newnode->u.tlabel = tlabel;
- newnode->chain = *stack;
- *stack = newnode;
-}
-
-/* Pop a label entry from the given STACK. */
-
-rtx
-pop_label_entry (stack)
- struct label_node **stack;
-{
- rtx label;
- struct label_node *tempnode;
-
- if (! *stack)
- return NULL_RTX;
-
- tempnode = *stack;
- label = tempnode->u.rlabel;
- *stack = (*stack)->chain;
- free (tempnode);
-
- return label;
-}
-
-/* Return the top element of the given STACK. */
-
-tree
-top_label_entry (stack)
- struct label_node **stack;
-{
- if (! *stack)
- return NULL_TREE;
-
- return (*stack)->u.tlabel;
-}
-
-/* Make a copy of ENTRY using xmalloc to allocate the space. */
-
-static struct eh_entry *
-copy_eh_entry (entry)
- struct eh_entry *entry;
-{
- struct eh_entry *newentry;
-
- newentry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry));
- bcopy ((char *) entry, (char *) newentry, sizeof (struct eh_entry));
-
- return newentry;
-}
-
-/* Push a new eh_node entry onto STACK, and return the start label for
- the entry. */
-
-static rtx
-push_eh_entry (stack)
- struct eh_stack *stack;
-{
- struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node));
- struct eh_entry *entry = (struct eh_entry *) xmalloc (sizeof (struct eh_entry));
-
- entry->start_label = gen_label_rtx ();
- entry->end_label = gen_label_rtx ();
- entry->exception_handler_label = gen_label_rtx ();
- entry->finalization = NULL_TREE;
-
- node->entry = entry;
- node->chain = stack->top;
- stack->top = node;
-
- return entry->start_label;
-}
-
-/* Pop an entry from the given STACK. */
-
-static struct eh_entry *
-pop_eh_entry (stack)
- struct eh_stack *stack;
-{
- struct eh_node *tempnode;
- struct eh_entry *tempentry;
-
- tempnode = stack->top;
- tempentry = tempnode->entry;
- stack->top = stack->top->chain;
- free (tempnode);
-
- return tempentry;
-}
-
-/* Enqueue an ENTRY onto the given QUEUE. */
-
-static void
-enqueue_eh_entry (queue, entry)
- struct eh_queue *queue;
- struct eh_entry *entry;
-{
- struct eh_node *node = (struct eh_node *) xmalloc (sizeof (struct eh_node));
-
- node->entry = entry;
- node->chain = NULL;
-
- if (queue->head == NULL)
- {
- queue->head = node;
- }
- else
- {
- queue->tail->chain = node;
- }
- queue->tail = node;
-}
-
-/* Dequeue an entry from the given QUEUE. */
-
-static struct eh_entry *
-dequeue_eh_entry (queue)
- struct eh_queue *queue;
-{
- struct eh_node *tempnode;
- struct eh_entry *tempentry;
-
- if (queue->head == NULL)
- return NULL;
-
- tempnode = queue->head;
- queue->head = queue->head->chain;
-
- tempentry = tempnode->entry;
- free (tempnode);
-
- return tempentry;
-}
-
-/* Routine to see if exception exception handling is turned on.
- DO_WARN is non-zero if we want to inform the user that exception
- handling is turned off.
-
- This is used to ensure that -fexceptions has been specified if the
- compiler tries to use any exception-specific functions. */
-
-int
-doing_eh (do_warn)
- int do_warn;
-{
- if (! flag_exceptions)
- {
- static int warned = 0;
- if (! warned && do_warn)
- {
- error ("exception handling disabled, use -fexceptions to enable");
- warned = 1;
- }
- return 0;
- }
- return 1;
-}
-
-/* Given a return address in ADDR, determine the address we should use
- to find the corresponding EH region. */
-
-rtx
-eh_outer_context (addr)
- rtx addr;
-{
- /* First mask out any unwanted bits. */
-#ifdef MASK_RETURN_ADDR
- emit_insn (gen_rtx (SET, Pmode,
- addr,
- gen_rtx (AND, Pmode,
- addr, MASK_RETURN_ADDR)));
-#endif
-
- /* Then subtract out enough to get into the appropriate region. If
- this is defined, assume we don't need to subtract anything as it
- is already within the correct region. */
-#if ! defined (RETURN_ADDR_OFFSET)
- addr = plus_constant (addr, -1);
-#endif
-
- return addr;
-}
-
-/* Start a new exception region and push the HANDLER for the region
- onto protect_list. All of the regions created with add_partial_entry
- will be ended when end_protect_partials () is invoked. */
-
-void
-add_partial_entry (handler)
- tree handler;
-{
- expand_eh_region_start ();
-
- /* Make sure the entry is on the correct obstack. */
- push_obstacks_nochange ();
- resume_temporary_allocation ();
- protect_list = tree_cons (NULL_TREE, handler, protect_list);
- pop_obstacks ();
-}
-
-/* Output a note marking the start of an exception handling region.
- All instructions emitted after this point are considered to be part
- of the region until expand_eh_region_end () is invoked. */
-
-void
-expand_eh_region_start ()
-{
- rtx note;
-
- /* This is the old code. */
- if (! doing_eh (0))
- return;
-
-#if 0
- /* Maybe do this to prevent jumping in and so on... */
- pushlevel (0);
-#endif
-
- note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_BEG);
- emit_label (push_eh_entry (&ehstack));
- NOTE_BLOCK_NUMBER (note)
- = CODE_LABEL_NUMBER (ehstack.top->entry->exception_handler_label);
-}
-
-/* Output a note marking the end of the exception handling region on
- the top of ehstack.
-
- HANDLER is either the cleanup for the exception region, or if we're
- marking the end of a try block, HANDLER is integer_zero_node.
-
- HANDLER will be transformed to rtl when expand_leftover_cleanups ()
- is invoked. */
-
-void
-expand_eh_region_end (handler)
- tree handler;
-{
- rtx note;
-
- struct eh_entry *entry;
-
- if (! doing_eh (0))
- return;
-
- entry = pop_eh_entry (&ehstack);
-
- note = emit_note (NULL_PTR, NOTE_INSN_EH_REGION_END);
- NOTE_BLOCK_NUMBER (note) = CODE_LABEL_NUMBER (entry->exception_handler_label);
-
- /* Emit a label marking the end of this exception region. */
- emit_label (entry->end_label);
-
- /* Put in something that takes up space, as otherwise the end
- address for this EH region could have the exact same address as
- its outer region. This would cause us to miss the fact that
- resuming exception handling with this PC value would be inside
- the outer region. */
- emit_insn (gen_nop ());
-
- entry->finalization = handler;
-
- enqueue_eh_entry (&ehqueue, entry);
-
-#if 0
- /* Maybe do this to prevent jumping in and so on... */
- poplevel (1, 0, 0);
-#endif
-}
-
-/* Emit a call to __throw and note that we threw something, so we know
- we need to generate the necessary code for __throw.
-
- Before invoking throw, the __eh_pc variable must have been set up
- to contain the PC being thrown from. This address is used by
- __throw () to determine which exception region (if any) is
- responsible for handling the exception. */
-
-static void
-emit_throw ()
-{
-#ifdef JUMP_TO_THROW
- emit_indirect_jump (throw_libfunc);
-#else
- SYMBOL_REF_USED (throw_libfunc) = 1;
- emit_library_call (throw_libfunc, 0, VOIDmode, 0);
-#endif
- throw_used = 1;
- emit_barrier ();
-}
-
-/* An internal throw with an indirect CONTEXT we want to throw from.
- CONTEXT evaluates to the context of the throw. */
-
-static void
-expand_internal_throw_indirect (context)
- rtx context;
-{
- assemble_external (eh_saved_pc);
- emit_move_insn (eh_saved_pc_rtx, context);
- emit_throw ();
-}
-
-/* An internal throw with a direct CONTEXT we want to throw from.
- CONTEXT must be a label; its address will be used as the context of
- the throw. */
-
-void
-expand_internal_throw (context)
- rtx context;
-{
- expand_internal_throw_indirect (gen_rtx (LABEL_REF, Pmode, context));
-}
-
-/* Called from expand_exception_blocks and expand_end_catch_block to
- emit any pending handlers/cleanups queued from expand_eh_region_end (). */
-
-void
-expand_leftover_cleanups ()
-{
- struct eh_entry *entry;
-
- while ((entry = dequeue_eh_entry (&ehqueue)) != 0)
- {
- rtx prev;
-
- /* A leftover try block. Shouldn't be one here. */
- if (entry->finalization == integer_zero_node)
- abort ();
-
- /* Output the label for the start of the exception handler. */
- emit_label (entry->exception_handler_label);
-
- /* And now generate the insns for the handler. */
- expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
-
- prev = get_last_insn ();
- if (! (prev && GET_CODE (prev) == BARRIER))
- {
- /* The below can be optimized away, and we could just fall into the
- next EH handler, if we are certain they are nested. */
- /* Emit code to throw to the outer context if we fall off
- the end of the handler. */
- expand_internal_throw (entry->end_label);
- }
-
- free (entry);
- }
-}
-
-/* Called at the start of a block of try statements. */
-void
-expand_start_try_stmts ()
-{
- if (! doing_eh (1))
- return;
-
- expand_eh_region_start ();
-}
-
-/* Generate RTL for the start of a group of catch clauses.
-
- It is responsible for starting a new instruction sequence for the
- instructions in the catch block, and expanding the handlers for the
- internally-generated exception regions nested within the try block
- corresponding to this catch block. */
-
-void
-expand_start_all_catch ()
-{
- struct eh_entry *entry;
- tree label;
-
- if (! doing_eh (1))
- return;
-
- /* End the try block. */
- expand_eh_region_end (integer_zero_node);
-
- emit_line_note (input_filename, lineno);
- label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE);
-
- /* The label for the exception handling block that we will save.
- This is Lresume in the documention. */
- expand_label (label);
-
- /* Put in something that takes up space, as otherwise the end
- address for the EH region could have the exact same address as
- the outer region, causing us to miss the fact that resuming
- exception handling with this PC value would be inside the outer
- region. */
- emit_insn (gen_nop ());
-
- /* Push the label that points to where normal flow is resumed onto
- the top of the label stack. */
- push_label_entry (&caught_return_label_stack, NULL_RTX, label);
-
- /* Start a new sequence for all the catch blocks. We will add this
- to the global sequence catch_clauses when we have completed all
- the handlers in this handler-seq. */
- start_sequence ();
-
- while (1)
- {
- rtx prev;
-
- entry = dequeue_eh_entry (&ehqueue);
- /* Emit the label for the exception handler for this region, and
- expand the code for the handler.
-
- Note that a catch region is handled as a side-effect here;
- for a try block, entry->finalization will contain
- integer_zero_node, so no code will be generated in the
- expand_expr call below. But, the label for the handler will
- still be emitted, so any code emitted after this point will
- end up being the handler. */
- emit_label (entry->exception_handler_label);
- expand_expr (entry->finalization, const0_rtx, VOIDmode, 0);
-
- /* When we get down to the matching entry for this try block, stop. */
- if (entry->finalization == integer_zero_node)
- {
- /* Don't forget to free this entry. */
- free (entry);
- break;
- }
-
- prev = get_last_insn ();
- if (prev == NULL || GET_CODE (prev) != BARRIER)
- {
- /* Code to throw out to outer context when we fall off end
- of the handler. We can't do this here for catch blocks,
- so it's done in expand_end_all_catch () instead.
-
- The below can be optimized away (and we could just fall
- into the next EH handler) if we are certain they are
- nested. */
-
- expand_internal_throw (entry->end_label);
- }
- free (entry);
- }
-}
-
-/* Finish up the catch block. At this point all the insns for the
- catch clauses have already been generated, so we only have to add
- them to the catch_clauses list. We also want to make sure that if
- we fall off the end of the catch clauses that we rethrow to the
- outer EH region. */
-
-void
-expand_end_all_catch ()
-{
- rtx new_catch_clause;
-
- if (! doing_eh (1))
- return;
-
- /* Code to throw out to outer context, if we fall off end of catch
- handlers. This is rethrow (Lresume, same id, same obj) in the
- documentation. We use Lresume because we know that it will throw
- to the correct context.
-
- In other words, if the catch handler doesn't exit or return, we
- do a "throw" (using the address of Lresume as the point being
- thrown from) so that the outer EH region can then try to process
- the exception. */
-
- expand_internal_throw (DECL_RTL (top_label_entry (&caught_return_label_stack)));
-
- /* Now we have the complete catch sequence. */
- new_catch_clause = get_insns ();
- end_sequence ();
-
- /* This level of catch blocks is done, so set up the successful
- catch jump label for the next layer of catch blocks. */
- pop_label_entry (&caught_return_label_stack);
-
- /* Add the new sequence of catches to the main one for this function. */
- push_to_sequence (catch_clauses);
- emit_insns (new_catch_clause);
- catch_clauses = get_insns ();
- end_sequence ();
-
- /* Here we fall through into the continuation code. */
-}
-
-/* End all the pending exception regions on protect_list. The handlers
- will be emitted when expand_leftover_cleanups () is invoked. */
-
-void
-end_protect_partials ()
-{
- while (protect_list)
- {
- expand_eh_region_end (TREE_VALUE (protect_list));
- protect_list = TREE_CHAIN (protect_list);
- }
-}
-
-/* The exception table that we build that is used for looking up and
- dispatching exceptions, the current number of entries, and its
- maximum size before we have to extend it.
-
- The number in eh_table is the code label number of the exception
- handler for the region. This is added by add_eh_table_entry () and
- used by output_exception_table_entry (). */
-
-static int *eh_table;
-static int eh_table_size;
-static int eh_table_max_size;
-
-/* Note the need for an exception table entry for region N. If we
- don't need to output an explicit exception table, avoid all of the
- extra work.
-
- Called from final_scan_insn when a NOTE_INSN_EH_REGION_BEG is seen.
- N is the NOTE_BLOCK_NUMBER of the note, which comes from the code
- label number of the exception handler for the region. */
-
-void
-add_eh_table_entry (n)
- int n;
-{
-#ifndef OMIT_EH_TABLE
- if (eh_table_size >= eh_table_max_size)
- {
- if (eh_table)
- {
- eh_table_max_size += eh_table_max_size>>1;
-
- if (eh_table_max_size < 0)
- abort ();
-
- if ((eh_table = (int *) realloc (eh_table,
- eh_table_max_size * sizeof (int)))
- == 0)
- fatal ("virtual memory exhausted");
- }
- else
- {
- eh_table_max_size = 252;
- eh_table = (int *) xmalloc (eh_table_max_size * sizeof (int));
- }
- }
- eh_table[eh_table_size++] = n;
-#endif
-}
-
-/* Return a non-zero value if we need to output an exception table.
-
- On some platforms, we don't have to output a table explicitly.
- This routine doesn't mean we don't have one. */
-
-int
-exception_table_p ()
-{
- if (eh_table)
- return 1;
-
- return 0;
-}
-
-/* Output the entry of the exception table corresponding to to the
- exception region numbered N to file FILE.
-
- N is the code label number corresponding to the handler of the
- region. */
-
-static void
-output_exception_table_entry (file, n)
- FILE *file;
- int n;
-{
- char buf[256];
- rtx sym;
-
- ASM_GENERATE_INTERNAL_LABEL (buf, "LEHB", n);
- sym = gen_rtx (SYMBOL_REF, Pmode, buf);
- assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
-
- ASM_GENERATE_INTERNAL_LABEL (buf, "LEHE", n);
- sym = gen_rtx (SYMBOL_REF, Pmode, buf);
- assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
-
- ASM_GENERATE_INTERNAL_LABEL (buf, "L", n);
- sym = gen_rtx (SYMBOL_REF, Pmode, buf);
- assemble_integer (sym, POINTER_SIZE / BITS_PER_UNIT, 1);
-
- putc ('\n', file); /* blank line */
-}
-
-/* Output the exception table if we have and need one. */
-
-void
-output_exception_table ()
-{
- int i;
- extern FILE *asm_out_file;
-
- if (! doing_eh (0))
- return;
-
- exception_section ();
-
- /* Beginning marker for table. */
- assemble_align (GET_MODE_ALIGNMENT (ptr_mode));
- assemble_label ("__EXCEPTION_TABLE__");
-
- assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
- assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
- assemble_integer (const0_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
- putc ('\n', asm_out_file); /* blank line */
-
- for (i = 0; i < eh_table_size; ++i)
- output_exception_table_entry (asm_out_file, eh_table[i]);
-
- free (eh_table);
-
- /* Ending marker for table. */
- assemble_label ("__EXCEPTION_END__");
- assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
- assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
- assemble_integer (constm1_rtx, POINTER_SIZE / BITS_PER_UNIT, 1);
- putc ('\n', asm_out_file); /* blank line */
-}
-
-/* Generate code to initialize the exception table at program startup
- time. */
-
-void
-register_exception_table ()
-{
- emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "__register_exceptions"), 0,
- VOIDmode, 1,
- gen_rtx (SYMBOL_REF, Pmode, "__EXCEPTION_TABLE__"),
- Pmode);
-}
-
-/* Emit the RTL for the start of the per-function unwinder for the
- current function. See emit_unwinder () for further information.
-
- DOESNT_NEED_UNWINDER is a target-specific macro that determines if
- the current function actually needs a per-function unwinder or not.
- By default, all functions need one. */
-
-void
-start_eh_unwinder ()
-{
-#ifdef DOESNT_NEED_UNWINDER
- if (DOESNT_NEED_UNWINDER)
- return;
-#endif
-
- expand_eh_region_start ();
-}
-
-/* Emit insns for the end of the per-function unwinder for the
- current function. */
-
-void
-end_eh_unwinder ()
-{
- tree expr;
- rtx return_val_rtx, ret_val, label, end, insns;
-
- if (! doing_eh (0))
- return;
-
-#ifdef DOESNT_NEED_UNWINDER
- if (DOESNT_NEED_UNWINDER)
- return;
-#endif
-
- assemble_external (eh_saved_pc);
-
- expr = make_node (RTL_EXPR);
- TREE_TYPE (expr) = void_type_node;
- RTL_EXPR_RTL (expr) = const0_rtx;
- TREE_SIDE_EFFECTS (expr) = 1;
- start_sequence_for_rtl_expr (expr);
-
- /* ret_val will contain the address of the code where the call
- to the current function occurred. */
- ret_val = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
- 0, hard_frame_pointer_rtx);
- return_val_rtx = copy_to_reg (ret_val);
-
- /* Get the address we need to use to determine what exception
- handler should be invoked, and store it in __eh_pc. */
- return_val_rtx = eh_outer_context (return_val_rtx);
- emit_move_insn (eh_saved_pc_rtx, return_val_rtx);
-
- /* Either set things up so we do a return directly to __throw, or
- we return here instead. */
-#ifdef JUMP_TO_THROW
- emit_move_insn (ret_val, throw_libfunc);
-#else
- label = gen_label_rtx ();
- emit_move_insn (ret_val, gen_rtx (LABEL_REF, Pmode, label));
-#endif
-
-#ifdef RETURN_ADDR_OFFSET
- return_val_rtx = plus_constant (ret_val, -RETURN_ADDR_OFFSET);
- if (return_val_rtx != ret_val)
- emit_move_insn (ret_val, return_val_rtx);
-#endif
-
- end = gen_label_rtx ();
- emit_jump (end);
-
- RTL_EXPR_SEQUENCE (expr) = get_insns ();
- end_sequence ();
- expand_eh_region_end (expr);
-
- emit_jump (end);
-
-#ifndef JUMP_TO_THROW
- emit_label (label);
- emit_throw ();
-#endif
-
- expand_leftover_cleanups ();
-
- emit_label (end);
-}
-
-/* If necessary, emit insns for the per function unwinder for the
- current function. Called after all the code that needs unwind
- protection is output.
-
- The unwinder takes care of catching any exceptions that have not
- been previously caught within the function, unwinding the stack to
- the next frame, and rethrowing using the address of the current
- function's caller as the context of the throw.
-
- On some platforms __throw can do this by itself (or with the help
- of __unwind_function) so the per-function unwinder is
- unnecessary.
-
- We cannot place the unwinder into the function until after we know
- we are done inlining, as we don't want to have more than one
- unwinder per non-inlined function. */
-
-void
-emit_unwinder ()
-{
- rtx insns, insn;
-
- start_sequence ();
- start_eh_unwinder ();
- insns = get_insns ();
- end_sequence ();
-
- /* We place the start of the exception region associated with the
- per function unwinder at the top of the function. */
- if (insns)
- emit_insns_after (insns, get_insns ());
-
- start_sequence ();
- end_eh_unwinder ();
- insns = get_insns ();
- end_sequence ();
-
- /* And we place the end of the exception region before the USE and
- CLOBBER insns that may come at the end of the function. */
- if (insns == 0)
- return;
-
- insn = get_last_insn ();
- while (GET_CODE (insn) == NOTE
- || (GET_CODE (insn) == INSN
- && (GET_CODE (PATTERN (insn)) == USE
- || GET_CODE (PATTERN (insn)) == CLOBBER)))
- insn = PREV_INSN (insn);
-
- if (GET_CODE (insn) == CODE_LABEL
- && GET_CODE (PREV_INSN (insn)) == BARRIER)
- {
- insn = PREV_INSN (insn);
- }
- else
- {
- rtx label = gen_label_rtx ();
- emit_label_after (label, insn);
- insn = emit_jump_insn_after (gen_jump (label), insn);
- insn = emit_barrier_after (insn);
- }
-
- emit_insns_after (insns, insn);
-}
-
-/* Scan the current insns and build a list of handler labels. The
- resulting list is placed in the global variable exception_handler_labels.
-
- It is called after the last exception handling region is added to
- the current function (when the rtl is almost all built for the
- current function) and before the jump optimization pass. */
-
-void
-find_exception_handler_labels ()
-{
- rtx insn;
- int max_labelno = max_label_num ();
- int min_labelno = get_first_label_num ();
- rtx *labels;
-
- exception_handler_labels = NULL_RTX;
-
- /* If we aren't doing exception handling, there isn't much to check. */
- if (! doing_eh (0))
- return;
-
- /* Generate a handy reference to each label. */
-
- labels = (rtx *) alloca ((max_labelno - min_labelno) * sizeof (rtx));
- bzero ((char *) labels, (max_labelno - min_labelno) * sizeof (rtx));
-
- /* Arrange for labels to be indexed directly by CODE_LABEL_NUMBER. */
- labels -= min_labelno;
-
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == CODE_LABEL)
- if (CODE_LABEL_NUMBER (insn) >= min_labelno
- && CODE_LABEL_NUMBER (insn) < max_labelno)
- labels[CODE_LABEL_NUMBER (insn)] = insn;
- }
-
- /* For each start of a region, add its label to the list. */
-
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
- {
- rtx label = NULL_RTX;
-
- if (NOTE_BLOCK_NUMBER (insn) >= min_labelno
- && NOTE_BLOCK_NUMBER (insn) < max_labelno)
- {
- label = labels[NOTE_BLOCK_NUMBER (insn)];
-
- if (label)
- exception_handler_labels
- = gen_rtx (EXPR_LIST, VOIDmode,
- label, exception_handler_labels);
- else
- warning ("didn't find handler for EH region %d",
- NOTE_BLOCK_NUMBER (insn));
- }
- else
- warning ("mismatched EH region %d", NOTE_BLOCK_NUMBER (insn));
- }
- }
-}
-
-/* Perform sanity checking on the exception_handler_labels list.
-
- Can be called after find_exception_handler_labels is called to
- build the list of exception handlers for the current function and
- before we finish processing the current function. */
-
-void
-check_exception_handler_labels ()
-{
- rtx insn, handler;
-
- /* If we aren't doing exception handling, there isn't much to check. */
- if (! doing_eh (0))
- return;
-
- /* Ensure that the CODE_LABEL_NUMBER for the CODE_LABEL entry point
- in each handler corresponds to the CODE_LABEL_NUMBER of the
- handler. */
-
- for (handler = exception_handler_labels;
- handler;
- handler = XEXP (handler, 1))
- {
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == CODE_LABEL)
- {
- if (CODE_LABEL_NUMBER (insn)
- == CODE_LABEL_NUMBER (XEXP (handler, 0)))
- {
- if (insn != XEXP (handler, 0))
- warning ("mismatched handler %d",
- CODE_LABEL_NUMBER (insn));
- break;
- }
- }
- }
- if (insn == NULL_RTX)
- warning ("handler not found %d",
- CODE_LABEL_NUMBER (XEXP (handler, 0)));
- }
-
- /* Now go through and make sure that for each region there is a
- corresponding label. */
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == NOTE
- && (NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG ||
- NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
- {
- for (handler = exception_handler_labels;
- handler;
- handler = XEXP (handler, 1))
- {
- if (CODE_LABEL_NUMBER (XEXP (handler, 0))
- == NOTE_BLOCK_NUMBER (insn))
- break;
- }
- if (handler == NULL_RTX)
- warning ("region exists, no handler %d",
- NOTE_BLOCK_NUMBER (insn));
- }
- }
-}
-
-/* This group of functions initializes the exception handling data
- structures at the start of the compilation, initializes the data
- structures at the start of a function, and saves and restores the
- exception handling data structures for the start/end of a nested
- function. */
-
-/* Toplevel initialization for EH things. */
-
-void
-init_eh ()
-{
- /* Generate rtl to reference the variable in which the PC of the
- current context is saved. */
- tree type = build_pointer_type (make_node (VOID_TYPE));
-
- eh_saved_pc = build_decl (VAR_DECL, get_identifier ("__eh_pc"), type);
- DECL_EXTERNAL (eh_saved_pc) = 1;
- TREE_PUBLIC (eh_saved_pc) = 1;
- make_decl_rtl (eh_saved_pc, NULL_PTR, 1);
- eh_saved_pc_rtx = DECL_RTL (eh_saved_pc);
-}
-
-/* Initialize the per-function EH information. */
-
-void
-init_eh_for_function ()
-{
- ehstack.top = 0;
- ehqueue.head = ehqueue.tail = 0;
- catch_clauses = NULL_RTX;
- false_label_stack = 0;
- caught_return_label_stack = 0;
- protect_list = NULL_TREE;
-}
-
-/* Save some of the per-function EH info into the save area denoted by
- P.
-
- This is currently called from save_stmt_status (). */
-
-void
-save_eh_status (p)
- struct function *p;
-{
- assert (p != NULL);
-
- p->ehstack = ehstack;
- p->ehqueue = ehqueue;
- p->catch_clauses = catch_clauses;
- p->false_label_stack = false_label_stack;
- p->caught_return_label_stack = caught_return_label_stack;
- p->protect_list = protect_list;
-
- init_eh ();
-}
-
-/* Restore the per-function EH info saved into the area denoted by P.
-
- This is currently called from restore_stmt_status. */
-
-void
-restore_eh_status (p)
- struct function *p;
-{
- assert (p != NULL);
-
- protect_list = p->protect_list;
- caught_return_label_stack = p->caught_return_label_stack;
- false_label_stack = p->false_label_stack;
- catch_clauses = p->catch_clauses;
- ehqueue = p->ehqueue;
- ehstack = p->ehstack;
-}
-
-/* This section is for the exception handling specific optimization
- pass. First are the internal routines, and then the main
- optimization pass. */
-
-/* Determine if the given INSN can throw an exception. */
-
-static int
-can_throw (insn)
- rtx insn;
-{
- /* Calls can always potentially throw exceptions. */
- if (GET_CODE (insn) == CALL_INSN)
- return 1;
-
-#ifdef ASYNCH_EXCEPTIONS
- /* If we wanted asynchronous exceptions, then everything but NOTEs
- and CODE_LABELs could throw. */
- if (GET_CODE (insn) != NOTE && GET_CODE (insn) != CODE_LABEL)
- return 1;
-#endif
-
- return 0;
-}
-
-/* Scan a exception region looking for the matching end and then
- remove it if possible. INSN is the start of the region, N is the
- region number, and DELETE_OUTER is to note if anything in this
- region can throw.
-
- Regions are removed if they cannot possibly catch an exception.
- This is determined by invoking can_throw () on each insn within the
- region; if can_throw returns true for any of the instructions, the
- region can catch an exception, since there is an insn within the
- region that is capable of throwing an exception.
-
- Returns the NOTE_INSN_EH_REGION_END corresponding to this region, or
- calls abort () if it can't find one.
-
- Can abort if INSN is not a NOTE_INSN_EH_REGION_BEGIN, or if N doesn't
- correspond to the region number, or if DELETE_OUTER is NULL. */
-
-static rtx
-scan_region (insn, n, delete_outer)
- rtx insn;
- int n;
- int *delete_outer;
-{
- rtx start = insn;
-
- /* Assume we can delete the region. */
- int delete = 1;
-
- assert (insn != NULL_RTX
- && GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG
- && NOTE_BLOCK_NUMBER (insn) == n
- && delete_outer != NULL);
-
- insn = NEXT_INSN (insn);
-
- /* Look for the matching end. */
- while (! (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_END))
- {
- /* If anything can throw, we can't remove the region. */
- if (delete && can_throw (insn))
- {
- delete = 0;
- }
-
- /* Watch out for and handle nested regions. */
- if (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
- {
- insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &delete);
- }
-
- insn = NEXT_INSN (insn);
- }
-
- /* The _BEG/_END NOTEs must match and nest. */
- if (NOTE_BLOCK_NUMBER (insn) != n)
- abort ();
-
- /* If anything in this exception region can throw, we can throw. */
- if (! delete)
- *delete_outer = 0;
- else
- {
- /* Delete the start and end of the region. */
- delete_insn (start);
- delete_insn (insn);
-
- /* Only do this part if we have built the exception handler
- labels. */
- if (exception_handler_labels)
- {
- rtx x, *prev = &exception_handler_labels;
-
- /* Find it in the list of handlers. */
- for (x = exception_handler_labels; x; x = XEXP (x, 1))
- {
- rtx label = XEXP (x, 0);
- if (CODE_LABEL_NUMBER (label) == n)
- {
- /* If we are the last reference to the handler,
- delete it. */
- if (--LABEL_NUSES (label) == 0)
- delete_insn (label);
-
- if (optimize)
- {
- /* Remove it from the list of exception handler
- labels, if we are optimizing. If we are not, then
- leave it in the list, as we are not really going to
- remove the region. */
- *prev = XEXP (x, 1);
- XEXP (x, 1) = 0;
- XEXP (x, 0) = 0;
- }
-
- break;
- }
- prev = &XEXP (x, 1);
- }
- }
- }
- return insn;
-}
-
-/* Perform various interesting optimizations for exception handling
- code.
-
- We look for empty exception regions and make them go (away). The
- jump optimization code will remove the handler if nothing else uses
- it. */
-
-void
-exception_optimize ()
-{
- rtx insn, regions = NULL_RTX;
- int n;
-
- /* Remove empty regions. */
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == NOTE
- && NOTE_LINE_NUMBER (insn) == NOTE_INSN_EH_REGION_BEG)
- {
- /* Since scan_region () will return the NOTE_INSN_EH_REGION_END
- insn, we will indirectly skip through all the insns
- inbetween. We are also guaranteed that the value of insn
- returned will be valid, as otherwise scan_region () won't
- return. */
- insn = scan_region (insn, NOTE_BLOCK_NUMBER (insn), &n);
- }
- }
-}