diff options
Diffstat (limited to 'gcc/ch/except.c')
-rw-r--r-- | gcc/ch/except.c | 703 |
1 files changed, 703 insertions, 0 deletions
diff --git a/gcc/ch/except.c b/gcc/ch/except.c new file mode 100644 index 0000000..d3b3c70 --- /dev/null +++ b/gcc/ch/except.c @@ -0,0 +1,703 @@ +/* Exception support for GNU CHILL. + WARNING: Only works for native (needs setjmp.h)! FIXME! + Copyright (C) 1992, 93, 1994 Free Software Foundation, Inc. + +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. */ + +#include "config.h" +#include "tree.h" +#include "ch-tree.h" +#include "rtl.h" + +/* On Suns this can get you to the right definition if you + set the right value for TARGET. */ +#include <setjmp.h> +#ifdef sequent +/* Can you believe they forgot this? */ +#ifndef _JBLEN +#define _JBLEN 11 +#endif +#endif + +#ifndef _JBLEN +#define _JBLEN (sizeof(jmp_buf)/sizeof(int)) +#define _JBLEN_2 _JBLEN+20 +#else +/* if we use i.e. posix threads, this buffer must be longer */ +#define _JBLEN_2 _JBLEN+20 +#endif + +/* On Linux setjmp is __setjmp FIXME: what is for CROSS */ +#ifndef SETJMP_LIBRARY_NAME +#ifdef __linux__ +#define SETJMP_LIBRARY_NAME "__setjmp" +#else +#define SETJMP_LIBRARY_NAME "setjmp" +#endif +#endif + +extern int expand_exit_needed; +extern tree build_chill_exception_decl PROTO((char *)); +extern void chill_handle_case_default PROTO((void)); +extern void emit_jump PROTO((rtx)); +extern void expand_decl PROTO((tree)); +extern void fatal PROTO((char *, ...)); +extern void make_decl_rtl PROTO((tree, char *, int)); +extern void rest_of_decl_compilation PROTO((tree, char *, int, int)); + +static tree link_handler_decl; +static tree handler_link_pointer_type; +static tree unlink_handler_decl; +static int exceptions_initialized = 0; +static void emit_setup_handler PROTO((void)); +static void initialize_exceptions PROTO((void)); +static tree char_pointer_type_for_handler; + +/* If this is 1, operations to push and pop on the __exceptionStack + are inline. The default is is to use a function call, to + allow for a per-thread exception stack. */ +static int inline_exception_stack_ops = 0; + +struct handler_state +{ + struct handler_state *next; + + /* Starts at 0, then incremented for every <on-alternative>. */ + int prev_on_alternative; + + /* If > 0: handler number for ELSE handler. */ + int else_handler; + + int action_number; + + char do_pushlevel; + + tree on_alt_list; + tree setjmp_expr; + + /* A decl for the static handler array (used to map exception name to int).*/ + tree handler_array_decl; + + rtx end_label; + + /* Used to pass a tree from emit_setup_handler to chill_start_on. */ + tree handler_ref; + + tree unlink_cleanup; + + tree function; + + /* flag to indicate that we are currently compiling this handler. + is_handled will need this to determine an unhandled exception */ + int compiling; +}; + +/* This is incremented by one each time we start an action which + might have an ON-handler. It is reset between passes. */ +static int action_number = 0; + +int action_nesting_level = 0; + +/* The global_handler_list is constructed in pass 1. It is not sorted. + It contains one element for each action that actually had an ON-handler. + An element's ACTION_NUMBER matches the action_number + of that action. The global_handler_list is eaten up during pass 2. */ +#define ACTION_NUMBER(HANDLER) ((HANDLER)->action_number) +struct handler_state *global_handler_list = NULL; + +/* This is a stack of handlers, one for each nested ON-handler. */ +static struct handler_state *current_handler = NULL; + +static struct handler_state *free_handlers = NULL; /* freelist */ + +static tree handler_element_type; +static tree handler_link_type; +static tree BISJ; +static tree jbuf_ident, prev_ident, handlers_ident; +static tree exception_stack_decl = 0; + +/* Chain of cleanups assocated with exception handlers. + The TREE_PURPOSE is an INTEGER_CST whose value is the + DECL_ACTION_NESTING_LEVEL (when the handled actions was entered). + The TREE_VALUE is an expression to expand when we exit that action. */ + +static tree cleanup_chain = NULL_TREE; + +#if 0 +/* Merge the current sequence onto the tail of the previous one. */ + +void +pop_sequence () +{ + rtx sequence_first = get_insns (); + + end_sequence (); + emit_insns (sequence_first); + +} +#endif + +/* Things we need to do at the beginning of pass 2. */ + +void +except_init_pass_2 () +{ + /* First sort the global_handler_list on ACTION_NUMBER. + This will already be in close to reverse order (the exception being + nested ON-handlers), so insertion sort should essentially linear. */ + + register struct handler_state *old_list = global_handler_list; + + /* First add a dummy final element. */ + if (free_handlers) + global_handler_list = free_handlers; + else + global_handler_list + = (struct handler_state*) permalloc (sizeof (struct handler_state)); + /* Make the final dummy "larger" than any other element. */ + ACTION_NUMBER (global_handler_list) = action_number + 1; + /* Now move all the elements in old_list over to global_handler_list. */ + while (old_list != NULL) + { + register struct handler_state **ptr = &global_handler_list; + /* Unlink from old_list. */ + register struct handler_state *current = old_list; + old_list = old_list->next; + + while (ACTION_NUMBER (current) > ACTION_NUMBER (*ptr)) + ptr = &(*ptr)->next; + /* Link into proper place in global_handler_list (new list). */ + current->next = *ptr; + *ptr = current; + } + + /* Don't forget to reset action_number. */ + action_number = 0; +} + +/* This function is called at the beginning of an action that might be + followed by an ON-handler. Chill syntax doesn't let us know if + we actually have an ON-handler until we see the ON, so we save + away during pass 1 that information for use during pass 2. */ + +void +push_handler () +{ + register struct handler_state *hstate; + + action_number++; + action_nesting_level++; + + if (pass == 1) + { + if (free_handlers) + { + hstate = free_handlers; + free_handlers = hstate->next; + } + else + { + hstate = + (struct handler_state*) permalloc (sizeof (struct handler_state)); + } + + hstate->next = current_handler; + current_handler = hstate; + hstate->prev_on_alternative = 0; + hstate->else_handler = 0; + hstate->on_alt_list = NULL_TREE; + hstate->compiling = 0; + + ACTION_NUMBER (hstate) = action_number; + return; + } + + if (ACTION_NUMBER (global_handler_list) != action_number) + return; + + /* OK. This action actually has an ON-handler. + Pop it from global_handler_list, and use it. */ + + hstate = global_handler_list; + global_handler_list = hstate->next; + + /* Since this is pass 2, let's generate prologue code for that. */ + + hstate->next = current_handler; + current_handler = hstate; + + hstate->prev_on_alternative = 0; + hstate->function = current_function_decl; + + emit_setup_handler (); +} + +static tree +start_handler_array () +{ + tree handler_array_type, decl; + + push_obstacks_nochange (); + end_temporary_allocation (); + handler_array_type = build_array_type (handler_element_type, NULL_TREE); + decl = build_lang_decl (VAR_DECL, + get_unique_identifier ("handler_table"), + handler_array_type); + +/* TREE_TYPE (decl) = handler_array_type;*/ + TREE_READONLY (decl) = 1; + TREE_STATIC (decl) = 1; + DECL_INITIAL (decl) = error_mark_node; + + pushdecl (decl); + make_decl_rtl (decl, NULL_PTR, 0); + current_handler->handler_array_decl = decl; + return decl; +} + +static void +finish_handler_array () +{ + tree decl = current_handler->handler_array_decl; + tree t; + tree handler_array_init = NULL_TREE; + int handlers_count = 1; + int nelts; + + /* Build the table mapping exceptions to handler(-number)s. + This is done in reverse order. */ + + /* First push the end of the list. This is either the ELSE + handler (current_handler->else_handler>0) or NULL handler to indicate + the end of the list (if current_handler->else-handler == 0). + The following works either way. */ + handler_array_init = build_tree_list + (NULL_TREE, chill_expand_tuple + (handler_element_type, + build_nt (CONSTRUCTOR, NULL_TREE, + tree_cons (NULL_TREE, + null_pointer_node, + build_tree_list (NULL_TREE, + build_int_2 (current_handler->else_handler, + 0)))))); + + for (t = current_handler->on_alt_list; t != NULL_TREE; t = TREE_CHAIN (t)) + { tree handler_number = TREE_PURPOSE(t); + tree elist = TREE_VALUE (t); + for ( ; elist != NULL_TREE; elist = TREE_CHAIN (elist)) + { + tree ex_decl = + build_chill_exception_decl (IDENTIFIER_POINTER(TREE_VALUE(elist))); + tree ex_addr = build1 (ADDR_EXPR, + char_pointer_type_for_handler, + ex_decl); + tree el = build_nt (CONSTRUCTOR, NULL_TREE, + tree_cons (NULL_TREE, + ex_addr, + build_tree_list (NULL_TREE, + handler_number))); + mark_addressable (ex_decl); + TREE_CONSTANT (ex_addr) = 1; + handler_array_init = + tree_cons (NULL_TREE, + chill_expand_tuple (handler_element_type, el), + handler_array_init); + handlers_count++; + } + } + +#if 1 + nelts = list_length (handler_array_init); + TYPE_DOMAIN (TREE_TYPE (decl)) + = build_index_type (build_int_2 (nelts - 1, - (nelts == 0))); + layout_type (TREE_TYPE (decl)); + DECL_INITIAL (decl) + = convert (TREE_TYPE (decl), + build_nt (CONSTRUCTOR, NULL_TREE, handler_array_init)); + + /* Pop back to the obstack that is current for this binding level. + This is because MAXINDEX, rtl, etc. to be made below + must go in the permanent obstack. But don't discard the + temporary data yet. */ + pop_obstacks (); + layout_decl (decl, 0); + /* To prevent make_decl_rtl (called indiectly by rest_of_decl_compilation) + throwing the existing RTL (which has already been used). */ + PUT_MODE (DECL_RTL (decl), DECL_MODE (decl)); + rest_of_decl_compilation (decl, (char*)0, 0, 0); + expand_decl_init (decl); +#else + /* To prevent make_decl_rtl (called indirectly by finish_decl) + altering the existing RTL. */ + GET_MODE (DECL_RTL (current_handler->handler_array_decl)) = + DECL_MODE (current_handler->handler_array_decl); + + finish_decl (current_handler->handler_array_decl, + build_nt (CONSTRUCTOR, NULL_TREE, handler_array_init), + NULL_TREE); +#endif +} + + +void +pop_handler (used) + int used; +{ + action_nesting_level--; + if (pass == 1) + { + struct handler_state *old = current_handler; + if (old == NULL) + fatal ("internal error: on stack out of sync"); + current_handler = old->next; + + if (used) + { /* Push unto global_handler_list. */ + old->next = global_handler_list; + global_handler_list = old; + } + else + { + /* Push onto free_handlers free list. */ + old->next = free_handlers; + free_handlers = old; + } + } + else if (used) + { + current_handler = current_handler->next; + } +} + +/* Emit code before an action that has an ON-handler. */ + +static void +emit_setup_handler () +{ + tree handler_decl, handler_addr, t; + + /* Field references. */ + tree jbuf_ref, handlers_ref,prev_ref; + if (!exceptions_initialized) + { + /* We temporarily reset the maximum_field_alignment to zero so the + compiler's exception data structures can be compatible with the + run-time system, even when we're compiling with -fpack. */ + extern int maximum_field_alignment; + int save_maximum_field_alignment = maximum_field_alignment; + maximum_field_alignment = 0; + push_obstacks_nochange (); + end_temporary_allocation (); + initialize_exceptions (); + pop_obstacks (); + maximum_field_alignment = save_maximum_field_alignment; + } + + push_momentary (); + + handler_decl = build_lang_decl (VAR_DECL, + get_unique_identifier ("handler"), + handler_link_type); + push_obstacks_nochange (); + pushdecl(handler_decl); + expand_decl (handler_decl); + finish_decl (handler_decl); + + jbuf_ref = build_component_ref (handler_decl, jbuf_ident); + jbuf_ref = build_chill_arrow_expr (jbuf_ref, 1); + handlers_ref = build_component_ref (handler_decl, handlers_ident); + prev_ref = build_component_ref (handler_decl, prev_ident); + + /* Emit code to link in handler in __exceptionStack chain. */ + mark_addressable (handler_decl); + handler_addr = build1 (ADDR_EXPR, handler_link_pointer_type, handler_decl); + if (inline_exception_stack_ops) + { + expand_expr_stmt (build_chill_modify_expr (prev_ref, + exception_stack_decl)); + expand_expr_stmt (build_chill_modify_expr (exception_stack_decl, + handler_addr)); + current_handler->handler_ref = prev_ref; + } + else + { + expand_expr_stmt (build_chill_function_call (link_handler_decl, + build_tree_list (NULL_TREE, + handler_addr))); + current_handler->handler_ref = handler_addr; + } + + /* Expand: handler->__handlers = { <<array mapping names to ints } */ + t = build1 (NOP_EXPR, build_pointer_type (handler_element_type), + build_chill_arrow_expr (start_handler_array (), 1)); + expand_expr_stmt (build_chill_modify_expr (handlers_ref, t)); + + /* Emit code to unlink handler. */ + if (inline_exception_stack_ops) + current_handler->unlink_cleanup + = build_chill_modify_expr (exception_stack_decl, + current_handler->handler_ref); + else + current_handler->unlink_cleanup + = build_chill_function_call (unlink_handler_decl, + build_tree_list(NULL_TREE, + current_handler->handler_ref)); + cleanup_chain = tree_cons (build_int_2 (action_nesting_level, 0), + current_handler->unlink_cleanup, + cleanup_chain); + + /* Emit code for setjmp. */ + + current_handler->setjmp_expr = + build_chill_function_call (BISJ, build_tree_list (NULL_TREE, jbuf_ref)); + expand_start_case (1, current_handler->setjmp_expr, + integer_type_node, "on handler"); + + chill_handle_case_label (integer_zero_node, current_handler->setjmp_expr); +} + +/* Start emitting code for: <actions> ON <handlers> END. + Assume we've parsed <actions>, and the setup needed for it. */ + +void +chill_start_on () +{ + expand_expr_stmt (current_handler->unlink_cleanup); + + /* Emit code to jump past the handlers. */ + current_handler->end_label = gen_label_rtx (); + current_handler->compiling = 1; + emit_jump (current_handler->end_label); +} + +void +chill_finish_on () +{ + expand_end_case (current_handler->setjmp_expr); + + finish_handler_array (); + + emit_label (current_handler->end_label); + + pop_momentary (); + + cleanup_chain = TREE_CHAIN (cleanup_chain); +} + +void +chill_handle_on_labels (labels) + tree labels; +{ + int alternative = ++current_handler->prev_on_alternative; + if (pass == 1) + { + tree handler_number = build_int_2 (alternative, 0); + current_handler->on_alt_list = + tree_cons (handler_number, labels, current_handler->on_alt_list); + } + else + { + /* Find handler_number saved in pass 1. */ + tree tmp = current_handler->on_alt_list; + while (TREE_INT_CST_LOW (TREE_PURPOSE (tmp)) != alternative) + tmp = TREE_CHAIN (tmp); + if (expand_exit_needed) + expand_exit_something (), expand_exit_needed = 0; + chill_handle_case_label (TREE_PURPOSE (tmp), + current_handler->setjmp_expr); + } +} + +void +chill_start_default_handler () +{ + current_handler->else_handler = ++current_handler->prev_on_alternative; + if (!ignoring) + { + chill_handle_case_default (); + } +} + +void +chill_check_no_handlers () +{ + if (current_handler != NULL) + fatal ("internal error: on stack not empty when done"); +} + +static void +initialize_exceptions () +{ + tree jmp_buf_type = build_array_type (integer_type_node, + build_index_type (build_int_2 (_JBLEN_2-1, 0))); + tree setjmp_fndecl, link_ftype; + tree parmtypes + = tree_cons (NULL_TREE, build_pointer_type (jmp_buf_type), void_list_node); + + setjmp_fndecl = builtin_function ("setjmp", + build_function_type (integer_type_node, + parmtypes), + NOT_BUILT_IN, + SETJMP_LIBRARY_NAME); + BISJ = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (setjmp_fndecl)), + setjmp_fndecl); + + char_pointer_type_for_handler + = build_pointer_type (build_type_variant (char_type_node, 1, 0)); + handler_element_type = + build_chill_struct_type (chainon + (build_decl (FIELD_DECL, + get_identifier("__exceptid"), + char_pointer_type_for_handler), + build_decl (FIELD_DECL, + get_identifier("__handlerno"), + integer_type_node))); + + jbuf_ident = get_identifier("__jbuf"); + prev_ident = get_identifier("__prev"); + handlers_ident = get_identifier("__handlers"); + + handler_link_type = + build_chill_struct_type + (chainon + (build_decl (FIELD_DECL, prev_ident, ptr_type_node), + chainon + (build_decl (FIELD_DECL, handlers_ident, + build_pointer_type (handler_element_type)), + build_decl (FIELD_DECL, jbuf_ident, jmp_buf_type)))); + + handler_link_pointer_type = build_pointer_type (handler_link_type); + + if (inline_exception_stack_ops) + { + exception_stack_decl = + build_lang_decl (VAR_DECL, + get_identifier("__exceptionStack"), + handler_link_pointer_type); + TREE_STATIC (exception_stack_decl) = 1; + TREE_PUBLIC (exception_stack_decl) = 1; + DECL_EXTERNAL (exception_stack_decl) = 1; + push_obstacks_nochange (); + pushdecl(exception_stack_decl); + make_decl_rtl (exception_stack_decl, NULL_PTR, 1); + finish_decl (exception_stack_decl); + } + + link_ftype = build_function_type (void_type_node, + tree_cons (NULL_TREE, + handler_link_pointer_type, + void_list_node)); + link_handler_decl = builtin_function ("__ch_link_handler", link_ftype, + NOT_BUILT_IN, NULL_PTR); + unlink_handler_decl = builtin_function ("__ch_unlink_handler", link_ftype, + NOT_BUILT_IN, NULL_PTR); + + exceptions_initialized = 1; +} + +/* Do the cleanup(s) needed for a GOTO label. + We only need to do the last of the cleanups. */ + +void +expand_goto_except_cleanup (label_level) + int label_level; +{ + tree list = cleanup_chain; + tree last = NULL_TREE; + for ( ; list != NULL_TREE; list = TREE_CHAIN (list)) + { + if (TREE_INT_CST_LOW (TREE_PURPOSE (list)) > label_level) + last = list; + else + break; + } + if (last) + expand_expr_stmt (TREE_VALUE (last)); +} + +/* Returns true if there is a valid handler for EXCEPT_NAME + in the current static scope. + 0 ... no handler found + 1 ... local handler available + 2 ... function may propagate this exception +*/ + +int +is_handled (except_name) + tree except_name; +{ + tree t; + struct handler_state *h = current_handler; + + /* if we are are currently compiling this handler + we have to start at the next level */ + if (h && h->compiling) + h = h->next; + while (h != NULL) + { + if (h->function != current_function_decl) + break; + if (h->else_handler > 0) + return 1; + for (t = h->on_alt_list; t != NULL_TREE; t = TREE_CHAIN (t)) + { + if (value_member (except_name, TREE_VALUE (t))) + return 1; + } + h = h->next; + } + + t = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (current_function_decl)); + + if (value_member (except_name, t)) + return 2; + return 0; +} + +/* function generates code to reraise exceptions + for PROC's propagating exceptions. */ + +void +chill_reraise_exceptions (exceptions) + tree exceptions; +{ + tree wrk; + + if (exceptions == NULL_TREE) + return; /* just in case */ + + if (pass == 1) + { + for (wrk = exceptions; wrk != NULL_TREE; wrk = TREE_CHAIN (wrk)) + chill_handle_on_labels (build_tree_list (NULL_TREE, TREE_VALUE (wrk))); + } + else /* pass == 2 */ + { + chill_start_on (); + expand_exit_needed = 0; + + for (wrk = exceptions; wrk != NULL_TREE; wrk = TREE_CHAIN (wrk)) + { + chill_handle_on_labels (TREE_VALUE (wrk)); + /* do a CAUSE exception */ + expand_expr_stmt (build_cause_exception (TREE_VALUE (wrk), 0)); + expand_exit_needed = 1; + } + chill_finish_on (); + } + pop_handler (1); +} |