Another inliner patch

Andrew Haley aph@redhat.com
Thu Oct 3 08:49:00 GMT 2002


This testcase doesn't work correctly at -O3 because static
initializers are not called from inlined methods:

// Test to make sure static initializers are called

class bar
{
  public static int zog;
  public static int zag;

  static
  {
    zog = 12;
    zag = 2;
  }

  public bar() { } 
}

public class StaticConstructor
{
  static int foo ()
  {
    return new bar().zog;
  }

  public static void main(String args[])
  {
    System.out.println ("" + (foo() + bar.zag));
  }
}

The patch to fix this is rather complicated.

Andrew.


	* tree-inline.c (remap_block): All local class initialization
	flags go in the outermost scope.
	(expand_call_inline): Call java_inlining_map_static_initializers.
	(expand_call_inline): Call java_inlining_merge_static_initializers.


	* java/lang.c (merge_init_test_initialization): New.
	(java_inlining_merge_static_initializers): New.
	(inline_init_test_initialization): New.
	(java_inlining_map_static_initializers): New.


Index: tree-inline.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-inline.c,v
retrieving revision 1.31
diff -c -2 -p -r1.31 tree-inline.c
*** tree-inline.c	27 Sep 2002 12:48:04 -0000	1.31
--- tree-inline.c	3 Oct 2002 15:42:36 -0000
*************** remap_block (block, decls, id)
*** 337,340 ****
--- 337,357 ----
        tree new_var;
  
+       /* All local class initialization flags go in the outermost
+ 	 scope.  */
+       if (LOCAL_CLASS_INITIALIZATION_FLAG_P (old_var))
+ 	{
+ 	  /* We may already have one.  */
+ 	  if (! splay_tree_lookup (id->decl_map, (splay_tree_key) old_var))
+ 	    {
+ 	      tree outermost_block;
+ 	      new_var = remap_decl (old_var, id);
+ 	      DECL_ABSTRACT_ORIGIN (new_var) = NULL;
+ 	      outermost_block = DECL_SAVED_TREE (current_function_decl);
+ 	      TREE_CHAIN (new_var) = BLOCK_VARS (outermost_block);
+ 	      BLOCK_VARS (outermost_block) = new_var;
+ 	    }
+ 	  continue;
+ 	}
+ 
        /* Remap the variable.  */
        new_var = remap_decl (old_var, id);
*************** expand_call_inline (tp, walk_subtrees, d
*** 1181,1185 ****
  #else /* INLINER_FOR_JAVA */
    {
!     tree new_body = copy_body (id);
      TREE_TYPE (new_body) = TREE_TYPE (TREE_TYPE (fn));
      BLOCK_EXPR_BODY (expr)
--- 1198,1204 ----
  #else /* INLINER_FOR_JAVA */
    {
!     tree new_body;
!     java_inlining_map_static_initializers (fn, id->decl_map);
!     new_body = copy_body (id);
      TREE_TYPE (new_body) = TREE_TYPE (TREE_TYPE (fn));
      BLOCK_EXPR_BODY (expr)
*************** expand_call_inline (tp, walk_subtrees, d
*** 1222,1225 ****
--- 1241,1246 ----
        = add_stmt_to_compound (BLOCK_EXPR_BODY (expr), 
  			      TREE_TYPE (retvar), retvar);
+   
+   java_inlining_merge_static_initializers (fn, id->decl_map);
  #endif /* INLINER_FOR_JAVA */
  
Index: java/lang.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/lang.c,v
retrieving revision 1.109
diff -c -2 -p -r1.109 lang.c
*** java/lang.c	21 Sep 2002 02:19:44 -0000	1.109
--- java/lang.c	3 Oct 2002 15:42:37 -0000
*************** The Free Software Foundation is independ
*** 42,45 ****
--- 42,46 ----
  #include "diagnostic.h"
  #include "tree-inline.h"
+ #include "splay-tree.h"
  
  struct string_option
*************** static int process_option_with_no PARAMS
*** 63,75 ****
  					   const struct string_option *,
  					   int));
! static tree java_tree_inlining_walk_subtrees  PARAMS ((tree *,
! 						       int *,
! 						       walk_tree_fn,
! 						       void *,
! 						       void *));
  static int java_unsafe_for_reeval PARAMS ((tree));
  static bool java_can_use_bit_fields_p PARAMS ((void));
  
- 
  #ifndef TARGET_OBJECT_SUFFIX
  # define TARGET_OBJECT_SUFFIX ".o"
--- 64,79 ----
  					   const struct string_option *,
  					   int));
! static tree java_tree_inlining_walk_subtrees PARAMS ((tree *,
! 						      int *,
! 						      walk_tree_fn,
! 						      void *,
! 						      void *));
  static int java_unsafe_for_reeval PARAMS ((tree));
+ static int merge_init_test_initialization PARAMS ((void * *, 
+ 						   void *));
+ static int inline_init_test_initialization PARAMS ((void * *, 
+ 						    void *));
  static bool java_can_use_bit_fields_p PARAMS ((void));
  
  #ifndef TARGET_OBJECT_SUFFIX
  # define TARGET_OBJECT_SUFFIX ".o"
*************** java_unsafe_for_reeval (t)
*** 916,919 ****
--- 920,1032 ----
  
    return -1;
+ }
+ 
+ /* Every call to a static constructor has an associated boolean
+    variable which is in the outermost scope of the calling method.
+    This variable is used to avoid multiple calls to the static
+    constructor for each class.  
+ 
+    It looks somthing like this:
+ 
+    foo ()
+    {
+       boolean dummy = OtherClass.is_initialized;
+   
+      ...
+   
+      if (! dummy)
+        OtherClass.initialize();
+ 
+      ... use OtherClass.data ...
+    }
+ 
+    Each of these boolean variables has an entry in the
+    DECL_FUNCTION_INIT_TEST_TABLE of a method.  When inlining a method
+    we must merge the DECL_FUNCTION_INIT_TEST_TABLE from the function
+    being linlined and create the boolean variables in the outermost
+    scope of the method being inlined into.  */
+ 
+ /* Create a mapping from a boolean variable in a method being inlined
+    to one in the scope of the method being inlined into.  */
+ 
+ static int
+ merge_init_test_initialization (entry, x)
+      void * * entry;
+      void * x;
+ {
+   struct treetreehash_entry *ite = (struct treetreehash_entry *) *entry;
+   splay_tree decl_map = (splay_tree)x;
+   splay_tree_node n;
+   tree *init_test_decl;
+   
+   /* See if we have remapped this declaration.  If we haven't there's
+      a bug in the inliner.  */
+   n = splay_tree_lookup (decl_map, (splay_tree_key) ite->value);
+   if (! n)
+     abort ();
+ 
+   /* Create a new entry for the class and its remapped boolean
+      variable.  If we already have a mapping for this class we've
+      already initialized it, so don't overwrite the value.  */
+   init_test_decl = java_treetreehash_new
+     (DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl), ite->key);
+   if (!*init_test_decl)
+     *init_test_decl = (tree)n->value;
+ 
+   return true;
+ }
+ 
+ /* Merge the DECL_FUNCTION_INIT_TEST_TABLE from the function we're
+    inlining.  */
+ 
+ void
+ java_inlining_merge_static_initializers (fn, decl_map)
+      tree fn;
+      void *decl_map;
+ {
+   htab_traverse 
+     (DECL_FUNCTION_INIT_TEST_TABLE (fn),
+      merge_init_test_initialization, decl_map);
+ }
+ 
+ /* Lookup a DECL_FUNCTION_INIT_TEST_TABLE entry in the method we're
+    inlining into.  If we already have a corresponding entry in that
+    class we don't need to create another one, so we create a mapping
+    from the variable in the inlined class to the corresponding
+    pre-existing one.  */
+ 
+ static int
+ inline_init_test_initialization (entry, x)
+      void * * entry;
+      void * x;
+ {
+   struct treetreehash_entry *ite = (struct treetreehash_entry *) *entry;
+   splay_tree decl_map = (splay_tree)x;
+   
+   tree h = java_treetreehash_find 
+     (DECL_FUNCTION_INIT_TEST_TABLE (current_function_decl), ite->key);
+   if (! h)
+     return true;
+ 
+   splay_tree_insert (decl_map,
+ 		     (splay_tree_key) ite->value,
+ 		     (splay_tree_value) h);
+ 
+   return true;
+ }
+ 
+ /* Look up the boolean variables in the DECL_FUNCTION_INIT_TEST_TABLE
+    of a method being inlined.  For each hone, if we already have a
+    variable associated with the same class in the method being inlined
+    into, create a new mapping for it.  */
+ 
+ void
+ java_inlining_map_static_initializers (fn, decl_map)
+      tree fn;
+      void *decl_map;
+ {
+   htab_traverse 
+     (DECL_FUNCTION_INIT_TEST_TABLE (fn),
+      inline_init_test_initialization, decl_map);
  }
  
  



More information about the Gcc-patches mailing list