aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorAlexandre Petit-Bianco <apbianco@redhat.com>2001-08-09 04:19:12 +0000
committerAlexandre Petit-Bianco <apbianco@redhat.com>2001-08-09 04:19:12 +0000
commit358ff2a2827efce7818d9e4c6a7c6f441ad53656 (patch)
tree1c89de048bc9427066e52a374b05b125c6ec6ba2 /gcc
parent52c92b1e211f8864a731eee1bf2e9d5ac891d843 (diff)
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. (http://gcc.gnu.org/ml/gcc-patches/2001-08/msg00454.html) git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@44733 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/java/ChangeLog53
-rw-r--r--gcc/java/check-init.c44
-rw-r--r--gcc/java/class.c29
-rw-r--r--gcc/java/decl.c39
-rw-r--r--gcc/java/expr.c69
-rw-r--r--gcc/java/gcj.texi8
-rw-r--r--gcc/java/java-tree.h36
-rw-r--r--gcc/java/lang-options.h2
-rw-r--r--gcc/java/lang.c13
-rw-r--r--gcc/java/parse.y401
10 files changed, 541 insertions, 153 deletions
diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog
index 5273aaf5cfa..7734a5f1dae 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 0e340dd8541..b108c45ce3a 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 8113340e3fc..7e153b5b8f5 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 3b856db50d8..4eb073933a9 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 c9b4a3cd833..f7056fee4f4 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 1ccd7c9ed1f..6a52fc64ca5 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 03d3cc2e3e6..d114747a8e7 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 17a16966551..06a4c4d6910 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 91f89be08e6..b9cb0fea7b6 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 05be63314a8..a1f36aaa281 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;
+}