[mcs] Implements C# 7.2 readonly structs
[mono.git] / mono / utils / mono-tls.c
index 007dc4e6d225bd81336071a0fcda25750b5b8091..c83fb10d5321125c91aa0c0fba353ab0e7ee53e5 100644 (file)
-/*
- * mono-tls.c: Low-level TLS support
+/**
+ * \file
+ * Low-level TLS support
+ *
+ * Thread local variables that are accessed both from native and managed code
+ * are defined here and should be accessed only through this APIs
  *
  * Copyright 2013 Xamarin, Inc (http://www.xamarin.com)
  */
 
-#include <config.h>
+#include <mono/utils/mach-support.h>
 
 #include "mono-tls.h"
 
-static int tls_offsets [TLS_KEY_NUM];
-static gboolean tls_offset_set [TLS_KEY_NUM];
-
 /*
- * mono_tls_key_get_offset:
+ * On all platforms we should be able to use either __thread or pthread/TlsGetValue.
+ * Certain platforms will support fast tls only when using one of the thread local
+ * storage backends. By default this is __thread if we have HAVE_KW_THREAD defined.
  *
- *   Return the TLS offset used by the TLS var identified by KEY, previously initialized by a call to
- * mono_tls_key_set_offset (). Return -1 if the offset is not known.
+ * By default all platforms will call into these native getters whenever they need
+ * to get a tls value. On certain platforms we can try to be faster than this and
+ * avoid the call. We call this fast tls and each platform defines its own way to
+ * achieve this. For this, a platform has to define MONO_ARCH_HAVE_INLINED_TLS,
+ * and provide alternative getters/setters for a MonoTlsKey. In order to have fast
+ * getter/setters, the platform has to declare a way to fetch an internal offset
+ * (MONO_THREAD_VAR_OFFSET) which is stored here, and in the arch specific file
+ * probe the system to see if we can use the offset initialized here. If these
+ * run-time checks don't succeed we just use the fallbacks.
+ *
+ * In case we would wish to provide fast inlined tls for aot code, we would need
+ * to be sure that, at run-time, these two platform checks would never fail
+ * otherwise the tls getter/setters that we emitted would not work. Normally,
+ * there is little incentive to support this since tls access is most common in
+ * wrappers and managed allocators, both of which are not aot-ed by default.
+ * So far, we never supported inlined fast tls on full-aot systems.
+ */
+
+#ifdef USE_KW_THREAD
+
+/* tls attribute */
+#if HAVE_TLS_MODEL_ATTR
+
+#if defined(__PIC__) && !defined(PIC)
+/*
+ * Must be compiling -fPIE, for executables.  Build PIC
+ * but with initial-exec.
+ * http://bugs.gentoo.org/show_bug.cgi?id=165547
+ */
+#define PIC
+#define PIC_INITIAL_EXEC
+#endif
+
+/*
+ * Define this if you want a faster libmono, which cannot be loaded dynamically as a
+ * module.
  */
-int
-mono_tls_key_get_offset (MonoTlsKey key)
+//#define PIC_INITIAL_EXEC
+
+#if defined(PIC)
+
+#ifdef PIC_INITIAL_EXEC
+#define MONO_TLS_FAST __attribute__ ((__tls_model__("initial-exec")))
+#else
+#if defined (__powerpc__)
+/* local dynamic requires a call to __tls_get_addr to look up the
+   TLS block address via the Dynamic Thread Vector. In this case Thread
+   Pointer relative offsets can't be used as this modules TLS was
+   allocated separately (none contiguoiusly) from the initial TLS
+   block.
+
+   For now we will disable this. */
+#define MONO_TLS_FAST
+#else
+#define MONO_TLS_FAST __attribute__ ((__tls_model__("local-dynamic")))
+#endif
+#endif
+
+#else
+
+#define MONO_TLS_FAST __attribute__ ((__tls_model__("local-exec")))
+
+#endif
+
+#else
+#define MONO_TLS_FAST
+#endif
+
+/* Runtime offset detection */
+#if defined(TARGET_AMD64) && !defined(TARGET_MACH) && !defined(HOST_WIN32) /* __thread likely not tested on mac/win */
+
+#if defined(PIC)
+// This only works if libmono is linked into the application
+#define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;  __asm ("movq " #var "@GOTTPOFF(%%rip), %0" : "=r" (foo)); offset = foo; } while (0)
+#else
+#define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;  __asm ("movq $" #var "@TPOFF, %0" : "=r" (foo)); offset = foo; } while (0)
+#endif
+
+#elif defined(TARGET_X86) && !defined(TARGET_MACH) && !defined(HOST_WIN32) && defined(__GNUC__)
+
+#if defined(PIC)
+#define MONO_THREAD_VAR_OFFSET(var,offset) do { int tmp; __asm ("call 1f; 1: popl %0; addl $_GLOBAL_OFFSET_TABLE_+[.-1b], %0; movl " #var "@gotntpoff(%0), %1" : "=r" (tmp), "=r" (offset)); } while (0)
+#else
+#define MONO_THREAD_VAR_OFFSET(var,offset) __asm ("movl $" #var "@ntpoff, %0" : "=r" (offset))
+#endif
+
+#elif defined(TARGET_ARM64) && !defined(PIC)
+
+#define MONO_THREAD_VAR_OFFSET(var,offset) \
+       __asm ( "mov %0, #0\n add %0, %0, #:tprel_hi12:" #var "\n add %0, %0, #:tprel_lo12_nc:" #var "\n" \
+               : "=r" (offset))
+
+#elif defined(TARGET_ARM) && defined(__ARM_EABI__) && !defined(PIC)
+
+#define MONO_THREAD_VAR_OFFSET(var,offset) __asm ("     ldr     %0, 1f; b 2f; 1: .word " #var "(tpoff); 2:" : "=r" (offset))
+
+#elif defined(TARGET_S390X)
+# if defined(__PIC__)
+#  if !defined(__PIE__)
+// This only works if libmono is linked into the application
+#   define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                                \
+                                               __asm__ ("basr  %%r1,0\n\t"                     \
+                                                        "j     0f\n\t"                         \
+                                                        ".quad " #var "@TLSGD\n"               \
+                                                        "0:\n\t"                               \
+                                                        "lg    %%r2,4(%%r1)\n\t"               \
+                                                        "brasl %%r14,__tls_get_offset@PLT:tls_gdcall:"#var"\n\t" \
+                                                        "lgr   %0,%%r2\n\t"                    \
+                                                       : "=r" (foo) :                          \
+                                                       : "1", "2", "14", "cc");                \
+                                               offset = foo; } while (0)
+#  elif __PIE__ == 1
+#   define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                                        \
+                                               __asm__ ("lg    %0," #var "@GOTNTPOFF(%%r12)\n\t"       \
+                                                        : "=r" (foo));                                 \
+                                               offset = foo; } while (0)
+#  elif __PIE__ == 2
+#   define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                                \
+                                               __asm__ ("larl  %%r1," #var "@INDNTPOFF\n\t"    \
+                                                        "lg    %0,0(%%r1)\n\t"                 \
+                                                        : "=r" (foo) :                         \
+                                                        : "1", "cc");                          \
+                                               offset = foo; } while (0)
+#  endif
+# else
+#  define MONO_THREAD_VAR_OFFSET(var,offset) do { guint64 foo;                         \
+                                               __asm__ ("basr  %%r1,0\n\t"             \
+                                                        "j     0f\n\t"                 \
+                                                        ".quad " #var "@NTPOFF\n"      \
+                                                        "0:\n\t"                       \
+                                                        "lg    %0,4(%%r1)\n\t"         \
+                                                       : "=r" (foo) : : "1");          \
+                                               offset = foo; } while (0)
+# endif
+#else
+
+#define MONO_THREAD_VAR_OFFSET(var,offset) (offset) = -1
+
+#endif
+
+/* Tls variables for each MonoTlsKey */
+
+static __thread gpointer mono_tls_thread MONO_TLS_FAST;
+static __thread gpointer mono_tls_jit_tls MONO_TLS_FAST;
+static __thread gpointer mono_tls_domain MONO_TLS_FAST;
+static __thread gpointer mono_tls_lmf MONO_TLS_FAST;
+static __thread gpointer mono_tls_sgen_thread_info MONO_TLS_FAST;
+static __thread gpointer mono_tls_lmf_addr MONO_TLS_FAST;
+
+#else
+
+#if defined(TARGET_AMD64) && (defined(TARGET_MACH) || defined(HOST_WIN32))
+#define MONO_THREAD_VAR_OFFSET(key,offset) (offset) = (gint32)key
+#elif defined(TARGET_X86) && (defined(TARGET_MACH) || defined(HOST_WIN32))
+#define MONO_THREAD_VAR_OFFSET(key,offset) (offset) = (gint32)key
+#else
+#define MONO_THREAD_VAR_OFFSET(var,offset) (offset) = -1
+#endif
+
+static MonoNativeTlsKey mono_tls_key_thread;
+static MonoNativeTlsKey mono_tls_key_jit_tls;
+static MonoNativeTlsKey mono_tls_key_domain;
+static MonoNativeTlsKey mono_tls_key_sgen_thread_info;
+static MonoNativeTlsKey mono_tls_key_lmf_addr;
+
+#endif
+
+static gint32 tls_offsets [TLS_KEY_NUM];
+
+#ifdef USE_KW_THREAD
+#define MONO_TLS_GET_VALUE(tls_var,tls_key) (tls_var)
+#define MONO_TLS_SET_VALUE(tls_var,tls_key,value) (tls_var = value)
+#else
+#define MONO_TLS_GET_VALUE(tls_var,tls_key) (mono_native_tls_get_value (tls_key))
+#define MONO_TLS_SET_VALUE(tls_var,tls_key,value) (mono_native_tls_set_value (tls_key, value))
+#endif
+
+void
+mono_tls_init_gc_keys (void)
 {
-       g_assert (tls_offset_set [key]);
+#ifdef USE_KW_THREAD
+       MONO_THREAD_VAR_OFFSET (mono_tls_sgen_thread_info, tls_offsets [TLS_KEY_SGEN_THREAD_INFO]);
+#else
+       mono_native_tls_alloc (&mono_tls_key_sgen_thread_info, NULL);
+       MONO_THREAD_VAR_OFFSET (mono_tls_key_sgen_thread_info, tls_offsets [TLS_KEY_SGEN_THREAD_INFO]);
+#endif
+}
+
+void
+mono_tls_init_runtime_keys (void)
+{
+#ifdef USE_KW_THREAD
+       MONO_THREAD_VAR_OFFSET (mono_tls_thread, tls_offsets [TLS_KEY_THREAD]);
+       MONO_THREAD_VAR_OFFSET (mono_tls_jit_tls, tls_offsets [TLS_KEY_JIT_TLS]);
+       MONO_THREAD_VAR_OFFSET (mono_tls_domain, tls_offsets [TLS_KEY_DOMAIN]);
+       MONO_THREAD_VAR_OFFSET (mono_tls_lmf_addr, tls_offsets [TLS_KEY_LMF_ADDR]);
+#else
+       mono_native_tls_alloc (&mono_tls_key_thread, NULL);
+       MONO_THREAD_VAR_OFFSET (mono_tls_key_thread, tls_offsets [TLS_KEY_THREAD]);
+       mono_native_tls_alloc (&mono_tls_key_jit_tls, NULL);
+       MONO_THREAD_VAR_OFFSET (mono_tls_key_jit_tls, tls_offsets [TLS_KEY_JIT_TLS]);
+       mono_native_tls_alloc (&mono_tls_key_domain, NULL);
+       MONO_THREAD_VAR_OFFSET (mono_tls_key_domain, tls_offsets [TLS_KEY_DOMAIN]);
+       mono_native_tls_alloc (&mono_tls_key_lmf_addr, NULL);
+       MONO_THREAD_VAR_OFFSET (mono_tls_key_lmf_addr, tls_offsets [TLS_KEY_LMF_ADDR]);
+#endif
+}
+
+void
+mono_tls_free_keys (void)
+{
+#ifndef USE_KW_THREAD
+       mono_native_tls_free (mono_tls_key_thread);
+       mono_native_tls_free (mono_tls_key_jit_tls);
+       mono_native_tls_free (mono_tls_key_domain);
+       mono_native_tls_free (mono_tls_key_sgen_thread_info);
+       mono_native_tls_free (mono_tls_key_lmf_addr);
+#endif
+}
+
+
+/*
+ * Gets the tls offset associated with the key. This offset is set at key
+ * initialization (at runtime). Certain targets can implement computing
+ * this offset and using it at runtime for fast inlined tls access.
+ */
+gint32
+mono_tls_get_tls_offset (MonoTlsKey key)
+{
+       g_assert (tls_offsets [key]);
        return tls_offsets [key];
 }
 
 /*
- * mono_tls_key_set_offset:
- *
- *   Set the TLS offset used by the TLS var identified by KEY.
+ * Returns the getter (gpointer (*)(void)) for the mono tls key.
+ * Managed code will always get the value by calling this getter.
  */
-void
-mono_tls_key_set_offset (MonoTlsKey key, int offset)
+gpointer
+mono_tls_get_tls_getter (MonoTlsKey key, gboolean name)
+{
+       switch (key) {
+       case TLS_KEY_THREAD:
+               return name ? (gpointer)"mono_tls_get_thread" : (gpointer)mono_tls_get_thread;
+       case TLS_KEY_JIT_TLS:
+               return name ? (gpointer)"mono_tls_get_jit_tls" : (gpointer)mono_tls_get_jit_tls;
+       case TLS_KEY_DOMAIN:
+               return name ? (gpointer)"mono_tls_get_domain" : (gpointer)mono_tls_get_domain;
+       case TLS_KEY_SGEN_THREAD_INFO:
+               return name ? (gpointer)"mono_tls_get_sgen_thread_info" : (gpointer)mono_tls_get_sgen_thread_info;
+       case TLS_KEY_LMF_ADDR:
+               return name ? (gpointer)"mono_tls_get_lmf_addr" : (gpointer)mono_tls_get_lmf_addr;
+       }
+       g_assert_not_reached ();
+       return NULL;
+}
+
+/* Returns the setter (void (*)(gpointer)) for the mono tls key */
+gpointer
+mono_tls_get_tls_setter (MonoTlsKey key, gboolean name)
+{
+       switch (key) {
+       case TLS_KEY_THREAD:
+               return name ? (gpointer)"mono_tls_set_thread" : (gpointer)mono_tls_set_thread;
+       case TLS_KEY_JIT_TLS:
+               return name ? (gpointer)"mono_tls_set_jit_tls" : (gpointer)mono_tls_set_jit_tls;
+       case TLS_KEY_DOMAIN:
+               return name ? (gpointer)"mono_tls_set_domain" : (gpointer)mono_tls_set_domain;
+       case TLS_KEY_SGEN_THREAD_INFO:
+               return name ? (gpointer)"mono_tls_set_sgen_thread_info" : (gpointer)mono_tls_set_sgen_thread_info;
+       case TLS_KEY_LMF_ADDR:
+               return name ? (gpointer)"mono_tls_set_lmf_addr" : (gpointer)mono_tls_set_lmf_addr;
+       }
+       g_assert_not_reached ();
+       return NULL;
+}
+
+/* Getters for each tls key */
+gpointer mono_tls_get_thread (void)
+{
+       return MONO_TLS_GET_VALUE (mono_tls_thread, mono_tls_key_thread);
+}
+
+gpointer mono_tls_get_jit_tls (void)
+{
+       return MONO_TLS_GET_VALUE (mono_tls_jit_tls, mono_tls_key_jit_tls);
+}
+
+gpointer mono_tls_get_domain (void)
+{
+       return MONO_TLS_GET_VALUE (mono_tls_domain, mono_tls_key_domain);
+}
+
+gpointer mono_tls_get_sgen_thread_info (void)
+{
+       return MONO_TLS_GET_VALUE (mono_tls_sgen_thread_info, mono_tls_key_sgen_thread_info);
+}
+
+gpointer mono_tls_get_lmf_addr (void)
+{
+       return MONO_TLS_GET_VALUE (mono_tls_lmf_addr, mono_tls_key_lmf_addr);
+}
+
+/* Setters for each tls key */
+void mono_tls_set_thread (gpointer value)
+{
+       MONO_TLS_SET_VALUE (mono_tls_thread, mono_tls_key_thread, value);
+}
+
+void mono_tls_set_jit_tls (gpointer value)
+{
+       MONO_TLS_SET_VALUE (mono_tls_jit_tls, mono_tls_key_jit_tls, value);
+}
+
+void mono_tls_set_domain (gpointer value)
+{
+       MONO_TLS_SET_VALUE (mono_tls_domain, mono_tls_key_domain, value);
+}
+
+void mono_tls_set_sgen_thread_info (gpointer value)
+{
+       MONO_TLS_SET_VALUE (mono_tls_sgen_thread_info, mono_tls_key_sgen_thread_info, value);
+}
+
+void mono_tls_set_lmf_addr (gpointer value)
 {
-       tls_offsets [key] = offset;
-       tls_offset_set [key] = TRUE;
+       MONO_TLS_SET_VALUE (mono_tls_lmf_addr, mono_tls_key_lmf_addr, value);
 }