diff options
Diffstat (limited to 'gnu/usr.bin/cc/cc1plus/except.c')
-rw-r--r-- | gnu/usr.bin/cc/cc1plus/except.c | 1481 |
1 files changed, 1481 insertions, 0 deletions
diff --git a/gnu/usr.bin/cc/cc1plus/except.c b/gnu/usr.bin/cc/cc1plus/except.c new file mode 100644 index 0000000..dc91b9d --- /dev/null +++ b/gnu/usr.bin/cc/cc1plus/except.c @@ -0,0 +1,1481 @@ +/* Handle exceptional things in C++. + Copyright (C) 1989, 1992, 1993, 1994 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, 675 Mass Ave, Cambridge, MA 02139, 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" + +extern void (*interim_eh_hook) PROTO((tree)); + +/* holds the fndecl for __builtin_return_address () */ +tree builtin_return_address_fndecl; + +/* Define at your own risk! */ +#ifndef CROSS_COMPILE +#ifdef sun +#ifdef sparc +#define TRY_NEW_EH +#endif +#endif +#endif + +#ifndef TRY_NEW_EH + +static void +sorry_no_eh () +{ + static int warned = 0; + if (! warned) + { + sorry ("exception handling not supported"); + warned = 1; + } +} + +void +build_exception_table () +{ +} + +void +expand_exception_blocks () +{ +} + +void +start_protect () +{ +} + +void +end_protect (finalization) + tree finalization; +{ +} + +void +expand_start_try_stmts () +{ + sorry_no_eh (); +} + +void +expand_end_try_stmts () +{ +} + +void +expand_start_all_catch () +{ +} + +void +expand_end_all_catch () +{ +} + +void +expand_start_catch_block (declspecs, declarator) + tree declspecs, declarator; +{ +} + +void +expand_end_catch_block () +{ +} + +void +init_exception_processing () +{ +} + +void +expand_throw (exp) + tree exp; +{ + sorry_no_eh (); +} + +#else + +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 Stroustrup's 2nd edition. +It is a complete rewrite of all the EH stuff that was here before + Shortcomings: + 1. The type of the throw and catch must still match + exactly (no support yet for matching base classes) + 2. Throw specifications of functions still doesnt't work. + Cool Things: + 1. Destructors are called properly :-) + 2. No overhead for the non-exception thrown case. + 3. Fixing shortcomings 1 and 2 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]; + + fprintf (file, "\t%s\t ", ASM_LONG); + if (GET_CODE (start_label) == CODE_LABEL) + { + ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (start_label)); + assemble_name (file, label); + } + else if (GET_CODE (start_label) == SYMBOL_REF) + { + fprintf (stderr, "YYYYYYYYYEEEEEEEESSSSSSSSSSSS!!!!!!!!!!\n"); + assemble_name (file, XSTR (start_label, 0)); + } + putc ('\n', file); + + fprintf (file, "\t%s\t ", ASM_LONG); + ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (end_label)); + assemble_name (file, label); + putc ('\n', file); + + fprintf (file, "\t%s\t ", ASM_LONG); + ASM_GENERATE_INTERNAL_LABEL (label, "L", CODE_LABEL_NUMBER (eh_label)); + assemble_name (file, label); + putc ('\n', file); + + putc ('\n', file); /* blank line */ +} + +static void +easy_expand_asm (str) + char *str; +{ + expand_asm (build_string (strlen (str)+1, str)); +} + +/* unwind the stack. */ +static void +do_unwind (throw_label) + rtx throw_label; +{ +#ifdef sparc + extern FILE *asm_out_file; + 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, SImode, 0); + /* In the return, the new pc is pc+8, as the value comming in is + really the address of the call insn, not the next insn. */ + emit_move_insn (return_val_rtx, plus_constant(gen_rtx (LABEL_REF, + Pmode, + throw_label), -8)); + /* We use three values, PC, type, and value */ + easy_expand_asm ("st %l0,[%fp]"); + easy_expand_asm ("st %l1,[%fp+4]"); + easy_expand_asm ("st %l2,[%fp+8]"); + easy_expand_asm ("ret"); + easy_expand_asm ("restore"); + emit_barrier (); +#endif +#if 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 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 (".gcc_except_table"); +#else + text_section (); +#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 have a couple of routines "get_last_insn ()" and + "set_last_insn ()" provided. When the start of a catch block is + encountered, we save a pointer to the last insn generated. After + the catch block is generated, we save a pointer to the first + catch block insn and the last catch block insn with the routines + "NEXT_INSN ()" and "get_last_insn ()". We then set the last insn + to be the last insn generated before the catch block, and set the + NEXT_INSN (last_insn) to zero. + + Since catch blocks might be nested inside other catch blocks, and + we munge the chain of generated insns after the catch block is + generated, we need to store the pointers to the last insn + generated in a stack, so that when the end of a catch block is + encountered, the last insn before the current catch block can be + popped and set to be the last insn, and the first and last insns + of the catch block just generated can be enqueue'd for output at + a later time. + + 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; + +/* 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; + }; + +struct ehNode { + struct ehEntry *entry; + struct ehNode *chain; + }; + +struct ehStack { + struct ehNode *top; + }; + +struct ehQueue { + struct ehNode *head; + struct ehNode *tail; + }; + +struct exceptNode { + rtx catchstart; + rtx catchend; + + struct exceptNode *chain; + }; + +struct exceptStack { + struct exceptNode *top; + }; +/* ========================================================================= */ + + + +/* 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" */ +rtx saved_pc; +/* Holds the type of the thing being thrown. */ +rtx saved_throw_type; +/* Holds the value being thrown. */ +rtx saved_throw_value; + +rtx throw_label; + +static struct ehStack ehstack; +static struct ehQueue ehqueue; +static struct ehQueue eh_table_output_queue; +static struct exceptStack exceptstack; +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 void push_except_stmts PROTO((struct exceptStack *exceptstack, + rtx catchstart, rtx catchend)); +static int pop_except_stmts PROTO((struct exceptStack *exceptstack, + rtx *catchstart, rtx *catchend)); +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 new_except_stack PROTO((struct exceptStack *queue)); +static void push_last_insn PROTO(()); +static rtx pop_last_insn PROTO(()); +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; +} + +static void +push_except_stmts (exceptstack, catchstart, catchend) + struct exceptStack *exceptstack; + rtx catchstart, catchend; +{ + struct exceptNode *newnode = (struct exceptNode*) + xmalloc (sizeof (struct exceptNode)); + + newnode->catchstart = catchstart; + newnode->catchend = catchend; + newnode->chain = exceptstack->top; + + exceptstack->top = newnode; +} + +static int +pop_except_stmts (exceptstack, catchstart, catchend) + struct exceptStack *exceptstack; + rtx *catchstart, *catchend; +{ + struct exceptNode *tempnode; + + if (!exceptstack->top) { + *catchstart = *catchend = NULL_RTX; + return 0; + } + + tempnode = exceptstack->top; + exceptstack->top = exceptstack->top->chain; + + *catchstart = tempnode->catchstart; + *catchend = tempnode->catchend; + free (tempnode); + + return 1; +} + +/* 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 (); + + entry->finalization = NULL_TREE; + + 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; +} + +static void +new_except_stack (stack) + struct exceptStack *stack; +{ + stack->top = NULL; +} +/* ========================================================================= */ + +void +lang_interim_eh (finalization) + tree finalization; +{ + if (finalization) + end_protect (finalization); + else + start_protect (); +} + +/* 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 + - Initializing the exceptstack +*/ + +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 temp, PFV; + + interim_eh_hook = lang_interim_eh; + + /* void (*)() */ + 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 () */ + temp = tree_cons (NULL_TREE, PFV, void_list_node); + + push_lang_context (lang_name_c); + + set_terminate_fndecl = + define_function ("set_terminate", + build_function_type (PFV, temp), + NOT_BUILT_IN, + pushdecl, + 0); + set_unexpected_fndecl = + define_function ("set_unexpected", + build_function_type (PFV, temp), + NOT_BUILT_IN, + pushdecl, + 0); + + unexpected_fndecl = + define_function ("unexpected", + build_function_type (void_type_node, void_list_node), + NOT_BUILT_IN, + pushdecl, + 0); + terminate_fndecl = + define_function ("terminate", + build_function_type (void_type_node, void_list_node), + NOT_BUILT_IN, + pushdecl, + 0); + catch_match_fndecl = + define_function ("__throw_type_match", + build_function_type (integer_type_node, + tree_cons (NULL_TREE, string_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); + + 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); + BuiltinReturnAddress = default_conversion (builtin_return_address_fndecl); + + TerminateFunctionCall = build_function_call (Terminate, NULL_TREE); + + pop_lang_context (); + throw_label = gen_label_rtx (); + saved_pc = gen_rtx (REG, Pmode, 16); + saved_throw_type = gen_rtx (REG, Pmode, 17); + saved_throw_value = gen_rtx (REG, Pmode, 18); + + new_eh_queue (&ehqueue); + new_eh_queue (&eh_table_output_queue); + new_eh_stack (&ehstack); + new_except_stack (&exceptstack); +} + +/* call this to begin a block of unwind protection (ie: when an object is + constructed) */ +void +start_protect () +{ + if (doing_eh (0)) + { + 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 = pop_eh_entry (&ehstack); + + if (! doing_eh (0)) + return; + + emit_label (entry->end_label); + + 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)) + { + start_protect (); + } +} + +void +expand_end_try_stmts () +{ + end_protect (integer_zero_node); +} + +struct insn_save_node { + rtx last; + struct insn_save_node *chain; + }; + +static struct insn_save_node *InsnSave = NULL; + + +/* Used to keep track of where the catch blocks start. */ +static void +push_last_insn () +{ + struct insn_save_node *newnode = (struct insn_save_node*) + xmalloc (sizeof (struct insn_save_node)); + + newnode->last = get_last_insn (); + newnode->chain = InsnSave; + InsnSave = newnode; +} + +/* Use to keep track of where the catch blocks start. */ +static rtx +pop_last_insn () +{ + struct insn_save_node *tempnode; + rtx temprtx; + + if (!InsnSave) return NULL_RTX; + + tempnode = InsnSave; + temprtx = tempnode->last; + InsnSave = InsnSave->chain; + + free (tempnode); + + return temprtx; +} + +/* 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. */ + emit_label (label); + + push_label_entry (&caught_return_label_stack, label); + + /* Remember where we started. */ + push_last_insn (); + + emit_insn (gen_nop ()); + + /* Will this help us not stomp on it? */ + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type)); + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value)); + + 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; + + free (entry); + } + + /* This goes when the below moves out of our way. */ +#if 1 + label = gen_label_rtx (); + emit_jump (label); +#endif + + /* All this should be out of line, and saved back in the exception handler + block area. */ +#if 1 + entry->start_label = entry->exception_handler_label; + /* These are saved for the exception table. */ + push_rtl_perm (); + entry->end_label = gen_label_rtx (); + entry->exception_handler_label = gen_label_rtx (); + entry->finalization = TerminateFunctionCall; + pop_rtl_from_perm (); + emit_label (entry->end_label); + + enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (entry)); + + /* After running the finalization, continue on out to the next + cleanup, if we have nothing better to do. */ + emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry->end_label)); + /* Will this help us not stomp on it? */ + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type)); + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value)); + emit_jump (throw_label); + emit_label (entry->exception_handler_label); + expand_expr (entry->finalization, const0_rtx, VOIDmode, 0); + emit_barrier (); +#endif + emit_label (label); +} + +/* call this to end processing of all the catch blocks. */ +void +expand_end_all_catch () +{ + rtx catchstart, catchend, last; + rtx label; + + if (! doing_eh (1)) + return; + + /* Find the start of the catch block. */ + last = pop_last_insn (); + catchstart = NEXT_INSN (last); + catchend = get_last_insn (); + + NEXT_INSN (last) = 0; + set_last_insn (last); + + /* 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); + + push_except_stmts (&exceptstack, catchstart, catchend); + + /* Here we fall through into the continuation code. */ +} + + +/* this is called from expand_exception_blocks () to expand the toplevel + finalizations for a function. */ +void +expand_leftover_cleanups () +{ + struct ehEntry *entry; + rtx first_label = NULL_RTX; + + if (! doing_eh (0)) + return; + + /* Will this help us not stomp on it? */ + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type)); + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value)); + + 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); + + /* leftover try block, opps. */ + if (entry->finalization == integer_zero_node) + abort (); + + free (entry); + } + if (first_label) + { + rtx label; + struct ehEntry entry; + /* These are saved for the exception table. */ + push_rtl_perm (); + label = gen_label_rtx (); + entry.start_label = first_label; + entry.end_label = label; + entry.exception_handler_label = gen_label_rtx (); + entry.finalization = TerminateFunctionCall; + pop_rtl_from_perm (); + emit_label (label); + + enqueue_eh_entry (&eh_table_output_queue, copy_eh_entry (&entry)); + + /* After running the finalization, continue on out to the next + cleanup, if we have nothing better to do. */ + emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, entry.end_label)); + /* Will this help us not stomp on it? */ + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_type)); + emit_insn (gen_rtx (USE, VOIDmode, saved_throw_value)); + emit_jump (throw_label); + emit_label (entry.exception_handler_label); + expand_expr (entry.finalization, const0_rtx, VOIDmode, 0); + emit_barrier (); + } +} + +/* 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 type; + tree decl; + tree init; + + if (! doing_eh (1)) + return; + + /* Create a binding level for the parm. */ + expand_start_bindings (0); + + if (declspecs) + { + tree init_type; + decl = grokdeclarator (declarator, declspecs, NORMAL, 1, NULL_TREE); + + /* Figure out the type that the initializer is. */ + init_type = TREE_TYPE (decl); + if (TREE_CODE (init_type) != REFERENCE_TYPE) + init_type = build_reference_type (init_type); + + init = convert_from_reference (save_expr (make_tree (init_type, saved_throw_value))); + + /* Do we need the below two lines? */ + /* Let `finish_decl' know that this initializer is ok. */ + DECL_INITIAL (decl) = init; + /* This needs to be preallocated under the try block, + in a union of all catch variables. */ + pushdecl (decl); + type = TREE_TYPE (decl); + + /* peel back references, so they match. */ + if (TREE_CODE (type) == REFERENCE_TYPE) + type = TREE_TYPE (type); + } + else + type = NULL_TREE; + + false_label_rtx = gen_label_rtx (); + push_label_entry (&false_label_stack, false_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, protect_label_rtx); + + if (type) + { + tree params; + char *typestring; + rtx call_rtx, return_value_rtx; + tree catch_match_fcall; + tree catchmatch_arg, argval; + + typestring = build_overload_name (type, 1, 1); + + params = tree_cons (NULL_TREE, + combine_strings (build_string (strlen (typestring)+1, typestring)), + tree_cons (NULL_TREE, + make_tree (ptr_type_node, saved_throw_type), + NULL_TREE)); + catch_match_fcall = build_function_call (CatchMatch, params); + call_rtx = expand_call (catch_match_fcall, NULL_RTX, 0); + + return_value_rtx = + hard_function_value (integer_type_node, catch_match_fcall); + + /* did the throw type match function return TRUE? */ + emit_cmp_insn (return_value_rtx, const0_rtx, NE, 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_bne (false_label_rtx)); + finish_decl (decl, init, NULL_TREE, 0); + } + 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); +} + + +/* 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 () +{ + if (doing_eh (1)) + { + rtx start_protect_label_rtx; + rtx end_protect_label_rtx; + tree decls; + struct ehEntry entry; + + /* label we jump to if we caught the exception */ + emit_jump (top_label_entry (&caught_return_label_stack)); + + /* Code to throw out to outer context, if we get an 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 (); + emit_label (entry.exception_handler_label); + emit_move_insn (saved_pc, gen_rtx (LABEL_REF, + Pmode, + top_label_entry (&caught_return_label_stack))); + emit_jump (throw_label); + /* No associated finalization. */ + entry.finalization = NULL_TREE; + + /* Because we are reordered 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 paramater. */ + expand_end_bindings (decls = getdecls (), decls != NULL_TREE, 0); + + /* label we emit to jump to if this catch block didn't match. */ + emit_label (end_protect_label_rtx = 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; + + /* 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)); + } +} + +/* 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; +} + + +/* is called from expand_excpetion_blocks () to generate the code in a function + to "throw" if anything in the function needs to preform a throw. + + expands "throw" as the following psuedo 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; + + */ +static void +expand_builtin_throw () +{ + tree fcall; + tree params; + rtx return_val_rtx; + rtx gotta_rethrow_it = gen_label_rtx (); + rtx gotta_call_terminate = gen_label_rtx (); + rtx unwind_and_throw = gen_label_rtx (); + rtx goto_unwind_and_throw = gen_label_rtx (); + + emit_label (throw_label); + + /* search for an exception handler for the saved_pc */ + return_val_rtx = do_function_call (FirstExceptionMatch, + tree_cons (NULL_TREE, make_tree (ptr_type_node, saved_pc), NULL_TREE), + ptr_type_node); + + /* 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 () */ + params=tree_cons (NULL_TREE, integer_zero_node, NULL_TREE); + fcall = build_function_call (BuiltinReturnAddress, params); + return_val_rtx = expand_expr (fcall, NULL_RTX, SImode, 0); + + /* 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)); + + /* yes it did */ + emit_move_insn (saved_pc, return_val_rtx); + do_unwind (throw_label); + emit_jump (throw_label); + + /* no it didn't --> therefore we need to call terminate */ + emit_label (gotta_call_terminate); + do_function_call (Terminate, NULL_TREE, NULL_TREE); +} + + +/* 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 () +{ + rtx catchstart, catchend; + rtx last; + static rtx funcend; + + funcend = gen_label_rtx (); + emit_jump (funcend); + /* expand_null_return (); */ + + while (pop_except_stmts (&exceptstack, &catchstart, &catchend)) { + last = get_last_insn (); + NEXT_INSN (last) = catchstart; + PREV_INSN (catchstart) = last; + NEXT_INSN (catchend) = 0; + set_last_insn (catchend); + } + + expand_leftover_cleanups (); + + { + static int have_done = 0; + if (! have_done && TREE_PUBLIC (current_function_decl) + && ! DECL_INLINE (current_function_decl)) + { + have_done = 1; + expand_builtin_throw (); + } + } + emit_label (funcend); +} + + +/* 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 allocate 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; + tree type; + + 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); + emit_move_insn (saved_pc, gen_rtx (LABEL_REF, Pmode, label)); + + if (exp) + { + /* throw expression */ + /* First, decay it. */ + exp = default_conversion (exp); + type = TREE_TYPE (exp); + + { + char *typestring = build_overload_name (type, 1, 1); + tree throw_type = build1 (ADDR_EXPR, ptr_type_node, combine_strings (build_string (strlen (typestring)+1, typestring))); + rtx throw_type_rtx = expand_expr (throw_type, NULL_RTX, VOIDmode, 0); + rtx throw_value_rtx; + + emit_move_insn (saved_throw_type, throw_type_rtx); + exp = convert_to_reference (build_reference_type (build_type_variant (TREE_TYPE (exp), 1, 0)), exp, CONV_STATIC, LOOKUP_COMPLAIN, NULL_TREE); + if (exp == error_mark_node) + error (" in thrown expression"); + throw_value_rtx = expand_expr (build_unary_op (ADDR_EXPR, exp, 0), NULL_RTX, VOIDmode, 0); + emit_move_insn (saved_throw_value, throw_value_rtx); + } + } + else + { + /* rethrow current exception */ + /* This part is easy, as we dont' have to do anything else. */ + } + + emit_jump (throw_label); +} + + +/* output the exception table */ +void +build_exception_table () +{ + extern FILE *asm_out_file; + struct ehEntry *entry; + + if (! doing_eh (0)) + return; + + exception_section (); + + /* Beginning marker for table. */ + fprintf (asm_out_file, " .global ___EXCEPTION_TABLE__\n"); + fprintf (asm_out_file, " .align 4\n"); + fprintf (asm_out_file, "___EXCEPTION_TABLE__:\n"); + fprintf (asm_out_file, " .word 0, 0, 0\n"); + + while (entry = dequeue_eh_entry (&eh_table_output_queue)) { + output_exception_table_entry (asm_out_file, + entry->start_label, entry->end_label, entry->exception_handler_label); + } + + /* Ending marker for table. */ + fprintf (asm_out_file, " .global ___EXCEPTION_END__\n"); + fprintf (asm_out_file, "___EXCEPTION_END__:\n"); + fprintf (asm_out_file, " .word -1, -1, -1\n"); +} + +/* end of: my-cp-except.c */ +#endif + + +/* Build a throw expression. */ +tree +build_throw (e) + tree e; +{ + e = build1 (THROW_EXPR, void_type_node, e); + TREE_SIDE_EFFECTS (e) = 1; + return e; +} |