Permalink
Browse files

2008-03-14 Mark Probst <mark.probst@gmail.com>

	* mini.c, mini.h: Share static generic code by passing it an
	implicit argument pointing to the runtime generic context.

	* mini-ops.h, inssel.brg, inssel-long.brg, inssel-long32.brg,
	inssel-long32-mips.brg: New opcodes for calling shared static,
	which need to be passed the runtime generic context.

	* mini-amd64.c, mini-x86.c: Save the runtime generic context
	argument on the stack in the prologue if needed.  New function for
	finding the runtime generic context argument from the registers
	saved by the trampoline.

	* mini-amd64.h, mini-x86.h: Specify which register to use for the
	runtime generic context argument.

	* tramp-amd64.c: Also restore the register used for the runtime
	generic context argument.

	* mini-trampoline.c: Resolve shared static calls by consulting the
	runtime generic context via the new argument.

	* generic-sharing.c: Add an argument to sharability-checking
	functions that specifies whether type variables should be treated
	as sharable type arguments.

	* inssel-x86.brg: Removed a superfluous, buggy rule.

	* graph.c, local-propagation.c, aliasing.c: Added the new opcodes
	to switches.

2008-03-14  Mark Probst  <mark.probst@gmail.com>

	* marshal.c, marshal.h, metadata-internals.h, image.c,
	wrapper-types.h: New wrapper for invoking a shared static method
	without having to pass the runtime generic context argument.

2008-03-14  Mark Probst  <mark.probst@gmail.com>

	* generics-sharing.2.cs: New tests for static methods.

	* generic-inlining.2.cs: Added.  Tests generic method inlining.

	* shared-generic-synchronized.2.cs: Added.  Tests shared
	synchronized methods.

	* Makefile.am: Added the new tests.  New target for running the
	generic code sharing with the optimization enabled.

svn path=/trunk/mono/; revision=98345
  • Loading branch information...
1 parent 28f0381 commit a4681ac79ddf0748bae3d5478afa6f98c20840d3 @schani schani committed Mar 14, 2008
View
6 mono/metadata/ChangeLog
@@ -1,3 +1,9 @@
+2008-03-14 Mark Probst <mark.probst@gmail.com>
+
+ * marshal.c, marshal.h, metadata-internals.h, image.c,
+ wrapper-types.h: New wrapper for invoking a shared static method
+ without having to pass the runtime generic context argument.
+
2008-03-14 Rodrigo Kumpera <rkumpera@novell.com>
* icall-def.h: Add missing function PerformanceCounterCategory::GetInstanceNames.
View
2 mono/metadata/image.c
@@ -1296,6 +1296,8 @@ mono_image_close (MonoImage *image)
g_hash_table_destroy (image->isinst_cache);
g_hash_table_destroy (image->castclass_cache);
g_hash_table_destroy (image->proxy_isinst_cache);
+ if (image->static_rgctx_invoke_cache)
+ g_hash_table_destroy (image->static_rgctx_invoke_cache);
/* The ownership of signatures is not well defined */
//g_hash_table_foreach (image->memberref_signatures, free_mr_signatures, NULL);
View
52 mono/metadata/marshal.c
@@ -4920,6 +4920,58 @@ mono_marshal_get_runtime_invoke (MonoMethod *method)
return res;
}
+/*
+ * mono_marshal_get_static_rgctx_invoke:
+ * @method: a method
+ *
+ * Generates a wrapper for calling a static method. We need this for
+ * ldftn when we do generic code sharing. Instead of producing the
+ * address of the static method we produce the address of a wrapper
+ * for the method because the wrapper passes the runtime generic
+ * context argument which calli cannot do.
+ */
+MonoMethod *
+mono_marshal_get_static_rgctx_invoke (MonoMethod *method)
+{
+ MonoMethodBuilder *mb;
+ MonoMethod *res;
+ MonoClass *target_klass = method->klass;
+ MonoMethodSignature *sig = mono_method_signature (method);
+ int i;
+ char *name;
+ GHashTable *cache;
+ MonoImage *image = method->klass->image;
+
+ if (!(cache = image->static_rgctx_invoke_cache)) {
+ mono_marshal_lock ();
+ if (!(cache = image->static_rgctx_invoke_cache)) {
+ cache = image->static_rgctx_invoke_cache =
+ g_hash_table_new (mono_aligned_addr_hash, NULL);
+ }
+ mono_marshal_unlock ();
+ }
+
+ if ((res = mono_marshal_find_in_cache (cache, method)))
+ return res;
+
+ name = mono_signature_to_name (mono_method_signature (method), "static_rgctx_invoke");
+ mb = mono_mb_new (target_klass, name, MONO_WRAPPER_STATIC_RGCTX_INVOKE);
+ g_free (name);
+
+ for (i = 0; i < sig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i);
+ mono_mb_emit_op (mb, CEE_CALL, method);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ res = mono_mb_create_and_cache (cache, method, mb, mono_method_signature (method), sig->param_count + 4);
+ res->skip_visibility = TRUE;
+ res->flags = method->flags;
+
+ mono_mb_free (mb);
+
+ return res;
+}
+
static void
mono_mb_emit_auto_layout_exception (MonoMethodBuilder *mb, MonoClass *klass)
{
View
3 mono/metadata/marshal.h
@@ -135,6 +135,9 @@ MonoMethod *
mono_marshal_get_runtime_invoke (MonoMethod *method) MONO_INTERNAL;
MonoMethod *
+mono_marshal_get_static_rgctx_invoke (MonoMethod *method) MONO_INTERNAL;
+
+MonoMethod *
mono_marshal_get_managed_wrapper (MonoMethod *method, MonoClass *delegate_klass, MonoObject *this) MONO_INTERNAL;
MonoMethod *
View
1 mono/metadata/metadata-internals.h
@@ -188,6 +188,7 @@ struct _MonoImage {
GHashTable *unbox_wrapper_cache;
GHashTable *cominterop_invoke_cache;
GHashTable *cominterop_wrapper_cache;
+ GHashTable *static_rgctx_invoke_cache; /* LOCKING: marshal lock */
/*
* indexed by MonoClass pointers
View
1 mono/metadata/wrapper-types.h
@@ -27,3 +27,4 @@ WRAPPER(UNKNOWN, "unknown")
WRAPPER(COMINTEROP_INVOKE, "cominterop-invoke")
WRAPPER(COMINTEROP, "cominterop")
WRAPPER(ALLOC, "alloc")
+WRAPPER(STATIC_RGCTX_INVOKE, "static-rgctx-invoke")
View
32 mono/mini/ChangeLog
@@ -1,3 +1,35 @@
+2008-03-14 Mark Probst <mark.probst@gmail.com>
+
+ * mini.c, mini.h: Share static generic code by passing it an
+ implicit argument pointing to the runtime generic context.
+
+ * mini-ops.h, inssel.brg, inssel-long.brg, inssel-long32.brg,
+ inssel-long32-mips.brg: New opcodes for calling shared static,
+ which need to be passed the runtime generic context.
+
+ * mini-amd64.c, mini-x86.c: Save the runtime generic context
+ argument on the stack in the prologue if needed. New function for
+ finding the runtime generic context argument from the registers
+ saved by the trampoline.
+
+ * mini-amd64.h, mini-x86.h: Specify which register to use for the
+ runtime generic context argument.
+
+ * tramp-amd64.c: Also restore the register used for the runtime
+ generic context argument.
+
+ * mini-trampoline.c: Resolve shared static calls by consulting the
+ runtime generic context via the new argument.
+
+ * generic-sharing.c: Add an argument to sharability-checking
+ functions that specifies whether type variables should be treated
+ as sharable type arguments.
+
+ * inssel-x86.brg: Removed a superfluous, buggy rule.
+
+ * graph.c, local-propagation.c, aliasing.c: Added the new opcodes
+ to switches.
+
2008-03-14 Martin Baulig <martin@ximian.com>
* debug-debugger.c (main_thread_handler): Call
View
7 mono/mini/aliasing.c
@@ -168,7 +168,12 @@ print_tree_node (MonoInst *tree) {
case OP_VCALLVIRT:
case OP_VOIDCALL:
case OP_VOIDCALLVIRT:
- case OP_TRAMPCALL_VTABLE: {
+ case OP_TRAMPCALL_VTABLE:
+ case OP_CALL_RGCTX:
+ case OP_FCALL_RGCTX:
+ case OP_VOIDCALL_RGCTX:
+ case OP_LCALL_RGCTX:
+ case OP_VCALL_RGCTX: {
MonoCallInst *call = (MonoCallInst*)tree;
if (call->method)
printf ("[%s]", call->method->name);
View
25 mono/mini/generic-sharing.c
@@ -59,13 +59,22 @@ mono_method_check_context_used (MonoMethod *method)
}
static gboolean
-generic_inst_is_sharable (MonoGenericInst *inst)
+generic_inst_is_sharable (MonoGenericInst *inst, gboolean allow_type_vars)
{
int i;
for (i = 0; i < inst->type_argc; ++i) {
- if (!MONO_TYPE_IS_REFERENCE (inst->type_argv [i]))
- return FALSE;
+ MonoType *type = inst->type_argv [i];
+ int type_type;
+
+ if (MONO_TYPE_IS_REFERENCE (type))
+ continue;
+
+ type_type = mono_type_get_type (type);
+ if (allow_type_vars && (type_type == MONO_TYPE_VAR || type_type == MONO_TYPE_MVAR))
+ continue;
+
+ return FALSE;
}
return TRUE;
@@ -79,14 +88,14 @@ generic_inst_is_sharable (MonoGenericInst *inst)
* is sharable iff all of its type arguments are reference type.
*/
gboolean
-mono_generic_context_is_sharable (MonoGenericContext *context)
+mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars)
{
g_assert (context->class_inst || context->method_inst);
- if (context->class_inst && !generic_inst_is_sharable (context->class_inst))
+ if (context->class_inst && !generic_inst_is_sharable (context->class_inst, allow_type_vars))
return FALSE;
- if (context->method_inst && !generic_inst_is_sharable (context->method_inst))
+ if (context->method_inst && !generic_inst_is_sharable (context->method_inst, allow_type_vars))
return FALSE;
return TRUE;
@@ -123,7 +132,7 @@ mono_method_is_generic_sharable_impl (MonoMethod *method)
MonoMethodInflated *inflated = (MonoMethodInflated*)method;
MonoGenericContext *context = &inflated->context;
- if (!mono_generic_context_is_sharable (context))
+ if (!mono_generic_context_is_sharable (context, FALSE))
return FALSE;
g_assert (inflated->declaring);
@@ -137,7 +146,7 @@ mono_method_is_generic_sharable_impl (MonoMethod *method)
}
if (method->klass->generic_class) {
- if (!mono_generic_context_is_sharable (&method->klass->generic_class->context))
+ if (!mono_generic_context_is_sharable (&method->klass->generic_class->context, FALSE))
return FALSE;
g_assert (method->klass->generic_class->container_class &&
View
5 mono/mini/graph.c
@@ -236,6 +236,11 @@ mono_print_label (FILE *fp, MonoInst *tree) {
case OP_VCALL_REG:
case OP_VOIDCALL_REG:
case OP_TRAMPCALL_VTABLE:
+ case OP_CALL_RGCTX:
+ case OP_FCALL_RGCTX:
+ case OP_VOIDCALL_RGCTX:
+ case OP_LCALL_RGCTX:
+ case OP_VCALL_RGCTX:
mono_print_label (fp, tree->inst_left);
break;
case CEE_BNE_UN:
View
7 mono/mini/inssel-long.brg
@@ -350,6 +350,13 @@ reg: OP_LCALL {
mono_bblock_add_inst (s->cbb, tree);
}
+reg: OP_LCALL_RGCTX (reg) {
+ emit_rgctx_argument (s, tree, state->left->reg1, OP_LCALL);
+
+ tree->dreg = state->reg1;
+ mono_bblock_add_inst (s->cbb, &call->inst);
+}
+
reg: OP_LCALL_REG (reg) {
tree->sreg1 = state->left->reg1;
tree->dreg = state->reg1;
View
7 mono/mini/inssel-long32-mips.brg
@@ -1179,6 +1179,13 @@ lreg: OP_LCALL {
mono_bblock_add_inst (s->cbb, tree);
}
+lreg: OP_LCALL_RGCTX (reg) {
+ emit_rgctx_argument (s, tree, state->left->reg1, OP_LCALL);
+
+ tree->dreg = state->reg1;
+ mono_bblock_add_inst (s->cbb, tree);
+}
+
lreg: OP_LCALL_REG (reg) {
tree->sreg1 = state->left->reg1;
tree->dreg = state->reg1;
View
7 mono/mini/inssel-long32.brg
@@ -827,6 +827,13 @@ lreg: OP_LCALL {
mono_bblock_add_inst (s->cbb, tree);
}
+lreg: OP_LCALL_RGCTX (reg) {
+ emit_rgctx_argument (s, tree, state->left->reg1, OP_LCALL);
+
+ tree->dreg = state->reg1;
+ mono_bblock_add_inst (s->cbb, tree);
+}
+
lreg: OP_LCALL_REG (reg) {
tree->sreg1 = state->left->reg1;
tree->dreg = state->reg1;
View
3 mono/mini/inssel-x86.brg
@@ -740,8 +740,7 @@ reg: OP_ATOMIC_EXCHANGE_I4 (base, reg) {
# Optimized call instructions
reg: OP_CALL_REG (CEE_LDIND_I (base)),
-freg: OP_FCALL_REG (CEE_LDIND_I (base)),
-reg: OP_LCALL_REG (CEE_LDIND_I (base)) {
+freg: OP_FCALL_REG (CEE_LDIND_I (base)) {
tree->opcode = call_reg_to_call_membase (tree->opcode);
tree->inst_basereg = state->left->left->tree->inst_basereg;
tree->inst_offset = state->left->left->tree->inst_offset;
View
44 mono/mini/inssel.brg
@@ -938,6 +938,33 @@ stmt: OP_TRAMPCALL_VTABLE (reg) {
#endif
}
+reg: OP_CALL_RGCTX (reg) {
+ emit_rgctx_argument (s, tree, state->left->reg1, OP_CALL);
+
+ tree->dreg = state->reg1;
+ mono_bblock_add_inst (s->cbb, tree);
+}
+
+freg: OP_FCALL_RGCTX (reg) {
+ emit_rgctx_argument (s, tree, state->left->reg1, OP_FCALL);
+
+ tree->dreg = state->reg1;
+ mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VOIDCALL_RGCTX (reg) {
+ emit_rgctx_argument (s, tree, state->left->reg1, OP_VOIDCALL);
+
+ mono_bblock_add_inst (s->cbb, tree);
+}
+
+stmt: OP_VCALL_RGCTX (reg, reg) {
+ emit_rgctx_argument (s, tree, state->right->reg1, OP_VCALL);
+
+ mono_arch_emit_this_vret_args (s, (MonoCallInst*)tree, -1, -1, state->left->reg1);
+ mono_bblock_add_inst (s->cbb, tree);
+}
+
stmt: OP_SAVE_LMF,
stmt: OP_RESTORE_LMF {
mono_bblock_add_inst (s->cbb, tree);
@@ -1863,6 +1890,23 @@ mini_emit_virtual_call (MonoCompile *cfg, void *st, MonoInst *tree, int novirtop
mono_bblock_add_inst (cfg->cbb, tree);
}
+static void
+emit_rgctx_argument (MonoCompile *s, MonoInst *tree, int reg, int new_opcode)
+{
+#ifdef MONO_ARCH_RGCTX_REG
+ MonoCallInst *call = (MonoCallInst*)tree;
+ int rgctx_reg = mono_regstate_next_int (s->rs);
+
+ MONO_EMIT_NEW_UNALU (s, OP_MOVE, rgctx_reg, reg);
+
+ call->inst.opcode = new_opcode;
+
+ mono_call_inst_add_outarg_reg (s, call, rgctx_reg, MONO_ARCH_RGCTX_REG, FALSE);
+#else
+ g_assert_not_reached ();
+#endif
+}
+
/*
* Emit code for ISINST/CASTCLASS
*/
View
7 mono/mini/local-propagation.c
@@ -717,7 +717,12 @@ mono_cprop_invalidate_values (MonoInst *tree, TreeMover *tree_mover, MonoInst **
case OP_VOIDCALL_REG:
case OP_VOIDCALLVIRT:
case OP_VOIDCALL:
- case OP_TRAMPCALL_VTABLE: {
+ case OP_TRAMPCALL_VTABLE:
+ case OP_CALL_RGCTX:
+ case OP_FCALL_RGCTX:
+ case OP_VOIDCALL_RGCTX:
+ case OP_LCALL_RGCTX:
+ case OP_VCALL_RGCTX: {
MonoCallInst *call = (MonoCallInst *)tree;
MonoMethodSignature *sig = call->signature;
int i, byref = FALSE;
View
16 mono/mini/mini-amd64.c
@@ -4540,9 +4540,11 @@ mono_arch_emit_prolog (MonoCompile *cfg)
* - push rbp, mov rbp, rsp
* - save callee saved regs using pushes
* - allocate frame
+ * - save rgctx if needed
* - save lmf if needed
* FP not present:
* - allocate frame
+ * - save rgctx if needed
* - save lmf if needed
* - save callee saved regs using moves
*/
@@ -4646,6 +4648,14 @@ mono_arch_emit_prolog (MonoCompile *cfg)
}
}
+ /* store runtime generic context */
+ if (cfg->rgctx_var) {
+ g_assert (cfg->rgctx_var->opcode == OP_REGOFFSET &&
+ (cfg->rgctx_var->inst_basereg == AMD64_RBP || cfg->rgctx_var->inst_basereg == AMD64_RSP));
+
+ amd64_mov_membase_reg (code, cfg->rgctx_var->inst_basereg, cfg->rgctx_var->inst_offset, MONO_ARCH_RGCTX_REG, 8);
+ }
+
/* compute max_offset in order to use short forward jumps */
max_offset = 0;
if (cfg->opt & MONO_OPT_BRANCH) {
@@ -5982,6 +5992,12 @@ mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSha
}
#endif
+MonoRuntimeGenericContext*
+mono_arch_find_static_call_rgctx (gpointer *regs, guint8 *code)
+{
+ return (MonoRuntimeGenericContext*) regs [MONO_ARCH_RGCTX_REG];
+}
+
MonoInst*
mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
View
6 mono/mini/mini-amd64.h
@@ -275,6 +275,12 @@ typedef struct {
#define MONO_ARCH_HAVE_IMT 1
#define MONO_ARCH_IMT_REG AMD64_R11
#define MONO_ARCH_VTABLE_REG AMD64_R11
+/*
+ * We use r10 for the rgctx register rather than r11 because r11 is
+ * used by the trampoline as a scratch register and hence might be
+ * clobbered across method call boundaries.
+ */
+#define MONO_ARCH_RGCTX_REG AMD64_R10
#define MONO_ARCH_COMMON_VTABLE_TRAMPOLINE 1
#define MONO_ARCH_HAVE_NOTIFY_PENDING_EXC 1
#define MONO_ARCH_ENABLE_NORMALIZE_OPCODES 1
View
5 mono/mini/mini-ops.h
@@ -38,6 +38,11 @@ MINI_OP(OP_VOIDCALLVIRT, "voidcallvirt")
MINI_OP(OP_VOIDCALL_REG, "voidcall_reg")
MINI_OP(OP_VOIDCALL_MEMBASE, "voidcall_membase")
MINI_OP(OP_TRAMPCALL_VTABLE, "trampcall_vtable")
+MINI_OP(OP_VOIDCALL_RGCTX, "voidcall_rgctx")
+MINI_OP(OP_LCALL_RGCTX, "lcall_rgctx")
+MINI_OP(OP_VCALL_RGCTX, "vcall_rgctx")
+MINI_OP(OP_CALL_RGCTX, "call_rgctx")
+MINI_OP(OP_FCALL_RGCTX, "fcall_rgctx")
MINI_OP(OP_FCALL, "fcall")
MINI_OP(OP_FCALLVIRT, "fcallvirt")
MINI_OP(OP_FCALL_REG, "fcall_reg")
View
6 mono/mini/mini-trampolines.c
@@ -156,7 +156,13 @@ mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp)
if (m->flags & METHOD_ATTRIBUTE_STATIC) {
+#ifdef MONO_ARCH_RGCTX_REG
+ MonoRuntimeGenericContext *rgctx = mono_arch_find_static_call_rgctx ((gpointer*)regs, code);
+
+ klass = rgctx->vtable->klass;
+#else
g_assert_not_reached ();
+#endif
} else {
#ifdef MONO_ARCH_HAVE_IMT
MonoObject *this_argument = mono_arch_find_this_argument ((gpointer*)regs, m,
View
13 mono/mini/mini-x86.c
@@ -3931,6 +3931,13 @@ mono_arch_emit_prolog (MonoCompile *cfg)
if (mono_jit_trace_calls != NULL && mono_trace_eval (method))
code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
+ /* store runtime generic context */
+ if (cfg->rgctx_var) {
+ g_assert (cfg->rgctx_var->opcode == OP_REGOFFSET && cfg->rgctx_var->inst_basereg == X86_EBP);
+
+ x86_mov_membase_reg (code, X86_EBP, cfg->rgctx_var->inst_offset, MONO_ARCH_RGCTX_REG, 4);
+ }
+
/* load arguments allocated to register from the stack */
sig = mono_method_signature (method);
pos = 0;
@@ -4454,6 +4461,12 @@ mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSha
}
#endif
+MonoRuntimeGenericContext*
+mono_arch_find_static_call_rgctx (gpointer *regs, guint8 *code)
+{
+ return (MonoRuntimeGenericContext*) regs [MONO_ARCH_RGCTX_REG];
+}
+
MonoInst*
mono_arch_get_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
{
View
1 mono/mini/mini-x86.h
@@ -276,6 +276,7 @@ typedef struct {
#define MONO_ARCH_IMT_REG X86_EDX
#define MONO_ARCH_VTABLE_REG X86_EDX
#define MONO_ARCH_COMMON_VTABLE_TRAMPOLINE 1
+#define MONO_ARCH_RGCTX_REG X86_EDX
#define MONO_ARCH_ENABLE_NORMALIZE_OPCODES 1
#if !defined(__APPLE__)
View
235 mono/mini/mini.c
@@ -110,8 +110,7 @@
} while (0)
#define GENERIC_SHARING_FAILURE(opcode) do { \
if (cfg->generic_sharing_context) { \
- if (!(method->flags & METHOD_ATTRIBUTE_STATIC)) \
- /*g_print ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", method->klass->name_space, method->klass->name, method->name, method->signature->param_count, mono_opcode_name ((opcode)), __LINE__)*/; \
+ /*g_print ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", method->klass->name_space, method->klass->name, method->name, method->signature->param_count, mono_opcode_name ((opcode)), __LINE__)*/; \
cfg->exception_type = MONO_EXCEPTION_GENERIC_SHARING_FAILED; \
goto exception_exit; \
} \
@@ -1803,6 +1802,20 @@ mono_get_got_var (MonoCompile *cfg)
#endif
}
+static MonoInst *
+mono_get_rgctx_var (MonoCompile *cfg)
+{
+ g_assert (cfg->generic_sharing_context);
+
+ if (!cfg->rgctx_var) {
+ cfg->rgctx_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+ /* force the var to be stack allocated */
+ cfg->rgctx_var->flags |= MONO_INST_INDIRECT;
+ }
+
+ return cfg->rgctx_var;
+}
+
MonoInst*
mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode)
{
@@ -2597,7 +2610,7 @@ mono_spill_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoCallInst *call, M
temp->backend.is_pinvoke = sig->pinvoke;
NEW_TEMPLOADA (cfg, loada, temp->inst_c0);
- if (call->inst.opcode == OP_VCALL)
+ if (call->inst.opcode == OP_VCALL || call->inst.opcode == OP_VCALL_RGCTX)
ins->inst_left = loada;
else
ins->inst_right = loada; /* a virtual or indirect call */
@@ -2776,6 +2789,44 @@ mono_emit_jit_icall (MonoCompile *cfg, MonoBasicBlock *bblock, gconstpointer fun
return mono_emit_native_call (cfg, bblock, mono_icall_get_wrapper (info), info->sig, args, ip, FALSE, FALSE);
}
+static MonoCallInst*
+mono_emit_rgctx_method_call (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method, MonoMethodSignature *sig,
+ MonoInst **args, MonoInst *rgctx_arg, const guint8 *ip, MonoInst *this)
+{
+ MonoCallInst *call = mono_emit_method_call_full (cfg, bblock, method, sig, args, ip, this, FALSE);
+
+ if (rgctx_arg) {
+ switch (call->inst.opcode) {
+ case OP_CALL: call->inst.opcode = OP_CALL_RGCTX; break;
+ case OP_VOIDCALL: call->inst.opcode = OP_VOIDCALL_RGCTX; break;
+ case OP_FCALL: call->inst.opcode = OP_FCALL_RGCTX; break;
+ case OP_LCALL: call->inst.opcode = OP_LCALL_RGCTX; break;
+ case OP_VCALL: call->inst.opcode = OP_VCALL_RGCTX; break;
+ default: g_assert_not_reached ();
+ }
+
+ if (call->inst.opcode != OP_VCALL_RGCTX) {
+ g_assert (!call->inst.inst_left);
+ call->inst.inst_left = rgctx_arg;
+ } else {
+ g_assert (!call->inst.inst_right);
+ call->inst.inst_right = rgctx_arg;
+ }
+ }
+
+ return call;
+}
+
+inline static int
+mono_emit_rgctx_method_call_spilled (MonoCompile *cfg, MonoBasicBlock *bblock, MonoMethod *method,
+ MonoMethodSignature *signature, MonoInst **args, MonoInst *rgctx_arg, const guint8 *ip,
+ MonoInst *this)
+{
+ MonoCallInst *call = mono_emit_rgctx_method_call (cfg, bblock, method, signature, args, rgctx_arg, ip, this);
+
+ return mono_spill_call (cfg, bblock, call, signature, method->string_ctor, ip, FALSE);
+}
+
static void
mono_emulate_opcode (MonoCompile *cfg, MonoInst *tree, MonoInst **iargs, MonoJitICallInfo *info)
{
@@ -4361,6 +4412,21 @@ get_runtime_generic_context_from_this (MonoCompile *cfg, MonoInst *this, unsigne
return rgc_ptr;
}
+static MonoInst*
+get_runtime_generic_context (MonoCompile *cfg, MonoMethod *method, MonoInst *this, unsigned char *ip)
+{
+ if (method->flags & METHOD_ATTRIBUTE_STATIC) {
+ MonoInst *rgctx_loc, *rgctx_var;
+
+ rgctx_loc = mono_get_rgctx_var (cfg);
+ NEW_TEMPLOAD (cfg, rgctx_var, rgctx_loc->inst_c0);
+
+ return rgctx_var;
+ } else {
+ return get_runtime_generic_context_from_this (cfg, this, ip);
+ }
+}
+
static gpointer
create_rgctx_lazy_fetch_trampoline (guint32 offset)
{
@@ -4764,8 +4830,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if ((method->flags & METHOD_ATTRIBUTE_STATIC) &&
clause->flags != MONO_EXCEPTION_CLAUSE_FILTER &&
clause->data.catch_class &&
+ cfg->generic_sharing_context &&
mono_class_check_context_used (clause->data.catch_class)) {
- GENERIC_SHARING_FAILURE (CEE_NOP);
+ mono_get_rgctx_var (cfg);
}
GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
@@ -5384,8 +5451,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
MonoMethodSignature *fsig = NULL;
int temp, array_rank = 0;
int virtual = *ip == CEE_CALLVIRT;
+ MonoInst *rgctx_arg = NULL;
gboolean no_spill;
int context_used = 0;
+ gboolean pass_rgctx = FALSE;
CHECK_OPSIZE (5);
token = read32 (ip + 1);
@@ -5511,6 +5580,34 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (*ip != CEE_CALLI && check_call_signature (cfg, fsig, sp))
UNVERIFIED;
+ if (cmethod && (cmethod->flags & METHOD_ATTRIBUTE_STATIC) &&
+ (cmethod->klass->generic_class || cmethod->klass->generic_container)) {
+ gboolean sharing_enabled = mono_class_generic_sharing_enabled (cmethod->klass);
+ MonoGenericContext *context = mini_class_get_context (cmethod->klass);
+ gboolean context_sharable = mono_generic_context_is_sharable (context, TRUE);
+
+ /*
+ * Pass rgctx iff target method might
+ * be shared, which means that sharing
+ * is enabled for its class and its
+ * context is sharable.
+ *
+ * Cancel sharing of compiled method
+ * iff we don't know the target method
+ * statically (assuming that we share
+ * all sharable methods), which means
+ * that the method depends on the
+ * caller's context, sharing is
+ * enabled for its class but its
+ * context is not sharable.
+ */
+ if (sharing_enabled && context_sharable)
+ pass_rgctx = TRUE;
+
+ if (sharing_enabled && !context_sharable && mono_method_check_context_used (cmethod))
+ GENERIC_SHARING_FAILURE (*ip);
+ }
+
if (cfg->generic_sharing_context && cmethod) {
MonoGenericContext *cmethod_context = mono_method_get_context (cmethod);
@@ -5522,12 +5619,41 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (context_used &&
((cmethod->klass->flags & TYPE_ATTRIBUTE_INTERFACE) ||
(cmethod_context && cmethod_context->method_inst) ||
- cmethod->klass->valuetype ||
- (cmethod->flags & METHOD_ATTRIBUTE_STATIC))) {
+ cmethod->klass->valuetype)) {
GENERIC_SHARING_FAILURE (*ip);
}
}
+ if (pass_rgctx) {
+ if (context_used) {
+ MonoInst *this = NULL, *rgctx, *vtable, *field_offset, *field_addr;
+
+ if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+ NEW_ARGLOAD (cfg, this, 0);
+ rgctx = get_runtime_generic_context (cfg, method, this, ip);
+ vtable = get_runtime_generic_context_ptr (cfg, method, bblock, cmethod->klass,
+ token, MINI_TOKEN_SOURCE_METHOD, generic_context,
+ rgctx, MONO_RGCTX_INFO_VTABLE, ip);
+
+ NEW_ICONST (cfg, field_offset, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
+
+ MONO_INST_NEW (cfg, field_addr, OP_PADD);
+ field_addr->cil_code = ip;
+ field_addr->inst_left = vtable;
+ field_addr->inst_right = field_offset;
+ field_addr->type = STACK_PTR;
+
+ MONO_INST_NEW (cfg, rgctx_arg, CEE_LDIND_I);
+ rgctx_arg->cil_code = ip;
+ rgctx_arg->inst_left = field_addr;
+ rgctx_arg->type = STACK_PTR;
+ } else {
+ MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
+
+ NEW_PCONST (cfg, rgctx_arg, vtable->runtime_generic_context);
+ }
+ }
+
if (cmethod && virtual &&
(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) &&
!((cmethod->flags & METHOD_ATTRIBUTE_FINAL) &&
@@ -5572,6 +5698,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
break;
}
+ /* FIXME: runtime generic context pointer for jumps? */
if ((ins_flag & MONO_INST_TAILCALL) && cmethod && (*ip == CEE_CALL) &&
(mono_metadata_signature_equal (mono_method_signature (method), mono_method_signature (cmethod)))) {
int i;
@@ -5680,7 +5807,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
inline_costs += 10 * num_calls++;
/* tail recursion elimination */
- if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == method && ip [5] == CEE_RET) {
+ if ((cfg->opt & MONO_OPT_TAILC) && *ip == CEE_CALL && cmethod == method && ip [5] == CEE_RET &&
+ !rgctx_arg) {
gboolean has_vtargs = FALSE;
int i;
@@ -5727,6 +5855,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
no_spill = FALSE;
if (*ip == CEE_CALLI) {
+ g_assert (!rgctx_arg);
/* Prevent inlining of methods with indirect calls */
INLINE_FAILURE;
if (no_spill) {
@@ -5805,10 +5934,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
sp++;
}
} else if (no_spill) {
- ins = (MonoInst*)mono_emit_method_call (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL);
+ ins = (MonoInst*)mono_emit_rgctx_method_call (cfg, bblock, cmethod, fsig, sp, rgctx_arg, ip, virtual ? sp [0] : NULL);
*sp++ = ins;
} else {
- if ((temp = mono_emit_method_call_spilled (cfg, bblock, cmethod, fsig, sp, ip, virtual ? sp [0] : NULL)) != -1) {
+ if ((temp = mono_emit_rgctx_method_call_spilled (cfg, bblock, cmethod, fsig, sp, rgctx_arg, ip, virtual ? sp [0] : NULL)) != -1) {
MonoInst *load;
NEW_TEMPLOAD (cfg, load, temp);
@@ -7142,12 +7271,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
GENERIC_SHARING_FAILURE (*ip);
if (context_used) {
- relation = mono_class_generic_class_relation (klass, MONO_RGCTX_INFO_VTABLE, method->klass, generic_context, NULL);
-
- if (!(method->flags & METHOD_ATTRIBUTE_STATIC) /*&& relation != MINI_GENERIC_CLASS_RELATION_OTHER*/)
- shared_access = TRUE;
- else
- GENERIC_SHARING_FAILURE (*ip);
+ relation = mono_class_generic_class_relation (klass, MONO_RGCTX_INFO_VTABLE,
+ method->klass, generic_context, NULL);
+ shared_access = TRUE;
}
}
@@ -7178,18 +7304,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (mono_class_needs_cctor_run (klass, method)) {
MonoMethodSignature *sig = helper_sig_generic_class_init_trampoline;
MonoCallInst *call;
- MonoInst *this, *vtable;
+ MonoInst *vtable;
+ MonoInst *this = NULL;
- NEW_ARGLOAD (cfg, this, 0);
+ if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+ NEW_ARGLOAD (cfg, this, 0);
- if (relation == MINI_GENERIC_CLASS_RELATION_SELF) {
+ if (relation == MINI_GENERIC_CLASS_RELATION_SELF && this) {
MONO_INST_NEW (cfg, vtable, CEE_LDIND_I);
vtable->cil_code = ip;
vtable->inst_left = this;
vtable->type = STACK_PTR;
vtable->klass = klass;
} else {
- MonoInst *rgctx = get_runtime_generic_context_from_this (cfg, this, ip);
+ MonoInst *rgctx = get_runtime_generic_context (cfg, method, this, ip);
vtable = get_runtime_generic_context_ptr (cfg, method, bblock, klass,
token, MINI_TOKEN_SOURCE_FIELD, generic_context,
@@ -7211,8 +7339,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
* super_info.static_data + field->offset
*/
- NEW_ARGLOAD (cfg, this, 0);
- rgctx = get_runtime_generic_context_from_this (cfg, this, ip);
+ if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+ NEW_ARGLOAD (cfg, this, 0);
+ rgctx = get_runtime_generic_context (cfg, method, this, ip);
static_data = get_runtime_generic_context_ptr (cfg, method, bblock, klass,
token, MINI_TOKEN_SOURCE_FIELD, generic_context,
rgctx, MONO_RGCTX_INFO_STATIC_DATA, ip);
@@ -7521,25 +7650,22 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (context_used & MONO_GENERIC_CONTEXT_USED_METHOD || klass->valuetype)
GENERIC_SHARING_FAILURE (CEE_NEWARR);
- if (context_used) {
- if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
- shared_access = TRUE;
- else
- GENERIC_SHARING_FAILURE (CEE_NEWARR);
- }
+ if (context_used)
+ shared_access = TRUE;
}
if (shared_access) {
- MonoInst *this, *rgctx;
+ MonoInst *this = NULL, *rgctx;
MonoInst *args [3];
int temp;
/* domain */
NEW_DOMAINCONST (cfg, args [0]);
/* klass */
- NEW_ARGLOAD (cfg, this, 0);
- rgctx = get_runtime_generic_context_from_this (cfg, this, ip);
+ if (!(method->flags & METHOD_ATTRIBUTE_STATIC))
+ NEW_ARGLOAD (cfg, this, 0);
+ rgctx = get_runtime_generic_context (cfg, method, this, ip);
args [1] = get_runtime_generic_context_ptr (cfg, method, bblock, klass,
token, MINI_TOKEN_SOURCE_CLASS, generic_context, rgctx, MONO_RGCTX_INFO_KLASS, ip);
@@ -8396,6 +8522,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
MonoInst *argconst;
MonoMethod *cil_method, *ctor_method;
int temp;
+ gboolean is_shared;
CHECK_STACK_OVF (1);
CHECK_OPSIZE (6);
@@ -8408,6 +8535,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
if (cfg->generic_sharing_context && mono_method_check_context_used (cmethod))
GENERIC_SHARING_FAILURE (CEE_LDFTN);
+ is_shared = (cmethod->flags & METHOD_ATTRIBUTE_STATIC) &&
+ (cmethod->klass->generic_class || cmethod->klass->generic_container) &&
+ mono_class_generic_sharing_enabled (cmethod->klass);
+
cil_method = cmethod;
if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
METHOD_ACCESS_FAILURE;
@@ -8424,7 +8555,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
*/
#if defined(MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE) && !defined(HAVE_WRITE_BARRIERS)
/* FIXME: SGEN support */
- if ((sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ctor_method = mini_get_method (method, read32 (ip + 7), NULL, generic_context)) && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
+ /* FIXME: handle shared static generic methods */
+ if (!is_shared && (sp > stack_start) && (ip + 6 + 5 < end) && ip_in_bb (cfg, bblock, ip + 6) && (ip [6] == CEE_NEWOBJ) && (ctor_method = mini_get_method (method, read32 (ip + 7), NULL, generic_context)) && (ctor_method->klass->parent == mono_defaults.multicastdelegate_class)) {
MonoInst *target_ins;
ip += 6;
@@ -8441,7 +8573,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
handle_loaded_temps (cfg, bblock, stack_start, sp);
- NEW_METHODCONST (cfg, argconst, cmethod);
+ if (is_shared)
+ NEW_METHODCONST (cfg, argconst, mono_marshal_get_static_rgctx_invoke (cmethod));
+ else
+ NEW_METHODCONST (cfg, argconst, cmethod);
if (method->wrapper_type != MONO_WRAPPER_SYNCHRONIZED)
temp = mono_emit_jit_icall (cfg, bblock, mono_ldftn, &argconst, ip);
else
@@ -10204,9 +10339,13 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code,
case MONO_PATCH_INFO_METHOD:
if (patch_info->data.method == method) {
target = code;
- } else
+ } else {
/* get the trampoline to the method from the domain */
- target = mono_create_jit_trampoline (patch_info->data.method);
+ if (method->wrapper_type == MONO_WRAPPER_STATIC_RGCTX_INVOKE)
+ target = mono_ldftn_nosync (patch_info->data.method);
+ else
+ target = mono_create_jit_trampoline (patch_info->data.method);
+ }
break;
case MONO_PATCH_INFO_SWITCH: {
gpointer *jump_table;
@@ -11993,7 +12132,13 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
jinfo->cas_inited = FALSE; /* initialization delayed at the first stalk walk using this method */
jinfo->num_clauses = header->num_clauses;
- if (cfg->generic_sharing_context && !(method_to_compile->flags & METHOD_ATTRIBUTE_STATIC)) {
+ /*
+ * Static methods only get a generic JIT info if they use the
+ * rgctx variable (which they are forced to if they have any
+ * open catch clauses).
+ */
+ if (cfg->generic_sharing_context &&
+ (cfg->rgctx_var || !(method_to_compile->flags & METHOD_ATTRIBUTE_STATIC))) {
MonoInst *inst;
MonoGenericJitInfo *gi;
@@ -12004,9 +12149,12 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
gi->generic_sharing_context = cfg->generic_sharing_context;
- g_assert (!(method_to_compile->flags & METHOD_ATTRIBUTE_STATIC));
-
- inst = cfg->varinfo [0];
+ if (method_to_compile->flags & METHOD_ATTRIBUTE_STATIC) {
+ inst = cfg->rgctx_var;
+ g_assert (inst->opcode == OP_REGOFFSET);
+ } else {
+ inst = cfg->varinfo [0];
+ }
if (inst->opcode == OP_REGVAR) {
gi->this_in_reg = 1;
@@ -12480,6 +12628,7 @@ mono_jit_find_compiled_method (MonoDomain *domain, MonoMethod *method)
static MonoObject*
mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc)
{
+ MonoMethod *to_compile;
MonoMethod *invoke;
MonoObject *(*runtime_invoke) (MonoObject *this, void **params, MonoObject **exc, void* compiled_method);
void* compiled_method;
@@ -12489,6 +12638,14 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
return NULL;
}
+ if ((method->flags & METHOD_ATTRIBUTE_STATIC) &&
+ mono_class_generic_sharing_enabled (method->klass) &&
+ mono_method_is_generic_sharable_impl (method)) {
+ to_compile = mono_marshal_get_static_rgctx_invoke (method);
+ } else {
+ to_compile = method;
+ }
+
invoke = mono_marshal_get_runtime_invoke (method);
runtime_invoke = mono_jit_compile_method (invoke);
@@ -12505,7 +12662,7 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
*/
compiled_method = NULL;
} else {
- compiled_method = mono_jit_compile_method (method);
+ compiled_method = mono_jit_compile_method (to_compile);
}
return runtime_invoke (obj, params, exc, compiled_method);
}
View
4 mono/mini/mini.h
@@ -765,6 +765,7 @@ typedef struct {
MonoMethod *inlined_method; /* the method which is currently inlined */
MonoInst *domainvar; /* a cache for the current domain */
MonoInst *got_var; /* Global Offset Table variable */
+ MonoInst *rgctx_var; /* Runtime generic context variable (for static generic methods) */
MonoInst **args;
/*
@@ -1263,6 +1264,7 @@ gpointer mono_arch_get_delegate_invoke_impl (MonoMethodSignature *sig, gbool
gpointer mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len) MONO_INTERNAL;
void mono_arch_emit_imt_argument (MonoCompile *cfg, MonoCallInst *call) MONO_INTERNAL;
MonoMethod* mono_arch_find_imt_method (gpointer *regs, guint8 *code) MONO_INTERNAL;
+MonoRuntimeGenericContext* mono_arch_find_static_call_rgctx (gpointer *regs, guint8 *code) MONO_INTERNAL;
MonoObject* mono_arch_find_this_argument (gpointer *regs, MonoMethod *method, MonoGenericSharingContext *gsctx) MONO_INTERNAL;
gpointer mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count) MONO_INTERNAL;
void mono_arch_notify_pending_exc (void) MONO_INTERNAL;
@@ -1351,7 +1353,7 @@ int mono_method_check_context_used (MonoMethod *method) MONO_INTERNAL;
gboolean mono_generic_context_equal_deep (MonoGenericContext *context1, MonoGenericContext *context2) MONO_INTERNAL;
-gboolean mono_generic_context_is_sharable (MonoGenericContext *context) MONO_INTERNAL;
+gboolean mono_generic_context_is_sharable (MonoGenericContext *context, gboolean allow_type_vars) MONO_INTERNAL;
gboolean mono_method_is_generic_impl (MonoMethod *method) MONO_INTERNAL;
gboolean mono_method_is_generic_sharable_impl (MonoMethod *method) MONO_INTERNAL;
View
5 mono/mini/tramp-amd64.c
@@ -388,9 +388,10 @@ mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
amd64_mov_reg_membase (code, AMD64_R11, AMD64_RBP, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), 8);
amd64_mov_membase_reg (code, AMD64_R11, 0, AMD64_RCX, 8);
- /* Restore argument registers */
+ /* Restore argument registers and r10 (needed to pass rgctx to
+ static shared generic methods). */
for (i = 0; i < AMD64_NREG; ++i)
- if (AMD64_IS_ARGUMENT_REG (i))
+ if (AMD64_IS_ARGUMENT_REG (i) || i == AMD64_R10)
amd64_mov_reg_membase (code, i, AMD64_RBP, saved_regs_offset + (i * 8), 8);
for (i = 0; i < 8; ++i)
View
12 mono/tests/ChangeLog
@@ -1,3 +1,15 @@
+2008-03-14 Mark Probst <mark.probst@gmail.com>
+
+ * generics-sharing.2.cs: New tests for static methods.
+
+ * generic-inlining.2.cs: Added. Tests generic method inlining.
+
+ * shared-generic-synchronized.2.cs: Added. Tests shared
+ synchronized methods.
+
+ * Makefile.am: Added the new tests. New target for running the
+ generic code sharing with the optimization enabled.
+
2008-03-04 Mark Probst <mark.probst@gmail.com>
* generics-sharing.2.c: Added test for open catch clauses in
View
12 mono/tests/Makefile.am
@@ -253,6 +253,8 @@ BASE_TEST_CS_SRC= \
generics-invoke-byref.2.cs \
generic-signature-compare.2.cs \
generics-sharing.2.cs \
+ shared-generic-synchronized.2.cs \
+ generic-inlining.2.cs \
recursive-generics.2.cs \
bug-80392.2.cs \
dynamic-method-access.2.cs \
@@ -370,14 +372,14 @@ EXTRA_DIST=test-driver $(TEST_CS_SRC) $(TEST_IL_SRC) \
# mkbundle works on ppc, but the pkg-config POC doesn't when run with make test
if POWERPC
-test: assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535
+test: assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 test-generic-sharing
else
# Can't use mkbundle on win32 since there is no static build there
# Can't run test-unhandled-exception on Windows because of all the debug popups...
if PLATFORM_WIN32
-test: assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535
+test: assemblyresolve/test/asm.dll testjit test-type-load test-inline-call-stack test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 test-generic-sharing
else
-test: assemblyresolve/test/asm.dll testjit testbundle test-type-load test-inline-call-stack test-iomap-regression test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 custom-modifiers
+test: assemblyresolve/test/asm.dll testjit testbundle test-type-load test-inline-call-stack test-iomap-regression test-bug-80307 test-bug-81673 test-bug-81691 test-bug-81466 test-bug-324535 custom-modifiers test-generic-sharing
endif
endif
@@ -633,6 +635,10 @@ test-coreclr-security : coreclr-security.exe
$(RUNTIME21) --security=core-clr-test coreclr-security.exe
endif
+test-generic-sharing : generics-sharing.2.exe shared-generic-synchronized.2.exe
+ $(RUNTIME) -O=gshared generics-sharing.2.exe
+ $(RUNTIME) -O=gshared shared-generic-synchronized.2.exe
+
EXTRA_DIST += async-exceptions.cs
async-exceptions.exe : async-exceptions.cs
$(MCS) -out:async-exceptions.exe $(srcdir)/async-exceptions.cs
View
55 mono/tests/generic-inlining.2.cs
@@ -0,0 +1,55 @@
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+public class Gen<T> {
+ public T[] method () {
+ return new T[3];
+ }
+
+ public static T[] staticMethod () {
+ return new T[3];
+ }
+
+ public S[] genericMethod<S> () {
+ return new S[3];
+ }
+}
+
+public class main {
+ static bool callMethod<T> (Gen<T> g) {
+ return g.method ().GetType () == typeof (T[]);
+ }
+
+ static bool callStaticMethod<T> () {
+ return Gen<T>.staticMethod ().GetType () == typeof (T[]);
+ }
+
+ static bool callGenericMethod<T,S> (Gen<T> g) {
+ return g.genericMethod<S> ().GetType () == typeof (S[]);
+ }
+
+ [MethodImpl (MethodImplOptions.NoInlining)]
+ static bool work<T,S> () {
+ Gen<T> g = new Gen<T> ();
+
+ if (!callMethod<T> (g))
+ return false;
+ if (!callStaticMethod<T> ())
+ return false;
+ if (!callGenericMethod<T,S> (g))
+ return false;
+ return true;
+ }
+
+ public static int Main () {
+ if (!work<string,string> ())
+ return 1;
+ if (!work<int,int> ())
+ return 1;
+ if (!work<string,int> ())
+ return 1;
+ if (!work<int,string> ())
+ return 1;
+ return 0;
+ }
+}
View
177 mono/tests/generics-sharing.2.cs
@@ -1,5 +1,10 @@
using System;
using System.Collections.Generic;
+using System.Reflection;
+
+namespace GenericSharingTest {
+
+public delegate int IntVoidDelegate ();
public class ClassA {}
public class ClassB {}
@@ -15,6 +20,47 @@ public class NonGen {
}
}
+public class GenBi<S,T> {
+ public static int field = 123;
+ public static float floatField = 1.0f;
+
+ public static int staticMethod (int x) {
+ return x + field;
+ }
+
+ public static void staticVoidMethod (int x) {
+ field = x;
+ }
+
+ public static float staticFloatMethod () {
+ return floatField;
+ }
+
+ public static long staticLongMethod (long x) {
+ return x + field;
+ }
+
+ public static GenStruct<T> staticValueMethod (int x) {
+ return new GenStruct<T> (x);
+ }
+}
+
+public struct GenStruct<T> {
+ public int field;
+ public int dummy1;
+ public int dummy2;
+ public int dummy3;
+
+ public GenStruct (int f) {
+ field = f;
+ dummy1 = dummy2 = dummy3 = 0;
+ }
+
+ public int method (int x) {
+ return x + field;
+ }
+}
+
public class GenA<T> {
public static T[] arr;
@@ -75,13 +121,73 @@ public class GenA<T> {
public void except () {
try {
- NonGen.doThrow();
+ NonGen.doThrow ();
}
- catch (GenExc<T>)
- {
+ catch (GenExc<T>) {
//Console.WriteLine("exception thrown");
}
}
+
+ public static void staticExcept () {
+ try {
+ NonGen.doThrow ();
+ }
+ catch (GenExc<T>) {
+ Console.WriteLine("exception thrown and caught");
+ }
+ }
+
+ public static int staticField = 54321;
+
+ public static int staticMethod () {
+ return staticField;
+ }
+
+ public static int staticMethodCaller () {
+ return staticMethod ();
+ }
+
+ public static float staticFloatField = 1.0f;
+
+ public static float staticFloatMethod () {
+ return staticFloatField;
+ }
+
+ public static int staticBiCaller (int x) {
+ return GenBi<int,T>.staticMethod (x);
+ }
+
+ public static void staticBiVoidCaller (int x) {
+ GenBi<int,T>.staticVoidMethod (x);
+ }
+
+ public static float staticBiFloatCaller () {
+ return GenBi<int,T>.staticFloatMethod ();
+ }
+
+ public static GenStruct<T> staticBiValueCaller (int x) {
+ return GenBi<int,T>.staticValueMethod (x);
+ }
+
+ public static int staticSharedBiCaller (int x) {
+ return GenBi<T,T>.staticMethod (x);
+ }
+
+ public static void staticSharedBiVoidCaller (int x) {
+ GenBi<T,T>.staticVoidMethod (x);
+ }
+
+ public static float staticSharedBiFloatCaller () {
+ return GenBi<T,T>.staticFloatMethod ();
+ }
+
+ public static GenStruct<T> staticSharedBiValueCaller (int x) {
+ return GenBi<T,T>.staticValueMethod (x);
+ }
+
+ public static long staticBiLongCaller (long x) {
+ return GenBi<int, T>.staticLongMethod (x);
+ }
}
public class GenB<T> {
@@ -229,6 +335,10 @@ public class main {
error ("object from " + method + " should have type " + t.ToString () + " but has type " + obj.GetType ().ToString ());
}
+ public static int callStaticMethod<T> () {
+ return GenA<T>.staticMethod ();
+ }
+
public static void work<T> (T obj, bool mustCatch) {
EqualityComparer<T> comp = EqualityComparer<T>.Default;
@@ -256,6 +366,50 @@ public class main {
if (!comp.Equals (ga.cast (obj), obj))
error ("cast");
+ if (callStaticMethod<T> () != 54321)
+ error ("staticMethod");
+
+ GenBi<int,T>.field = 123;
+ if (GenA<T>.staticBiCaller (123) != 246)
+ error ("staticBiCaller");
+ GenA<T>.staticBiVoidCaller (1234);
+ if (GenBi<int,T>.field != 1234)
+ error ("staticBiVoidCaller");
+ if (GenA<T>.staticBiFloatCaller () != 1.0f)
+ error ("staticBiFloatCaller");
+ if (GenA<T>.staticBiLongCaller (123) != 123 + 1234)
+ error ("staticBiLongCaller");
+ GenStruct<T> gs = GenA<T>.staticBiValueCaller (987);
+ if (gs.field != 987)
+ error ("staticBiValueCaller");
+
+ GenBi<T,T>.field = 123;
+ if (GenA<T>.staticSharedBiCaller (123) != 246)
+ error ("staticSharedBiCaller");
+ GenA<T>.staticSharedBiVoidCaller (1234);
+ if (GenBi<T,T>.field != 1234)
+ error ("staticSharedBiVoidCaller");
+ if (GenA<T>.staticSharedBiFloatCaller () != 1.0f)
+ error ("staticSharedBiFloatCaller");
+ GenStruct<T> gss = GenA<T>.staticSharedBiValueCaller (987);
+ if (gss.field != 987)
+ error ("staticSharedBiValueCaller");
+
+ IntVoidDelegate ivdel = new IntVoidDelegate (GenA<T>.staticMethod);
+ if (ivdel () != 54321)
+ error ("staticMethod delegate");
+
+ Type gatype = typeof (GenA<T>);
+ MethodInfo staticMethodInfo = gatype.GetMethod ("staticMethod");
+ if ((Convert.ToInt32 (staticMethodInfo.Invoke (null, null))) != 54321)
+ error ("staticMethod reflection");
+
+ if (GenA<T>.staticMethodCaller () != 54321)
+ error ("staticMethodCaller");
+
+ if (GenA<T>.staticFloatMethod () != 1.0)
+ error ("staticFloatMethod");
+
new GenADeriv<T> ();
if (mustCatch) {
@@ -266,11 +420,22 @@ public class main {
} catch (GenExc<ClassA>) {
didCatch = true;
}
-
if (!didCatch)
error ("except");
- } else
+
+ didCatch = false;
+
+ try {
+ GenA<T>.staticExcept ();
+ } catch (GenExc<ClassA>) {
+ didCatch = true;
+ }
+ if (!didCatch)
+ error ("staticExcept");
+ } else {
ga.except ();
+ GenA<T>.staticExcept ();
+ }
MyDict<T, ClassB> dtb = new MyDict<T, ClassB> (obj, new ClassB ());
@@ -373,3 +538,5 @@ public static int Main ()
return 0;
}
}
+
+}
View
92 mono/tests/shared-generic-synchronized.2.cs
@@ -0,0 +1,92 @@
+//
+// shared-generic-synchronized.2.cs:
+//
+// Tests for the 'synchronized' method attribute in shared generic methods
+//
+
+using System;
+using System.Threading;
+using System.Runtime.CompilerServices;
+
+public class Test<T> {
+
+ [MethodImplAttribute(MethodImplOptions.Synchronized)]
+ public int test () {
+ Monitor.Exit (this);
+ Monitor.Enter (this);
+ return 2 + 2;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.Synchronized)]
+ public static int test_static () {
+ Monitor.Exit (typeof (Test<T>));
+ Monitor.Enter (typeof (Test<T>));
+ return 2 + 2;
+ }
+
+ [MethodImplAttribute(MethodImplOptions.Synchronized)]
+ public int test_exception () {
+ Monitor.Exit (this);
+ throw new Exception ("A");
+ }
+
+ [MethodImplAttribute(MethodImplOptions.Synchronized)]
+ public virtual int test_virtual () {
+ Monitor.Exit (this);
+ Monitor.Enter (this);
+ return 2 + 2;
+ }
+}
+
+class main {
+ public delegate int Delegate1 ();
+
+ static public int Main (String[] args) {
+ Test<string> b = new Test<string> ();
+ int res;
+
+ Console.WriteLine ("Test1...");
+ b.test ();
+ Console.WriteLine ("Test2...");
+ Test<string>.test_static ();
+ Console.WriteLine ("Test3...");
+ try {
+ b.test_exception ();
+ }
+ catch (SynchronizationLockException ex) {
+ return 1;
+ }
+ catch (Exception ex) {
+ // OK
+ }
+
+ Console.WriteLine ("Test4...");
+ b.test_virtual ();
+
+ Console.WriteLine ("Test5...");
+ Delegate1 d = new Delegate1 (b.test);
+ res = d ();
+
+ Console.WriteLine ("Test6...");
+ d = new Delegate1 (Test<string>.test_static);
+ res = d ();
+
+ Console.WriteLine ("Test7...");
+ d = new Delegate1 (b.test_virtual);
+ res = d ();
+
+ Console.WriteLine ("Test8...");
+ d = new Delegate1 (b.test_exception);
+ try {
+ d ();
+ }
+ catch (SynchronizationLockException ex) {
+ return 2;
+ }
+ catch (Exception ex) {
+ // OK
+ }
+
+ return 0;
+ }
+}

0 comments on commit a4681ac

Please sign in to comment.