diff options
-rw-r--r-- | gcc/java/ChangeLog | 53 | ||||
-rw-r--r-- | gcc/java/check-init.c | 44 | ||||
-rw-r--r-- | gcc/java/class.c | 29 | ||||
-rw-r--r-- | gcc/java/decl.c | 39 | ||||
-rw-r--r-- | gcc/java/expr.c | 69 | ||||
-rw-r--r-- | gcc/java/gcj.texi | 8 | ||||
-rw-r--r-- | gcc/java/java-tree.h | 36 | ||||
-rw-r--r-- | gcc/java/lang-options.h | 2 | ||||
-rw-r--r-- | gcc/java/lang.c | 13 | ||||
-rw-r--r-- | gcc/java/parse.y | 401 |
10 files changed, 541 insertions, 153 deletions
diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog index 5273aaf..7734a5f 100644 --- a/gcc/java/ChangeLog +++ b/gcc/java/ChangeLog @@ -1,3 +1,56 @@ +2001-08-08 Alexandre Petit-Bianco <apbianco@redhat.com> + + * check-init.c (flags.h): Include + (check_init): Don't report uninitialized static class + initialization flags, don't free bit index when doing static class + initialization optimization. + (check_for_initialization): Return type changed to `unsigned int.' + (attach_initialized_static_class): New function. + * class.c (add_method_1): Create the initialized static class + table if necessary. + (finish_class): Always emit deferred inline methods. + * decl.c (emit_init_test_initialization): Moved to expr.c + (complete_start_java_method): Don't traverse + DECL_FUNCTION_INIT_TEST_TABLE. + (lang_mark_tree): Mark hash tables in function decls. + * expr.c (emit_init_test_initialization): Moved from decl.c. + (build_class_init): Create LAG_DECL_SPECIFIC for the static class + initialization flag, set DECL_CONTEXT and + LOCAL_CLASS_INITIALIZATION_FLAG. + (java_lang_expand_expr): Emit initialization code for static class + initialized flags when entering block, if necessary. + * gcj.texi (-fno-optimize-static-class-initialization): Documented. + * java-tree.h (flag_optimize_sci): New global variable declaration. + (DECL_FUNCTION_INITIALIZED_CLASS_TABLE): New macro. + (DECL_FUNCTION_STATIC_METHOD_INVOCATION_COMPOUND): Likewise. + (LOCAL_FINAL_P): Fixed typo in comment. + (FINAL_VARIABLE_P): Likewise. + (LOCAL_CLASS_INITIALIZATIO_FLAG): New macro. + (LOCAL_CLASS_INITIALIZATIO_FLAG_P): Likewise. + (struct lang_decl): New fields `ict', `smic' and `cif.' + (check_for_initialization): New returned value for global. + (attach_initialized_static_class): New global function. + (STATIC_CLASS_INIT_OPT_P): New macro. + * lang-options.h (-fno-optimize-static-class-initialization): New flag. + * lang.c (java_decode_option): Handle + `-fno-optimize-static-class-initialization' + * parse.y (start_complete_expand_method): New function. + (java_expand_method_bodies): Likewise. + (attach_init_test_initialization_flags): Likewise. + (adjust_init_test_initialization): Likewise. + (emit_test_initialization): Likewise. + (java_complete_expand_methods): Nullify abstract and native method + bodies. + (java_complete_expand_method): New locals `fbody', `block_body' + and `exception_copy.' Reorganized: directly return on empty method + bodies, call `start_complete_expand_method', remember definitely + initialized static class in function, don't expand method bodies. + (java_expand_classes): Call `java_expand_method_bodies' before + `finish_class' when compiling to native. + (resolve_expression_name): Use `orig' after building outer class + field access. + (patch_invoke): Remember static method invokations. + 2001-08-06 Richard Henderson <rth@redhat.com> * class.c (emit_register_classes): Pass a symbol_ref and priority diff --git a/gcc/java/check-init.c b/gcc/java/check-init.c index 0e340dd..b108c45 100644 --- a/gcc/java/check-init.c +++ b/gcc/java/check-init.c @@ -25,6 +25,7 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */ #include "config.h" #include "system.h" #include "tree.h" +#include "flags.h" /* Needed for optimize. */ #include "java-tree.h" #include "toplev.h" /* Needed for fatal. */ @@ -370,7 +371,12 @@ check_init (exp, before) if (! FIELD_STATIC (exp) && DECL_NAME (exp) != NULL_TREE) { int index = DECL_BIT_INDEX (exp); - if (index >= 0 && ! SET_P (before, index)) + /* We don't want to report and mark as non initialized flags + the are, they will be marked initialized later on when + assigned to `true.' */ + if ((STATIC_CLASS_INIT_OPT_P () + && ! LOCAL_CLASS_INITIALIZATION_FLAG_P (exp)) + && index >= 0 && ! SET_P (before, index)) { parse_error_context (wfl, "Variable `%s' may not have been initialized", @@ -398,8 +404,13 @@ check_init (exp, before) if (index >= 0) SET_BIT (before, index); - /* Minor optimization. See comment for start_current_locals. */ - if (index >= start_current_locals + /* Minor optimization. See comment for start_current_locals. + If we're optimizing for class initialization, we keep + this information to check whether the variable is + definitely assigned when once we checked the whole + function. */ + if (! STATIC_CLASS_INIT_OPT_P () + && index >= start_current_locals && index == num_current_locals - 1) { num_current_locals--; @@ -732,10 +743,35 @@ check_init (exp, before) } } -void +unsigned int check_for_initialization (body) tree body; { word before = 0; check_init (body, &before); + return before; +} + +/* Call for every element in DECL_FUNCTION_INITIALIZED_CLASS_TABLE of + a method to consider whether the type indirectly described by ENTRY + is definitly initialized and thus remembered as such. */ + +bool +attach_initialized_static_class (entry, ptr) + struct hash_entry *entry; + PTR ptr; +{ + struct init_test_hash_entry *ite = (struct init_test_hash_entry *) entry; + tree fndecl = DECL_CONTEXT (ite->init_test_decl); + int index = DECL_BIT_INDEX (ite->init_test_decl); + + /* If the initializer flag has been definitly assigned (not taking + into account its first mandatory assignment which has been + already added but escaped analysis.) */ + if (fndecl && METHOD_STATIC (fndecl) + && (DECL_INITIAL (ite->init_test_decl) == boolean_true_node + || (index >= 0 && SET_P (((word *) ptr), index)))) + hash_lookup (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (fndecl), + entry->key, TRUE, NULL); + return true; } diff --git a/gcc/java/class.c b/gcc/java/class.c index 8113340..7e153b5 100644 --- a/gcc/java/class.c +++ b/gcc/java/class.c @@ -673,6 +673,18 @@ add_method_1 (handle_class, access_flags, name, function_type) init_test_hash_newfunc, java_hash_hash_tree_node, java_hash_compare_tree_node); + /* Initialize the initialized (static) class table. */ + if (access_flags & ACC_STATIC) + hash_table_init (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (fndecl), + init_test_hash_newfunc, java_hash_hash_tree_node, + java_hash_compare_tree_node); + + /* Initialize the static method invocation compound table */ + if (STATIC_CLASS_INIT_OPT_P ()) + hash_table_init (&DECL_FUNCTION_STATIC_METHOD_INVOCATION_COMPOUND (fndecl), + init_test_hash_newfunc, java_hash_hash_tree_node, + java_hash_compare_tree_node); + TREE_CHAIN (fndecl) = TYPE_METHODS (handle_class); TYPE_METHODS (handle_class) = fndecl; @@ -1484,18 +1496,11 @@ finish_class () { if (! TREE_ASM_WRITTEN (method) && DECL_SAVED_INSNS (method) != 0) { - /* It's a deferred inline method. Decide if we need to emit it. */ - if (flag_keep_inline_functions - || TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (method)) - || ! METHOD_PRIVATE (method) - || saw_native_method) - { - output_inline_function (method); - /* Scan the list again to see if there are any earlier - methods to emit. */ - method = type_methods; - continue; - } + output_inline_function (method); + /* Scan the list again to see if there are any earlier + methods to emit. */ + method = type_methods; + continue; } method = TREE_CHAIN (method); } diff --git a/gcc/java/decl.c b/gcc/java/decl.c index 3b856db..4eb0739 100644 --- a/gcc/java/decl.c +++ b/gcc/java/decl.c @@ -49,8 +49,6 @@ static tree push_jvm_slot PARAMS ((int, tree)); static tree lookup_name_current_level PARAMS ((tree)); static tree push_promoted_type PARAMS ((const char *, tree)); static struct binding_level *make_binding_level PARAMS ((void)); -static bool emit_init_test_initialization PARAMS ((struct hash_entry *, - hash_table_key)); static tree create_primitive_vtable PARAMS ((const char *)); static tree check_local_named_variable PARAMS ((tree, tree, int, int *)); static tree check_local_unnamed_variable PARAMS ((tree, tree, tree)); @@ -1639,35 +1637,6 @@ build_result_decl (fndecl) return (DECL_RESULT (fndecl) = build_decl (RESULT_DECL, NULL_TREE, restype)); } -/* Called for every element in DECL_FUNCTION_INIT_TEST_TABLE in order - to emit initialization code for each test flag. */ - -static bool -emit_init_test_initialization (entry, key) - struct hash_entry *entry; - hash_table_key key ATTRIBUTE_UNUSED; -{ - struct init_test_hash_entry *ite = (struct init_test_hash_entry *) entry; - tree klass = build_class_ref ((tree) entry->key); - expand_decl (ite->init_test_decl); - - /* We initialize the class init check variable by looking at the - `state' field of the class to see if it is already initialized. - This makes things a bit faster if the class is already - initialized, which should be the common case. */ - expand_expr_stmt - (build (MODIFY_EXPR, boolean_type_node, - ite->init_test_decl, - build (GE_EXPR, boolean_type_node, - build (COMPONENT_REF, byte_type_node, - build1 (INDIRECT_REF, class_type_node, klass), - lookup_field (&class_type_node, - get_identifier ("state"))), - build_int_2 (JV_STATE_DONE, 0)))); - - return true; -} - void complete_start_java_method (fndecl) tree fndecl; @@ -1679,11 +1648,6 @@ complete_start_java_method (fndecl) /* Set up parameters and prepare for return, for the function. */ expand_function_start (fndecl, 0); - - /* Emit initialization code for test flags. */ - if (! always_initialize_class_p) - hash_traverse (&DECL_FUNCTION_INIT_TEST_TABLE (fndecl), - emit_init_test_initialization, 0); } #if 0 @@ -1871,6 +1835,9 @@ lang_mark_tree (t) ggc_mark_tree (ld->function_decl_body); ggc_mark_tree (ld->called_constructor); ggc_mark_tree (ld->inner_access); + ggc_mark_tree_hash_table (&ld->init_test_table); + ggc_mark_tree_hash_table (&ld->ict); + ggc_mark_tree_hash_table (&ld->smic); } } else if (TYPE_P (t)) diff --git a/gcc/java/expr.c b/gcc/java/expr.c index c9b4a3c..f7056fe 100644 --- a/gcc/java/expr.c +++ b/gcc/java/expr.c @@ -82,6 +82,8 @@ static tree build_java_check_indexed_type PARAMS ((tree, tree)); static tree java_array_data_offset PARAMS ((tree)); static tree case_identity PARAMS ((tree, tree)); static unsigned char peek_opcode_at_pc PARAMS ((struct JCF *, int, int)); +static bool emit_init_test_initialization PARAMS ((struct hash_entry *, + PTR ptr)); static tree operand_type[59]; extern struct obstack permanent_obstack; @@ -1710,10 +1712,20 @@ build_class_init (clas, expr) TRUE, NULL); if (ite->init_test_decl == 0) - ite->init_test_decl = build_decl (VAR_DECL, NULL_TREE, - boolean_type_node); - /* Tell the check-init code to ignore this decl. */ - DECL_BIT_INDEX(ite->init_test_decl) = -1; + { + /* Build a declaration and mark it as a flag used to track + static class initializations. */ + ite->init_test_decl = build_decl (VAR_DECL, NULL_TREE, + boolean_type_node); + MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (ite->init_test_decl); + LOCAL_CLASS_INITIALIZATION_FLAG (ite->init_test_decl) = 1; + DECL_CONTEXT (ite->init_test_decl) = current_function_decl; + + /* Tell the check-init code to ignore this decl when not + optimizing class initialization. */ + if (!STATIC_CLASS_INIT_OPT_P ()) + DECL_BIT_INDEX(ite->init_test_decl) = -1; + } init = build (CALL_EXPR, void_type_node, build_address_of (soft_initclass_node), @@ -2459,16 +2471,31 @@ java_lang_expand_expr (exp, target, tmode, modifier) { tree local; tree body = BLOCK_EXPR_BODY (exp); + /* Set to 1 or more when we found a static class + initialization flag. */ + int found_class_initialization_flag = 0; + pushlevel (2); /* 2 and above */ expand_start_bindings (0); local = BLOCK_EXPR_DECLS (exp); while (local) { tree next = TREE_CHAIN (local); + found_class_initialization_flag += + LOCAL_CLASS_INITIALIZATION_FLAG_P (local); layout_decl (local, 0); expand_decl (pushdecl (local)); local = next; } + + /* Emit initialization code for test flags if we saw one. */ + if (! always_initialize_class_p + && current_function_decl + && found_class_initialization_flag) + hash_traverse + (&DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl), + emit_init_test_initialization, NULL); + /* Avoid deep recursion for long block. */ while (TREE_CODE (body) == COMPOUND_EXPR) { @@ -3335,3 +3362,37 @@ force_evaluation_order (node) } return node; } + +/* Called for every element in DECL_FUNCTION_INIT_TEST_TABLE of a + method in order to emit initialization code for each test flag. */ + +static bool +emit_init_test_initialization (entry, key) + struct hash_entry *entry; + hash_table_key key ATTRIBUTE_UNUSED; +{ + struct init_test_hash_entry *ite = (struct init_test_hash_entry *) entry; + tree klass = build_class_ref ((tree) entry->key); + tree rhs; + + /* If the DECL_INITIAL of the test flag is set to true, it + means that the class is already initialized the time it + is in use. */ + if (DECL_INITIAL (ite->init_test_decl) == boolean_true_node) + rhs = boolean_true_node; + /* Otherwise, we initialize the class init check variable by looking + at the `state' field of the class to see if it is already + initialized. This makes things a bit faster if the class is + already initialized, which should be the common case. */ + else + rhs = build (GE_EXPR, boolean_type_node, + build (COMPONENT_REF, byte_type_node, + build1 (INDIRECT_REF, class_type_node, klass), + lookup_field (&class_type_node, + get_identifier ("state"))), + build_int_2 (JV_STATE_DONE, 0)); + + expand_expr_stmt (build (MODIFY_EXPR, boolean_type_node, + ite->init_test_decl, rhs)); + return true; +} diff --git a/gcc/java/gcj.texi b/gcc/java/gcj.texi index 1ccd7c9..6a52fc6 100644 --- a/gcc/java/gcj.texi +++ b/gcc/java/gcj.texi @@ -356,6 +356,14 @@ compiling a class with native methods, and these methods are implemented using JNI, then you must use @code{-fjni}. This option causes @code{gcj} to generate stubs which will invoke the underlying JNI methods. + +@item -fno-optimize-static-class-initialization +When the optimization level is greather or equal to @code{-O2}, +@code{gcj} will try to optimize the way calls into the runtime are made +to initialize static classes upon their first use (this optimization +isn't carried out if @code{-C} was specified.) When compiling to native +code, @code{-fno-optimize-static-class-initialization} will turn this +optimization off, regardless of the optimization level in use. @end table diff --git a/gcc/java/java-tree.h b/gcc/java/java-tree.h index 03d3cc2..d114747 100644 --- a/gcc/java/java-tree.h +++ b/gcc/java/java-tree.h @@ -186,6 +186,10 @@ extern int flag_hash_synchronization; /* When non zero, generate checks for references to NULL. */ extern int flag_check_references; +/* Used through STATIC_CLASS_INIT_OPT_P to check whether static + initialization optimization should be performed. */ +extern int flag_optimize_sci; + /* Encoding used for source files. */ extern const char *current_encoding; @@ -704,6 +708,16 @@ struct lang_identifier class has been initialized in this function, and FALSE otherwise. */ #define DECL_FUNCTION_INIT_TEST_TABLE(DECL) \ (DECL_LANG_SPECIFIC(DECL)->init_test_table) +/* For each static function decl, itc contains a hash table whose + entries are keyed on class named that are definitively initialized + in DECL. */ +#define DECL_FUNCTION_INITIALIZED_CLASS_TABLE(DECL) \ + (DECL_LANG_SPECIFIC(DECL)->ict) +/* For each static function call, smic contains contains a hash table + whose entries are keyed on the compound statement that encapsulate + the invocation. */ +#define DECL_FUNCTION_STATIC_METHOD_INVOCATION_COMPOUND(DECL) \ + (DECL_LANG_SPECIFIC(DECL)->smic) /* The Number of Artificial Parameters (NAP) DECL contains. this$<n> is excluded, because sometimes created as a parameter before the function decl exists. */ @@ -815,11 +829,18 @@ struct lang_identifier (((struct lang_decl_var*)DECL_LANG_SPECIFIC(NODE))->local_final) /* True if NODE is a local final. */ #define LOCAL_FINAL_P(NODE) (DECL_LANG_SPECIFIC (NODE) && LOCAL_FINAL (NODE)) -/* True if NODE is a final variable */ +/* True if NODE is a final variable. */ #define FINAL_VARIABLE_P(NODE) (FIELD_FINAL (NODE) && !FIELD_STATIC (NODE)) -/* True if NODE is a class final variable */ +/* True if NODE is a class final variable. */ #define CLASS_FINAL_VARIABLE_P(NODE) \ (FIELD_FINAL (NODE) && FIELD_STATIC (NODE)) +/* True if NODE is a class initialization flag. This macro accesses + the flag to read or set it. */ +#define LOCAL_CLASS_INITIALIZATION_FLAG(NODE) \ + (((struct lang_decl_var*)DECL_LANG_SPECIFIC(NODE))->cif) +/* True if NODE is a class initialization flag. */ +#define LOCAL_CLASS_INITIALIZATION_FLAG_P(NODE) \ + (DECL_LANG_SPECIFIC (NODE) && LOCAL_CLASS_INITIALIZATION_FLAG(NODE)) /* Create a DECL_LANG_SPECIFIC if necessary. */ #define MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC(T) \ if (DECL_LANG_SPECIFIC (T) == NULL) \ @@ -858,6 +879,8 @@ struct lang_decl list of other constructor it calls */ struct hash_table init_test_table; /* Class initialization test variables */ + struct hash_table ict; /* Initialized (static) Class Table */ + struct hash_table smic; /* Static method invocation compound */ tree inner_access; /* The identifier of the access method used for invocation from inner classes */ int nap; /* Number of artificial parameters */ @@ -888,6 +911,7 @@ struct lang_decl_var int final_liic : 1; /* Final locally initialized in ctors */ int final_ierr : 1; /* Initialization error already detected */ int local_final : 1; /* True if the decl is a local final */ + int cif : 1; /* True: decl is a class initialization flag */ }; /* Macro to access fields in `struct lang_type'. */ @@ -1061,7 +1085,7 @@ extern void parse_error_context PARAMS ((tree cl, const char *, ...)) extern tree build_primtype_type_ref PARAMS ((const char *)); extern void finish_class PARAMS ((void)); extern void java_layout_seen_class_methods PARAMS ((void)); -extern void check_for_initialization PARAMS ((tree)); +extern unsigned int check_for_initialization PARAMS ((tree)); extern tree pushdecl_top_level PARAMS ((tree)); extern int alloc_class_constant PARAMS ((tree)); @@ -1129,6 +1153,8 @@ extern tree get_boehm_type_descriptor PARAMS ((tree)); extern unsigned long java_hash_hash_tree_node PARAMS ((hash_table_key)); extern bool java_hash_compare_tree_node PARAMS ((hash_table_key, hash_table_key)); +extern bool attach_initialized_static_class PARAMS ((struct hash_entry *, + PTR)); extern void java_check_methods PARAMS ((tree)); extern void init_jcf_parse PARAMS((void)); extern void init_src_parse PARAMS((void)); @@ -1559,6 +1585,10 @@ extern tree *type_map; (inherits_from_p ((TYPE), runtime_exception_type_node) \ || inherits_from_p ((TYPE), error_exception_type_node)) +/* True when we can perform static class initialization optimization */ +#define STATIC_CLASS_INIT_OPT_P() \ + (flag_optimize_sci && (optimize >= 2) && ! flag_emit_class_files) + extern int java_error_count; \ /* Make the current function where this macro is invoked report error diff --git a/gcc/java/lang-options.h b/gcc/java/lang-options.h index 17a1696..06a4c4d 100644 --- a/gcc/java/lang-options.h +++ b/gcc/java/lang-options.h @@ -52,3 +52,5 @@ DEFINE_LANG_NAME ("Java") N_("Warn if .class files are out of date") }, { "-fforce-classes-archive-check", N_("Always check for non gcj generated classes archives") }, + { "-fno-optimize-static-class-initialization", + N_("Never optimize static class initialization code") }, diff --git a/gcc/java/lang.c b/gcc/java/lang.c index 91f89be..b9cb0fe 100644 --- a/gcc/java/lang.c +++ b/gcc/java/lang.c @@ -146,6 +146,10 @@ int flag_extraneous_semicolon; /* When non zero, always check for a non gcj generated classes archive. */ int flag_force_classes_archive_check; +/* When zero, don't optimize static class initialization. This flag shouldn't + be tested alone, use STATIC_CLASS_INITIALIZATION_OPTIMIZATION_P instead. */ +int flag_optimize_sci = 1; + /* Table of language-dependent -f options. STRING is the option name. VARIABLE is the address of the variable. ON_VALUE is the value to store in VARIABLE @@ -295,6 +299,15 @@ java_decode_option (argc, argv) } #undef ARG +#undef ARG +#define ARG "-fno-optimize-static-class-initialization" + if (strncmp (p, ARG, sizeof (ARG) - 1) == 0) + { + flag_optimize_sci = 0; + return 1; + } +#undef ARG + if (p[0] == '-' && p[1] == 'f') { /* Some kind of -f option. diff --git a/gcc/java/parse.y b/gcc/java/parse.y index 05be633..a1f36aa 100644 --- a/gcc/java/parse.y +++ b/gcc/java/parse.y @@ -147,7 +147,9 @@ static tree java_complete_tree PARAMS ((tree)); static tree maybe_generate_pre_expand_clinit PARAMS ((tree)); static int analyze_clinit_body PARAMS ((tree)); static int maybe_yank_clinit PARAMS ((tree)); +static void start_complete_expand_method PARAMS ((tree)); static void java_complete_expand_method PARAMS ((tree)); +static void java_expand_method_bodies PARAMS ((tree)); static int unresolved_type_p PARAMS ((tree, tree *)); static void create_jdep_list PARAMS ((struct parser_ctxt *)); static tree build_expr_block PARAMS ((tree, tree)); @@ -332,6 +334,12 @@ static void create_new_parser_context PARAMS ((int)); static void mark_parser_ctxt PARAMS ((void *)); static tree maybe_build_class_init_for_field PARAMS ((tree, tree)); +static bool attach_init_test_initialization_flags PARAMS ((struct hash_entry *, + PTR)); +static bool adjust_init_test_initialization PARAMS ((struct hash_entry *, + PTR)); +static bool emit_test_initialization PARAMS ((struct hash_entry *, PTR)); + /* Number of error found so far. */ int java_error_count; /* Number of warning found so far. */ @@ -7513,12 +7521,17 @@ java_complete_expand_methods (class_decl) /* First, do the ordinary methods. */ for (decl = first_decl; decl; decl = TREE_CHAIN (decl)) { + /* Ctors aren't part of this batch. */ + if (DECL_CONSTRUCTOR_P (decl) || DECL_CLINIT_P (decl)) + continue; + /* Skip abstract or native methods -- but do handle native methods when generating JNI stubs. */ - if (METHOD_ABSTRACT (decl) - || (! flag_jni && METHOD_NATIVE (decl)) - || DECL_CONSTRUCTOR_P (decl) || DECL_CLINIT_P (decl)) - continue; + if (METHOD_ABSTRACT (decl) || (! flag_jni && METHOD_NATIVE (decl))) + { + DECL_FUNCTION_BODY (decl) = NULL_TREE; + continue; + } if (METHOD_NATIVE (decl)) { @@ -7751,6 +7764,40 @@ maybe_yank_clinit (mdecl) return 1; } +/* Install the argument from MDECL. Suitable to completion and + expansion of mdecl's body. */ + +static void +start_complete_expand_method (mdecl) + tree mdecl; +{ + tree tem, *ptr; + + pushlevel (1); /* Prepare for a parameter push */ + ptr = &DECL_ARGUMENTS (mdecl); + tem = BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (current_function_decl)); + + while (tem) + { + tree next = TREE_CHAIN (tem); + tree type = TREE_TYPE (tem); + if (PROMOTE_PROTOTYPES + && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) + && INTEGRAL_TYPE_P (type)) + type = integer_type_node; + DECL_ARG_TYPE (tem) = type; + layout_decl (tem, 0); + pushdecl (tem); + *ptr = tem; + ptr = &TREE_CHAIN (tem); + tem = next; + } + *ptr = NULL_TREE; + pushdecl_force_head (DECL_ARGUMENTS (mdecl)); + lineno = DECL_SOURCE_LINE_FIRST (mdecl); + build_result_decl (mdecl); +} + /* Complete and expand a method. */ @@ -7758,7 +7805,7 @@ static void java_complete_expand_method (mdecl) tree mdecl; { - int yank_clinit = 0; + tree fbody, block_body, exception_copy; current_function_decl = mdecl; /* Fix constructors before expanding them */ @@ -7766,103 +7813,131 @@ java_complete_expand_method (mdecl) fix_constructors (mdecl); /* Expand functions that have a body */ - if (DECL_FUNCTION_BODY (mdecl)) - { - tree fbody = DECL_FUNCTION_BODY (mdecl); - tree block_body = BLOCK_EXPR_BODY (fbody); - tree exception_copy = NULL_TREE; - tree tem, *ptr; - - current_function_decl = mdecl; - - if (! quiet_flag) - fprintf (stderr, " [%s.", - lang_printable_name (DECL_CONTEXT (mdecl), 0)); - announce_function (mdecl); - if (! quiet_flag) - fprintf (stderr, "]"); - - pushlevel (1); /* Prepare for a parameter push */ - ptr = &DECL_ARGUMENTS (mdecl); - tem = BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (current_function_decl)); - while (tem) - { - tree next = TREE_CHAIN (tem); - tree type = TREE_TYPE (tem); - if (PROMOTE_PROTOTYPES - && TYPE_PRECISION (type) < TYPE_PRECISION (integer_type_node) - && INTEGRAL_TYPE_P (type)) - type = integer_type_node; - DECL_ARG_TYPE (tem) = type; - layout_decl (tem, 0); - pushdecl (tem); - *ptr = tem; - ptr = &TREE_CHAIN (tem); - tem = next; - } - *ptr = NULL_TREE; - pushdecl_force_head (DECL_ARGUMENTS (mdecl)); - lineno = DECL_SOURCE_LINE_FIRST (mdecl); - - build_result_decl (mdecl); - - current_this - = (!METHOD_STATIC (mdecl) ? - BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (mdecl)) : NULL_TREE); - - /* Purge the `throws' list of unchecked exceptions. If we're - doing xref, save a copy of the list and re-install it - later. */ - if (flag_emit_xref) - exception_copy = copy_list (DECL_FUNCTION_THROWS (mdecl)); - - purge_unchecked_exceptions (mdecl); - - /* Install exceptions thrown with `throws' */ - PUSH_EXCEPTIONS (DECL_FUNCTION_THROWS (mdecl)); + if (!DECL_FUNCTION_BODY (mdecl)) + return; - if (block_body != NULL_TREE) - { - block_body = java_complete_tree (block_body); + fbody = DECL_FUNCTION_BODY (mdecl); + block_body = BLOCK_EXPR_BODY (fbody); + exception_copy = NULL_TREE; - if (! flag_emit_xref && ! METHOD_NATIVE (mdecl)) - check_for_initialization (block_body); - ctxp->explicit_constructor_p = 0; - } + current_function_decl = mdecl; - BLOCK_EXPR_BODY (fbody) = block_body; + if (! quiet_flag) + fprintf (stderr, " [%s.", + lang_printable_name (DECL_CONTEXT (mdecl), 0)); + announce_function (mdecl); + if (! quiet_flag) + fprintf (stderr, "]"); + + /* Prepare the function for tree completion */ + start_complete_expand_method (mdecl); - /* If we saw a return but couldn't evaluate it properly, we'll - have an error_mark_node here. */ - if (block_body != error_mark_node - && (block_body == NULL_TREE || CAN_COMPLETE_NORMALLY (block_body)) - && TREE_CODE (TREE_TYPE (TREE_TYPE (mdecl))) != VOID_TYPE - && !flag_emit_xref) - missing_return_error (current_function_decl); + /* Install the current this */ + current_this = (!METHOD_STATIC (mdecl) ? + BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (mdecl)) : NULL_TREE); - /* Check wether we could just get rid of clinit, now the picture - is complete. */ - if (!(yank_clinit = maybe_yank_clinit (mdecl))) - complete_start_java_method (mdecl); + /* Purge the `throws' list of unchecked exceptions. If we're doing + xref, save a copy of the list and re-install it later. */ + if (flag_emit_xref) + exception_copy = copy_list (DECL_FUNCTION_THROWS (mdecl)); + purge_unchecked_exceptions (mdecl); + + /* Install exceptions thrown with `throws' */ + PUSH_EXCEPTIONS (DECL_FUNCTION_THROWS (mdecl)); + + if (block_body != NULL_TREE) + { + block_body = java_complete_tree (block_body); - /* Don't go any further if we've found error(s) during the - expansion */ - if (!java_error_count && !yank_clinit) - source_end_java_method (); - else + /* Before we check initialization, attached all class initialization + variable to the block_body */ + hash_traverse (&DECL_FUNCTION_INIT_TEST_TABLE (mdecl), + attach_init_test_initialization_flags, block_body); + + if (! flag_emit_xref && ! METHOD_NATIVE (mdecl)) { - if (java_error_count) - pushdecl_force_head (DECL_ARGUMENTS (mdecl)); - poplevel (1, 0, 1); + unsigned int state = check_for_initialization (block_body); + + /* Go through all the flags marking the initialization of + static variables and see whether they're definitively + assigned, in which case the type is remembered as + definitively initialized in MDECL. */ + if (STATIC_CLASS_INIT_OPT_P ()) + { + hash_traverse (&DECL_FUNCTION_INIT_TEST_TABLE (mdecl), + attach_initialized_static_class, (PTR)&state); + + /* Always register the context as properly initialized in + MDECL. This used with caution helps removing extra + initialization of self. */ + if (METHOD_STATIC (mdecl)) + hash_lookup (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (mdecl), + (hash_table_key) DECL_CONTEXT (mdecl), + TRUE, NULL); + } } + ctxp->explicit_constructor_p = 0; + } + + BLOCK_EXPR_BODY (fbody) = block_body; + + /* If we saw a return but couldn't evaluate it properly, we'll have + an error_mark_node here. */ + if (block_body != error_mark_node + && (block_body == NULL_TREE || CAN_COMPLETE_NORMALLY (block_body)) + && TREE_CODE (TREE_TYPE (TREE_TYPE (mdecl))) != VOID_TYPE + && !flag_emit_xref) + missing_return_error (current_function_decl); - /* Pop the exceptions and sanity check */ - POP_EXCEPTIONS(); - if (currently_caught_type_list) - abort (); + /* See if we can get rid of <clinit> if MDECL happens to be <clinit> */ + maybe_yank_clinit (mdecl); - if (flag_emit_xref) - DECL_FUNCTION_THROWS (mdecl) = exception_copy; + /* Pop the current level, with special measures if we found errors. */ + if (java_error_count) + pushdecl_force_head (DECL_ARGUMENTS (mdecl)); + poplevel (1, 0, 1); + + /* Pop the exceptions and sanity check */ + POP_EXCEPTIONS(); + if (currently_caught_type_list) + abort (); + + /* Restore the copy of the list of exceptions if emitting xrefs. */ + if (flag_emit_xref) + DECL_FUNCTION_THROWS (mdecl) = exception_copy; +} + +/* For with each class for which there's code to generate. */ + +static void +java_expand_method_bodies (class) + tree class; +{ + tree decl; + for (decl = TYPE_METHODS (class); decl; decl = TREE_CHAIN (decl)) + { + if (!DECL_FUNCTION_BODY (decl)) + continue; + + current_function_decl = decl; + + /* It's time to assign the variable flagging static class + initialization based on which classes invoked static methods + are definitely initializing. This should be flagged. */ + if (STATIC_CLASS_INIT_OPT_P ()) + hash_traverse (&DECL_FUNCTION_STATIC_METHOD_INVOCATION_COMPOUND (decl), + adjust_init_test_initialization, NULL); + + /* Prepare the function for RTL expansion */ + start_complete_expand_method (decl); + + /* Expand function start, generate initialization flag + assignment, and handle synchronized methods. */ + complete_start_java_method (decl); + + /* Expand the rest of the function body and terminate + expansion. */ + source_end_java_method (); } } @@ -8783,7 +8858,10 @@ java_expand_classes () if (flag_emit_xref) expand_xref (current_class); else if (! flag_syntax_only) - finish_class (); + { + java_expand_method_bodies (current_class); + finish_class (); + } } } } @@ -8963,7 +9041,10 @@ resolve_expression_name (id, orig) static_ref_err (id, DECL_NAME (decl), current_class); return error_mark_node; } - return build_outer_field_access (id, decl); + access = build_outer_field_access (id, decl); + if (orig) + *orig = access; + return access; } /* Otherwise build what it takes to access the field */ @@ -10438,6 +10519,30 @@ patch_invoke (patch, method, args) TREE_SIDE_EFFECTS (patch) = 1; } + /* In order to be able to modify PATCH later, we SAVE_EXPR it and + put it as the first expression of a COMPOUND_EXPR. The second + expression being an empty statement to be later patched if + necessary. We remember a TREE_LIST (the PURPOSE is the method, + the VALUE is the compound) in a hashtable and return a + COMPOUND_EXPR built so that the result of the evaluation of the + original PATCH node is returned. */ + if (STATIC_CLASS_INIT_OPT_P () + && current_function_decl && METHOD_STATIC (method)) + { + tree list; + tree fndecl = current_function_decl; + tree save = save_expr (patch); + tree type = TREE_TYPE (patch); + + patch = build (COMPOUND_EXPR, type, save, empty_stmt_node); + list = build_tree_list (method, patch); + + hash_lookup (&DECL_FUNCTION_STATIC_METHOD_INVOCATION_COMPOUND (fndecl), + (const hash_table_key) list, TRUE, NULL); + + patch = build (COMPOUND_EXPR, type, patch, save); + } + return patch; } @@ -15853,3 +15958,111 @@ init_src_parse () /* Register roots with the garbage collector. */ ggc_add_tree_root (src_parse_roots, sizeof (src_parse_roots) / sizeof(tree)); } + + + +/* This section deals with the functions that are called when tables + recording class initialization information are traversed. */ + +/* Attach to PTR (a block) the declaration found in ENTRY. */ + +static bool +attach_init_test_initialization_flags (entry, ptr) + struct hash_entry *entry; + PTR ptr; +{ + tree block = (tree)ptr; + struct init_test_hash_entry *ite = (struct init_test_hash_entry *) entry; + + TREE_CHAIN (ite->init_test_decl) = BLOCK_EXPR_DECLS (block); + BLOCK_EXPR_DECLS (block) = ite->init_test_decl; + return true; +} + +/* This function is called for each statement calling a static + function. ENTRY is a TREE_LIST whose PURPOSE is the called + function and VALUE is a compound whose second operand can be + patched with static class initialization flag assignments. */ + +static bool +adjust_init_test_initialization (entry, info) + struct hash_entry *entry; + PTR info ATTRIBUTE_UNUSED; +{ + tree list = (tree)(entry->key); + tree called_method = TREE_PURPOSE (list); + tree compound = TREE_VALUE (list); + tree assignment_compound_list = build_tree_list (called_method, NULL); + + /* For each class definitely initialized in CALLED_METHOD, fill + ASSIGNMENT_COMPOUND with assignment to the class initialization flag. */ + hash_traverse (&DECL_FUNCTION_INITIALIZED_CLASS_TABLE (called_method), + emit_test_initialization, assignment_compound_list); + + if (TREE_VALUE (assignment_compound_list)) + TREE_OPERAND (compound, 1) = TREE_VALUE (assignment_compound_list); + + return true; +} + +/* This function is called for each classes that is known definitely + assigned when a given static method was called. This function + augments a compound expression (INFO) storing all assignment to + initialized static class flags if a flag already existed, otherwise + a new one is created. */ + +static bool +emit_test_initialization (entry, info) + struct hash_entry *entry; + PTR info; +{ + tree l = (tree) info; + tree decl, init; + + struct init_test_hash_entry *ite = (struct init_test_hash_entry *) + hash_lookup (&DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl), + entry->key, FALSE, NULL); + + /* If we haven't found a flag and we're dealing with self registered + with current_function_decl, then don't do anything. Self is + always added as definitely initialized but this information is + valid only if used outside the current function. */ + if (! ite) + { + if (current_function_decl != TREE_PURPOSE (l)) + ite = (struct init_test_hash_entry *) + hash_lookup (&DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl), + entry->key, TRUE, NULL); + else + return true; + } + + /* If we don't have a variable, create one and install it. */ + if (! ite->init_test_decl) + { + tree block; + + decl = build_decl (VAR_DECL, NULL_TREE, boolean_type_node); + MAYBE_CREATE_VAR_LANG_DECL_SPECIFIC (decl); + LOCAL_CLASS_INITIALIZATION_FLAG (decl) = 1; + DECL_CONTEXT (decl) = current_function_decl; + DECL_INITIAL (decl) = boolean_true_node; + + /* The trick is to find the right context for it. */ + block = BLOCK_SUBBLOCKS (GET_CURRENT_BLOCK (current_function_decl)); + TREE_CHAIN (decl) = BLOCK_EXPR_DECLS (block); + BLOCK_EXPR_DECLS (block) = decl; + ite->init_test_decl = decl; + } + else + decl = ite->init_test_decl; + + /* Now simply augment the compound that holds all the assignments + pertaining to this method invocation. */ + init = build (MODIFY_EXPR, boolean_type_node, decl, boolean_true_node); + TREE_SIDE_EFFECTS (init) = 1; + TREE_VALUE (l) = add_stmt_to_compound (TREE_VALUE (l), void_type_node, init); + TREE_SIDE_EFFECTS (TREE_VALUE (l)) = 1; + + return true; +} |