OSDN Git Service

PR java/28892:
[pf3gnuchains/gcc-fork.git] / gcc / java / expr.c
index 785ccc4..b08d33e 100644 (file)
@@ -130,6 +130,10 @@ static GTY(()) tree quick_stack;
 /* A free-list of unused permanent TREE_LIST nodes.  */
 static GTY((deletable)) tree tree_list_free_list;
 
+/* The physical memory page size used in this computer.  See
+   build_field_ref().  */
+static GTY(()) tree page_size;
+
 /* The stack pointer of the Java virtual machine.
    This does include the size of the quick_stack. */
 
@@ -1447,7 +1451,6 @@ expand_iinc (unsigned int local_var_index, int ival, int pc)
   constant_value = build_int_cst (NULL_TREE, ival);
   res = fold_build2 (PLUS_EXPR, int_type_node, local_var, constant_value);
   java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (local_var), local_var, res));
-  update_aliases (local_var, local_var_index, pc);
 }
 
 
@@ -1678,18 +1681,37 @@ build_field_ref (tree self_value, tree self_class, tree name)
     }
   else
     {
-      int check = (flag_check_references
-                  && ! (DECL_P (self_value)
-                        && DECL_NAME (self_value) == this_identifier_node));
-
       tree base_type = promote_type (base_class);
+
+      /* CHECK is true if self_value is not the this pointer.  */
+      int check = (! (DECL_P (self_value)
+                     && DECL_NAME (self_value) == this_identifier_node));
+
+      /* Determine whether a field offset from NULL will lie within
+        Page 0: this is necessary on those GNU/Linux/BSD systems that
+        trap SEGV to generate NullPointerExceptions.  
+
+        We assume that Page 0 will be mapped with NOPERM, and that
+        memory may be allocated from any other page, so only field
+        offsets < pagesize are guaranteed to trap.  We also assume
+        the smallest page size we'll encounter is 4k bytes.  */
+      if (! flag_syntax_only && check && ! flag_check_references 
+         && ! flag_indirect_dispatch)
+       {
+         tree field_offset = byte_position (field_decl);
+         if (! page_size)
+           page_size = size_int (4096);              
+         check = ! INT_CST_LT_UNSIGNED (field_offset, page_size);
+       }
+
       if (base_type != TREE_TYPE (self_value))
        self_value = fold_build1 (NOP_EXPR, base_type, self_value);
       if (! flag_syntax_only && flag_indirect_dispatch)
        {
          tree otable_index
            = build_int_cst (NULL_TREE, get_symbol_table_index 
-                            (field_decl, &TYPE_OTABLE_METHODS (output_class)));
+                            (field_decl, NULL_TREE, 
+                             &TYPE_OTABLE_METHODS (output_class)));
          tree field_offset
            = build4 (ARRAY_REF, integer_type_node,
                      TYPE_OTABLE_DECL (output_class), otable_index,
@@ -1708,6 +1730,7 @@ build_field_ref (tree self_value, tree self_class, tree name)
                        field_offset);
          
          field_offset = fold (convert (sizetype, field_offset));
+         self_value = java_check_reference (self_value, check);
          address 
            = fold_build2 (PLUS_EXPR, 
                           build_pointer_type (TREE_TYPE (field_decl)),
@@ -1997,10 +2020,94 @@ build_class_init (tree clas, tree expr)
   return init;
 }
 
+\f
+
+/* Rewrite expensive calls that require stack unwinding at runtime to
+   cheaper alternatives.  The logic here performs these
+   transformations:
+
+   java.lang.Class.forName("foo") -> java.lang.Class.forName("foo", class$)
+   java.lang.Class.getClassLoader() -> java.lang.Class.getClassLoader(class$)
+
+*/
+
+typedef struct
+{
+  const char *classname;
+  const char *method;
+  const char *signature;
+  const char *new_signature;
+  int flags;
+  tree (*rewrite_arglist) (tree arglist);
+} rewrite_rule;
+
+/* Add this.class to the end of an arglist.  */
+
+static tree 
+rewrite_arglist_getclass (tree arglist)
+{
+  return chainon (arglist, 
+                 tree_cons (NULL_TREE, build_class_ref (output_class), NULL_TREE));
+}
+
+static rewrite_rule rules[] =
+  {{"java.lang.Class", "getClassLoader", "()Ljava/lang/ClassLoader;", 
+    "(Ljava/lang/Class;)Ljava/lang/ClassLoader;", 
+    ACC_FINAL|ACC_PRIVATE, rewrite_arglist_getclass},
+   {"java.lang.Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;",
+    "(Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Class;",
+    ACC_FINAL|ACC_PRIVATE|ACC_STATIC, rewrite_arglist_getclass},
+   {NULL, NULL, NULL, NULL, 0, NULL}};
+
+/* Scan the rules list for replacements for *METHOD_P and replace the
+   args accordingly.  If the rewrite results in an access to a private
+   method, update SPECIAL.*/
+
+void
+maybe_rewrite_invocation (tree *method_p, tree *arg_list_p, 
+                         tree *method_signature_p, tree *special)
+{
+  tree context = DECL_NAME (TYPE_NAME (DECL_CONTEXT (*method_p)));
+  rewrite_rule *p;
+  *special = NULL_TREE;
+
+  for (p = rules; p->classname; p++)
+    {
+      if (get_identifier (p->classname) == context)
+       {
+         tree method = DECL_NAME (*method_p);
+         if (get_identifier (p->method) == method
+             && get_identifier (p->signature) == *method_signature_p)
+           {
+             tree maybe_method
+               = lookup_java_method (DECL_CONTEXT (*method_p),
+                                     method,
+                                     get_identifier (p->new_signature));
+             if (! maybe_method && ! flag_verify_invocations)
+               {
+                 maybe_method
+                   = add_method (DECL_CONTEXT (*method_p), p->flags, 
+                                 method, get_identifier (p->new_signature));
+                 DECL_EXTERNAL (maybe_method) = 1;
+               }
+             *method_p = maybe_method;
+             gcc_assert (*method_p);
+             *arg_list_p = p->rewrite_arglist (*arg_list_p);
+             *method_signature_p = get_identifier (p->new_signature);
+             *special = integer_one_node;
+
+             break;
+           }
+       }
+    }
+}
+
+\f
+
 tree
 build_known_method_ref (tree method, tree method_type ATTRIBUTE_UNUSED,
                        tree self_type, tree method_signature ATTRIBUTE_UNUSED,
-                       tree arg_list ATTRIBUTE_UNUSED)
+                       tree arg_list ATTRIBUTE_UNUSED, tree special)
 {
   tree func;
   if (is_compiled_class (self_type))
@@ -2018,8 +2125,10 @@ build_known_method_ref (tree method, tree method_type ATTRIBUTE_UNUSED,
       else
        {
          tree table_index
-           = build_int_cst (NULL_TREE, get_symbol_table_index 
-                            (method, &TYPE_ATABLE_METHODS (output_class)));
+           = build_int_cst (NULL_TREE, 
+                            (get_symbol_table_index 
+                             (method, special,
+                              &TYPE_ATABLE_METHODS (output_class))));
          func 
            = build4 (ARRAY_REF,  
                      TREE_TYPE (TREE_TYPE (TYPE_ATABLE_DECL (output_class))),
@@ -2104,14 +2213,14 @@ invoke_build_dtable (int is_invoke_interface, tree arg_list)
    reused.  */
 
 int
-get_symbol_table_index (tree t, tree *symbol_table)
+get_symbol_table_index (tree t, tree special, tree *symbol_table)
 {
   int i = 1;
   tree method_list;
 
   if (*symbol_table == NULL_TREE)
     {
-      *symbol_table = build_tree_list (t, t);
+      *symbol_table = build_tree_list (special, t);
       return 1;
     }
   
@@ -2120,7 +2229,8 @@ get_symbol_table_index (tree t, tree *symbol_table)
   while (1)
     {
       tree value = TREE_VALUE (method_list);
-      if (value == t)
+      tree purpose = TREE_PURPOSE (method_list);
+      if (value == t && purpose == special)
        return i;
       i++;
       if (TREE_CHAIN (method_list) == NULL_TREE)
@@ -2129,12 +2239,12 @@ get_symbol_table_index (tree t, tree *symbol_table)
         method_list = TREE_CHAIN (method_list);
     }
 
-  TREE_CHAIN (method_list) = build_tree_list (t, t);
+  TREE_CHAIN (method_list) = build_tree_list (special, t);
   return i;
 }
 
 tree 
-build_invokevirtual (tree dtable, tree method)
+build_invokevirtual (tree dtable, tree method, tree special)
 {
   tree func;
   tree nativecode_ptr_ptr_type_node
@@ -2148,7 +2258,8 @@ build_invokevirtual (tree dtable, tree method)
 
       otable_index 
        = build_int_cst (NULL_TREE, get_symbol_table_index 
-                        (method, &TYPE_OTABLE_METHODS (output_class)));
+                        (method, special,
+                         &TYPE_OTABLE_METHODS (output_class)));
       method_index = build4 (ARRAY_REF, integer_type_node, 
                             TYPE_OTABLE_DECL (output_class), 
                             otable_index, NULL_TREE, NULL_TREE);
@@ -2204,7 +2315,7 @@ build_invokeinterface (tree dtable, tree method)
     {
       int itable_index 
        = 2 * (get_symbol_table_index 
-              (method, &TYPE_ITABLE_METHODS (output_class)));
+              (method, NULL_TREE, &TYPE_ITABLE_METHODS (output_class)));
       interface 
        = build4 (ARRAY_REF, 
                 TREE_TYPE (TREE_TYPE (TYPE_ITABLE_DECL (output_class))),
@@ -2257,6 +2368,8 @@ expand_invoke (int opcode, int method_ref_index, int nargs ATTRIBUTE_UNUSED)
   tree call, func, method, arg_list, method_type;
   tree check = NULL_TREE;
 
+  tree special = NULL_TREE;
+
   if (! CLASS_LOADED_P (self_type))
     {
       load_class (self_type, 1);
@@ -2371,10 +2484,13 @@ expand_invoke (int opcode, int method_ref_index, int nargs ATTRIBUTE_UNUSED)
   arg_list = pop_arguments (TYPE_ARG_TYPES (method_type));
   flush_quick_stack ();
 
+  maybe_rewrite_invocation (&method, &arg_list, &method_signature,
+                           &special);
+
   func = NULL_TREE;
   if (opcode == OPCODE_invokestatic)
     func = build_known_method_ref (method, method_type, self_type,
-                                  method_signature, arg_list);
+                                  method_signature, arg_list, special);
   else if (opcode == OPCODE_invokespecial
           || (opcode == OPCODE_invokevirtual
               && (METHOD_PRIVATE (method)
@@ -2394,14 +2510,14 @@ expand_invoke (int opcode, int method_ref_index, int nargs ATTRIBUTE_UNUSED)
       TREE_VALUE (arg_list) = save_arg;
       check = java_check_reference (save_arg, ! DECL_INIT_P (method));
       func = build_known_method_ref (method, method_type, self_type,
-                                    method_signature, arg_list);
+                                    method_signature, arg_list, special);
     }
   else
     {
       tree dtable = invoke_build_dtable (opcode == OPCODE_invokeinterface, 
                                         arg_list);
       if (opcode == OPCODE_invokevirtual)
-       func = build_invokevirtual (dtable, method);
+       func = build_invokevirtual (dtable, method, special);
       else
        func = build_invokeinterface (dtable, method);
     }
@@ -2625,6 +2741,25 @@ build_jni_stub (tree method)
   return bind;
 }
 
+
+/* Given lvalue EXP, return a volatile expression that references the
+   same object.  */
+
+tree
+java_modify_addr_for_volatile (tree exp)
+{
+  tree exp_type = TREE_TYPE (exp);
+  tree v_type 
+    = build_qualified_type (exp_type,
+                           TYPE_QUALS (exp_type) | TYPE_QUAL_VOLATILE);
+  tree addr = build_fold_addr_expr (exp);
+  v_type = build_pointer_type (v_type);
+  addr = fold_convert (v_type, addr);
+  exp = build_fold_indirect_ref (addr);
+  return exp;
+}
+
+
 /* Expand an operation to extract from or store into a field.
    IS_STATIC is 1 iff the field is static.
    IS_PUTTING is 1 for putting into a field;  0 for getting from the field.
@@ -2648,6 +2783,7 @@ expand_java_field_op (int is_static, int is_putting, int field_ref_index)
   int is_error = 0;
   tree original_self_type = self_type;
   tree field_decl;
+  tree modify_expr;
   
   if (! CLASS_LOADED_P (self_type))
     load_class (self_type, 1);  
@@ -2668,6 +2804,13 @@ expand_java_field_op (int is_static, int is_putting, int field_ref_index)
                                  field_type, flags); 
          DECL_ARTIFICIAL (field_decl) = 1;
          DECL_IGNORED_P (field_decl) = 1;
+#if 0
+         /* FIXME: We should be pessimistic about volatility.  We
+            don't know one way or another, but this is safe.
+            However, doing this has bad effects on code quality.  We
+            need to look at better ways to do this.  */
+         TREE_THIS_VOLATILE (field_decl) = 1;
+#endif
        }
       else
        {      
@@ -2703,27 +2846,49 @@ expand_java_field_op (int is_static, int is_putting, int field_ref_index)
          if (DECL_CONTEXT (field_decl) != current_class)
             error ("assignment to final field %q+D not in field's class",
                    field_decl);
-         else if (FIELD_STATIC (field_decl))
-           {
-             if (!DECL_CLINIT_P (current_function_decl))
-               warning (0, "assignment to final static field %q+D not in "
-                         "class initializer",
-                         field_decl);
-           }
-         else
-           {
-             tree cfndecl_name = DECL_NAME (current_function_decl);
-             if (! DECL_CONSTRUCTOR_P (current_function_decl)
-                 && !ID_FINIT_P (cfndecl_name))
-                warning (0, "assignment to final field %q+D not in constructor",
-                        field_decl);
-           }
-       }
-      java_add_stmt (build2 (MODIFY_EXPR, TREE_TYPE (field_ref),
-                            field_ref, new_value));
+         /* We used to check for assignments to final fields not
+            occurring in the class initializer or in a constructor
+            here.  However, this constraint doesn't seem to be
+            enforced by the JVM.  */
+       }      
+
+      if (TREE_THIS_VOLATILE (field_decl))
+       field_ref = java_modify_addr_for_volatile (field_ref);
+
+      modify_expr = build2 (MODIFY_EXPR, TREE_TYPE (field_ref),
+                           field_ref, new_value);
+
+      if (TREE_THIS_VOLATILE (field_decl))
+       java_add_stmt 
+         (build3 
+          (CALL_EXPR, void_type_node,
+           build_address_of (built_in_decls[BUILT_IN_SYNCHRONIZE]),
+           NULL_TREE, NULL_TREE));
+         
+      java_add_stmt (modify_expr);
     }
   else
-    push_value (field_ref);
+    {
+      tree temp = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (field_ref));
+      java_add_local_var (temp);
+
+      if (TREE_THIS_VOLATILE (field_decl))
+       field_ref = java_modify_addr_for_volatile (field_ref);
+
+      modify_expr 
+       = build2 (MODIFY_EXPR, TREE_TYPE (field_ref), temp, field_ref);
+      java_add_stmt (modify_expr);
+
+      if (TREE_THIS_VOLATILE (field_decl))
+       java_add_stmt 
+         (build3 
+          (CALL_EXPR, void_type_node,
+           build_address_of (built_in_decls[BUILT_IN_SYNCHRONIZE]),
+           NULL_TREE, NULL_TREE));
+
+      push_value (temp);
+    }      
+  TREE_THIS_VOLATILE (field_ref) = TREE_THIS_VOLATILE (field_decl);
 }
 
 void
@@ -3012,6 +3177,12 @@ java_push_constant_from_pool (JCF *jcf, int index)
       c = build_ref_from_constant_pool (index);
       c = convert (promote_type (string_type_node), c);
     }
+  else if (JPOOL_TAG (jcf, index) == CONSTANT_Class
+          || JPOOL_TAG (jcf, index) == CONSTANT_ResolvedClass)
+    {
+      tree record = get_class_constant (jcf, index);
+      c = build_class_ref (record);
+    }
   else
     c = get_constant (jcf, index);
   push_value (c);
@@ -3230,7 +3401,6 @@ process_jvm_instruction (int PC, const unsigned char* byte_ops,
     decl = find_local_variable (index, type, oldpc);           \
     set_local_type (index, type);                              \
     java_add_stmt (build2 (MODIFY_EXPR, type, decl, value));   \
-    update_aliases (decl, index, PC);                          \
   }
 
 #define STORE(OPERAND_TYPE, OPERAND_VALUE) \