Class initialization optimization.

Alexandre Petit-Bianco apbianco@cygnus.com
Fri Aug 3 17:32:00 GMT 2001


This patch implements some optimization with regard to class
initialization. It's the first phase and there will be others.

This patch extends the work of Anthony Green
( http://gcc.gnu.org/ml/gcc-patches/2000-02/msg00621.html ) to find out
what are the classes that static methods are definitely initializing,
by including the class initialization flags in the definite assignment
check.

When static methods are called, classes definitely initialized in that
methods have their initialization flags (eventually created for the
occasion) turned to true, which helps the compiler eliminate
unnecessary calls to Jv_InitClass. It implies that now all class
methods are walked before being expanded, which forced me to consider
the following patch in java/class.c to let me not endup with undefined
methods at link stage (at the cost of a 200K bloat in the .so:)

@@ -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);
     }

As for the results, consider this rather ideal case:

  class G {
    static void foo (int j) {
        if (j == 3) j = ZZ.y;
        else {
            if (j == 4) j = ZZ.y;
            else j = ZZ.y+1;
            j = GG.y;
        }
        j = OO.y;
    }
    static void bar (int i) {
        foo (3);
	ZZ.y = 3;
    }
}

The compiler will determine that after having called `foo' 'OO' and
`ZZ' will be initialized. This is used in `bar' to skip the
initialization of `ZZ'. gcc 3.0 `with -O3 and -fno-inline' generates the
following code for `bar':

	pushl	%ebp
	movl	%esp, %ebp
	pushl	%ebx
	subl	$16, %esp
	cmpb	$11, _ZN2ZZ6class$E+70	; Test readiness of ZZ
	pushl	$_ZN1G6class$E
	setg	%bl
	call	_Jv_InitClass
	movl	$3, (%esp)
	call	_ZN1G3fooEi
	addl	$16, %esp
	testb	%bl, %bl		; Initialize ZZ if necessary
	je	.L32
.L19:
	movl	$3, %eax
	movl	-4(%ebp), %ebx
	movl	%eax, _ZN2ZZ1yE		; Store 3 in ZZ.y
	movl	%ebp, %esp
	popl	%ebp
	ret
.L32:
        subl    $12, %esp
        pushl   $_ZN2ZZ6class$E
        call    _Jv_InitClass
        addl    $16, %esp
        jmp     .L19

With this the new class optimization (which kicks in at -O2 and
above) we get:

	pushl	%ebp
	movl	%esp, %ebp
	subl	$8, %esp
	movl	$_ZN1G6class$E, (%esp)
	call	_Jv_InitClass
	movl	$3, (%esp)
	call	_ZN1G3fooEi
	movl	$3, %eax
	movl	%eax, _ZN2ZZ1yE
	movl	%ebp, %esp
	popl	%ebp
	ret

The optimization doesn't apply to bytecode generation and can be
turned off using `-fno-optimize-static-class-initialization' (if you
have a better name, I'll change it but I'd like to keep it meaningful.)

This patch has been tested on x86 and alpha linux without showing any
regressions in extended tests (libgcj's `make check' included.) I'm
trying ppc as I speak. I thought I would let people play a bit with
it.

One possible improvement which was suggested by Anthony Green is to
create a section in the class file to store some information on
definitely initialized class so that this knowledge can be reused by
the compiler when the class files are kept around (like it's the case
with the runtime.) I also noticed that the optimization doesn't occur
across classes when using `@file', even though it should be giving us
better cross class visibility -- something I'll have to look into.

An other improvement which might require some profiling is to
pre-initialize some useful classes in the runtime and let the compiler
know about them.

./A

Index: check-init.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/check-init.c,v
retrieving revision 1.32
diff -u -p -r1.32 check-init.c
--- check-init.c	2001/08/01 02:28:42	1.32
+++ check-init.c	2001/08/03 23:13:51
@@ -25,6 +25,7 @@ The Free Software Foundation is independ
 #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;
 }
Index: class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/class.c,v
retrieving revision 1.101
diff -u -p -r1.101 class.c
--- class.c	2001/07/31 22:20:05	1.101
+++ class.c	2001/08/03 23:13:52
@@ -672,6 +672,18 @@ add_method_1 (handle_class, access_flags
 		   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);
     }
Index: decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/decl.c,v
retrieving revision 1.104
diff -u -p -r1.104 decl.c
--- decl.c	2001/08/01 02:28:42	1.104
+++ decl.c	2001/08/03 23:13:54
@@ -49,8 +49,6 @@ static tree push_jvm_slot PARAMS ((int, 
 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))
Index: expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/expr.c,v
retrieving revision 1.113
diff -u -p -r1.113 expr.c
--- expr.c	2001/07/12 17:06:40	1.113
+++ expr.c	2001/08/03 23:13:56
@@ -82,6 +82,8 @@ static tree build_java_check_indexed_typ
 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, tmod
 	{
 	  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)
 	    {
@@ -3334,4 +3361,38 @@ 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;
 }
Index: gcj.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/gcj.texi,v
retrieving revision 1.12
diff -u -p -r1.12 gcj.texi
--- gcj.texi	2001/07/04 16:16:43	1.12
+++ gcj.texi	2001/08/03 23:13:57
@@ -356,6 +356,14 @@ compiling a class with native methods, a
 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
 
 
Index: java-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/java-tree.h,v
retrieving revision 1.117
diff -u -p -r1.117 java-tree.h
--- java-tree.h	2001/08/03 00:20:50	1.117
+++ java-tree.h	2001/08/03 23:13:58
@@ -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 (
 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 PA
 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));
@@ -1558,6 +1584,10 @@ extern tree *type_map;
 #define IS_UNCHECKED_EXCEPTION_P(TYPE)				\
   (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;					\
 
Index: lang-options.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/lang-options.h,v
retrieving revision 1.27
diff -u -p -r1.27 lang-options.h
--- lang-options.h	2001/07/06 04:31:03	1.27
+++ lang-options.h	2001/08/03 23:13:58
@@ -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") },
Index: lang.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/lang.c,v
retrieving revision 1.67
diff -u -p -r1.67 lang.c
--- lang.c	2001/07/06 04:31:03	1.67
+++ lang.c	2001/08/03 23:13:58
@@ -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
@@ -291,6 +295,15 @@ java_decode_option (argc, argv)
   if (strncmp (p, ARG, sizeof (ARG) - 1) == 0)
     {
       current_encoding = p + sizeof (ARG) - 1;
+      return 1;
+    }
+#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
Index: parse.y
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/parse.y,v
retrieving revision 1.298
diff -u -p -r1.298 parse.y
--- parse.y	2001/08/01 02:28:42	1.298
+++ parse.y	2001/08/03 23:14:07
@@ -147,7 +147,9 @@ static tree java_complete_tree PARAMS ((
 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 PA
 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,14 +7764,48 @@ 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.  */
 
 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));
+  if (!DECL_FUNCTION_BODY (mdecl))
+    return;
 
-      purge_unchecked_exceptions (mdecl);
+  fbody = DECL_FUNCTION_BODY (mdecl);
+  block_body = BLOCK_EXPR_BODY (fbody);
+  exception_copy = NULL_TREE;
 
-      /* Install exceptions thrown with `throws' */
-      PUSH_EXCEPTIONS (DECL_FUNCTION_THROWS (mdecl));
+  current_function_decl = mdecl;
 
-      if (block_body != NULL_TREE)
-	{
-	  block_body = java_complete_tree (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);
+
+  /* Install the current this */
+  current_this = (!METHOD_STATIC (mdecl) ? 
+		  BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (mdecl)) : NULL_TREE);
 
-	  if (! flag_emit_xref && ! METHOD_NATIVE (mdecl))
-	    check_for_initialization (block_body);
-	  ctxp->explicit_constructor_p = 0;
+  /* 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);
+      
+      /* 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))
+	{
+	  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);
 
-      BLOCK_EXPR_BODY (fbody) = block_body;
+  /* See if we can get rid of <clinit> if MDECL happens to be <clinit> */
+  maybe_yank_clinit (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);
+  /* 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);
 
-      /* 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); 
-      
-      /* 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
-	{
-	  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 ();
 
-      /* 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;
+}
 
-      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;
 }
 
@@ -15852,4 +15957,112 @@ 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;
 }



More information about the Java mailing list