提交 0c292567 编写于 作者: R Rodrigo Kumpera

Implement fast stelemref.

	* class.c (mono_class_setup_vtable_general): Inject
	the array stelemref method on vectors of reference types.

	* class.c (mono_class_init): Properly handle the fact
	that arrays have different vtable sizes.

	* marshal.c (mono_marshal_get_virtual_stelemref): New
	function that returns a better tailored stelemref wrapper
	for a given array type.

	* method-to-ir.c (mono_method_to_ir): Emit a virtual
	call to the new faster stelemref wrapper.

	This schema uses 4 wrappers instead of one and puts
	it into the vtable of vectors of reference types.

	This change improves a synthetic benchmark of an
	equal mix of stores as compiling corlib by 25%.

	It improves pystones by 3-4%.
上级 08ce0295
......@@ -3752,6 +3752,13 @@ verify_class_overrides (MonoClass *class, MonoMethod **overrides, int onum)
}
return TRUE;
}
static gboolean
mono_class_need_stelemref_method (MonoClass *class)
{
return class->rank == 1 && MONO_TYPE_IS_REFERENCE (&class->element_class->byval_arg);
}
/*
* LOCKING: this is supposed to be called with the loader lock held.
*/
......@@ -3771,6 +3778,7 @@ mono_class_setup_vtable_general (MonoClass *class, MonoMethod **overrides, int o
int first_non_interface_slot;
#endif
GSList *virt_methods = NULL, *l;
int stelemref_slot = 0;
if (class->vtable)
return;
......@@ -3811,6 +3819,13 @@ mono_class_setup_vtable_general (MonoClass *class, MonoMethod **overrides, int o
max_vtsize += class->method.count;
/*Array have a slot for stelemref*/
if (mono_class_need_stelemref_method (class)) {
stelemref_slot = cur_slot;
++max_vtsize;
++cur_slot;
}
vtable = alloca (sizeof (gpointer) * max_vtsize);
memset (vtable, 0, sizeof (gpointer) * max_vtsize);
......@@ -3897,6 +3912,17 @@ mono_class_setup_vtable_general (MonoClass *class, MonoMethod **overrides, int o
}
}
/*Array have a slot for stelemref*/
if (mono_class_need_stelemref_method (class)) {
MonoMethod *method = mono_marshal_get_virtual_stelemref (class);
if (!method->slot)
method->slot = stelemref_slot;
else
g_assert (method->slot == stelemref_slot);
vtable [stelemref_slot] = method;
}
TRACE_INTERFACE_VTABLE (print_vtable_full (class, vtable, cur_slot, first_non_interface_slot, "AFTER INHERITING PARENT VTABLE", TRUE));
/* override interface methods */
for (i = 0; i < onum; i++) {
......@@ -4621,14 +4647,19 @@ mono_class_init (MonoClass *class)
class->ghcimpl = cached_info.ghcimpl;
class->has_cctor = cached_info.has_cctor;
} else if (class->rank == 1 && class->byval_arg.type == MONO_TYPE_SZARRAY) {
static int szarray_vtable_size = 0;
/* SZARRAY can have 2 vtable layouts, with and without the stelemref method.
* The first slot if for array with.
*/
static int szarray_vtable_size[2] = { 0 };
int slot = MONO_TYPE_IS_REFERENCE (&class->element_class->byval_arg) ? 0 : 1;
/* SZARRAY case */
if (!szarray_vtable_size) {
if (!szarray_vtable_size [slot]) {
mono_class_setup_vtable (class);
szarray_vtable_size = class->vtable_size;
szarray_vtable_size [slot] = class->vtable_size;
} else {
class->vtable_size = szarray_vtable_size;
class->vtable_size = szarray_vtable_size[slot];
}
} else if (class->generic_class && !MONO_CLASS_IS_INTERFACE (class)) {
MonoClass *gklass = class->generic_class->container_class;
......@@ -4726,6 +4757,7 @@ mono_class_init (MonoClass *class)
}
if (class->parent) {
int first_iface_slot;
/* This will compute class->parent->vtable_size for some classes */
mono_class_init (class->parent);
if (class->parent->exception_type) {
......@@ -4744,7 +4776,10 @@ mono_class_init (MonoClass *class)
if (mono_loader_get_last_error ())
goto leave;
}
setup_interface_offsets (class, class->parent->vtable_size);
first_iface_slot = class->parent->vtable_size;
if (mono_class_need_stelemref_method (class))
++first_iface_slot;
setup_interface_offsets (class, first_iface_slot);
} else {
setup_interface_offsets (class, 0);
}
......
......@@ -9562,6 +9562,361 @@ mono_marshal_get_unbox_wrapper (MonoMethod *method)
return res;
}
enum {
STELEMREF_OBJECT, /*no check at all*/
STELEMREF_SEALED_CLASS, /*check vtable->klass->element_type */
STELEMREF_CLASS, /*only the klass->parents check*/
STELEMREF_COMPLEX, /*arrays, interfaces, MBR or classes with variant generic args - go straight to icalls*/
STELEMREF_KIND_COUNT
};
static const char *strelemref_wrapper_name[] = {
"object", "sealed_class", "class", "complex"
};
static gboolean
is_monomorphic_array (MonoClass *klass)
{
MonoClass *element_class;
if (klass->rank != 1)
return FALSE;
element_class = klass->element_class;
return (element_class->flags & TYPE_ATTRIBUTE_SEALED) || element_class->valuetype;
}
static int
get_virtual_stelemref_kind (MonoClass *element_class)
{
if (element_class == mono_defaults.object_class)
return STELEMREF_OBJECT;
if (is_monomorphic_array (element_class))
return STELEMREF_SEALED_CLASS;
/*Arrays are sealed but are covariant on their element type, We can't use any of the fast paths.*/
if (MONO_CLASS_IS_INTERFACE (element_class) || element_class->marshalbyref || element_class->rank || mono_class_has_variant_generic_params (element_class))
return STELEMREF_COMPLEX;
if (element_class->flags & TYPE_ATTRIBUTE_SEALED)
return STELEMREF_SEALED_CLASS;
return STELEMREF_CLASS;
}
static void
load_array_element_address (MonoMethodBuilder *mb)
{
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class);
}
static void
load_array_class (MonoMethodBuilder *mb, int aklass)
{
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoObject, vtable));
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoVTable, klass));
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, element_class));
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_stloc (mb, aklass);
}
static void
load_value_class (MonoMethodBuilder *mb, int vklass)
{
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoObject, vtable));
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoVTable, klass));
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_stloc (mb, vklass);
}
#if 0
static void
record_vstore_2 (MonoObject *array, size_t index, MonoObject *value)
{
char *name = mono_type_get_full_name (array->vtable->klass->element_class);
printf ("slow vstore of %s\n", name);
g_free (name);
}
#endif
/*
* TODO:
* - Separate simple interfaces from variant interfaces or mbr types. This way we can avoid the icall for them.
* - Emit a (new) mono bytecode that produces OP_COND_EXC_NE_UN to raise ArrayTypeMismatch
* - Maybe mve some MonoClass field into the vtable to reduce the number of loads
* - Add a case for arrays of arrays.
*/
MonoMethod*
mono_marshal_get_virtual_stelemref (MonoClass *array_class)
{
static MonoMethod *cached_methods [STELEMREF_KIND_COUNT] = { NULL }; /*object iface sealed regular*/
static MonoMethodSignature *signature;
MonoMethodBuilder *mb;
MonoMethod *res;
int kind;
guint32 b1, b2, b3;
int aklass, vklass;
int array_slot_addr;
g_assert (array_class->rank == 1);
kind = get_virtual_stelemref_kind (array_class->element_class);
if (cached_methods [kind])
return cached_methods [kind];
mb = mono_mb_new_no_dup_name (mono_defaults.object_class, g_strdup_printf ("virt_stelemref_%s", strelemref_wrapper_name [kind]), MONO_WRAPPER_STELEMREF);
if (!signature) {
MonoMethodSignature *sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
/* void this::stelemref (size_t idx, void* value) */
sig->ret = &mono_defaults.void_class->byval_arg;
sig->hasthis = TRUE;
sig->params [0] = &mono_defaults.int_class->byval_arg; /* this is a natural sized int */
sig->params [1] = &mono_defaults.object_class->byval_arg;
signature = sig;
}
/*For now simply call plain old stelemref*/
switch (kind) {
case STELEMREF_OBJECT:
/* ldelema (implicit bound check) */
load_array_element_address (mb);
/* do_store */
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_byte (mb, CEE_STIND_REF);
mono_mb_emit_byte (mb, CEE_RET);
break;
case STELEMREF_COMPLEX:
/*
<ldelema (bound check)>
if (!value)
goto store;
if (!mono_object_isinst (value, aklass))
goto do_exception;
do_store:
*array_slot_addr = value;
do_exception:
throw new ArrayTypeMismatchException ();
*/
aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg);
#if 0
{
/*Use this to debug stores that are going thru the slow path*/
MonoMethodSignature *csig;
csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
csig->ret = &mono_defaults.void_class->byval_arg;
csig->params [0] = &mono_defaults.object_class->byval_arg;
csig->params [1] = &mono_defaults.int_class->byval_arg; /* this is a natural sized int */
csig->params [2] = &mono_defaults.object_class->byval_arg;
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_native_call (mb, csig, record_vstore_2);
}
#endif
/* ldelema (implicit bound check) */
load_array_element_address (mb);
mono_mb_emit_stloc (mb, array_slot_addr);
/* if (!value) goto do_store */
mono_mb_emit_ldarg (mb, 2);
b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
/* aklass = array->vtable->klass->element_class */
load_array_class (mb, aklass);
/*if (mono_object_isinst (value, aklass)) */
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldloc (mb, aklass);
mono_mb_emit_icall (mb, mono_object_isinst);
b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
/* do_store: */
mono_mb_patch_branch (mb, b1);
mono_mb_emit_ldloc (mb, array_slot_addr);
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_byte (mb, CEE_STIND_REF);
mono_mb_emit_byte (mb, CEE_RET);
/* do_exception: */
mono_mb_patch_branch (mb, b2);
mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
break;
case STELEMREF_SEALED_CLASS:
/*
<ldelema (bound check)>
if (!value)
goto store;
aklass = array->vtable->klass->element_class;
vklass = value->vtable->klass;
if (vklass != aklass)
goto do_exception;
do_store:
*array_slot_addr = value;
do_exception:
throw new ArrayTypeMismatchException ();
*/
aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg);
/* ldelema (implicit bound check) */
load_array_element_address (mb);
mono_mb_emit_stloc (mb, array_slot_addr);
/* if (!value) goto do_store */
mono_mb_emit_ldarg (mb, 2);
b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
/* aklass = array->vtable->klass->element_class */
load_array_class (mb, aklass);
/* vklass = value->vtable->klass */
load_value_class (mb, vklass);
/*if (vklass != aklass) goto do_exception; */
mono_mb_emit_ldloc (mb, aklass);
mono_mb_emit_ldloc (mb, vklass);
b2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
/* do_store: */
mono_mb_patch_branch (mb, b1);
mono_mb_emit_ldloc (mb, array_slot_addr);
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_byte (mb, CEE_STIND_REF);
mono_mb_emit_byte (mb, CEE_RET);
/* do_exception: */
mono_mb_patch_branch (mb, b2);
mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
break;
case STELEMREF_CLASS:
/*
the method:
<ldelema (bound check)>
if (!value)
goto do_store;
aklass = array->vtable->klass->element_class;
vklass = value->vtable->klass;
if (vklass->idepth < aklass->idepth)
goto do_exception;
if (vklass->supertypes [aklass->idepth - 1] != aklass)
goto do_exception;
do_store:
*array_slot_addr = value;
return;
long:
throw new ArrayTypeMismatchException ();
*/
aklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
vklass = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
array_slot_addr = mono_mb_add_local (mb, &mono_defaults.object_class->this_arg);
/* ldelema (implicit bound check) */
load_array_element_address (mb);
mono_mb_emit_stloc (mb, array_slot_addr);
/* if (!value) goto do_store */
mono_mb_emit_ldarg (mb, 2);
b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
/* aklass = array->vtable->klass->element_class */
load_array_class (mb, aklass);
/* vklass = value->vtable->klass */
load_value_class (mb, vklass);
/*if (mono_object_isinst (value, aklass)) */
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_ldloc (mb, aklass);
mono_mb_emit_icall (mb, mono_object_isinst);
b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
/* if (vklass->idepth < aklass->idepth) goto failue */
mono_mb_emit_ldloc (mb, vklass);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, idepth));
mono_mb_emit_byte (mb, CEE_LDIND_U2);
mono_mb_emit_ldloc (mb, aklass);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, idepth));
mono_mb_emit_byte (mb, CEE_LDIND_U2);
b2 = mono_mb_emit_branch (mb, CEE_BLT_UN);
/* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
mono_mb_emit_ldloc (mb, vklass);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, supertypes));
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_ldloc (mb, aklass);
mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoClass, idepth));
mono_mb_emit_byte (mb, CEE_LDIND_U2);
mono_mb_emit_icon (mb, 1);
mono_mb_emit_byte (mb, CEE_SUB);
mono_mb_emit_icon (mb, sizeof (void*));
mono_mb_emit_byte (mb, CEE_MUL);
mono_mb_emit_byte (mb, CEE_ADD);
mono_mb_emit_byte (mb, CEE_LDIND_I);
mono_mb_emit_ldloc (mb, aklass);
b3 = mono_mb_emit_branch (mb, CEE_BNE_UN);
/* do_store: */
mono_mb_patch_branch (mb, b1);
mono_mb_emit_ldloc (mb, array_slot_addr);
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_byte (mb, CEE_STIND_REF);
mono_mb_emit_byte (mb, CEE_RET);
/* do_exception: */
mono_mb_patch_branch (mb, b2);
mono_mb_patch_branch (mb, b3);
mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
break;
default:
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_ldarg (mb, 2);
mono_mb_emit_managed_call (mb, mono_marshal_get_stelemref (), NULL);
mono_mb_emit_byte (mb, CEE_RET);
g_assert (0);
}
res = mono_mb_create_method (mb, signature, 4);
res->flags |= METHOD_ATTRIBUTE_VIRTUAL;
cached_methods [kind] = res;
mono_mb_free (mb);
return res;
}
MonoMethod*
mono_marshal_get_stelemref ()
{
......
......@@ -265,6 +265,9 @@ mono_marshal_get_proxy_cancast (MonoClass *klass) MONO_INTERNAL;
MonoMethod *
mono_marshal_get_stelemref (void) MONO_INTERNAL;
MonoMethod*
mono_marshal_get_virtual_stelemref (MonoClass *array_class) MONO_INTERNAL;
MonoMethod*
mono_marshal_get_array_address (int rank, int elem_size) MONO_INTERNAL;
......
......@@ -8955,9 +8955,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
/* storing a NULL doesn't need any of the complex checks in stelemref */
if (generic_class_is_reference_type (cfg, klass) &&
!(sp [2]->opcode == OP_PCONST && sp [2]->inst_p0 == NULL)) {
MonoMethod* helper = mono_marshal_get_stelemref ();
MonoClass *obj_array = mono_array_class_get_cached (mono_defaults.object_class, 1);
MonoMethod *helper = mono_marshal_get_virtual_stelemref (obj_array);
MonoInst *iargs [3];
if (!helper->slot)
mono_class_setup_vtable (obj_array);
g_assert (helper->slot);
if (sp [0]->type != STACK_OBJ)
UNVERIFIED;
if (sp [2]->type != STACK_OBJ)
......@@ -8966,8 +8971,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
iargs [2] = sp [2];
iargs [1] = sp [1];
iargs [0] = sp [0];
mono_emit_method_call (cfg, helper, iargs, NULL);
mono_emit_method_call (cfg, helper, iargs, sp [0]);
} else {
if (sp [1]->opcode == OP_ICONST) {
int array_reg = sp [0]->dreg;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册