X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fjava%2Fexcept.c;h=048a92fc80220a6278f72b4e90774ad2fba19ca4;hb=72a6b07c4c8afddb3a5b5db4b1334a65dd713abf;hp=258bbc0b2a15ed9100c7a94d3b5fb08dfecae0e1;hpb=d7c47c0ef0309b9b24b450072b3fbbebf52f7b5a;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/java/except.c b/gcc/java/except.c index 258bbc0b2a1..048a92fc802 100644 --- a/gcc/java/except.c +++ b/gcc/java/except.c @@ -1,22 +1,23 @@ /* Handle exceptions for GNU compiler for the Java(TM) language. - Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + Copyright (C) 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. -This file is part of GNU CC. +This file is part of GCC. -GNU CC is free software; you can redistribute it and/or modify +GCC 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, +GCC 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. +along with GCC; see the file COPYING. If not, write to +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. @@ -24,6 +25,8 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "config.h" #include "system.h" +#include "coretypes.h" +#include "tm.h" #include "tree.h" #include "real.h" #include "rtl.h" @@ -34,25 +37,18 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "function.h" #include "except.h" #include "java-except.h" -#include "eh-common.h" #include "toplev.h" -static void expand_start_java_handler PARAMS ((struct eh_range *)); -static void expand_end_java_handler PARAMS ((struct eh_range *)); -static struct eh_range *find_handler_in_range PARAMS ((int, struct eh_range *, - struct eh_range *)); -static void link_handler PARAMS ((struct eh_range *, struct eh_range *)); -static void check_start_handlers PARAMS ((struct eh_range *, int)); -static void free_eh_ranges PARAMS ((struct eh_range *range)); - -extern struct obstack permanent_obstack; +static void expand_start_java_handler (struct eh_range *); +static struct eh_range *find_handler_in_range (int, struct eh_range *, + struct eh_range *); +static void check_start_handlers (struct eh_range *, int); +static void free_eh_ranges (struct eh_range *range); struct eh_range *current_method_handlers; struct eh_range *current_try_block = NULL; -struct eh_range *eh_range_freelist = NULL; - /* These variables are used to speed up find_handler. */ static int cache_range_start, cache_range_end; @@ -63,12 +59,60 @@ static struct eh_range *cache_next_child; struct eh_range whole_range; +/* Check the invariants of the structure we're using to contain + exception regions. Either returns true or fails an assertion + check. */ + +bool +sanity_check_exception_range (struct eh_range *range) +{ + struct eh_range *ptr = range->first_child; + for (; ptr; ptr = ptr->next_sibling) + { + gcc_assert (ptr->outer == range + && ptr->end_pc > ptr->start_pc); + if (ptr->next_sibling) + gcc_assert (ptr->next_sibling->start_pc >= ptr->end_pc); + gcc_assert (ptr->start_pc >= ptr->outer->start_pc + && ptr->end_pc <= ptr->outer->end_pc); + (void) sanity_check_exception_range (ptr); + } + return true; +} + #if defined(DEBUG_JAVA_BINDING_LEVELS) -int binding_depth; -int is_class_level; -int current_pc; -extern void indent (); +extern int is_class_level; +extern int current_pc; +extern int binding_depth; +extern void indent (void); +static void +print_ranges (struct eh_range *range) +{ + if (! range) + return; + + struct eh_range *child = range->first_child; + + indent (); + fprintf (stderr, "handler pc %d --> %d ", range->start_pc, range->end_pc); + + tree handler = range->handlers; + for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler)) + { + tree type = TREE_PURPOSE (handler); + if (type == NULL) + type = throwable_type_node; + fprintf (stderr, " type=%s ", IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)))); + } + fprintf (stderr, "\n"); + + int saved = binding_depth; + binding_depth++; + print_ranges (child); + binding_depth = saved; + print_ranges (range->next_sibling); +} #endif /* Search for the most specific eh_range containing PC. @@ -77,10 +121,7 @@ extern void indent (); previous children have end_pc values that are too low. */ static struct eh_range * -find_handler_in_range (pc, range, child) - int pc; - struct eh_range *range; - register struct eh_range *child; +find_handler_in_range (int pc, struct eh_range *range, struct eh_range *child) { for (; child != NULL; child = child->next_sibling) { @@ -99,8 +140,7 @@ find_handler_in_range (pc, range, child) /* Find the inner-most handler that contains PC. */ struct eh_range * -find_handler (pc) - int pc; +find_handler (int pc) { struct eh_range *h; if (pc >= cache_range_start) @@ -122,117 +162,15 @@ find_handler (pc) return find_handler_in_range (pc, h, cache_next_child); } -/* Recursive helper routine for check_nested_ranges. */ - static void -link_handler (range, outer) - struct eh_range *range, *outer; -{ - struct eh_range **ptr; - - if (range->start_pc == outer->start_pc && range->end_pc == outer->end_pc) - { - outer->handlers = chainon (outer->handlers, range->handlers); - return; - } - - /* If the new range completely encloses the `outer' range, then insert it - between the outer range and its parent. */ - if (range->start_pc <= outer->start_pc && range->end_pc >= outer->end_pc) - { - range->outer = outer->outer; - range->next_sibling = NULL; - range->first_child = outer; - { - struct eh_range **pr = &(outer->outer->first_child); - while (*pr != outer) - pr = &(*pr)->next_sibling; - *pr = range; - } - outer->outer = range; - return; - } - - /* Handle overlapping ranges by splitting the new range. */ - if (range->start_pc < outer->start_pc || range->end_pc > outer->end_pc) - { - struct eh_range *h - = (struct eh_range *) xmalloc (sizeof (struct eh_range)); - if (range->start_pc < outer->start_pc) - { - h->start_pc = range->start_pc; - h->end_pc = outer->start_pc; - range->start_pc = outer->start_pc; - } - else - { - h->start_pc = outer->end_pc; - h->end_pc = range->end_pc; - range->end_pc = outer->end_pc; - } - h->first_child = NULL; - h->outer = NULL; - h->handlers = build_tree_list (TREE_PURPOSE (range->handlers), - TREE_VALUE (range->handlers)); - h->next_sibling = NULL; - /* Restart both from the top to avoid having to make this - function smart about reentrancy. */ - link_handler (h, &whole_range); - link_handler (range, &whole_range); - return; - } - - ptr = &outer->first_child; - for (;; ptr = &(*ptr)->next_sibling) - { - if (*ptr == NULL || range->end_pc <= (*ptr)->start_pc) - { - range->next_sibling = *ptr; - range->first_child = NULL; - range->outer = outer; - *ptr = range; - return; - } - else if (range->start_pc < (*ptr)->end_pc) - { - link_handler (range, *ptr); - return; - } - /* end_pc > (*ptr)->start_pc && start_pc >= (*ptr)->end_pc. */ - } -} - -/* The first pass of exception range processing (calling add_handler) - constructs a linked list of exception ranges. We turn this into - the data structure expected by the rest of the code, and also - ensure that exception ranges are properly nested. */ - -void -handle_nested_ranges () -{ - struct eh_range *ptr, *next; - - ptr = whole_range.first_child; - whole_range.first_child = NULL; - for (; ptr; ptr = next) - { - next = ptr->next_sibling; - ptr->next_sibling = NULL; - link_handler (ptr, &whole_range); - } -} - -/* Free RANGE as well as its children and siblings. */ - -static void -free_eh_ranges (range) - struct eh_range *range; +free_eh_ranges (struct eh_range *range) { while (range) { struct eh_range *next = range->next_sibling; free_eh_ranges (range->first_child); - free (range); + if (range != &whole_range) + free (range); range = next; } } @@ -240,7 +178,7 @@ free_eh_ranges (range) /* Called to re-initialize the exception machinery for a new method. */ void -method_init_exceptions () +method_init_exceptions (void) { free_eh_ranges (&whole_range); whole_range.start_pc = 0; @@ -249,124 +187,322 @@ method_init_exceptions () whole_range.first_child = NULL; whole_range.next_sibling = NULL; cache_range_start = 0xFFFFFF; - java_set_exception_lang_code (); } -void -java_set_exception_lang_code () +/* Split an exception range into two at PC. The sub-ranges that + belong to the range are split and distributed between the two new + ranges. */ + +static void +split_range (struct eh_range *range, int pc) { - set_exception_lang_code (EH_LANG_Java); - set_exception_version_code (1); -} + struct eh_range *ptr; + struct eh_range **first_child, **second_child; + struct eh_range *h; -/* Add an exception range. If we already have an exception range - which has the same handler and label, and the new range overlaps - that one, then we simply extend the existing range. Some bytecode - obfuscators generate seemingly nonoverlapping exception ranges - which, when coalesced, do in fact nest correctly. - - This constructs an ordinary linked list which check_nested_ranges() - later turns into the data structure we actually want. + /* First, split all the sub-ranges. */ + for (ptr = range->first_child; ptr; ptr = ptr->next_sibling) + { + if (pc > ptr->start_pc + && pc < ptr->end_pc) + { + split_range (ptr, pc); + } + } + + /* Create a new range. */ + h = xmalloc (sizeof (struct eh_range)); + + h->start_pc = pc; + h->end_pc = range->end_pc; + h->next_sibling = range->next_sibling; + range->next_sibling = h; + range->end_pc = pc; + h->handlers = build_tree_list (TREE_PURPOSE (range->handlers), + TREE_VALUE (range->handlers)); + h->next_sibling = NULL; + h->expanded = 0; + h->stmt = NULL; + h->outer = range->outer; + h->first_child = NULL; + + ptr = range->first_child; + first_child = &range->first_child; + second_child = &h->first_child; + + /* Distribute the sub-ranges between the two new ranges. */ + for (ptr = range->first_child; ptr; ptr = ptr->next_sibling) + { + if (ptr->start_pc < pc) + { + *first_child = ptr; + ptr->outer = range; + first_child = &ptr->next_sibling; + } + else + { + *second_child = ptr; + ptr->outer = h; + second_child = &ptr->next_sibling; + } + } + *first_child = NULL; + *second_child = NULL; +} + + +/* Add an exception range. + + There are some missed optimization opportunities here. For + example, some bytecode obfuscators generate seemingly + nonoverlapping exception ranges which, when coalesced, do in fact + nest correctly. We could merge these, but we'd have to fix up all + the enclosed regions first and perhaps create a new range anyway if + it overlapped existing ranges. - We expect the input to come in order of increasing START_PC. This - function doesn't attempt to detect the case where two previously - added disjoint ranges could be coalesced by a new range; that is - what the sorting counteracts. */ + Also, we don't attempt to detect the case where two previously + added disjoint ranges could be coalesced by a new range. */ -void -add_handler (start_pc, end_pc, handler, type) - int start_pc, end_pc; - tree handler; - tree type; +void +add_handler (int start_pc, int end_pc, tree handler, tree type) { - struct eh_range *ptr, *prev = NULL, *h; + struct eh_range *ptr, *h; + struct eh_range **first_child, **prev; + /* First, split all the existing ranges that we need to enclose. */ for (ptr = whole_range.first_child; ptr; ptr = ptr->next_sibling) { - if (start_pc >= ptr->start_pc - && start_pc <= ptr->end_pc - && TREE_PURPOSE (ptr->handlers) == type - && TREE_VALUE (ptr->handlers) == handler) + if (start_pc > ptr->start_pc + && start_pc < ptr->end_pc) + { + split_range (ptr, start_pc); + } + + if (end_pc > ptr->start_pc + && end_pc < ptr->end_pc) { - /* Already found an overlapping range, so coalesce. */ - ptr->end_pc = MAX (ptr->end_pc, end_pc); - return; + split_range (ptr, end_pc); } - prev = ptr; + + if (ptr->start_pc >= end_pc) + break; } - h = (struct eh_range *) xmalloc (sizeof (struct eh_range)); + /* Create the new range. */ + h = xmalloc (sizeof (struct eh_range)); + first_child = &h->first_child; + h->start_pc = start_pc; h->end_pc = end_pc; h->first_child = NULL; - h->outer = NULL; + h->outer = NULL_EH_RANGE; h->handlers = build_tree_list (type, handler); h->next_sibling = NULL; h->expanded = 0; + h->stmt = NULL; - if (prev == NULL) - whole_range.first_child = h; - else - prev->next_sibling = h; -} + /* Find every range at the top level that will be a sub-range of the + range we're inserting and make it so. */ + { + struct eh_range **prev = &whole_range.first_child; + for (ptr = *prev; ptr;) + { + struct eh_range *next = ptr->next_sibling; + + if (ptr->start_pc >= end_pc) + break; + + if (ptr->start_pc < start_pc) + { + prev = &ptr->next_sibling; + } + else if (ptr->start_pc >= start_pc + && ptr->start_pc < end_pc) + { + *prev = next; + *first_child = ptr; + first_child = &ptr->next_sibling; + ptr->outer = h; + ptr->next_sibling = NULL; + } + + ptr = next; + } + } + /* Find the right place to insert the new range. */ + prev = &whole_range.first_child; + for (ptr = *prev; ptr; prev = &ptr->next_sibling, ptr = ptr->next_sibling) + { + gcc_assert (ptr->outer == NULL_EH_RANGE); + if (ptr->start_pc >= start_pc) + break; + } + /* And insert it there. */ + *prev = h; + if (ptr) + { + h->next_sibling = ptr; + h->outer = ptr->outer; + } +} + + /* if there are any handlers for this range, issue start of region */ static void -expand_start_java_handler (range) - struct eh_range *range; +expand_start_java_handler (struct eh_range *range) { #if defined(DEBUG_JAVA_BINDING_LEVELS) indent (); fprintf (stderr, "expand start handler pc %d --> %d\n", current_pc, range->end_pc); #endif /* defined(DEBUG_JAVA_BINDING_LEVELS) */ + pushlevel (0); + register_exception_range (range, range->start_pc, range->end_pc); range->expanded = 1; - expand_eh_region_start (); } tree -prepare_eh_table_type (type) - tree type; +prepare_eh_table_type (tree type) { tree exp; + tree *slot; + const char *name; + char *buf; + tree decl; + tree utf8_ref; - /* The "type" (metch_info) in a (Java) exception table is one: + /* The "type" (match_info) in a (Java) exception table is a pointer to: * a) NULL - meaning match any type in a try-finally. - * b) a pointer to a (ccmpiled) class (low-order bit 0). - * c) a pointer to the Utf8Const name of the class, plus one - * (which yields a value with low-order bit 1). */ + * b) a pointer to a pointer to a class. + * c) a pointer to a pointer to a utf8_ref. The pointer is + * rewritten to point to the appropriate class. */ if (type == NULL_TREE) - exp = CATCH_ALL_TYPE; - else if (is_compiled_class (type)) - exp = build_class_ref (type); + return NULL_TREE; + + if (TYPE_TO_RUNTIME_MAP (output_class) == NULL) + TYPE_TO_RUNTIME_MAP (output_class) = java_treetreehash_create (10, 1); + + slot = java_treetreehash_new (TYPE_TO_RUNTIME_MAP (output_class), type); + if (*slot != NULL) + return TREE_VALUE (*slot); + + if (is_compiled_class (type) && !flag_indirect_dispatch) + { + name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); + buf = alloca (strlen (name) + 5); + sprintf (buf, "%s_ref", name); + decl = build_decl (VAR_DECL, get_identifier (buf), ptr_type_node); + TREE_STATIC (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + TREE_READONLY (decl) = 1; + TREE_THIS_VOLATILE (decl) = 0; + DECL_INITIAL (decl) = build_class_ref (type); + layout_decl (decl, 0); + pushdecl (decl); + exp = build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (decl)), decl); + } else - exp = fold (build - (PLUS_EXPR, ptr_type_node, - build_utf8_ref (build_internal_class_name (type)), - size_one_node)); + { + utf8_ref = build_utf8_ref (DECL_NAME (TYPE_NAME (type))); + name = IDENTIFIER_POINTER (DECL_NAME (TREE_OPERAND (utf8_ref, 0))); + buf = alloca (strlen (name) + 5); + sprintf (buf, "%s_ref", name); + decl = build_decl (VAR_DECL, get_identifier (buf), utf8const_ptr_type); + TREE_STATIC (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + TREE_READONLY (decl) = 1; + TREE_THIS_VOLATILE (decl) = 0; + layout_decl (decl, 0); + pushdecl (decl); + exp = build1 (ADDR_EXPR, build_pointer_type (utf8const_ptr_type), decl); + TYPE_CATCH_CLASSES (output_class) = + tree_cons (NULL, make_catch_class_record (exp, utf8_ref), + TYPE_CATCH_CLASSES (output_class)); + } + + exp = convert (ptr_type_node, exp); + + *slot = tree_cons (type, exp, NULL_TREE); + return exp; } -/* if there are any handlers for this range, isssue end of range, +static int +expand_catch_class (void **entry, void *x ATTRIBUTE_UNUSED) +{ + struct treetreehash_entry *ite = (struct treetreehash_entry *) *entry; + tree addr = TREE_VALUE ((tree)ite->value); + tree decl; + STRIP_NOPS (addr); + decl = TREE_OPERAND (addr, 0); + rest_of_decl_compilation (decl, global_bindings_p (), 0); + return true; +} + +/* For every class in the TYPE_TO_RUNTIME_MAP, expand the + corresponding object that is used by the runtime type matcher. */ + +void +java_expand_catch_classes (tree this_class) +{ + if (TYPE_TO_RUNTIME_MAP (this_class)) + htab_traverse + (TYPE_TO_RUNTIME_MAP (this_class), + expand_catch_class, NULL); +} + +/* Build a reference to the jthrowable object being carried in the + exception header. */ + +tree +build_exception_object_ref (tree type) +{ + tree obj; + + /* Java only passes object via pointer and doesn't require adjusting. + The java object is immediately before the generic exception header. */ + obj = build0 (EXC_PTR_EXPR, build_pointer_type (type)); + obj = build2 (MINUS_EXPR, TREE_TYPE (obj), obj, + TYPE_SIZE_UNIT (TREE_TYPE (obj))); + obj = build1 (INDIRECT_REF, type, obj); + + return obj; +} + +/* If there are any handlers for this range, isssue end of range, and then all handler blocks */ -static void -expand_end_java_handler (range) - struct eh_range *range; +void +expand_end_java_handler (struct eh_range *range) { tree handler = range->handlers; - force_poplevels (range->start_pc); - expand_start_all_catch (); + for ( ; handler != NULL_TREE; handler = TREE_CHAIN (handler)) { - start_catch_handler (prepare_eh_table_type (TREE_PURPOSE (handler))); - /* Push the thrown object on the top of the stack */ - expand_goto (TREE_VALUE (handler)); - expand_resume_after_catch (); - end_catch_handler (); + /* For bytecode we treat exceptions a little unusually. A + `finally' clause looks like an ordinary exception handler for + Throwable. The reason for this is that the bytecode has + already expanded the finally logic, and we would have to do + extra (and difficult) work to get this to look like a + gcc-style finally clause. */ + tree type = TREE_PURPOSE (handler); + if (type == NULL) + type = throwable_type_node; + type = prepare_eh_table_type (type); + + { + tree catch_expr = build2 (CATCH_EXPR, void_type_node, type, + build1 (GOTO_EXPR, void_type_node, + TREE_VALUE (handler))); + tree try_catch_expr = build2 (TRY_CATCH_EXPR, void_type_node, + *get_stmts (), catch_expr); + *get_stmts () = try_catch_expr; + } } - expand_end_all_catch (); #if defined(DEBUG_JAVA_BINDING_LEVELS) indent (); fprintf (stderr, "expand end handler pc %d <-- %d\n", @@ -377,9 +513,7 @@ expand_end_java_handler (range) /* Recursive helper routine for maybe_start_handlers. */ static void -check_start_handlers (range, pc) - struct eh_range *range; - int pc; +check_start_handlers (struct eh_range *range, int pc) { if (range != NULL_EH_RANGE && range->start_pc == pc) { @@ -396,9 +530,7 @@ static struct eh_range *current_range; end_pc. */ void -maybe_start_try (start_pc, end_pc) - int start_pc; - int end_pc; +maybe_start_try (int start_pc, int end_pc) { struct eh_range *range; if (! doing_eh (1)) @@ -413,48 +545,3 @@ maybe_start_try (start_pc, end_pc) check_start_handlers (range, start_pc); } -/* Emit any end-of-try-range ending at end_pc and starting before - start_pc. */ - -void -maybe_end_try (start_pc, end_pc) - int start_pc; - int end_pc; -{ - if (! doing_eh (1)) - return; - - while (current_range != NULL_EH_RANGE && current_range->end_pc <= end_pc - && current_range->start_pc >= start_pc) - { - expand_end_java_handler (current_range); - current_range = current_range->outer; - } -} - -/* Emit the handler labels and their code */ - -void -emit_handlers () -{ - if (catch_clauses) - { - rtx funcend = gen_label_rtx (); - emit_jump (funcend); - - emit_insns (catch_clauses); - catch_clauses = catch_clauses_last = NULL_RTX; - expand_leftover_cleanups (); - - emit_label (funcend); - } -} - -/* Resume executing at the statement immediately after the end of an - exception region. */ - -void -expand_resume_after_catch () -{ - expand_goto (top_label_entry (&caught_return_label_stack)); -}