+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.
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);
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)
{
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;
GHashTable *unbox_wrapper_cache;
GHashTable *cominterop_invoke_cache;
GHashTable *cominterop_wrapper_cache;
+ GHashTable *static_rgctx_invoke_cache; /* LOCKING: marshal lock */
/*
* indexed by MonoClass pointers
WRAPPER(COMINTEROP_INVOKE, "cominterop-invoke")
WRAPPER(COMINTEROP, "cominterop")
WRAPPER(ALLOC, "alloc")
+WRAPPER(STATIC_RGCTX_INVOKE, "static-rgctx-invoke")
+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
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);
}
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;
* 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;
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);
}
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 &&
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:
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;
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;
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;
# 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;
#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);
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
*/
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;
* - 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
*/
}
}
+ /* 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) {
}
#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)
{
#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
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")
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,
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;
}
#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)
{
#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__)
} 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; \
} \
#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)
{
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 */
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)
{
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)
{
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);
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);
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);
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) &&
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;
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;
no_spill = FALSE;
if (*ip == CEE_CALLI) {
+ g_assert (!rgctx_arg);
/* Prevent inlining of methods with indirect calls */
INLINE_FAILURE;
if (no_spill) {
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);
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;
}
}
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,
* 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);
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;
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);
MonoInst *argconst;
MonoMethod *cil_method, *ctor_method;
int temp;
+ gboolean is_shared;
CHECK_STACK_OVF (1);
CHECK_OPSIZE (6);
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;
*/
#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;
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
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;
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;
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;
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;
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);
*/
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);
}
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;
/*
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;
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;
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)
+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
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 \
# 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
$(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
--- /dev/null
+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;
+ }
+}
using System;
using System.Collections.Generic;
+using System.Reflection;
+
+namespace GenericSharingTest {
+
+public delegate int IntVoidDelegate ();
public class ClassA {}
public class ClassB {}
}
}
+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;
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> {
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;
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) {
} 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 ());
return 0;
}
}
+
+}
--- /dev/null
+//
+// 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;
+ }
+}