diff options
Diffstat (limited to 'contrib/gcc/cp/except.c')
-rw-r--r-- | contrib/gcc/cp/except.c | 1690 |
1 files changed, 1690 insertions, 0 deletions
diff --git a/contrib/gcc/cp/except.c b/contrib/gcc/cp/except.c new file mode 100644 index 0000000..51577f8 --- /dev/null +++ b/contrib/gcc/cp/except.c @@ -0,0 +1,1690 @@ +/* Handle exceptional things in C++. + Copyright (C) 1989, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. + Contributed by Michael Tiemann <tiemann@cygnus.com> + Rewritten by Mike Stump <mrs@cygnus.com>, based upon an + initial re-implementation courtesy Tad Hunt. + +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. */ + + +/* High-level class interface. */ + +#include "config.h" +#include "tree.h" +#include "rtl.h" +#include "cp-tree.h" +#include "flags.h" +#include "obstack.h" +#include "expr.h" + +tree protect_list; + +extern void (*interim_eh_hook) PROTO((tree)); +rtx expand_builtin_return_addr PROTO((enum built_in_function, int, rtx)); + +/* holds the fndecl for __builtin_return_address () */ +tree builtin_return_address_fndecl; +tree throw_fndecl; + +static int +doing_eh (do_warn) + int do_warn; +{ + if (! flag_handle_exceptions) + { + static int warned = 0; + if (! warned && do_warn) + { + error ("exception handling disabled, use -fhandle-exceptions to enable."); + warned = 1; + } + return 0; + } + return 1; +} + + +/* +NO GNEWS IS GOOD GNEWS WITH GARRY GNUS: This version is much closer +to supporting exception handling as per ANSI C++ working draft. +It is a complete rewrite of all the EH stuff that was here before + Shortcomings: + 1. Throw specifications of functions still don't work. + Cool Things: + 1. Destructors are called properly :-) + 2. No overhead for the non-exception thrown case. + 3. Fixing shortcoming 1 is simple. + -Tad Hunt (tad@mail.csh.rit.edu) + +*/ + +/* A couple of backend routines from m88k.c */ + +/* used to cache a call to __builtin_return_address () */ +static tree BuiltinReturnAddress; + + +#include <stdio.h> + +/* XXX - Tad: for EH */ +/* output an exception table entry */ + +static void +output_exception_table_entry (file, start_label, end_label, eh_label) + FILE *file; + rtx start_label, end_label, eh_label; +{ + char label[100]; + + assemble_integer (start_label, GET_MODE_SIZE (Pmode), 1); + assemble_integer (end_label, GET_MODE_SIZE (Pmode), 1); + assemble_integer (eh_label, GET_MODE_SIZE (Pmode), 1); + putc ('\n', file); /* blank line */ +} + +static void +easy_expand_asm (str) + char *str; +{ + expand_asm (build_string (strlen (str)+1, str)); +} + + +#if 0 +/* This is the startup, and finish stuff per exception table. */ + +/* XXX - Tad: exception handling section */ +#ifndef EXCEPT_SECTION_ASM_OP +#define EXCEPT_SECTION_ASM_OP "section\t.gcc_except_table,\"a\",@progbits" +#endif + +#ifdef EXCEPT_SECTION_ASM_OP +typedef struct { + void *start_protect; + void *end_protect; + void *exception_handler; + } exception_table; +#endif /* EXCEPT_SECTION_ASM_OP */ + +#ifdef EXCEPT_SECTION_ASM_OP + + /* on machines which support it, the exception table lives in another section, + but it needs a label so we can reference it... This sets up that + label! */ +asm (EXCEPT_SECTION_ASM_OP); +exception_table __EXCEPTION_TABLE__[1] = { (void*)0, (void*)0, (void*)0 }; +asm (TEXT_SECTION_ASM_OP); + +#endif /* EXCEPT_SECTION_ASM_OP */ + +#ifdef EXCEPT_SECTION_ASM_OP + + /* we need to know where the end of the exception table is... so this + is how we do it! */ + +asm (EXCEPT_SECTION_ASM_OP); +exception_table __EXCEPTION_END__[1] = { (void*)-1, (void*)-1, (void*)-1 }; +asm (TEXT_SECTION_ASM_OP); + +#endif /* EXCEPT_SECTION_ASM_OP */ + +#endif + +void +exception_section () +{ +#ifdef ASM_OUTPUT_SECTION_NAME + named_section (NULL_TREE, ".gcc_except_table"); +#else + if (flag_pic) + data_section (); + else +#if defined(TARGET_POWERPC) /* are we on a __rs6000? */ + data_section (); +#else + readonly_data_section (); +#endif +#endif +} + + + + +/* from: my-cp-except.c */ + +/* VI: ":set ts=4" */ +#if 0 +#include <stdio.h> */ +#include "config.h" +#include "tree.h" +#include "rtl.h" +#include "cp-tree.h" +#endif +#include "decl.h" +#if 0 +#include "flags.h" +#endif +#include "insn-flags.h" +#include "obstack.h" +#if 0 +#include "expr.h" +#endif + +/* ====================================================================== + Briefly the algorithm works like this: + + When a constructor or start of a try block is encountered, + push_eh_entry (&eh_stack) is called. Push_eh_entry () creates a + new entry in the unwind protection stack and returns a label to + output to start the protection for that block. + + When a destructor or end try block is encountered, pop_eh_entry + (&eh_stack) is called. Pop_eh_entry () returns the ehEntry it + created when push_eh_entry () was called. The ehEntry structure + contains three things at this point. The start protect label, + the end protect label, and the exception handler label. The end + protect label should be output before the call to the destructor + (if any). If it was a destructor, then its parse tree is stored + in the finalization variable in the ehEntry structure. Otherwise + the finalization variable is set to NULL to reflect the fact that + is the the end of a try block. Next, this modified ehEntry node + is enqueued in the finalizations queue by calling + enqueue_eh_entry (&queue,entry). + + +---------------------------------------------------------------+ + |XXX: Will need modification to deal with partially | + | constructed arrays of objects | + | | + | Basically, this consists of keeping track of how many | + | of the objects have been constructed already (this | + | should be in a register though, so that shouldn't be a | + | problem. | + +---------------------------------------------------------------+ + + When a catch block is encountered, there is a lot of work to be + done. + + Since we don't want to generate the catch block inline with the + regular flow of the function, we need to have some way of doing + so. Luckily, we can use sequences to defer the catch sections. + When the start of a catch block is encountered, we start the + sequence. After the catch block is generated, we end the + sequence. + + Next we must insure that when the catch block is executed, all + finalizations for the matching try block have been completed. If + any of those finalizations throw an exception, we must call + terminate according to the ARM (section r.15.6.1). What this + means is that we need to dequeue and emit finalizations for each + entry in the ehQueue until we get to an entry with a NULL + finalization field. For any of the finalization entries, if it + is not a call to terminate (), we must protect it by giving it + another start label, end label, and exception handler label, + setting its finalization tree to be a call to terminate (), and + enqueue'ing this new ehEntry to be output at an outer level. + Finally, after all that is done, we can get around to outputting + the catch block which basically wraps all the "catch (...) {...}" + statements in a big if/then/else construct that matches the + correct block to call. + + ===================================================================== */ + +extern rtx emit_insn PROTO((rtx)); +extern rtx gen_nop PROTO(()); + +/* local globals for function calls + ====================================================================== */ + +/* used to cache "terminate ()", "unexpected ()", "set_terminate ()", and + "set_unexpected ()" after default_conversion. (lib-except.c) */ +static tree Terminate, Unexpected, SetTerminate, SetUnexpected, CatchMatch, Throw; + +/* used to cache __find_first_exception_table_match () + for throw (lib-except.c) */ +static tree FirstExceptionMatch; + +/* used to cache a call to __unwind_function () (lib-except.c) */ +static tree Unwind; + +/* holds a ready to emit call to "terminate ()". */ +static tree TerminateFunctionCall; + +/* ====================================================================== */ + + + +/* data structures for my various quick and dirty stacks and queues + Eventually, most of this should go away, because I think it can be + integrated with stuff already built into the compiler. */ + +/* =================================================================== */ + +struct labelNode { + rtx label; + struct labelNode *chain; +}; + + +/* this is the most important structure here. Basically this is how I store + an exception table entry internally. */ +struct ehEntry { + rtx start_label; + rtx end_label; + rtx exception_handler_label; + + tree finalization; + tree context; +}; + +struct ehNode { + struct ehEntry *entry; + struct ehNode *chain; +}; + +struct ehStack { + struct ehNode *top; +}; + +struct ehQueue { + struct ehNode *head; + struct ehNode *tail; +}; +/* ========================================================================= */ + + + +/* local globals - these local globals are for storing data necessary for + generating the exception table and code in the correct order. + + ========================================================================= */ + +/* Holds the pc for doing "throw" */ +tree saved_pc; +/* Holds the type of the thing being thrown. */ +tree saved_throw_type; +/* Holds the value being thrown. */ +tree saved_throw_value; + +int throw_used; + +static rtx catch_clauses; +static first_catch_label; + +static struct ehStack ehstack; +static struct ehQueue ehqueue; +static struct ehQueue eh_table_output_queue; +static struct labelNode *false_label_stack = NULL; +static struct labelNode *caught_return_label_stack = NULL; +/* ========================================================================= */ + +/* function prototypes */ +static struct ehEntry *pop_eh_entry PROTO((struct ehStack *stack)); +static void enqueue_eh_entry PROTO((struct ehQueue *queue, struct ehEntry *entry)); +static rtx push_eh_entry PROTO((struct ehStack *stack)); +static struct ehEntry *dequeue_eh_entry PROTO((struct ehQueue *queue)); +static void new_eh_queue PROTO((struct ehQueue *queue)); +static void new_eh_stack PROTO((struct ehStack *stack)); +static void push_label_entry PROTO((struct labelNode **labelstack, rtx label)); +static rtx pop_label_entry PROTO((struct labelNode **labelstack)); +static rtx top_label_entry PROTO((struct labelNode **labelstack)); +static struct ehEntry *copy_eh_entry PROTO((struct ehEntry *entry)); + + + +/* All my cheesy stack/queue/misc data structure handling routines + + ========================================================================= */ + +static void +push_label_entry (labelstack, label) + struct labelNode **labelstack; + rtx label; +{ + struct labelNode *newnode=(struct labelNode*)xmalloc (sizeof (struct labelNode)); + + newnode->label = label; + newnode->chain = *labelstack; + *labelstack = newnode; +} + +static rtx +pop_label_entry (labelstack) + struct labelNode **labelstack; +{ + rtx label; + struct labelNode *tempnode; + + if (! *labelstack) return NULL_RTX; + + tempnode = *labelstack; + label = tempnode->label; + *labelstack = (*labelstack)->chain; + free (tempnode); + + return label; +} + +static rtx +top_label_entry (labelstack) + struct labelNode **labelstack; +{ + if (! *labelstack) return NULL_RTX; + + return (*labelstack)->label; +} + +/* Push to permanent obstack for rtl generation. + One level only! */ +static struct obstack *saved_rtl_obstack; +void +push_rtl_perm () +{ + extern struct obstack permanent_obstack; + extern struct obstack *rtl_obstack; + + saved_rtl_obstack = rtl_obstack; + rtl_obstack = &permanent_obstack; +} + +/* Pop back to normal rtl handling. */ +static void +pop_rtl_from_perm () +{ + extern struct obstack permanent_obstack; + extern struct obstack *rtl_obstack; + + rtl_obstack = saved_rtl_obstack; +} + +static rtx +push_eh_entry (stack) + struct ehStack *stack; +{ + struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode)); + struct ehEntry *entry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry)); + + if (stack == NULL) { + free (node); + free (entry); + return NULL_RTX; + } + + /* These are saved for the exception table. */ + push_rtl_perm (); + entry->start_label = gen_label_rtx (); + entry->end_label = gen_label_rtx (); + entry->exception_handler_label = gen_label_rtx (); + pop_rtl_from_perm (); + + LABEL_PRESERVE_P (entry->start_label) = 1; + LABEL_PRESERVE_P (entry->end_label) = 1; + LABEL_PRESERVE_P (entry->exception_handler_label) = 1; + + entry->finalization = NULL_TREE; + entry->context = current_function_decl; + + node->entry = entry; + node->chain = stack->top; + stack->top = node; + + enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry)); + + return entry->start_label; +} + +static struct ehEntry * +pop_eh_entry (stack) + struct ehStack *stack; +{ + struct ehNode *tempnode; + struct ehEntry *tempentry; + + if (stack && (tempnode = stack->top)) { + tempentry = tempnode->entry; + stack->top = stack->top->chain; + free (tempnode); + + return tempentry; + } + + return NULL; +} + +static struct ehEntry * +copy_eh_entry (entry) + struct ehEntry *entry; +{ + struct ehEntry *newentry; + + newentry = (struct ehEntry*)xmalloc (sizeof (struct ehEntry)); + memcpy ((void*)newentry, (void*)entry, sizeof (struct ehEntry)); + + return newentry; +} + +static void +enqueue_eh_entry (queue, entry) + struct ehQueue *queue; + struct ehEntry *entry; +{ + struct ehNode *node = (struct ehNode*)xmalloc (sizeof (struct ehNode)); + + node->entry = entry; + node->chain = NULL; + + if (queue->head == NULL) + { + queue->head = node; + } + else + { + queue->tail->chain = node; + } + queue->tail = node; +} + +static struct ehEntry * +dequeue_eh_entry (queue) + struct ehQueue *queue; +{ + struct ehNode *tempnode; + struct ehEntry *tempentry; + + if (queue->head == NULL) + return NULL; + + tempnode = queue->head; + queue->head = queue->head->chain; + + tempentry = tempnode->entry; + free (tempnode); + + return tempentry; +} + +static void +new_eh_queue (queue) + struct ehQueue *queue; +{ + queue->head = queue->tail = NULL; +} + +static void +new_eh_stack (stack) + struct ehStack *stack; +{ + stack->top = NULL; +} + +/* cheesyness to save some typing. returns the return value rtx */ +rtx +do_function_call (func, params, return_type) + tree func, params, return_type; +{ + tree func_call; + func_call = build_function_call (func, params); + expand_call (func_call, NULL_RTX, 0); + if (return_type != NULL_TREE) + return hard_function_value (return_type, func_call); + return NULL_RTX; +} + +static void +expand_internal_throw (pc) + rtx pc; +{ + tree params; + + emit_move_insn (DECL_RTL (saved_pc), pc); +#ifdef JUMP_TO_THROW + emit_indirect_jump (gen_rtx (SYMBOL_REF, Pmode, "__throw")); +#else + do_function_call (Throw, NULL_TREE, NULL_TREE); +#endif + throw_used = 1; +} + +/* ========================================================================= */ + +void +lang_interim_eh (finalization) + tree finalization; +{ + if (finalization) + end_protect (finalization); + else + start_protect (); +} + +extern tree auto_function PROTO((tree, tree, enum built_in_function)); + +/* sets up all the global eh stuff that needs to be initialized at the + start of compilation. + + This includes: + - Setting up all the function call trees + - Initializing the ehqueue + - Initializing the eh_table_output_queue + - Initializing the ehstack +*/ + +void +init_exception_processing () +{ + extern tree define_function (); + tree unexpected_fndecl, terminate_fndecl; + tree set_unexpected_fndecl, set_terminate_fndecl; + tree catch_match_fndecl; + tree find_first_exception_match_fndecl; + tree unwind_fndecl; + tree declspecs; + tree d; + + /* void (*)() */ + tree PFV = build_pointer_type (build_function_type + (void_type_node, void_list_node)); + + /* arg list for the build_function_type call for set_terminate () and + set_unexpected () */ + tree pfvlist = tree_cons (NULL_TREE, PFV, void_list_node); + + /* void (*pfvtype (void (*) ()))() */ + tree pfvtype = build_function_type (PFV, pfvlist); + + /* void vtype () */ + tree vtype = build_function_type (void_type_node, void_list_node); + + set_terminate_fndecl = auto_function (get_identifier ("set_terminate"), + pfvtype, NOT_BUILT_IN); + set_unexpected_fndecl = auto_function (get_identifier ("set_unexpected"), + pfvtype, NOT_BUILT_IN); + unexpected_fndecl = auto_function (get_identifier ("unexpected"), + vtype, NOT_BUILT_IN); + terminate_fndecl = auto_function (get_identifier ("terminate"), + vtype, NOT_BUILT_IN); + + interim_eh_hook = lang_interim_eh; + + push_lang_context (lang_name_c); + + catch_match_fndecl = + define_function (flag_rtti + ? "__throw_type_match_rtti" + : "__throw_type_match", + build_function_type (ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, + void_list_node)))), + NOT_BUILT_IN, + pushdecl, + 0); + find_first_exception_match_fndecl = + define_function ("__find_first_exception_table_match", + build_function_type (ptr_type_node, + tree_cons (NULL_TREE, ptr_type_node, + void_list_node)), + NOT_BUILT_IN, + pushdecl, + 0); + unwind_fndecl = + define_function ("__unwind_function", + build_function_type (void_type_node, + tree_cons (NULL_TREE, ptr_type_node, + void_list_node)), + NOT_BUILT_IN, + pushdecl, + 0); + throw_fndecl = + define_function ("__throw", + build_function_type (void_type_node, void_list_node), + NOT_BUILT_IN, + pushdecl, + 0); + DECL_EXTERNAL (throw_fndecl) = 0; + TREE_PUBLIC (throw_fndecl) = 0; + + Unexpected = default_conversion (unexpected_fndecl); + Terminate = default_conversion (terminate_fndecl); + SetTerminate = default_conversion (set_terminate_fndecl); + SetUnexpected = default_conversion (set_unexpected_fndecl); + CatchMatch = default_conversion (catch_match_fndecl); + FirstExceptionMatch = default_conversion (find_first_exception_match_fndecl); + Unwind = default_conversion (unwind_fndecl); + Throw = default_conversion (throw_fndecl); + BuiltinReturnAddress = default_conversion (builtin_return_address_fndecl); + + TerminateFunctionCall = build_function_call (Terminate, NULL_TREE); + + pop_lang_context (); + + new_eh_queue (&ehqueue); + new_eh_queue (&eh_table_output_queue); + new_eh_stack (&ehstack); + + declspecs = tree_cons (NULL_TREE, get_identifier ("void"), NULL_TREE); + d = build_parse_node (INDIRECT_REF, get_identifier ("__eh_pc")); + d = start_decl (d, declspecs, 0, NULL_TREE); + DECL_COMMON (d) = 1; + cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0); + saved_pc = lookup_name (get_identifier ("__eh_pc"), 0); + + declspecs = tree_cons (NULL_TREE, get_identifier ("void"), NULL_TREE); + d = build_parse_node (INDIRECT_REF, get_identifier ("__eh_type")); + d = start_decl (d, declspecs, 0, NULL_TREE); + DECL_COMMON (d) = 1; + cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0); + saved_throw_type = lookup_name (get_identifier ("__eh_type"), 0); + + declspecs = tree_cons (NULL_TREE, get_identifier ("void"), NULL_TREE); + d = build_parse_node (INDIRECT_REF, get_identifier ("__eh_value")); + d = start_decl (d, declspecs, 0, NULL_TREE); + DECL_COMMON (d) = 1; + cp_finish_decl (d, NULL_TREE, NULL_TREE, 0, 0); + saved_throw_value = lookup_name (get_identifier ("__eh_value"), 0); +} + +/* call this to begin a block of unwind protection (ie: when an object is + constructed) */ +void +start_protect () +{ + if (! doing_eh (0)) + return; + + emit_label (push_eh_entry (&ehstack)); +} + +/* call this to end a block of unwind protection. the finalization tree is + the finalization which needs to be run in order to cleanly unwind through + this level of protection. (ie: call this when a scope is exited)*/ +void +end_protect (finalization) + tree finalization; +{ + struct ehEntry *entry; + + if (! doing_eh (0)) + return; + + entry = pop_eh_entry (&ehstack); + + emit_label (entry->end_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 ()); + + entry->finalization = finalization; + + enqueue_eh_entry (&ehqueue, entry); +} + +/* call this on start of a try block. */ +void +expand_start_try_stmts () +{ + if (! doing_eh (1)) + return; + + start_protect (); +} + +void +expand_end_try_stmts () +{ + end_protect (integer_zero_node); +} + + +/* call this to start processing of all the catch blocks. */ +void +expand_start_all_catch () +{ + struct ehEntry *entry; + rtx label; + + if (! doing_eh (1)) + return; + + emit_line_note (input_filename, lineno); + label = gen_label_rtx (); + + /* The label for the exception handling block we will save. This is + Lresume, in the documention. */ + emit_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_label_entry (&caught_return_label_stack, label); + + /* Start a new sequence for all the catch blocks. We will add this + to the gloabl sequence catch_clauses, when we have completed all + the handlers in this handler-seq. */ + start_sequence (); + + while (1) + { + entry = dequeue_eh_entry (&ehqueue); + emit_label (entry->exception_handler_label); + + expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); + + /* When we get down to the matching entry, stop. */ + if (entry->finalization == integer_zero_node) + break; + + /* The below can be optimized away, and we could just fall into the + next EH handler, if we are certain they are nested. */ + /* Code to throw out to outer context, if we fall off end of the + handler. */ + expand_internal_throw (gen_rtx (LABEL_REF, + Pmode, + entry->end_label)); + free (entry); + } +} + +/* call this to end processing of all the catch blocks. */ +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. */ + expand_internal_throw (gen_rtx (LABEL_REF, + Pmode, + 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 catchs 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. */ +} + +/* Build a type value for use at runtime for a type that is matched + against by the exception handling system. */ +static tree +build_eh_type_type (type) + tree type; +{ + char *typestring; + tree exp; + + if (type == error_mark_node) + return error_mark_node; + + /* peel back references, so they match. */ + if (TREE_CODE (type) == REFERENCE_TYPE) + type = TREE_TYPE (type); + + /* Peel off cv qualifiers. */ + type = TYPE_MAIN_VARIANT (type); + + if (flag_rtti) + { + return build1 (ADDR_EXPR, ptr_type_node, get_typeid (type)); + } + + typestring = build_overload_name (type, 1, 1); + exp = combine_strings (build_string (strlen (typestring)+1, typestring)); + return build1 (ADDR_EXPR, ptr_type_node, exp); +} + +/* Build a type value for use at runtime for a exp that is thrown or + matched against by the exception handling system. */ +static tree +build_eh_type (exp) + tree exp; +{ + if (flag_rtti) + { + exp = build_typeid (exp); + return build1 (ADDR_EXPR, ptr_type_node, exp); + } + return build_eh_type_type (TREE_TYPE (exp)); +} + +/* call this to start a catch block. Typename is the typename, and identifier + is the variable to place the object in or NULL if the variable doesn't + matter. If typename is NULL, that means its a "catch (...)" or catch + everything. In that case we don't need to do any type checking. + (ie: it ends up as the "else" clause rather than an "else if" clause) */ +void +expand_start_catch_block (declspecs, declarator) + tree declspecs, declarator; +{ + rtx false_label_rtx; + rtx protect_label_rtx; + tree decl = NULL_TREE; + tree init; + + if (! doing_eh (1)) + return; + + /* Create a binding level for the parm. */ + expand_start_bindings (0); + + false_label_rtx = gen_label_rtx (); + /* This is saved for the exception table. */ + push_rtl_perm (); + protect_label_rtx = gen_label_rtx (); + pop_rtl_from_perm (); + push_label_entry (&false_label_stack, false_label_rtx); + push_label_entry (&false_label_stack, protect_label_rtx); + + if (declspecs) + { + tree exp; + rtx call_rtx, return_value_rtx; + tree init_type; + + decl = grokdeclarator (declarator, declspecs, CATCHPARM, 1, + NULL_TREE, NULL_TREE); + + if (decl == NULL_TREE) + { + error ("invalid catch parameter"); + return; + } + + /* Figure out the type that the initializer is. */ + init_type = TREE_TYPE (decl); + if (TREE_CODE (init_type) != REFERENCE_TYPE + && TREE_CODE (init_type) != POINTER_TYPE) + init_type = build_reference_type (init_type); + + exp = saved_throw_value; + exp = tree_cons (NULL_TREE, + build_eh_type_type (TREE_TYPE (decl)), + tree_cons (NULL_TREE, + saved_throw_type, + tree_cons (NULL_TREE, exp, NULL_TREE))); + exp = build_function_call (CatchMatch, exp); + call_rtx = expand_call (exp, NULL_RTX, 0); + assemble_external (TREE_OPERAND (CatchMatch, 0)); + + return_value_rtx = hard_function_value (ptr_type_node, exp); + + /* did the throw type match function return TRUE? */ + emit_cmp_insn (return_value_rtx, const0_rtx, EQ, NULL_RTX, + GET_MODE (return_value_rtx), 0, 0); + + /* if it returned FALSE, jump over the catch block, else fall into it */ + emit_jump_insn (gen_beq (false_label_rtx)); + + init = convert_from_reference (save_expr (make_tree (init_type, call_rtx))); + + /* Do we need the below two lines? */ + /* Let `cp_finish_decl' know that this initializer is ok. */ + DECL_INITIAL (decl) = init; + decl = pushdecl (decl); + cp_finish_decl (decl, init, NULL_TREE, 0, LOOKUP_ONLYCONVERTING); + } + else + { + /* Fall into the catch all section. */ + } + + /* This is the starting of something to protect. */ + emit_label (protect_label_rtx); + + emit_line_note (input_filename, lineno); +} + + +/* this is called from expand_exception_blocks and + expand_end_catch_block to expand the toplevel finalizations for a + function. We return the first label emitted, if any, otherwise + return NULL_RTX. */ +static rtx +expand_leftover_cleanups () +{ + struct ehEntry *entry; + rtx first_label = NULL_RTX; + + while ((entry = dequeue_eh_entry (&ehqueue)) != 0) + { + if (! first_label) + first_label = entry->exception_handler_label; + emit_label (entry->exception_handler_label); + + expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); + + /* The below can be optimized away, and we could just fall into the + next EH handler, if we are certain they are nested. */ + /* Code to throw out to outer context, if we fall off end of the + handler. */ + expand_internal_throw (gen_rtx (LABEL_REF, + Pmode, + entry->end_label)); + + /* leftover try block, opps. */ + if (entry->finalization == integer_zero_node) + abort (); + + free (entry); + } + + return first_label; +} + +/* Call this to end a catch block. Its responsible for emitting the + code to handle jumping back to the correct place, and for emitting + the label to jump to if this catch block didn't match. */ +void expand_end_catch_block () +{ + rtx start_protect_label_rtx; + rtx end_protect_label_rtx; + tree decls; + struct ehEntry entry; + + if (! doing_eh (1)) + return; + + /* fall to outside the try statement when done executing handler and + we fall off end of handler. This is jump Lresume in the + documentation. */ + emit_jump (top_label_entry (&caught_return_label_stack)); + + /* We end the rethrow protection region as soon as we hit a label. */ + end_protect_label_rtx = expand_leftover_cleanups (); + + /* Code to throw out to outer context, if we get a throw from within + our catch handler. */ + /* These are saved for the exception table. */ + push_rtl_perm (); + entry.exception_handler_label = gen_label_rtx (); + pop_rtl_from_perm (); + /* This label is Lhandler in the documentation. */ + emit_label (entry.exception_handler_label); + expand_internal_throw (gen_rtx (LABEL_REF, + Pmode, + top_label_entry (&caught_return_label_stack))); + + /* No associated finalization. */ + entry.finalization = NULL_TREE; + entry.context = current_function_decl; + + if (end_protect_label_rtx == NULL_RTX) + end_protect_label_rtx = entry.exception_handler_label; + + /* Because we are emitted out of line, we have to protect this. */ + /* label for the start of the protection region. */ + start_protect_label_rtx = pop_label_entry (&false_label_stack); + + /* Cleanup the EH parameter. */ + decls = getdecls (); + expand_end_bindings (decls, decls != NULL_TREE, 0); + + /* label we emit to jump to if this catch block didn't match. */ + /* This the closing } in the `if (eq) {' of the documentation. */ + emit_label (pop_label_entry (&false_label_stack)); + + /* Because we are reordered out of line, we have to protect this. */ + entry.start_label = start_protect_label_rtx; + entry.end_label = end_protect_label_rtx; + + LABEL_PRESERVE_P (entry.start_label) = 1; + LABEL_PRESERVE_P (entry.end_label) = 1; + LABEL_PRESERVE_P (entry.exception_handler_label) = 1; + + /* These set up a call to throw the caught exception into the outer + context. */ + enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry)); +} + +/* unwind the stack. */ +static void +do_unwind (inner_throw_label) + rtx inner_throw_label; +{ +#if defined(SPARC_STACK_ALIGN) /* was sparc */ + tree fcall; + tree params; + rtx return_val_rtx; + rtx temp; + + /* call to __builtin_return_address () */ + params = tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); + fcall = build_function_call (BuiltinReturnAddress, params); + return_val_rtx = expand_expr (fcall, NULL_RTX, Pmode, 0); + /* In the return, the new pc is pc+8, as the value coming in is + really the address of the call insn, not the next insn. */ + temp = gen_reg_rtx (Pmode); + emit_move_insn (temp, inner_throw_label); + emit_move_insn (return_val_rtx, plus_constant (temp, -8)); + easy_expand_asm ("ret"); + easy_expand_asm ("restore"); + emit_barrier (); +#endif +#if defined(ARM_FRAME_RTX) /* was __arm */ + if (flag_omit_frame_pointer) + sorry ("this implementation of exception handling requires a frame pointer"); + + emit_move_insn (stack_pointer_rtx, + gen_rtx (MEM, Pmode, plus_constant (hard_frame_pointer_rtx, -8))); + emit_move_insn (hard_frame_pointer_rtx, + gen_rtx (MEM, Pmode, plus_constant (hard_frame_pointer_rtx, -12))); +#endif +#if defined(TARGET_88000) /* was m88k */ + rtx temp_frame = frame_pointer_rtx; + + temp_frame = memory_address (Pmode, temp_frame); + temp_frame = copy_to_reg (gen_rtx (MEM, Pmode, temp_frame)); + + /* hopefully this will successfully pop the frame! */ + emit_move_insn (frame_pointer_rtx, temp_frame); + emit_move_insn (stack_pointer_rtx, frame_pointer_rtx); + emit_move_insn (arg_pointer_rtx, frame_pointer_rtx); + emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode, + (HOST_WIDE_INT)m88k_debugger_offset (stack_pointer_rtx, 0)))); + +#if 0 + emit_insn (gen_add2_insn (arg_pointer_rtx, gen_rtx (CONST_INT, VOIDmode, + -(HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0)))); + + emit_move_insn (stack_pointer_rtx, arg_pointer_rtx); + + emit_insn (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode, + (HOST_WIDE_INT)m88k_debugger_offset (arg_pointer_rtx, 0)))); +#endif +#endif +#if !defined(TARGET_88000) && !defined(ARM_FRAME_RTX) && !defined(SPARC_STACK_ALIGN) + tree fcall; + tree params; + rtx return_val_rtx; + + /* call to __builtin_return_address () */ + params = tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); + fcall = build_function_call (BuiltinReturnAddress, params); + return_val_rtx = expand_expr (fcall, NULL_RTX, Pmode, 0); +#if 0 + /* I would like to do this here, but doesn't seem to work. */ + emit_move_insn (return_val_rtx, inner_throw_label); + /* So, for now, just pass throw label to stack unwinder. */ +#endif + params = tree_cons (NULL_TREE, make_tree (ptr_type_node, + inner_throw_label), NULL_TREE); + + do_function_call (Unwind, params, NULL_TREE); + assemble_external (TREE_OPERAND (Unwind, 0)); + emit_barrier (); +#endif +} + + +/* is called from expand_exception_blocks () to generate the code in a function + to "throw" if anything in the function needs to perform a throw. + + expands "throw" as the following pseudo code: + + throw: + eh = find_first_exception_match (saved_pc); + if (!eh) goto gotta_rethrow_it; + goto eh; + + gotta_rethrow_it: + saved_pc = __builtin_return_address (0); + pop_to_previous_level (); + goto throw; + + */ +void +expand_builtin_throw () +{ + tree fcall; + tree params; + rtx return_val_rtx; + rtx gotta_rethrow_it; + rtx gotta_call_terminate; + rtx unwind_and_throw; + rtx goto_unwind_and_throw; + rtx top_of_loop; + rtx unwind_first; + tree t; + + if (! doing_eh (0)) + return; + + if (! throw_used) + return; + + params = void_list_node; + t = build_parse_node (CALL_EXPR, get_identifier ("__throw"), params, NULL_TREE); + start_function (decl_tree_cons (NULL_TREE, get_identifier ("static"), + void_list_node), + t, NULL_TREE, NULL_TREE, 0); + store_parm_decls (); + pushlevel (0); + clear_last_expr (); + push_momentary (); + expand_start_bindings (0); + + gotta_rethrow_it = gen_label_rtx (); + gotta_call_terminate = gen_label_rtx (); + unwind_and_throw = gen_label_rtx (); + goto_unwind_and_throw = gen_label_rtx (); + top_of_loop = gen_label_rtx (); + unwind_first = gen_label_rtx (); + + emit_jump (unwind_first); + + emit_label (top_of_loop); + + /* search for an exception handler for the saved_pc */ + return_val_rtx = do_function_call (FirstExceptionMatch, + tree_cons (NULL_TREE, saved_pc, NULL_TREE), + ptr_type_node); + assemble_external (TREE_OPERAND (FirstExceptionMatch, 0)); + + /* did we find one? */ + emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX, + GET_MODE (return_val_rtx), 0, 0); + + /* if not, jump to gotta_rethrow_it */ + emit_jump_insn (gen_beq (gotta_rethrow_it)); + + /* we found it, so jump to it */ + emit_indirect_jump (return_val_rtx); + + /* code to deal with unwinding and looking for it again */ + emit_label (gotta_rethrow_it); + + /* call to __builtin_return_address () */ +#if defined(ARM_FRAME_RTX) /* was __arm */ +/* This replaces a 'call' to __builtin_return_address */ + return_val_rtx = gen_reg_rtx (Pmode); + emit_move_insn (return_val_rtx, gen_rtx (MEM, Pmode, plus_constant (hard_frame_pointer_rtx, -4))); +#else + params = tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); + fcall = build_function_call (BuiltinReturnAddress, params); + return_val_rtx = expand_expr (fcall, NULL_RTX, Pmode, 0); +#endif + + /* did __builtin_return_address () return a valid address? */ + emit_cmp_insn (return_val_rtx, const0_rtx, EQ, NULL_RTX, + GET_MODE (return_val_rtx), 0, 0); + + emit_jump_insn (gen_beq (gotta_call_terminate)); + +#if defined(ARM_FRAME_RTX) /* was __arm */ + /* On the ARM, '__builtin_return_address', must have 4 + subtracted from it. */ + emit_insn (gen_add2_insn (return_val_rtx, GEN_INT (-4))); + + /* If we are generating code for an ARM2/ARM3 machine or for an ARM6 in 26 bit + mode, the condition codes must be masked out of the return value, or else + they will confuse BuiltinReturnAddress. This does not apply to ARM6 and + later processors when running in 32 bit mode. */ + if (!TARGET_6) + emit_insn (gen_rtx (SET, Pmode, return_val_rtx, gen_rtx (AND, Pmode, return_val_rtx, GEN_INT (0x03fffffc)))); +#else +#if !defined(SPARC_STACK_ALIGN) /* was sparc */ + /* On the SPARC, __builtin_return_address is already -8, no need to + subtract any more from it. */ + return_val_rtx = plus_constant (return_val_rtx, -1); +#endif +#endif + + /* yes it did */ + t = build_modify_expr (saved_pc, NOP_EXPR, make_tree (ptr_type_node, return_val_rtx)); + expand_expr (t, const0_rtx, VOIDmode, 0); + + do_unwind (gen_rtx (LABEL_REF, Pmode, top_of_loop)); + emit_jump (top_of_loop); + + /* no it didn't --> therefore we need to call terminate */ + emit_label (gotta_call_terminate); + do_function_call (Terminate, NULL_TREE, NULL_TREE); + assemble_external (TREE_OPERAND (Terminate, 0)); + + { + rtx ret_val, return_val_rtx; + emit_label (unwind_first); + ret_val = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, + 0, hard_frame_pointer_rtx); + + /* Set it up so that we continue inside, at the top of the loop. */ + emit_move_insn (ret_val, gen_rtx (LABEL_REF, Pmode, top_of_loop)); +#ifdef NORMAL_RETURN_ADDR_OFFSET + return_val_rtx = plus_constant (ret_val, -NORMAL_RETURN_ADDR_OFFSET); + if (return_val_rtx != ret_val) + emit_move_insn (ret_val, return_val_rtx); +#endif + + /* Fall into epilogue to unwind prologue. */ + } + + expand_end_bindings (getdecls(), 1, 0); + poplevel (1, 0, 0); + pop_momentary (); + + finish_function (lineno, 0, 0); +} + + +void +expand_start_eh_spec () +{ + start_protect (); +} + +void +expand_end_eh_spec (raises) + tree raises; +{ + tree expr, second_try; + rtx check = gen_label_rtx (); + rtx cont; + rtx ret = gen_reg_rtx (Pmode); + rtx flag = gen_reg_rtx (TYPE_MODE (integer_type_node)); + rtx end = gen_label_rtx (); + + 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); + cont = gen_label_rtx (); + emit_move_insn (ret, gen_rtx (LABEL_REF, Pmode, cont)); + emit_jump (check); + emit_label (cont); + jumpif (make_tree (integer_type_node, flag), end); + do_function_call (Terminate, NULL_TREE, NULL_TREE); + assemble_external (TREE_OPERAND (Terminate, 0)); + emit_barrier (); + RTL_EXPR_SEQUENCE (expr) = get_insns (); + end_sequence (); + + second_try = expr; + + 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); + + cont = gen_label_rtx (); + emit_move_insn (ret, gen_rtx (LABEL_REF, Pmode, cont)); + emit_jump (check); + emit_label (cont); + jumpif (make_tree (integer_type_node, flag), end); + start_protect (); + do_function_call (Unexpected, NULL_TREE, NULL_TREE); + assemble_external (TREE_OPERAND (Unexpected, 0)); + emit_barrier (); + end_protect (second_try); + + emit_label (check); + emit_move_insn (flag, const1_rtx); + cont = gen_label_rtx (); + while (raises) + { + tree exp; + tree match_type = TREE_VALUE (raises); + + if (match_type) + { + /* check TREE_VALUE (raises) here */ + exp = saved_throw_value; + exp = tree_cons (NULL_TREE, + build_eh_type_type (match_type), + tree_cons (NULL_TREE, + saved_throw_type, + tree_cons (NULL_TREE, exp, NULL_TREE))); + exp = build_function_call (CatchMatch, exp); + assemble_external (TREE_OPERAND (CatchMatch, 0)); + + jumpif (exp, cont); + } + + raises = TREE_CHAIN (raises); + } + emit_move_insn (flag, const0_rtx); + emit_label (cont); + emit_indirect_jump (ret); + emit_label (end); + + RTL_EXPR_SEQUENCE (expr) = get_insns (); + end_sequence (); + + end_protect (expr); +} + +/* This is called to expand all the toplevel exception handling + finalization for a function. It should only be called once per + function. */ +void +expand_exception_blocks () +{ + static rtx funcend; + rtx insns; + + start_sequence (); + + funcend = gen_label_rtx (); + emit_jump (funcend); + /* expand_null_return (); */ + + start_sequence (); + + /* Add all the catch clauses here. */ + emit_insns (catch_clauses); + catch_clauses = NULL_RTX; + + expand_leftover_cleanups (); + + insns = get_insns (); + end_sequence (); + + /* Do this after we expand leftover cleanups, so that the end_protect + that expand_end_eh_spec does will match the right start_protect, + and make sure it comes out before the terminate protected region. */ + if (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))) + { + expand_end_eh_spec (TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl))); + push_to_sequence (insns); + + /* Now expand any new ones. */ + expand_leftover_cleanups (); + + insns = get_insns (); + end_sequence (); + } + + if (insns) + { + struct ehEntry entry; + + /* These are saved for the exception table. */ + push_rtl_perm (); + entry.start_label = gen_label_rtx (); + entry.end_label = gen_label_rtx (); + entry.exception_handler_label = gen_label_rtx (); + entry.finalization = TerminateFunctionCall; + entry.context = current_function_decl; + assemble_external (TREE_OPERAND (Terminate, 0)); + pop_rtl_from_perm (); + + LABEL_PRESERVE_P (entry.start_label) = 1; + LABEL_PRESERVE_P (entry.end_label) = 1; + LABEL_PRESERVE_P (entry.exception_handler_label) = 1; + + emit_label (entry.start_label); + emit_insns (insns); + + enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry)); + + emit_label (entry.exception_handler_label); + expand_expr (entry.finalization, const0_rtx, VOIDmode, 0); + emit_label (entry.end_label); + emit_barrier (); + } + + { + /* Mark the end of the stack unwinder. */ + rtx unwind_insns; + start_sequence (); + end_eh_unwinder (funcend); + expand_leftover_cleanups (); + unwind_insns = get_insns (); + end_sequence (); + if (unwind_insns) + { + insns = unwind_insns; + emit_insns (insns); + } + } + + emit_label (funcend); + + /* Only if we had previous insns do we want to emit the jump around + them. If there weren't any, then insns will remain NULL_RTX. */ + if (insns) + insns = get_insns (); + end_sequence (); + + emit_insns (insns); +} + + +/* call this to expand a throw statement. This follows the following + algorithm: + + 1. Allocate space to save the current PC onto the stack. + 2. Generate and emit a label and save its address into the + newly allocated stack space since we can't save the pc directly. + 3. If this is the first call to throw in this function: + generate a label for the throw block + 4. jump to the throw block label. */ +void +expand_throw (exp) + tree exp; +{ + rtx label; + + if (! doing_eh (1)) + return; + + /* This is the label that represents where in the code we were, when + we got an exception. This needs to be updated when we rethrow an + exception, so that the matching routine knows to search out. */ + label = gen_label_rtx (); + emit_label (label); + + if (exp) + { + tree throw_type; + tree e; + + /* throw expression */ + /* First, decay it. */ + exp = decay_conversion (exp); + + if (TREE_CODE (TREE_TYPE (exp)) == POINTER_TYPE) + { + throw_type = build_eh_type (exp); + exp = build_reinterpret_cast (ptr_type_node, exp); + } + else + { + /* Make a copy of the thrown object. WP 15.1.5 */ + exp = build_new (NULL_TREE, TREE_TYPE (exp), + build_tree_list (NULL_TREE, exp), + 0); + + if (exp == error_mark_node) + error (" in thrown expression"); + + throw_type = build_eh_type (build_indirect_ref (exp, NULL_PTR)); + } + + e = build_modify_expr (saved_throw_type, NOP_EXPR, throw_type); + expand_expr (e, const0_rtx, VOIDmode, 0); + e = build_modify_expr (saved_throw_value, NOP_EXPR, exp); + e = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (e), e); + expand_expr (e, const0_rtx, VOIDmode, 0); + } + else + { + /* rethrow current exception */ + /* This part is easy, as we don't have to do anything else. */ + } + + expand_internal_throw (gen_rtx (LABEL_REF, Pmode, label)); +} + +void +end_protect_partials () { + while (protect_list) + { + end_protect (TREE_VALUE (protect_list)); + protect_list = TREE_CHAIN (protect_list); + } +} + +int +might_have_exceptions_p () +{ + if (eh_table_output_queue.head) + return 1; + return 0; +} + +/* Output the exception table. + Return the number of handlers. */ +void +emit_exception_table () +{ + int count = 0; + extern FILE *asm_out_file; + struct ehEntry *entry; + tree eh_node_decl; + + if (! doing_eh (0)) + return; + + exception_section (); + + /* Beginning marker for table. */ + assemble_align (GET_MODE_ALIGNMENT (Pmode)); + assemble_label ("__EXCEPTION_TABLE__"); + output_exception_table_entry (asm_out_file, + const0_rtx, const0_rtx, const0_rtx); + + while (entry = dequeue_eh_entry (&eh_table_output_queue)) + { + tree context = entry->context; + + if (context && ! TREE_ASM_WRITTEN (context)) + continue; + + count++; + output_exception_table_entry (asm_out_file, + entry->start_label, entry->end_label, + entry->exception_handler_label); + } + + /* Ending marker for table. */ + assemble_label ("__EXCEPTION_END__"); + output_exception_table_entry (asm_out_file, + constm1_rtx, constm1_rtx, constm1_rtx); +} + +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); +} + +/* Build a throw expression. */ +tree +build_throw (e) + tree e; +{ + if (e != error_mark_node) + { + e = build1 (THROW_EXPR, void_type_node, e); + TREE_SIDE_EFFECTS (e) = 1; + TREE_USED (e) = 1; + } + return e; +} + +start_eh_unwinder () +{ + start_protect (); +} + +end_eh_unwinder (end) + rtx end; +{ + tree expr; + rtx return_val_rtx, ret_val, label; + + if (! doing_eh (0)) + return; + + 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 = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS, + 0, hard_frame_pointer_rtx); + return_val_rtx = copy_to_reg (ret_val); +#ifdef NORMAL_RETURN_ADDR_OFFSET + return_val_rtx = plus_constant (return_val_rtx, NORMAL_RETURN_ADDR_OFFSET-1); +#else + return_val_rtx = plus_constant (return_val_rtx, -1); +#endif + emit_move_insn (DECL_RTL (saved_pc), return_val_rtx); + +#ifdef JUMP_TO_THROW + emit_move_insn (ret_val, gen_rtx (SYMBOL_REF, Pmode, "__throw")); +#else + label = gen_label_rtx (); + emit_move_insn (ret_val, gen_rtx (LABEL_REF, Pmode, label)); +#endif + +#ifdef NORMAL_RETURN_ADDR_OFFSET + return_val_rtx = plus_constant (ret_val, -NORMAL_RETURN_ADDR_OFFSET); + if (return_val_rtx != ret_val) + emit_move_insn (ret_val, return_val_rtx); +#endif + + emit_jump (end); + +#ifndef JUMP_TO_THROW + emit_label (label); + do_function_call (Throw, NULL_TREE, NULL_TREE); +#endif + + RTL_EXPR_SEQUENCE (expr) = get_insns (); + end_sequence (); + end_protect (expr); +} |