This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
PATCH: Reduce allocation overhead, fix IA64 GC descriptors
- To: "'java-patches at gcc dot gnu dot org'" <java-patches at gcc dot gnu dot org>
- Subject: PATCH: Reduce allocation overhead, fix IA64 GC descriptors
- From: "Boehm, Hans" <hans_boehm at hp dot com>
- Date: Thu, 1 Nov 2001 15:48:36 -0800
Here is a patch that mainly does four somewhat interrelated things:
1) Changes the vtable layout for Java on IA64, so that vtables are smaller,
and the garbage collector again works correctly. Only 2 words (i.e. one
descriptor's worth) are reserved for Java-specific stuff, i.e. the class
pointer and the GC descriptor. The GC descriptor is again in the second
word, where it more or less has to be.
2) Adds functions _Jv_AllocObjectNoInitNoFinalizer and
_Jv_AllocObjectNoFinalizer to the runtime, so that not all allocation calls
have to dynamically check for the existence of a finalizer, or an
uninitialized class. These checks are not free.
3) Fix the dynamic test for _Jv_AllocObject so that it works correctly on
IA64. The current code registers all objects for finalization on IA64,
which has horrible performance implications. (I was hoping to remove the
dynamic test completely. But that would have CNI and pobably JNI
implications. So I put it back in, but it's now infrequently executed.)
4) Changes the Java front end to invoke _Jv_AllocObjectNoFinalizer instead
of _Jv_AllocObject whenever possible. (The code to invoke
_Jv_AllocObjectNoInitNoFinalizer when appropriate is left an exercise to the
reader :-) I don't understand the class initialization logic well enough.
Once that's in place, we could directly invoke GC_gcj_malloc, which would
again substantially reduce allocation overhead.)
I'd appreciate it if someone could try this patch in isolation, since I
extracted it from a more heavily modified tree.
Suggestions? OK to commit to the trunk? Even better, can someone else do
so after more testing?
Hans
gcc/java/class.c: Fix java vtable layout for
TARGET_VTABLE_USES_DESCRIPTORS
gcc/java/decl.c,gcc/java/java-tree.h: Add alloc_no_finalizer_node.
gcc/java/expr.c, gcc/java/parse.y: Enable calls for finalizer-free
allocation.
libjava/prims.cc: Some old cleanups.
libjava/prims.cc, libjava/gcj/javaprims.h: Add
_Jv_AllocObjectNoInitNoFinalizer,
_Jv_AllocObjectNoFinalizer.
libjava/java/lang/Object.h, libjava/include/jvm.h: Adjust for
revised vtable layout on IA64.
Index: gcc/java/class.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/class.c,v
retrieving revision 1.115
diff -u -r1.115 class.c
--- class.c 2001/09/21 16:58:21 1.115
+++ class.c 2001/11/01 01:36:58
@@ -1376,6 +1376,7 @@
tree list = NULL_TREE;
int nvirtuals = TREE_VEC_LENGTH (vtable);
int arraysize;
+ tree gc_descr;
for (i = nvirtuals; --i >= 0; )
{
@@ -1417,15 +1418,17 @@
using the Boehm GC we sometimes stash a GC type descriptor
there. We set the PURPOSE to NULL_TREE not to interfere (reset)
the emitted byte count during the output to the assembly file. */
- for (j = 1; j < TARGET_VTABLE_USES_DESCRIPTORS; ++j)
- list = tree_cons (NULL_TREE, null_pointer_node, list);
- list = tree_cons (NULL_TREE, get_boehm_type_descriptor (type), list);
-
- for (j = 1; j < TARGET_VTABLE_USES_DESCRIPTORS; ++j)
- list = tree_cons (NULL_TREE, null_pointer_node, list);
+ /* With TARGET_VTABLE_USES_DESCRIPTORS, we only add one extra
+ fake "function descriptor". It's first word is the is the class
+ pointer, and subsequent words (usually one) contain the GC descriptor.
+ In all other cases, we reserve two extra vtable slots. */
+ gc_descr = get_boehm_type_descriptor (type);
+ list = tree_cons (NULL_TREE, gc_descr, list);
+ for (j = 1; j < TARGET_VTABLE_USES_DESCRIPTORS-1; ++j)
+ list = tree_cons (NULL_TREE, gc_descr, list);
list = tree_cons (integer_zero_node, this_class_addr, list);
- arraysize = nvirtuals + 2;
+ arraysize = (TARGET_VTABLE_USES_DESCRIPTORS? nvirtuals + 1 : nvirtuals +
2);
if (TARGET_VTABLE_USES_DESCRIPTORS)
arraysize *= TARGET_VTABLE_USES_DESCRIPTORS;
return build (CONSTRUCTOR,
Index: gcc/java/decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/decl.c,v
retrieving revision 1.109
diff -u -r1.109 decl.c
--- decl.c 2001/09/22 18:50:09 1.109
+++ decl.c 2001/11/01 01:36:58
@@ -729,6 +729,10 @@
build_function_type (ptr_type_node,
t),
0, NOT_BUILT_IN, NULL);
DECL_IS_MALLOC (alloc_object_node) = 1;
+ alloc_no_finalizer_node = builtin_function ("_Jv_AllocObjectNoFinalizer",
+ build_function_type (ptr_type_node,
t),
+ 0, NOT_BUILT_IN, NULL);
+ DECL_IS_MALLOC (alloc_no_finalizer_node) = 1;
t = tree_cons (NULL_TREE, ptr_type_node, endlink);
soft_initclass_node = builtin_function ("_Jv_InitClass",
Index: gcc/java/expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/expr.c,v
retrieving revision 1.117
diff -u -r1.117 expr.c
--- expr.c 2001/09/21 16:58:21 1.117
+++ expr.c 2001/11/01 01:36:58
@@ -1122,15 +1122,44 @@
return build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (value)), value);
}
+#define FINALIZE_INDEX 0
+
+bool class_has_finalize_method (type)
+ tree type;
+{
+ tree method;
+ tree super = CLASSTYPE_SUPER (type);
+
+ if (super == NULL_TREE)
+ return false; /* Every class with a real finalizer inherits */
+ /* from java.lang.Object. */
+ else
+ {
+ if (class_has_finalize_method(super)) return true;
+ for (method = TYPE_METHODS (type); method != NULL_TREE;
+ method = TREE_CHAIN (method))
+ {
+ if (DECL_VINDEX (method) != NULL_TREE
+ && tree_low_cst (DECL_VINDEX (method), 0) == FINALIZE_INDEX)
+ return true;
+ }
+ return false;
+ }
+}
+
static void
expand_java_NEW (type)
tree type;
{
+ tree alloc_node;
+
+ alloc_node = (class_has_finalize_method(type) ? alloc_object_node
+ : alloc_no_finalizer_node);
if (! CLASS_LOADED_P (type))
load_class (type, 1);
safe_layout_class (type);
push_value (build (CALL_EXPR, promote_type (type),
- build_address_of (alloc_object_node),
+ build_address_of (alloc_node),
tree_cons (NULL_TREE, build_class_ref (type),
build_tree_list (NULL_TREE,
size_in_bytes (type))),
@@ -1840,9 +1869,12 @@
= build_pointer_type (nativecode_ptr_type_node);
tree method_index = convert (sizetype, DECL_VINDEX (method));
- /* Add one to skip "class" field of dtable, and one to skip unused
- vtable entry (for C++ compatibility). */
- method_index = size_binop (PLUS_EXPR, method_index, size_int (2));
+ if (TARGET_VTABLE_USES_DESCRIPTORS)
+ /* Add one to skip bogus descriptor for class and GC descriptor. */
+ method_index = size_binop (PLUS_EXPR, method_index, size_int (1));
+ else
+ /* Add 1 to skip "class" field of dtable, and 1 to skip GC descriptor.
*/
+ method_index = size_binop (PLUS_EXPR, method_index, size_int (2));
method_index = size_binop (MULT_EXPR, method_index,
TYPE_SIZE_UNIT (nativecode_ptr_ptr_type_node));
Index: gcc/java/java-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/java-tree.h,v
retrieving revision 1.122
diff -u -r1.122 java-tree.h
--- java-tree.h 2001/09/14 22:58:37 1.122
+++ java-tree.h 2001/11/01 01:36:59
@@ -332,6 +332,7 @@
JTI_THROW_NODE,
JTI_ALLOC_OBJECT_NODE,
+ JTI_ALLOC_NO_FINALIZER_NODE,
JTI_SOFT_INSTANCEOF_NODE,
JTI_SOFT_CHECKCAST_NODE,
JTI_SOFT_INITCLASS_NODE,
@@ -566,6 +567,8 @@
java_global_trees[JTI_THROW_NODE]
#define alloc_object_node \
java_global_trees[JTI_ALLOC_OBJECT_NODE]
+#define alloc_no_finalizer_node \
+ java_global_trees[JTI_ALLOC_NO_FINALIZER_NODE]
#define soft_instanceof_node \
java_global_trees[JTI_SOFT_INSTANCEOF_NODE]
#define soft_checkcast_node \
@@ -1170,6 +1173,7 @@
extern void safe_layout_class PARAMS ((tree));
extern tree get_boehm_type_descriptor PARAMS ((tree));
+extern bool class_has_finalize_method PARAMS ((tree));
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));
Index: gcc/java/parse.y
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/parse.y,v
retrieving revision 1.321
diff -u -r1.321 parse.y
--- parse.y 2001/10/11 23:50:48 1.321
+++ parse.y 2001/11/01 01:37:00
@@ -10648,6 +10648,8 @@
{
tree class = DECL_CONTEXT (method);
tree c1, saved_new, size, new;
+ tree alloc_node;
+
if (flag_emit_class_files || flag_emit_xref)
{
TREE_TYPE (patch) = build_pointer_type (class);
@@ -10656,8 +10658,10 @@
if (!TYPE_SIZE (class))
safe_layout_class (class);
size = size_in_bytes (class);
+ alloc_node = (class_has_finalize_method(class) ? alloc_object_node
+ :
alloc_no_finalizer_node);
new = build (CALL_EXPR, promote_type (class),
- build_address_of (alloc_object_node),
+ build_address_of (alloc_node),
tree_cons (NULL_TREE, build_class_ref (class),
build_tree_list (NULL_TREE,
size_in_bytes (class))),
Index: libjava/prims.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/prims.cc,v
retrieving revision 1.62
diff -u -r1.62 prims.cc
--- prims.cc 2001/10/23 05:42:02 1.62
+++ prims.cc 2001/11/01 01:37:08
@@ -256,8 +256,6 @@
if (len < 0)
len = strlen (s);
Utf8Const* m = (Utf8Const*) _Jv_AllocBytes (sizeof(Utf8Const) + len + 1);
- if (! m)
- throw no_memory;
memcpy (m->data, s, len);
m->data[len] = 0;
m->length = len;
@@ -333,33 +331,13 @@
// The collector calls this when it encounters an out-of-memory condition.
void _Jv_ThrowNoMemory()
{
- _Jv_Throw (no_memory);
+ throw no_memory;
}
-// Allocate a new object of class KLASS. SIZE is the size of the object
-// to allocate. You might think this is redundant, but it isn't; some
-// classes, such as String, aren't of fixed size.
-jobject
-_Jv_AllocObject (jclass klass, jint size)
-{
- _Jv_InitClass (klass);
-
- jobject obj = (jobject) _Jv_AllocObj (size, klass);
-
- // If this class has inherited finalize from Object, then don't
- // bother registering a finalizer. We know that finalize() is the
- // very first method after the dummy entry. If this turns out to be
- // unreliable, a more robust implementation can be written. Such an
- // implementation would look for Object.finalize in Object's method
- // table at startup, and then use that information to find the
- // appropriate index in the method vector.
- if (klass->vtable->get_finalizer()
- != java::lang::Object::class$.vtable->get_finalizer())
- _Jv_RegisterFinalizer (obj, _Jv_FinalizeObject);
-
#ifdef ENABLE_JVMPI
- // Service JVMPI request.
-
+static void jvmpi_notify_alloc(jclass klass, jint size, jobject obj)
+{
+ // Service JVMPI allocation request.
if (__builtin_expect (_Jv_JVMPI_Notify_OBJECT_ALLOC != 0, false))
{
JVMPI_Event event;
@@ -384,8 +362,60 @@
(*_Jv_JVMPI_Notify_OBJECT_ALLOC) (&event);
_Jv_EnableGC ();
}
+}
+#else /* !ENABLE_JVMPI */
+# define jvmpi_notify_alloc(klass,size,obj) /* do nothing */
#endif
+// Allocate a new object of class KLASS. SIZE is the size of the object
+// to allocate. You might think this is redundant, but it isn't; some
+// classes, such as String, aren't of fixed size.
+// First a version that assumes that we have no finalizer, and that
+// the class is already initialized.
+// If we know that JVMPI is disabled, this can be replaced by a direct call
+// to the allocator for the appropriate GC.
+jobject
+_Jv_AllocObjectNoInitNoFinalizer (jclass klass, jint size)
+{
+ jobject obj = (jobject) _Jv_AllocObj (size, klass);
+ jvmpi_notify_alloc(klass, size, obj);
+ return obj;
+}
+
+// And now a version that initializes if necessary.
+jobject
+_Jv_AllocObjectNoFinalizer (jclass klass, jint size)
+{
+ _Jv_InitClass (klass);
+ jobject obj = (jobject) _Jv_AllocObj (size, klass);
+ jvmpi_notify_alloc(klass, size, obj);
+ return obj;
+}
+
+// And now the general version that registers a finalizer if necessary.
+jobject
+_Jv_AllocObject (jclass klass, jint size)
+{
+ jobject obj = _Jv_AllocObjectNoFinalizer (klass, size);
+
+ // We assume that the compiler only generates calls to this routine
+ // if there really is an interesting finalizer.
+ // Unfortunately, we still have to the dynamic test, since there may
+ // be cni calls to this routine.
+# ifndef __ia64__
+ if (klass->vtable->get_finalizer()
+ != java::lang::Object::class$.vtable->get_finalizer())
+ _Jv_RegisterFinalizer (obj, _Jv_FinalizeObject);
+# else
+ // On IA64, the vtable contains (pc,gp) pairs, not function pointers.
+ // Get_finalizer just returns a pointer to the vtable entry, which will
+ // never match the one in object. Hence we actually compare the pc
+ // fields.
+ if (((void **)klass->vtable->get_finalizer())[0]
+ != ((void **)
+ java::lang::Object::class$.vtable->get_finalizer())[0])
+ _Jv_RegisterFinalizer (obj, _Jv_FinalizeObject);
+# endif
return obj;
}
Index: libjava/gcj/javaprims.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gcj/javaprims.h,v
retrieving revision 1.31
diff -u -r1.31 javaprims.h
--- javaprims.h 2001/10/16 22:00:32 1.31
+++ javaprims.h 2001/11/01 01:37:09
@@ -384,6 +384,8 @@
typedef struct _Jv_Method *jmethodID;
extern "C" jobject _Jv_AllocObject (jclass, jint)
__attribute__((__malloc__));
+extern "C" jobject _Jv_AllocObjectNoFinalizer (jclass, jint)
__attribute__((__malloc__));
+extern "C" jobject _Jv_AllocObjectNoInitNoFinalizer (jclass, jint)
__attribute__((__malloc__));
#ifdef JV_HASH_SYNCHRONIZATION
extern "C" jobject _Jv_AllocPtrFreeObject (jclass, jint)
__attribute__((__malloc__));
Index: libjava/include/jvm.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/jvm.h,v
retrieving revision 1.45
diff -u -r1.45 jvm.h
--- jvm.h 2001/10/23 05:42:03 1.45
+++ jvm.h 2001/11/01 19:12:38
@@ -28,18 +28,12 @@
struct _Jv_VTable
{
#ifdef __ia64__
- jclass clas;
- unsigned long : 64;
- void *gc_descr;
- unsigned long : 64;
-
typedef struct { void *pc, *gp; } vtable_elt;
#else
- jclass clas;
- void *gc_descr;
-
typedef void *vtable_elt;
#endif
+ jclass clas;
+ void *gc_descr;
// This must be last, as derived classes "extend" this by
// adding new data members.
Index: libjava/java/lang/Object.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Object.h,v
retrieving revision 1.9
diff -u -r1.9 Object.h
--- Object.h 2001/05/24 05:40:37 1.9
+++ Object.h 2001/11/01 19:12:39
@@ -21,8 +21,11 @@
{
protected:
// New ABI Compatibility Dummy, #1 and 2.
- virtual void nacd_1 (void) {}; // This slot really contains the Class
pointer.
- virtual void nacd_2 (void) {}; // Actually the GC bitmap marking
descriptor.
+ virtual void nacd_1 (void) {}; // This slot really contains the Class
pointer.
+ // For IA64, the GC descriptor goes into the second word of the nacd1
descr.
+# ifndef __ia64__
+ virtual void nacd_2 (void) {}; // Actually the GC bitmap marking
descriptor.
+# endif
};
class java::lang::Object : public _JvObjectPrefix