+void**
+mono_monitor_get_object_monitor_weak_link (MonoObject *object)
+{
+ LockWord lw;
+ MonoThreadsSync *sync = NULL;
+
+ lw.sync = object->synchronisation;
+ if (lw.lock_word & LOCK_WORD_FAT_HASH) {
+ lw.lock_word &= ~LOCK_WORD_BITS_MASK;
+ sync = lw.sync;
+ } else if (!(lw.lock_word & LOCK_WORD_THIN_HASH)) {
+ sync = lw.sync;
+ }
+
+ if (sync && sync->data)
+ return &sync->data;
+ return NULL;
+}
+
+#ifndef DISABLE_JIT
+
+static void
+emit_obj_syncp_check (MonoMethodBuilder *mb, int syncp_loc, int *obj_null_branch, int *true_locktaken_branch, int *syncp_true_false_branch,
+ int *thin_hash_branch, gboolean branch_on_true)
+{
+ /*
+ ldarg 0 obj
+ brfalse.s obj_null
+ */
+
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ *obj_null_branch = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
+
+ /*
+ ldarg.1
+ ldind.i1
+ brtrue.s true_locktaken
+ */
+ if (true_locktaken_branch) {
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_byte (mb, CEE_LDIND_I1);
+ *true_locktaken_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+ }
+
+ /*
+ ldarg 0 obj
+ conv.i objp
+ ldc.i4 G_STRUCT_OFFSET(MonoObject, synchronisation) objp off
+ add &syncp
+ ldind.i syncp
+ stloc syncp
+ ldloc syncp syncp
+ brtrue/false.s syncp_true_false
+ */
+
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, syncp_loc);
+
+
+ if (mono_gc_is_moving ()) {
+ /*check for a thin hash*/
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, 0x01);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_byte (mb, CEE_AND);
+ *thin_hash_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+ /*clear gc bits*/
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, ~0x3);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_byte (mb, CEE_AND);
+ mono_mb_emit_stloc (mb, syncp_loc);
+ } else {
+ *thin_hash_branch = 0;
+ }
+
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ *syncp_true_false_branch = mono_mb_emit_short_branch (mb, branch_on_true ? CEE_BRTRUE_S : CEE_BRFALSE_S);
+}
+
+#endif
+
+static MonoMethod* monitor_il_fastpaths[3];
+
+gboolean
+mono_monitor_is_il_fastpath_wrapper (MonoMethod *method)
+{
+ int i;
+ for (i = 0; i < 3; ++i) {
+ if (monitor_il_fastpaths [i] == method)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+enum {
+ FASTPATH_ENTER,
+ FASTPATH_ENTERV4,
+ FASTPATH_EXIT
+};
+
+
+static MonoMethod*
+register_fastpath (MonoMethod *method, int idx)
+{
+ mono_memory_barrier ();
+ monitor_il_fastpaths [idx] = method;
+ return method;
+}
+
+static MonoMethod*
+mono_monitor_get_fast_enter_method (MonoMethod *monitor_enter_method)
+{
+ MonoMethodBuilder *mb;
+ MonoMethod *res;
+ static MonoMethod *compare_exchange_method;
+ int obj_null_branch, true_locktaken_branch = 0, syncp_null_branch, has_owner_branch, other_owner_branch, tid_branch, thin_hash_branch;
+ int tid_loc, syncp_loc, owner_loc;
+ int thread_tls_offset;
+ gboolean is_v4 = mono_method_signature (monitor_enter_method)->param_count == 2;
+ int fast_path_idx = is_v4 ? FASTPATH_ENTERV4 : FASTPATH_ENTER;
+ WrapperInfo *info;
+
+ /* The !is_v4 version is not used/tested */
+ g_assert (is_v4);
+
+ thread_tls_offset = mono_thread_get_tls_offset ();
+ if (thread_tls_offset == -1)
+ return NULL;
+
+ if (monitor_il_fastpaths [fast_path_idx])
+ return monitor_il_fastpaths [fast_path_idx];
+
+ if (!compare_exchange_method) {
+ MonoMethodDesc *desc;
+ MonoClass *class;
+
+ desc = mono_method_desc_new ("Interlocked:CompareExchange(intptr&,intptr,intptr)", FALSE);
+ class = mono_class_from_name (mono_defaults.corlib, "System.Threading", "Interlocked");
+ compare_exchange_method = mono_method_desc_search_in_class (desc, class);
+ mono_method_desc_free (desc);
+
+ if (!compare_exchange_method)
+ return NULL;
+ }
+
+ mb = mono_mb_new (mono_defaults.monitor_class, is_v4 ? "FastMonitorEnterV4" : "FastMonitorEnter", MONO_WRAPPER_UNKNOWN);
+
+ mb->method->slot = -1;
+ mb->method->flags = METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_STATIC |
+ METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL;
+
+#ifndef DISABLE_JIT
+ tid_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ syncp_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+ owner_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+
+ emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, is_v4 ? &true_locktaken_branch : NULL, &syncp_null_branch, &thin_hash_branch, FALSE);
+
+ /*
+ mono. tls thread_tls_offset threadp
+ ldc.i4 G_STRUCT_OFFSET(MonoThread, tid) threadp off
+ add &tid
+ ldind.i tid
+ stloc tid
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldind.i owner
+ stloc owner
+ ldloc owner owner
+ brtrue.s tid
+ */
+
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_TLS);
+ mono_mb_emit_i4 (mb, thread_tls_offset);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoInternalThread, tid));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, tid_loc);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, owner_loc);
+ mono_mb_emit_ldloc (mb, owner_loc);
+ tid_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+ /*
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldloc tid &owner tid
+ ldc.i4 0 &owner tid 0
+ call System.Threading.Interlocked.CompareExchange oldowner
+ brtrue.s has_owner
+ ret
+ */
+
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_ldloc (mb, tid_loc);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_managed_call (mb, compare_exchange_method, NULL);
+ has_owner_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+ if (is_v4) {
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_STIND_I1);
+ }
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ tid:
+ ldloc owner owner
+ ldloc tid owner tid
+ brne.s other_owner
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, nest) syncp off
+ add &nest
+ dup &nest &nest
+ ldind.i4 &nest nest
+ ldc.i4 1 &nest nest 1
+ add &nest nest+
+ stind.i4
+ ret
+ */
+
+ mono_mb_patch_short_branch (mb, tid_branch);
+ mono_mb_emit_ldloc (mb, owner_loc);
+ mono_mb_emit_ldloc (mb, tid_loc);
+ other_owner_branch = mono_mb_emit_short_branch (mb, CEE_BNE_UN_S);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, nest));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_DUP);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_STIND_I4);
+
+ if (is_v4) {
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_STIND_I1);
+ }
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ obj_null, syncp_null, has_owner, other_owner:
+ ldarg 0 obj
+ call System.Threading.Monitor.Enter
+ ret
+ */
+
+ if (thin_hash_branch)
+ mono_mb_patch_short_branch (mb, thin_hash_branch);
+ mono_mb_patch_short_branch (mb, obj_null_branch);
+ mono_mb_patch_short_branch (mb, syncp_null_branch);
+ mono_mb_patch_short_branch (mb, has_owner_branch);
+ mono_mb_patch_short_branch (mb, other_owner_branch);
+ if (true_locktaken_branch)
+ mono_mb_patch_short_branch (mb, true_locktaken_branch);
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ if (is_v4)
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_managed_call (mb, monitor_enter_method, NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+#endif
+
+ res = register_fastpath (mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_enter_method), 5), fast_path_idx);
+
+ info = mono_image_alloc0 (mono_defaults.corlib, sizeof (WrapperInfo));
+ info->subtype = is_v4 ? WRAPPER_SUBTYPE_FAST_MONITOR_ENTER_V4 : WRAPPER_SUBTYPE_FAST_MONITOR_ENTER;
+ mono_marshal_set_wrapper_info (res, info);
+
+ mono_mb_free (mb);
+ return res;
+}
+
+static MonoMethod*
+mono_monitor_get_fast_exit_method (MonoMethod *monitor_exit_method)
+{
+ MonoMethodBuilder *mb;
+ MonoMethod *res;
+ int obj_null_branch, has_waiting_branch, has_syncp_branch, owned_branch, nested_branch, thin_hash_branch;
+ int thread_tls_offset;
+ int syncp_loc;
+ WrapperInfo *info;
+
+ thread_tls_offset = mono_thread_get_tls_offset ();
+ if (thread_tls_offset == -1)
+ return NULL;
+
+ if (monitor_il_fastpaths [FASTPATH_EXIT])
+ return monitor_il_fastpaths [FASTPATH_EXIT];
+
+ mb = mono_mb_new (mono_defaults.monitor_class, "FastMonitorExit", MONO_WRAPPER_UNKNOWN);
+
+ mb->method->slot = -1;
+ mb->method->flags = METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_STATIC |
+ METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL;
+
+#ifndef DISABLE_JIT
+ syncp_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+
+ emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, NULL, &has_syncp_branch, &thin_hash_branch, TRUE);
+
+ /*
+ ret
+ */
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ has_syncp:
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldind.i owner
+ mono. tls thread_tls_offset owner threadp
+ ldc.i4 G_STRUCT_OFFSET(MonoThread, tid) owner threadp off
+ add owner &tid
+ ldind.i owner tid
+ beq.s owned
+ */
+
+ mono_mb_patch_short_branch (mb, has_syncp_branch);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_TLS);
+ mono_mb_emit_i4 (mb, thread_tls_offset);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoInternalThread, tid));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ owned_branch = mono_mb_emit_short_branch (mb, CEE_BEQ_S);
+
+ /*
+ ret
+ */
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ owned:
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, nest) syncp off
+ add &nest
+ dup &nest &nest
+ ldind.i4 &nest nest
+ dup &nest nest nest
+ ldc.i4 1 &nest nest nest 1
+ bgt.un.s nested &nest nest
+ */
+
+ mono_mb_patch_short_branch (mb, owned_branch);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, nest));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_DUP);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_DUP);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ nested_branch = mono_mb_emit_short_branch (mb, CEE_BGT_UN_S);
+
+ /*
+ pop &nest
+ pop
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, entry_count) syncp off
+ add &count
+ ldind.i4 count
+ brtrue.s has_waiting
+ */
+
+ mono_mb_emit_byte (mb, CEE_POP);
+ mono_mb_emit_byte (mb, CEE_POP);
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, entry_count));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ has_waiting_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+ /*
+ ldloc syncp syncp
+ ldc.i4 G_STRUCT_OFFSET(MonoThreadsSync, owner) syncp off
+ add &owner
+ ldnull &owner 0
+ stind.i
+ ret
+ */
+
+ mono_mb_emit_ldloc (mb, syncp_loc);
+ mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_byte (mb, CEE_STIND_I);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ nested:
+ ldc.i4 1 &nest nest 1
+ sub &nest nest-
+ stind.i4
+ ret
+ */
+
+ mono_mb_patch_short_branch (mb, nested_branch);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_byte (mb, CEE_STIND_I4);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ obj_null, has_waiting:
+ ldarg 0 obj
+ call System.Threading.Monitor.Exit
+ ret
+ */
+
+ if (thin_hash_branch)
+ mono_mb_patch_short_branch (mb, thin_hash_branch);
+ mono_mb_patch_short_branch (mb, obj_null_branch);
+ mono_mb_patch_short_branch (mb, has_waiting_branch);
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_managed_call (mb, monitor_exit_method, NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+#endif
+
+ res = register_fastpath (mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_exit_method), 5), FASTPATH_EXIT);
+ mono_mb_free (mb);
+
+ info = mono_image_alloc0 (mono_defaults.corlib, sizeof (WrapperInfo));
+ info->subtype = WRAPPER_SUBTYPE_FAST_MONITOR_EXIT;
+ mono_marshal_set_wrapper_info (res, info);
+
+ return res;
+}
+
+MonoMethod*
+mono_monitor_get_fast_path (MonoMethod *enter_or_exit)
+{
+ if (strcmp (enter_or_exit->name, "Enter") == 0)
+ return mono_monitor_get_fast_enter_method (enter_or_exit);
+ if (strcmp (enter_or_exit->name, "Exit") == 0)
+ return mono_monitor_get_fast_exit_method (enter_or_exit);
+ g_assert_not_reached ();
+ return NULL;
+}
+
+/*
+ * mono_monitor_threads_sync_member_offset:
+ * @owner_offset: returns size and offset of the "owner" member
+ * @nest_offset: returns size and offset of the "nest" member
+ * @entry_count_offset: returns size and offset of the "entry_count" member
+ *
+ * Returns the offsets and sizes of three members of the
+ * MonoThreadsSync struct. The Monitor ASM fastpaths need this.
+ */
+void
+mono_monitor_threads_sync_members_offset (int *owner_offset, int *nest_offset, int *entry_count_offset)
+{
+ MonoThreadsSync ts;
+
+#define ENCODE_OFF_SIZE(o,s) (((o) << 8) | ((s) & 0xff))
+
+ *owner_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, owner), sizeof (ts.owner));
+ *nest_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, nest), sizeof (ts.nest));
+ *entry_count_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, entry_count), sizeof (ts.entry_count));
+}
+