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(¤t_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