Patch: another try at PR gcj/2

Tom Tromey tromey@cygnus.com
Wed Apr 5 20:46:00 GMT 2000


This is another attempt to fix PR gcj/2 (invoking a final method on a
null object doesn't throw a null pointer exception).

This one takes a different approach: instead of doing a volatile
dereference of the object pointer, it does an explicit test and calls
a function in the runtime to throw the exception.

First, this is cleaner.  Also, this is better because it will work
even on MMU-less targets.  Once we get value range propagation, I
think that redundant calls will automatically be eliminated.
(Hopefully this will help with the StringBuffer problem too: we can
figure out how to tell the optimizer that `new' will never return
null.)

Ok to commit?

2000-04-05  Tom Tromey  <tromey@cygnus.com>

	Fix for PR gcj/2:
	* expr.c (expand_invoke): Generate check to see if object pointer
	is null in nonvirtual invocation case.
	* java-tree.h (soft_nullpointer_node): Declare.
	* decl.c (soft_nullpointer_node): New global.
	(init_decl_processing): Initialize soft_nullpointer_node.
	* parse.y (invocation_mode): Return INVOKE_NONVIRTUAL for `final'
	or `private' methods.
	(patch_invoke): Handle INVOKE_NONVIRTUAL case.

Tom

Index: decl.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/decl.c,v
retrieving revision 1.60
diff -u -r1.60 decl.c
--- decl.c	2000/04/04 20:40:19	1.60
+++ decl.c	2000/04/06 03:29:37
@@ -375,6 +375,7 @@
 tree soft_anewarray_node;
 tree soft_multianewarray_node;
 tree soft_badarrayindex_node;
+tree soft_nullpointer_node;
 tree throw_node [2];
 tree soft_checkarraystore_node;
 tree soft_monitorenter_node;
@@ -812,6 +813,15 @@
      effects.  */
   TREE_THIS_VOLATILE (soft_badarrayindex_node) = 1;
   TREE_SIDE_EFFECTS (soft_badarrayindex_node) = 1;
+
+  soft_nullpointer_node
+    = builtin_function ("_Jv_ThrowNullPointerException",
+			build_function_type (void_type_node, endlink),
+			0, NOT_BUILT_IN, NULL_PTR);
+  /* Mark soft_nullpointer_node as a `noreturn' function with side
+     effects.  */
+  TREE_THIS_VOLATILE (soft_nullpointer_node) = 1;
+  TREE_SIDE_EFFECTS (soft_nullpointer_node) = 1;
 
   t = tree_cons (NULL_TREE, class_ptr_type,
 		 tree_cons (NULL_TREE, object_ptr_type_node, endlink));
Index: expr.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/expr.c,v
retrieving revision 1.70
diff -u -r1.70 expr.c
--- expr.c	2000/03/30 23:41:57	1.70
+++ expr.c	2000/04/06 03:29:44
@@ -1719,6 +1719,7 @@
     (current_jcf, COMPONENT_REF_CLASS_INDEX(&current_jcf->cpool, method_ref_index));
   const char *self_name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (self_type)));
   tree call, func, method, arg_list, method_type;
+  tree cond = NULL_TREE;
 
   if (! CLASS_LOADED_P (self_type))
     {
@@ -1781,13 +1782,26 @@
   flush_quick_stack ();
 
   func = NULL_TREE;
-  if (opcode == OPCODE_invokestatic || opcode == OPCODE_invokespecial
-      || (opcode == OPCODE_invokevirtual
-	  && (METHOD_PRIVATE (method)
-	      || METHOD_FINAL (method) 
-	      || CLASS_FINAL (TYPE_NAME (self_type)))))
+  if (opcode == OPCODE_invokestatic)
     func = build_known_method_ref (method, method_type, self_type,
 				   method_signature, arg_list);
+  else if (opcode == OPCODE_invokespecial
+	   || (opcode == OPCODE_invokevirtual
+	       && (METHOD_PRIVATE (method)
+		   || METHOD_FINAL (method) 
+		   || CLASS_FINAL (TYPE_NAME (self_type)))))
+    {
+      /* If the object for the method call is null, we throw an
+	 exception.  We don't do this if the object is the current
+	 method's `this'.  In other cases we just rely on an
+	 optimization pass to eliminate redundant checks.  FIXME:
+	 Unfortunately there doesn't seem to be a way to determine
+	 what the current method is right now.  */
+      cond = build (EQ_EXPR, boolean_type_node,
+		    TREE_VALUE (arg_list), null_pointer_node);
+      func = build_known_method_ref (method, method_type, self_type,
+				     method_signature, arg_list);
+    }
   else
     {
       tree dtable = invoke_build_dtable (opcode == OPCODE_invokeinterface, 
@@ -1800,6 +1814,16 @@
   func = build1 (NOP_EXPR, build_pointer_type (method_type), func);
   call = build (CALL_EXPR, TREE_TYPE (method_type), func, arg_list, NULL_TREE);
   TREE_SIDE_EFFECTS (call) = 1;
+
+  if (cond != NULL_TREE)
+    {
+      call = build (COND_EXPR, void_type_node, cond,
+		    build (CALL_EXPR, void_type_node,
+			   build_address_of (soft_nullpointer_node),
+			   NULL_TREE, NULL_TREE),
+		    call);
+      TREE_SIDE_EFFECTS (call) = 1;
+    }
 
   if (TREE_CODE (TREE_TYPE (method_type)) == VOID_TYPE)
     expand_expr_stmt (call);
Index: java-tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/java-tree.h,v
retrieving revision 1.63
diff -u -r1.63 java-tree.h
--- java-tree.h	2000/03/25 18:34:13	1.63
+++ java-tree.h	2000/04/06 03:29:48
@@ -290,6 +290,7 @@
 extern tree soft_anewarray_node;
 extern tree soft_multianewarray_node;
 extern tree soft_badarrayindex_node;
+extern tree soft_nullpointer_node;
 extern tree throw_node[];
 extern tree soft_checkarraystore_node;
 extern tree soft_monitorenter_node;
Index: parse.y
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/parse.y,v
retrieving revision 1.147
diff -u -r1.147 parse.y
--- parse.y	2000/04/04 20:40:20	1.147
+++ parse.y	2000/04/06 03:31:13
@@ -9626,10 +9626,11 @@
 {
   tree dtable, func;
   tree original_call, t, ta;
+  tree cond = NULL_TREE;
 
   /* Last step for args: convert build-in types. If we're dealing with
      a new TYPE() type call, the first argument to the constructor
-     isn't found in the incomming argument list, but delivered by
+     isn't found in the incoming argument list, but delivered by
      `new' */
   t = TYPE_ARG_TYPES (TREE_TYPE (method));
   if (TREE_CODE (patch) == NEW_CLASS_EXPR)
@@ -9657,6 +9658,18 @@
 	  func = build_invokevirtual (dtable, method);
 	  break;
 
+	case INVOKE_NONVIRTUAL:
+	  /* If the object for the method call is null, we throw an
+	     exception.  We don't do this if the object is the current
+	     method's `this'.  In other cases we just rely on an
+	     optimization pass to eliminate redundant checks.  */
+	  if (TREE_VALUE (args) != current_this)
+	    {
+	      cond = build (EQ_EXPR, boolean_type_node,
+			    TREE_VALUE (args), null_pointer_node);
+	    }
+	  /* Fall through.  */
+
 	case INVOKE_SUPER:
 	case INVOKE_STATIC:
 	  func = build_known_method_ref (method, TREE_TYPE (method),
@@ -9712,6 +9725,19 @@
       TREE_SET_CODE (original_call, CALL_EXPR);
       patch = build (COMPOUND_EXPR, TREE_TYPE (new), patch, saved_new);
     }
+
+  /* If COND is set, then we are building a check to see if the object
+     is NULL.  */
+  if (cond != NULL_TREE)
+    {
+      patch = build (COND_EXPR, void_type_node, cond,
+		     build (CALL_EXPR, void_type_node,
+			    build_address_of (soft_nullpointer_node),
+			    NULL_TREE, NULL_TREE),
+		     patch);
+      TREE_SIDE_EFFECTS (patch) = 1;
+    }
+
   return patch;
 }
 
@@ -9725,17 +9751,22 @@
   if (super)
     return INVOKE_SUPER;
 
-  if (access & ACC_STATIC || access & ACC_FINAL || access & ACC_PRIVATE)
+  if (access & ACC_STATIC)
     return INVOKE_STATIC;
 
-  if (CLASS_FINAL (TYPE_NAME (DECL_CONTEXT (method))))
+  /* We have to look for a constructor before we handle nonvirtual
+     calls; otherwise the constructor will look nonvirtual.  */
+  if (DECL_CONSTRUCTOR_P (method))
     return INVOKE_STATIC;
-  
+
+  if (access & ACC_FINAL || access & ACC_PRIVATE)
+    return INVOKE_NONVIRTUAL;
+
+  if (CLASS_FINAL (TYPE_NAME (DECL_CONTEXT (method))))
+    return INVOKE_NONVIRTUAL;
+
   if (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method))))
     return INVOKE_INTERFACE;
-  
-  if (DECL_CONSTRUCTOR_P (method))
-    return INVOKE_STATIC;
 
   return INVOKE_VIRTUAL;
 }


More information about the Gcc-patches mailing list