#include "mini.h"
-//#define ALLOW_PARTIAL_SHARING TRUE
-#define ALLOW_PARTIAL_SHARING FALSE
+#define ALLOW_PARTIAL_SHARING TRUE
+//#define ALLOW_PARTIAL_SHARING FALSE
#if 0
#define DEBUG(...) __VA_ARGS__
static void
mono_class_unregister_image_generic_subclasses (MonoImage *image, gpointer user_data);
+static gboolean partial_supported;
+
+static inline gboolean
+partial_sharing_supported (void)
+{
+ if (!ALLOW_PARTIAL_SHARING)
+ return FALSE;
+ /* Enable this only when AOT compiling or running in full-aot mode */
+ if (partial_supported || mono_aot_only)
+ return TRUE;
+ return FALSE;
+}
+
static int
type_check_context_used (MonoType *type, gboolean recursive)
{
case MONO_RGCTX_INFO_VTABLE:
case MONO_RGCTX_INFO_TYPE:
case MONO_RGCTX_INFO_REFLECTION_TYPE:
- case MONO_RGCTX_INFO_CAST_CACHE: {
+ case MONO_RGCTX_INFO_CAST_CACHE:
+ case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
+ case MONO_RGCTX_INFO_VALUE_SIZE:
+ case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
+ case MONO_RGCTX_INFO_MEMCPY:
+ case MONO_RGCTX_INFO_BZERO:
+ case MONO_RGCTX_INFO_LOCAL_OFFSET:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
gpointer result = mono_class_inflate_generic_type_with_mempool (temporary ? NULL : class->image,
data, context, &error);
g_assert (mono_error_ok (&error)); /*FIXME proper error handling*/
g_assert (inflated_method->klass == inflated_class);
return inflated_method;
}
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
+ MonoGSharedVtMethodInfo *info = data;
+ MonoGSharedVtMethodInfo *res;
+ int i;
+
+ // FIXME:
+ res = g_new0 (MonoGSharedVtMethodInfo, 1);
+ /*
+ res->nlocals = info->nlocals;
+ res->locals_types = g_new0 (MonoType*, info->nlocals);
+ for (i = 0; i < info->nlocals; ++i)
+ res->locals_types [i] = mono_class_inflate_generic_type (info->locals_types [i], context);
+ */
+ res->entries = g_ptr_array_new ();
+ for (i = 0; i < info->entries->len; ++i) {
+ MonoRuntimeGenericContextInfoTemplate *otemplate = g_ptr_array_index (info->entries, i);
+ MonoRuntimeGenericContextInfoTemplate *template = g_new0 (MonoRuntimeGenericContextInfoTemplate, 1);
+
+ memcpy (template, otemplate, sizeof (MonoRuntimeGenericContextInfoTemplate));
+ template->data = inflate_info (template, context, class, FALSE);
+ g_ptr_array_add (res->entries, template);
+ }
+ return res;
+ }
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
+ MonoJumpInfoGSharedVtCall *info = data;
+ MonoMethod *method = info->method;
+ MonoMethod *inflated_method;
+ MonoType *inflated_type = mono_class_inflate_generic_type (&method->klass->byval_arg, context);
+ MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
+ MonoJumpInfoGSharedVtCall *res;
+
+ // FIXME:
+ res = g_new0 (MonoJumpInfoGSharedVtCall, 1);
+ /* Keep the original signature */
+ res->sig = info->sig;
+
+ mono_metadata_free_type (inflated_type);
- case MONO_RGCTX_INFO_CLASS_FIELD: {
+ mono_class_init (inflated_class);
+
+ g_assert (!method->wrapper_type);
+
+ if (inflated_class->byval_arg.type == MONO_TYPE_ARRAY ||
+ inflated_class->byval_arg.type == MONO_TYPE_SZARRAY) {
+ inflated_method = mono_method_search_in_array_class (inflated_class,
+ method->name, method->signature);
+ } else {
+ inflated_method = mono_class_inflate_generic_method (method, context);
+ }
+ mono_class_init (inflated_method->klass);
+ g_assert (inflated_method->klass == inflated_class);
+ res->method = inflated_method;
+
+ return res;
+ }
+
+ case MONO_RGCTX_INFO_CLASS_FIELD:
+ case MONO_RGCTX_INFO_FIELD_OFFSET: {
MonoClassField *field = data;
MonoType *inflated_type = mono_class_inflate_generic_type (&field->parent->byval_arg, context);
MonoClass *inflated_class = mono_class_from_mono_type (inflated_type);
return &inflated_class->fields [i];
}
+ case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
+ MonoMethodSignature *sig = data;
+ MonoMethodSignature *isig;
+ MonoError error;
+
+ isig = mono_inflate_generic_signature (sig, context, &error);
+ g_assert (mono_error_ok (&error));
+ return isig;
+ }
default:
g_assert_not_reached ();
gboolean allow_partial)
{
int i;
+ gboolean has_ref = FALSE;
for (i = 0; i < inst->type_argc; ++i) {
MonoType *type = inst->type_argv [i];
- if (MONO_TYPE_IS_REFERENCE (type) || (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR)))
+ if (MONO_TYPE_IS_REFERENCE (type) || (allow_type_vars && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))) {
+ has_ref = TRUE;
continue;
+ }
/*
* Allow non ref arguments, if there is at least one ref argument
return FALSE;
}
- return TRUE;
+ if (allow_partial)
+ return has_ref;
+ else
+ return TRUE;
}
/*
* get_shared_class:
*
* Return the class used to store information when using generic sharing.
- * For fully shared classes, it is the generic definition, for partially shared
- * classes, it is an instance with all ref type arguments replaced by the type parameters
- * of its generic definition.
*/
static MonoClass*
get_shared_class (MonoClass *class)
*/
//g_assert_not_reached ();
+#if 0
+ /* The gsharedvt changes break this */
+ if (ALLOW_PARTIAL_SHARING)
+ g_assert_not_reached ();
+#endif
+
+#if 0
if (class->is_inflated) {
MonoGenericContext *context = &class->generic_class->context;
MonoGenericContext *container_context;
return class;
}
}
+#endif
+ // FIXME: Use this in all cases can be problematic wrt domain/assembly unloading
return class_uninstantiated (class);
}
cache_data [1] = (gpointer)class;
return cache_data;
}
+ case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
+ return GUINT_TO_POINTER (mono_class_array_element_size (class));
+ case MONO_RGCTX_INFO_VALUE_SIZE:
+ if (MONO_TYPE_IS_REFERENCE (&class->byval_arg))
+ return GUINT_TO_POINTER (sizeof (gpointer));
+ else
+ return GUINT_TO_POINTER (mono_class_value_size (class, NULL));
+ case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
+ if (MONO_TYPE_IS_REFERENCE (&class->byval_arg))
+ return GUINT_TO_POINTER (1);
+ else if (mono_class_is_nullable (class))
+ return GUINT_TO_POINTER (2);
+ else
+ return GUINT_TO_POINTER (0);
+ case MONO_RGCTX_INFO_MEMCPY:
+ case MONO_RGCTX_INFO_BZERO: {
+ static MonoMethod *memcpy_method [17];
+ static MonoMethod *bzero_method [17];
+ MonoJitDomainInfo *domain_info;
+ int size;
+ guint32 align;
+
+ domain_info = domain_jit_info (domain);
+
+ if (MONO_TYPE_IS_REFERENCE (&class->byval_arg)) {
+ size = sizeof (gpointer);
+ align = sizeof (gpointer);
+ } else {
+ size = mono_class_value_size (class, &align);
+ }
+
+ if (size != 1 && size != 2 && size != 4 && size != 8)
+ size = 0;
+ if (align < size)
+ size = 0;
+
+ if (info_type == MONO_RGCTX_INFO_MEMCPY) {
+ if (!memcpy_method [size]) {
+ MonoMethod *m;
+ char name [32];
+
+ if (size == 0)
+ sprintf (name, "memcpy");
+ else
+ sprintf (name, "memcpy_aligned_%d", size);
+ m = mono_class_get_method_from_name (mono_defaults.string_class, name, 3);
+ g_assert (m);
+ mono_memory_barrier ();
+ memcpy_method [size] = m;
+ }
+ if (!domain_info->memcpy_addr [size]) {
+ gpointer addr = mono_compile_method (memcpy_method [size]);
+ mono_memory_barrier ();
+ domain_info->memcpy_addr [size] = addr;
+ }
+ return domain_info->memcpy_addr [size];
+ } else {
+ if (!bzero_method [size]) {
+ MonoMethod *m;
+ char name [32];
+
+ if (size == 0)
+ sprintf (name, "bzero");
+ else
+ sprintf (name, "bzero_aligned_%d", size);
+ m = mono_class_get_method_from_name (mono_defaults.string_class, name, 2);
+ g_assert (m);
+ mono_memory_barrier ();
+ bzero_method [size] = m;
+ }
+ if (!domain_info->bzero_addr [size]) {
+ gpointer addr = mono_compile_method (bzero_method [size]);
+ mono_memory_barrier ();
+ domain_info->bzero_addr [size] = addr;
+ }
+ return domain_info->bzero_addr [size];
+ }
+ }
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
+ MonoMethod *method;
+ gpointer addr;
+ MonoJitInfo *ji;
+ MonoGenericContext *ctx;
+
+ if (!mono_class_is_nullable (class))
+ /* This can happen since all the entries in MonoGSharedVtMethodInfo are inflated, even those which are not used */
+ return NULL;
+
+ if (info_type == MONO_RGCTX_INFO_NULLABLE_CLASS_BOX)
+ method = mono_class_get_method_from_name (class, "Box", 1);
+ else
+ method = mono_class_get_method_from_name (class, "Unbox", 1);
+
+ addr = mono_compile_method (method);
+ // The caller uses the gsharedvt call signature
+ ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr), NULL);
+ g_assert (ji);
+ if (mini_jit_info_is_gsharedvt (ji))
+ return mono_create_static_rgctx_trampoline (method, addr);
+ else {
+ MonoGenericSharingContext gsctx;
+ MonoMethodSignature *sig, *gsig;
+ MonoMethod *gmethod;
+
+ /* Need to add an out wrapper */
+
+ /* FIXME: We have no access to the gsharedvt signature/gsctx used by the caller, so have to construct it ourselves */
+ gmethod = mini_get_shared_method (method);
+ sig = mono_method_signature (method);
+ gsig = mono_method_signature (gmethod);
+ ctx = mono_method_get_context (gmethod);
+ mini_init_gsctx (ctx, &gsctx);
+
+ addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, &gsctx, -1, FALSE);
+ addr = mono_create_static_rgctx_trampoline (method, addr);
+ return addr;
+ }
+ }
default:
g_assert_not_reached ();
}
return NULL;
}
+static gboolean
+ji_is_gsharedvt (MonoJitInfo *ji)
+{
+ if (ji && ji->has_generic_jit_info && (mono_jit_info_get_generic_sharing_context (ji)->var_is_vt ||
+ mono_jit_info_get_generic_sharing_context (ji)->mvar_is_vt))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ * Describes the information used to construct a gsharedvt arg trampoline.
+ */
+typedef struct {
+ gboolean is_in;
+ gboolean calli;
+ gint32 vcall_offset;
+ gpointer addr;
+ MonoMethodSignature *sig, *gsig;
+ MonoGenericContext gsctx;
+} GSharedVtTrampInfo;
+
+static guint
+tramp_info_hash (gconstpointer key)
+{
+ GSharedVtTrampInfo *tramp = (gpointer)key;
+
+ return (gsize)tramp->addr;
+}
+
+static gboolean
+tramp_info_equal (gconstpointer a, gconstpointer b)
+{
+ GSharedVtTrampInfo *tramp1 = (gpointer)a;
+ GSharedVtTrampInfo *tramp2 = (gpointer)b;
+
+ /* The signatures should be internalized */
+ return tramp1->is_in == tramp2->is_in && tramp1->calli == tramp2->calli && tramp1->vcall_offset == tramp2->vcall_offset &&
+ tramp1->addr == tramp2->addr && tramp1->sig == tramp2->sig && tramp1->gsig == tramp2->gsig &&
+ tramp1->gsctx.class_inst == tramp2->gsctx.class_inst && tramp1->gsctx.method_inst == tramp2->gsctx.method_inst;
+}
+
+/*
+ * mini_get_gsharedvt_wrapper:
+ *
+ * Return a gsharedvt in/out wrapper for calling ADDR.
+ */
+gpointer
+mini_get_gsharedvt_wrapper (gboolean gsharedvt_in, gpointer addr, MonoMethodSignature *normal_sig, MonoMethodSignature *gsharedvt_sig, MonoGenericSharingContext *gsctx,
+ gint32 vcall_offset, gboolean calli)
+{
+ static gboolean inited = FALSE;
+ static int num_trampolines;
+ gpointer res, info;
+ MonoDomain *domain = mono_domain_get ();
+ MonoJitDomainInfo *domain_info;
+ GSharedVtTrampInfo *tramp_info;
+ GSharedVtTrampInfo tinfo;
+
+ if (!inited) {
+ mono_counters_register ("GSHAREDVT arg trampolines", MONO_COUNTER_JIT | MONO_COUNTER_INT, &num_trampolines);
+ inited = TRUE;
+ }
+
+ tinfo.is_in = gsharedvt_in;
+ tinfo.calli = calli;
+ tinfo.vcall_offset = vcall_offset;
+ tinfo.addr = addr;
+ tinfo.sig = normal_sig;
+ tinfo.gsig = gsharedvt_sig;
+ memcpy (&tinfo.gsctx, gsctx, sizeof (MonoGenericSharingContext));
+
+ domain_info = domain_jit_info (domain);
+
+ /*
+ * The arg trampolines might only have a finite number in full-aot, so use a cache.
+ */
+ mono_domain_lock (domain);
+ if (!domain_info->gsharedvt_arg_tramp_hash)
+ domain_info->gsharedvt_arg_tramp_hash = g_hash_table_new (tramp_info_hash, tramp_info_equal);
+ res = g_hash_table_lookup (domain_info->gsharedvt_arg_tramp_hash, &tinfo);
+ mono_domain_unlock (domain);
+ if (res)
+ return res;
+
+ info = mono_arch_get_gsharedvt_call_info (addr, normal_sig, gsharedvt_sig, gsctx, gsharedvt_in, vcall_offset, calli);
+
+ if (gsharedvt_in) {
+ static gpointer tramp_addr;
+ MonoMethod *wrapper;
+
+ if (!tramp_addr) {
+ wrapper = mono_marshal_get_gsharedvt_in_wrapper ();
+ addr = mono_compile_method (wrapper);
+ mono_memory_barrier ();
+ tramp_addr = addr;
+ }
+ addr = tramp_addr;
+ } else {
+ static gpointer tramp_addr;
+ MonoMethod *wrapper;
+
+ if (!tramp_addr) {
+ wrapper = mono_marshal_get_gsharedvt_out_wrapper ();
+ addr = mono_compile_method (wrapper);
+ mono_memory_barrier ();
+ tramp_addr = addr;
+ }
+ addr = tramp_addr;
+ }
+
+ if (mono_aot_only)
+ addr = mono_aot_get_gsharedvt_arg_trampoline (info, addr);
+ else
+ addr = mono_arch_get_gsharedvt_arg_trampoline (mono_domain_get (), info, addr);
+
+ num_trampolines ++;
+
+ /* Cache it */
+ tramp_info = mono_domain_alloc0 (domain, sizeof (GSharedVtTrampInfo));
+ memcpy (tramp_info, &tinfo, sizeof (GSharedVtTrampInfo));
+
+ mono_domain_lock (domain);
+ /* Duplicates are not a problem */
+ g_hash_table_insert (domain_info->gsharedvt_arg_tramp_hash, tramp_info, addr);
+ mono_domain_unlock (domain);
+
+ return addr;
+}
+
static gpointer
instantiate_info (MonoDomain *domain, MonoRuntimeGenericContextInfoTemplate *oti,
- MonoGenericContext *context, MonoClass *class)
+ MonoGenericContext *context, MonoClass *class, guint8 *caller)
{
gpointer data;
gboolean temporary;
case MONO_RGCTX_INFO_STATIC_DATA:
case MONO_RGCTX_INFO_KLASS:
case MONO_RGCTX_INFO_VTABLE:
- case MONO_RGCTX_INFO_CAST_CACHE: {
+ case MONO_RGCTX_INFO_CAST_CACHE:
+ case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
+ case MONO_RGCTX_INFO_VALUE_SIZE:
+ case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
+ case MONO_RGCTX_INFO_MEMCPY:
+ case MONO_RGCTX_INFO_BZERO:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: {
MonoClass *arg_class = mono_class_from_mono_type (data);
free_inflated_info (oti->info_type, data);
return mono_type_get_object (domain, data);
case MONO_RGCTX_INFO_METHOD:
return data;
- case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
- /*
- * We can't create a jump trampoline here, as it cannot be patched.
- */
- return mono_compile_method (data);
+ case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: {
+ gpointer addr;
+
+ addr = mono_compile_method (data);
+ return mini_add_method_trampoline (NULL, data, addr, mono_method_needs_static_rgctx_invoke (data, FALSE), FALSE);
+ }
+#ifndef DISABLE_REMOTING
case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
return mono_compile_method (mono_marshal_get_remoting_invoke_with_check (data));
+#endif
case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
return mono_domain_alloc0 (domain, sizeof (gpointer));
case MONO_RGCTX_INFO_CLASS_FIELD:
return data;
+ case MONO_RGCTX_INFO_FIELD_OFFSET: {
+ MonoClassField *field = data;
+
+ if (field->parent->valuetype && !(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
+ return GUINT_TO_POINTER (field->offset - sizeof (MonoObject));
+ else
+ return GUINT_TO_POINTER (field->offset);
+ }
case MONO_RGCTX_INFO_METHOD_RGCTX: {
MonoMethodInflated *method = data;
MonoVTable *vtable;
return method->context.method_inst;
}
+ case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: {
+ MonoMethodSignature *gsig = oti->data;
+ MonoMethodSignature *sig = data;
+ gpointer addr;
+ MonoJitInfo *caller_ji;
+ MonoGenericJitInfo *gji;
+
+ /*
+ * This is an indirect call to the address passed by the caller in the rgctx reg.
+ */
+ //printf ("CALLI\n");
+
+ g_assert (caller);
+ caller_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (caller), NULL);
+ g_assert (caller_ji);
+ gji = mono_jit_info_get_generic_jit_info (caller_ji);
+ g_assert (gji);
+
+ addr = mini_get_gsharedvt_wrapper (FALSE, NULL, sig, gsig, gji->generic_sharing_context, -1, TRUE);
+
+ return addr;
+ }
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: {
+ MonoJumpInfoGSharedVtCall *call_info = data;
+ MonoMethodSignature *call_sig;
+ MonoMethod *method;
+ gpointer addr;
+ MonoJitInfo *caller_ji, *callee_ji;
+ gboolean virtual = oti->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT;
+ gint32 vcall_offset;
+ MonoGenericJitInfo *gji, *callee_gji = NULL;
+ gboolean callee_gsharedvt;
+
+ /* This is the original generic signature used by the caller */
+ call_sig = call_info->sig;
+ /* This is the instantiated method which is called */
+ method = call_info->method;
+
+ g_assert (method->is_inflated);
+
+ if (!virtual)
+ addr = mono_compile_method (method);
+ else
+ addr = NULL;
+
+ if (virtual) {
+ /* Same as in mono_emit_method_call_full () */
+#ifndef MONO_ARCH_HAVE_IMT
+ NOT_IMPLEMENTED;
+#endif
+ if ((method->klass->parent == mono_defaults.multicastdelegate_class) && (!strcmp (method->name, "Invoke"))) {
+ /* See mono_emit_method_call_full () */
+ /* The gsharedvt trampoline will recognize this constant */
+ vcall_offset = MONO_GSHAREDVT_DEL_INVOKE_VT_OFFSET;
+ } else if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
+ guint32 imt_slot = mono_method_get_imt_slot (method);
+ vcall_offset = ((gint32)imt_slot - MONO_IMT_SIZE) * SIZEOF_VOID_P;
+ } else {
+ vcall_offset = G_STRUCT_OFFSET (MonoVTable, vtable) +
+ ((mono_method_get_vtable_index (method)) * (SIZEOF_VOID_P));
+ }
+ } else {
+ vcall_offset = -1;
+ }
+
+ g_assert (caller);
+ caller_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (caller), NULL);
+ g_assert (caller_ji);
+ gji = mono_jit_info_get_generic_jit_info (caller_ji);
+ g_assert (gji);
+
+ // FIXME: This loads information in the AOT case
+ callee_ji = mini_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (addr), NULL);
+ callee_gsharedvt = ji_is_gsharedvt (callee_ji);
+ if (callee_gsharedvt) {
+ callee_gji = mono_jit_info_get_generic_jit_info (callee_ji);
+ g_assert (callee_gji);
+ }
+
+ /*
+ * For gsharedvt calls made out of gsharedvt methods, the callee could end up being a gsharedvt method, or a normal
+ * non-shared method. The latter call cannot be patched, so instead of using a normal call, we make an indirect
+ * call through the rgctx, in effect patching the rgctx entry instead of the call site.
+ * For virtual calls, the caller might be a normal or a gsharedvt method. Since there is only one vtable slot,
+ * this difference needs to be handed on the caller side. This is currently implemented by adding a gsharedvt-in
+ * trampoline to all gsharedvt methods and storing this trampoline into the vtable slot. Virtual calls made from
+ * gsharedvt methods always go through a gsharedvt-out trampoline, so the calling sequence is:
+ * caller -> out trampoline -> in trampoline -> callee
+ * This is not very efficient, but it is easy to implement.
+ */
+ if (virtual || !callee_gsharedvt) {
+ MonoMethodSignature *sig, *gsig;
+
+ g_assert (method->is_inflated);
+
+ sig = mono_method_signature (method);
+ gsig = call_sig;
+
+ addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, gji->generic_sharing_context, vcall_offset, FALSE);
+#if 0
+ if (virtual)
+ printf ("OUT-VCALL: %s\n", mono_method_full_name (method, TRUE));
+ else
+ printf ("OUT: %s\n", mono_method_full_name (method, TRUE));
+#endif
+ // } else if (!mini_is_gsharedvt_variable_signature (mono_method_signature (caller_method)) && callee_gsharedvt) {
+ } else if (callee_gsharedvt) {
+ MonoMethodSignature *sig, *gsig;
+
+ /*
+ * This is a combination of the out and in cases, since both the caller and the callee are gsharedvt methods.
+ * The caller and the callee can use different gsharedvt signatures, so we have to add both an out and an in
+ * trampoline, i.e.:
+ * class Base<T> {
+ * public void foo<T1> (T1 t1, T t, object o) {}
+ * }
+ * class AClass : Base<long> {
+ * public void bar<T> (T t, long time, object o) {
+ * foo (t, time, o);
+ * }
+ * }
+ * Here, the caller uses !!0,long, while the callee uses !!0,!0
+ * FIXME: Optimize this.
+ */
+
+ if (call_sig == mono_method_signature (method)) {
+ } else {
+ sig = mono_method_signature (method);
+ gsig = mono_method_signature (callee_ji->method);
+
+ addr = mini_get_gsharedvt_wrapper (TRUE, callee_ji->code_start, sig, gsig, callee_gji->generic_sharing_context, -1, FALSE);
+
+ sig = mono_method_signature (method);
+ gsig = call_sig;
+
+ addr = mini_get_gsharedvt_wrapper (FALSE, addr, sig, gsig, gji->generic_sharing_context, -1, FALSE);
+
+ //printf ("OUT-IN-RGCTX: %s\n", mono_method_full_name (method, TRUE));
+ }
+ }
+
+ return addr;
+ }
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: {
+ MonoGSharedVtMethodInfo *info = data;
+ MonoGSharedVtMethodRuntimeInfo *res;
+ MonoType *t;
+ int i, offset, align, size;
+
+ // FIXME:
+ res = g_malloc0 (sizeof (MonoGSharedVtMethodRuntimeInfo) + (info->entries->len * sizeof (gpointer)));
+
+ offset = 0;
+ for (i = 0; i < info->entries->len; ++i) {
+ MonoRuntimeGenericContextInfoTemplate *template = g_ptr_array_index (info->entries, i);
+
+ switch (template->info_type) {
+ case MONO_RGCTX_INFO_LOCAL_OFFSET:
+ t = template->data;
+
+ size = mono_type_size (t, &align);
+
+ if (align < sizeof (gpointer))
+ align = sizeof (gpointer);
+ if (MONO_TYPE_ISSTRUCT (t) && align < 2 * sizeof (gpointer))
+ align = 2 * sizeof (gpointer);
+
+ // FIXME: Do the same things as alloc_stack_slots
+ offset += align - 1;
+ offset &= ~(align - 1);
+ res->entries [i] = GINT_TO_POINTER (offset);
+ offset += size;
+ break;
+ default:
+ res->entries [i] = instantiate_info (domain, template, context, class, NULL);
+ break;
+ }
+ }
+ res->locals_size = offset;
+
+ return res;
+ }
default:
g_assert_not_reached ();
}
}
}
-G_GNUC_UNUSED static const char*
-info_type_to_str (MonoRgctxInfoType type)
+const char*
+mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
{
switch (type) {
case MONO_RGCTX_INFO_STATIC_DATA: return "STATIC_DATA";
case MONO_RGCTX_INFO_TYPE: return "TYPE";
case MONO_RGCTX_INFO_REFLECTION_TYPE: return "REFLECTION_TYPE";
case MONO_RGCTX_INFO_METHOD: return "METHOD";
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO";
case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE";
case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD";
case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX";
case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: return "REMOTING_INVOKE_WITH_CHECK";
case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE: return "METHOD_DELEGATE_CODE";
case MONO_RGCTX_INFO_CAST_CACHE: return "CAST_CACHE";
+ case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE: return "ARRAY_ELEMENT_SIZE";
+ case MONO_RGCTX_INFO_VALUE_SIZE: return "VALUE_SIZE";
+ case MONO_RGCTX_INFO_CLASS_BOX_TYPE: return "CLASS_BOX_TYPE";
+ case MONO_RGCTX_INFO_FIELD_OFFSET: return "FIELD_OFFSET";
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE";
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT: return "METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT";
+ case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI: return "SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI";
+ case MONO_RGCTX_INFO_MEMCPY: return "MEMCPY";
+ case MONO_RGCTX_INFO_BZERO: return "BZERO";
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX: return "NULLABLE_CLASS_BOX";
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: return "NULLABLE_CLASS_UNBOX";
default:
- return "<>";
+ return "<UNKNOWN RGCTX INFO TYPE>";
}
}
break;
}
- DEBUG (printf ("set slot %s, infos [%d] = %s, %s\n", mono_type_get_full_name (class), i, info_type_to_str (info_type), rgctx_info_to_str (info_type, data)));
+ DEBUG (printf ("set slot %s, infos [%d] = %s, %s\n", mono_type_get_full_name (class), i, mono_rgctx_info_type_to_str (info_type), rgctx_info_to_str (info_type, data)));
/* Mark the slot as used in all parent classes (until we find
a parent class which already has it marked used). */
case MONO_RGCTX_INFO_TYPE:
case MONO_RGCTX_INFO_REFLECTION_TYPE:
case MONO_RGCTX_INFO_CAST_CACHE:
+ case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
+ case MONO_RGCTX_INFO_VALUE_SIZE:
+ case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
+ case MONO_RGCTX_INFO_MEMCPY:
+ case MONO_RGCTX_INFO_BZERO:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
return mono_class_from_mono_type (data1) == mono_class_from_mono_type (data2);
case MONO_RGCTX_INFO_METHOD:
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO:
case MONO_RGCTX_INFO_GENERIC_METHOD_CODE:
case MONO_RGCTX_INFO_CLASS_FIELD:
+ case MONO_RGCTX_INFO_FIELD_OFFSET:
case MONO_RGCTX_INFO_METHOD_RGCTX:
case MONO_RGCTX_INFO_METHOD_CONTEXT:
case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK:
case MONO_RGCTX_INFO_METHOD_DELEGATE_CODE:
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE:
+ case MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE_VIRT:
+ case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
return data1 == data2;
default:
g_assert_not_reached ();
return FALSE;
}
+/*
+ * mini_rgctx_info_type_to_patch_info_type:
+ *
+ * Return the type of the runtime object referred to by INFO_TYPE.
+ */
+MonoJumpInfoType
+mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type)
+{
+ switch (info_type) {
+ case MONO_RGCTX_INFO_STATIC_DATA:
+ case MONO_RGCTX_INFO_KLASS:
+ case MONO_RGCTX_INFO_VTABLE:
+ case MONO_RGCTX_INFO_TYPE:
+ case MONO_RGCTX_INFO_REFLECTION_TYPE:
+ case MONO_RGCTX_INFO_CAST_CACHE:
+ case MONO_RGCTX_INFO_ARRAY_ELEMENT_SIZE:
+ case MONO_RGCTX_INFO_VALUE_SIZE:
+ case MONO_RGCTX_INFO_CLASS_BOX_TYPE:
+ case MONO_RGCTX_INFO_MEMCPY:
+ case MONO_RGCTX_INFO_BZERO:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX:
+ case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX:
+ case MONO_RGCTX_INFO_LOCAL_OFFSET:
+ return MONO_PATCH_INFO_CLASS;
+ case MONO_RGCTX_INFO_FIELD_OFFSET:
+ return MONO_PATCH_INFO_FIELD;
+ default:
+ g_assert_not_reached ();
+ return -1;
+ }
+}
+
static int
lookup_or_register_info (MonoClass *class, int type_argc, gpointer data, MonoRgctxInfoType info_type,
MonoGenericContext *generic_context)
}
static gpointer
-fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint32 slot,
+fill_runtime_generic_context (MonoVTable *class_vtable, MonoRuntimeGenericContext *rgctx, guint8 *caller, guint32 slot,
MonoGenericInst *method_inst)
{
gpointer info;
oti = class_get_rgctx_template_oti (get_shared_class (class),
method_inst ? method_inst->type_argc : 0, slot, TRUE, TRUE, &do_free);
/* This might take the loader lock */
- info = instantiate_info (domain, &oti, &context, class);
+ info = instantiate_info (domain, &oti, &context, class, caller);
/*
if (method_inst)
/*
* mono_class_fill_runtime_generic_context:
* @class_vtable: a vtable
+ * @caller: caller method address
* @slot: a slot index to be instantiated
*
* Instantiates a slot in the RGCTX, returning its value.
*/
gpointer
-mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint32 slot)
+mono_class_fill_runtime_generic_context (MonoVTable *class_vtable, guint8 *caller, guint32 slot)
{
static gboolean inited = FALSE;
static int num_alloced = 0;
mono_domain_unlock (domain);
- info = fill_runtime_generic_context (class_vtable, rgctx, slot, 0);
+ info = fill_runtime_generic_context (class_vtable, rgctx, caller, slot, 0);
DEBUG (printf ("get rgctx slot: %s %d -> %p\n", mono_type_full_name (&class_vtable->klass->byval_arg), slot, info));
/*
* mono_method_fill_runtime_generic_context:
* @mrgctx: an MRGCTX
+ * @caller: caller method address
* @slot: a slot index to be instantiated
*
* Instantiates a slot in the MRGCTX.
*/
gpointer
-mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint32 slot)
+mono_method_fill_runtime_generic_context (MonoMethodRuntimeGenericContext *mrgctx, guint8* caller, guint32 slot)
{
gpointer info;
- info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, slot,
+ info = fill_runtime_generic_context (mrgctx->class_vtable, (MonoRuntimeGenericContext*)mrgctx, caller, slot,
mrgctx->method_inst);
return info;
gboolean
mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
{
- return mono_generic_context_is_sharable_full (context, allow_type_vars, ALLOW_PARTIAL_SHARING);
+ return mono_generic_context_is_sharable_full (context, allow_type_vars, partial_sharing_supported ());
}
/*
gboolean
mono_method_is_generic_impl (MonoMethod *method)
{
- if (method->is_inflated) {
- g_assert (method->wrapper_type == MONO_WRAPPER_NONE);
+ if (method->is_inflated)
return TRUE;
- }
/* We don't treat wrappers as generic code, i.e., we never
apply generic sharing to them. This is especially
important for static rgctx invoke wrappers, which only work
*/
}
+static gboolean
+mini_method_is_open (MonoMethod *method)
+{
+ if (method->is_inflated) {
+ MonoGenericContext *ctx = mono_method_get_context (method);
+
+ if (ctx->class_inst && ctx->class_inst->is_open)
+ return TRUE;
+ if (ctx->method_inst && ctx->method_inst->is_open)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static G_GNUC_UNUSED gboolean
+is_async_state_machine_class (MonoClass *klass)
+{
+ static MonoClass *iclass;
+ static gboolean iclass_set;
+
+ return FALSE;
+
+ if (!iclass_set) {
+ iclass = mono_class_from_name (mono_defaults.corlib, "System.Runtime.CompilerServices", "IAsyncStateMachine");
+ mono_memory_barrier ();
+ iclass_set = TRUE;
+ }
+
+ if (iclass && klass->valuetype && mono_class_is_assignable_from (iclass, klass))
+ return TRUE;
+ return FALSE;
+}
+
+static G_GNUC_UNUSED gboolean
+is_async_method (MonoMethod *method)
+{
+ MonoCustomAttrInfo *cattr;
+ MonoMethodSignature *sig;
+ gboolean res = FALSE;
+ static MonoClass *attr_class;
+ static gboolean attr_class_set;
+
+ return FALSE;
+
+ if (!attr_class_set) {
+ attr_class = mono_class_from_name (mono_defaults.corlib, "System.Runtime.CompilerServices", "AsyncStateMachineAttribute");
+ mono_memory_barrier ();
+ attr_class_set = TRUE;
+ }
+
+ /* Do less expensive checks first */
+ sig = mono_method_signature (method);
+ if (attr_class && sig && ((sig->ret->type == MONO_TYPE_VOID) ||
+ (sig->ret->type == MONO_TYPE_CLASS && (sig->ret->data.generic_class->container_class->name, "Task")) ||
+ (sig->ret->type == MONO_TYPE_GENERICINST && !strcmp (sig->ret->data.generic_class->container_class->name, "Task`1")))) {
+ //printf ("X: %s\n", mono_method_full_name (method, TRUE));
+ cattr = mono_custom_attrs_from_method (method);
+ if (cattr) {
+ if (mono_custom_attrs_has_attr (cattr, attr_class))
+ res = TRUE;
+ mono_custom_attrs_free (cattr);
+ }
+ }
+ return res;
+}
+
/*
- * mono_method_is_generic_sharable_impl_full:
+ * mono_method_is_generic_sharable_full:
* @method: a method
* @allow_type_vars: whether to regard type variables as reference types
- * @alloc_partial: whether to allow partial sharing
+ * @allow_partial: whether to allow partial sharing
+ * @allow_gsharedvt: whenever to allow sharing over valuetypes
*
* Returns TRUE iff the method is inflated or part of an inflated
* class, its context is sharable and it has no constraints on its
* type parameters. Otherwise returns FALSE.
*/
gboolean
-mono_method_is_generic_sharable_impl_full (MonoMethod *method, gboolean allow_type_vars,
- gboolean allow_partial)
+mono_method_is_generic_sharable_full (MonoMethod *method, gboolean allow_type_vars,
+ gboolean allow_partial, gboolean allow_gsharedvt)
{
if (!mono_method_is_generic_impl (method))
return FALSE;
+ if (!partial_sharing_supported ())
+ allow_partial = FALSE;
+
+ /*
+ * Generic async methods have an associated state machine class which is a generic struct. This struct
+ * is too large to be handled by gsharedvt so we make it visible to the AOT compiler by disabling sharing
+ * of the async method and the state machine class.
+ */
+ if (is_async_state_machine_class (method->klass))
+ return FALSE;
+
+ if (allow_gsharedvt && mini_is_gsharedvt_sharable_method (method)) {
+ if (is_async_method (method))
+ return FALSE;
+ return TRUE;
+ }
+
if (method->is_inflated) {
MonoMethodInflated *inflated = (MonoMethodInflated*)method;
MonoGenericContext *context = &inflated->context;
if (method->klass->generic_container && !allow_type_vars)
return FALSE;
+ /* This does potentially expensive cattr checks, so do it at the end */
+ if (is_async_method (method)) {
+ if (mini_method_is_open (method))
+ /* The JIT can't compile these without sharing */
+ return TRUE;
+ return FALSE;
+ }
+
return TRUE;
}
gboolean
-mono_method_is_generic_sharable_impl (MonoMethod *method, gboolean allow_type_vars)
+mono_method_is_generic_sharable (MonoMethod *method, gboolean allow_type_vars)
{
- return mono_method_is_generic_sharable_impl_full (method, allow_type_vars, ALLOW_PARTIAL_SHARING);
+ return mono_method_is_generic_sharable_full (method, allow_type_vars, partial_sharing_supported (), TRUE);
}
gboolean
if (!mono_class_generic_sharing_enabled (method->klass))
return FALSE;
- if (!mono_method_is_generic_sharable_impl (method, allow_type_vars))
+ if (!mono_method_is_generic_sharable (method, allow_type_vars))
return FALSE;
if (method->is_inflated && mono_method_get_context (method)->method_inst)
}
static gboolean gshared_supported;
+static gboolean gsharedvt_supported;
void
mono_set_generic_sharing_supported (gboolean supported)
gshared_supported = supported;
}
+void
+mono_set_generic_sharing_vt_supported (gboolean supported)
+{
+ gsharedvt_supported = supported;
+}
+
+void
+mono_set_partial_sharing_supported (gboolean supported)
+{
+ partial_supported = supported;
+}
+
/*
* mono_class_generic_sharing_enabled:
* @class: a class
MonoType*
mini_get_basic_type_from_generic (MonoGenericSharingContext *gsctx, MonoType *type)
{
+ /* FIXME: Some callers don't pass in a gsctx, like mono_dyn_call_prepare () */
+ /*
if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR))
g_assert (gsctx);
-
- return mono_type_get_basic_type_from_generic (type);
+ */
+ if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type_gsctx (gsctx, type))
+ return type;
+ else
+ return mono_type_get_basic_type_from_generic (type);
}
/*
{
if (type->byref)
return &mono_defaults.int_class->byval_arg;
- return mono_type_get_basic_type_from_generic (mono_type_get_underlying_type (type));
+ if (!type->byref && (type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && mini_is_gsharedvt_type_gsctx (gsctx, type))
+ return type;
+ return mini_get_basic_type_from_generic (gsctx, mono_type_get_underlying_type (type));
}
/*
/*
* mini_type_stack_size_full:
*
- * Same as mini_type_stack_size, but handle pinvoke data types as well.
+ * Same as mini_type_stack_size, but handle gsharedvt and pinvoke data types as well.
*/
int
mini_type_stack_size_full (MonoGenericSharingContext *gsctx, MonoType *t, guint32 *align, gboolean pinvoke)
{
int size;
+ /*
+ if (t->type == MONO_TYPE_VAR || t->type == MONO_TYPE_MVAR)
+ g_assert (gsctx);
+ */
+
+ //g_assert (!mini_is_gsharedvt_type_gsctx (gsctx, t));
+
if (pinvoke) {
size = mono_type_native_stack_size (t, align);
} else {
g_hash_table_destroy (generic_subclass_hash);
}
+/*
+ * mini_type_var_is_vt:
+ *
+ * Return whenever T is a type variable instantiated with a vtype.
+ */
+gboolean
+mini_type_var_is_vt (MonoCompile *cfg, MonoType *type)
+{
+ if (type->type == MONO_TYPE_VAR) {
+ if (cfg->generic_sharing_context->var_is_vt && cfg->generic_sharing_context->var_is_vt [type->data.generic_param->num])
+ return TRUE;
+ else
+ return FALSE;
+ } else if (type->type == MONO_TYPE_MVAR) {
+ if (cfg->generic_sharing_context->mvar_is_vt && cfg->generic_sharing_context->mvar_is_vt [type->data.generic_param->num])
+ return TRUE;
+ else
+ return FALSE;
+ } else {
+ g_assert_not_reached ();
+ }
+ return FALSE;
+}
+
gboolean
mini_type_is_reference (MonoCompile *cfg, MonoType *type)
{
if (!cfg->generic_sharing_context)
return FALSE;
/*FIXME the probably needs better handle under partial sharing*/
- return type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR;
+ return ((type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR) && !mini_type_var_is_vt (cfg, type));
+}
+
+/*
+ * mini_method_get_rgctx:
+ *
+ * Return the RGCTX which needs to be passed to M when it is called.
+ */
+gpointer
+mini_method_get_rgctx (MonoMethod *m)
+{
+ if (mini_method_get_context (m)->method_inst)
+ return mono_method_lookup_rgctx (mono_class_vtable (mono_domain_get (), m->klass), mini_method_get_context (m)->method_inst);
+ else
+ return mono_class_vtable (mono_domain_get (), m->klass);
+}
+
+/*
+ * mini_type_is_vtype:
+ *
+ * Return whenever T is a vtype, or a type param instantiated with a vtype.
+ * Should be used in place of MONO_TYPE_ISSTRUCT () which can't handle gsharedvt.
+ */
+gboolean
+mini_type_is_vtype (MonoCompile *cfg, MonoType *t)
+{
+ return MONO_TYPE_ISSTRUCT (t) || mini_is_gsharedvt_variable_type (cfg, t);
+}
+
+gboolean
+mini_class_is_generic_sharable (MonoClass *klass)
+{
+ if (klass->generic_class && is_async_state_machine_class (klass))
+ return FALSE;
+
+ return (klass->generic_class && mono_generic_context_is_sharable (&klass->generic_class->context, FALSE));
}
+
+#if defined(MONOTOUCH) || defined(MONO_EXTENSIONS)
+
+#include "../../../mono-extensions/mono/mini/mini-generic-sharing-gsharedvt.c"
+
+#else
+
+gboolean
+mini_is_gsharedvt_type_gsctx (MonoGenericSharingContext *gsctx, MonoType *t)
+{
+ return FALSE;
+}
+
+gboolean
+mini_is_gsharedvt_type (MonoCompile *cfg, MonoType *t)
+{
+ return FALSE;
+}
+
+gboolean
+mini_is_gsharedvt_klass (MonoCompile *cfg, MonoClass *klass)
+{
+ return FALSE;
+}
+
+gboolean
+mini_is_gsharedvt_signature (MonoCompile *cfg, MonoMethodSignature *sig)
+{
+ return FALSE;
+}
+
+gboolean
+mini_is_gsharedvt_variable_type (MonoCompile *cfg, MonoType *t)
+{
+ return FALSE;
+}
+
+gboolean
+mini_is_gsharedvt_sharable_method (MonoMethod *method)
+{
+ return FALSE;
+}
+
+gboolean
+mini_is_gsharedvt_variable_signature (MonoMethodSignature *sig)
+{
+ return FALSE;
+}
+
+#endif /* !MONOTOUCH */