Windows x64 full AOT support for mono/mini regression tests.
authorlateralusX <lateralusx.github@gmail.com>
Mon, 16 May 2016 08:31:38 +0000 (10:31 +0200)
committerlateralusX <lateralusx.github@gmail.com>
Mon, 16 May 2016 08:31:38 +0000 (10:31 +0200)
Fixes needed in order to get full AOT working for mono/mini regression tests on windows x64 builds (currently disabling the gsharedvt and dyncall tests).

Fixes are done in two main areas, exception and thread local storage support.

SEH exception handling enabled in order to catch CPU generated exceptions. All exception related regression tests pass with fix and runtime won't attempt to JIT any code since it is prevented when running with --full-aot startup argument.

Thread local storage (TLS) support was not complete on windows x64. When running full AOT the sgen allocator uses TLS for its allocator. AOT compiler will issues OP_TLS_GET_REG operations when compiling the “Alloc” methods. On win x64 this was not implemented, just the default OP_TLS_GET, meaning that methods compiled using OP_TLS_GET_REG was "silently" dropped when full AOT:ed compiled. At runtime, the sgen allocator would tried to locate the “Alloc” method but fails to find it and since this will cause an exception (that also needs the allocator to be used) and the program entered a loop that will end in a stack overflow crashing the program.

The fix implements OP_TLS_GET_REG and other needed support for win x64. It also fixes the offset calculation needed by full AOT builds (translating to correct runtime TLS key) for keys used by mono/mini regression tests. Since windows TLS support is a little more complex (two different conditions depending on where the TLS key gets allocated) the max size of tls_get_reg for amd64 has been increased from 32 to 64 byte as well.

mono/metadata/threads.c
mono/mini/cpu-amd64.md
mono/mini/mini-amd64.c
mono/mini/mini-amd64.h
mono/mini/mini-windows.c

index 2f7fcabf081a4d25662147a77241d59472c17981..081068c244de60ebf46231b58e13b9fe37361ca0 100644 (file)
@@ -257,8 +257,14 @@ mono_thread_get_tls_key (void)
 gint32
 mono_thread_get_tls_offset (void)
 {
-       int offset;
+       int offset = -1;
+
+#ifdef HOST_WIN32
+       if (current_object_key)
+               offset = current_object_key;
+#else
        MONO_THREAD_VAR_OFFSET (tls_current_object,offset);
+#endif
        return offset;
 }
 
index 4de253ba88306bebd026fdab4f1353ac27acb8c2..d00e5c26a8e9f8193e88881a87e4844dec9de9d7 100755 (executable)
@@ -332,7 +332,7 @@ amd64_set_xmmreg_r4: dest:f src1:f len:14 clob:m
 amd64_set_xmmreg_r8: dest:f src1:f len:14 clob:m
 amd64_save_sp_to_lmf: len:16
 tls_get: dest:i len:32
-tls_get_reg: dest:i src1:i len:32
+tls_get_reg: dest:i src1:i len:64
 tls_set: src1:i len:16
 tls_set_reg: src1:i src2:i len:32
 atomic_add_i4: src1:b src2:i dest:i len:32
index 901a6c99d76554f74d5ef72cec3a828b6e0802c8..c29d6c445e0425ed83914d8d99ee46b5d8cedec4 100644 (file)
@@ -3780,6 +3780,73 @@ mono_amd64_emit_tls_get (guint8* code, int dreg, int tls_offset)
        return code;
 }
 
+#ifdef TARGET_WIN32
+
+#define MAX_TEB_TLS_SLOTS 64
+#define TEB_TLS_SLOTS_OFFSET 0x1480
+#define TEB_TLS_EXPANSION_SLOTS_OFFSET 0x1780
+
+static guint8*
+emit_tls_get_reg_windows (guint8* code, int dreg, int offset_reg)
+{
+       int tmp_reg = -1;
+       guint8 * more_than_64_slots = NULL;
+       guint8 * empty_slot = NULL;
+       guint8 * tls_get_reg_done = NULL;
+       
+       //Use temporary register for offset calculation?
+       if (dreg == offset_reg) {
+               tmp_reg = dreg == AMD64_RAX ? AMD64_RCX : AMD64_RAX;
+               amd64_push_reg (code, tmp_reg);
+               amd64_mov_reg_reg (code, tmp_reg, offset_reg, sizeof (gpointer));
+               offset_reg = tmp_reg;
+       }
+
+       //TEB TLS slot array only contains MAX_TEB_TLS_SLOTS items, if more is used the expansion slots must be addressed.
+       amd64_alu_reg_imm (code, X86_CMP, offset_reg, MAX_TEB_TLS_SLOTS);
+       more_than_64_slots = code;
+       amd64_branch8 (code, X86_CC_GE, 0, TRUE);
+
+       //TLS slot array, _TEB.TlsSlots, is at offset TEB_TLS_SLOTS_OFFSET and index is offset * 8 in Windows 64-bit _TEB structure.
+       amd64_shift_reg_imm (code, X86_SHL, offset_reg, 3);
+       amd64_alu_reg_imm (code, X86_ADD, offset_reg, TEB_TLS_SLOTS_OFFSET);
+
+       //TEB pointer is stored in GS segment register on Windows x64. TLS slot is located at calculated offset from that pointer.
+       x86_prefix (code, X86_GS_PREFIX);
+       amd64_mov_reg_membase (code, dreg, offset_reg, 0, sizeof (gpointer));
+               
+       tls_get_reg_done = code;
+       amd64_jump8 (code, 0);
+
+       amd64_patch (more_than_64_slots, code);
+
+       //TLS expansion slots, _TEB.TlsExpansionSlots, is at offset TEB_TLS_EXPANSION_SLOTS_OFFSET in Windows 64-bit _TEB structure.
+       x86_prefix (code, X86_GS_PREFIX);
+       amd64_mov_reg_mem (code, dreg, TEB_TLS_EXPANSION_SLOTS_OFFSET, sizeof (gpointer));
+       
+       //Check for NULL in _TEB.TlsExpansionSlots.
+       amd64_test_reg_reg (code, dreg, dreg);
+       empty_slot = code;
+       amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
+       
+       //TLS expansion slots are at index offset into the expansion array.
+       //Calculate for the MAX_TEB_TLS_SLOTS offsets, since the interessting offset is offset_reg - MAX_TEB_TLS_SLOTS.
+       amd64_alu_reg_imm (code, X86_SUB, offset_reg, MAX_TEB_TLS_SLOTS);
+       amd64_shift_reg_imm (code, X86_SHL, offset_reg, 3);
+       
+       amd64_mov_reg_memindex (code, dreg, dreg, 0, offset_reg, 0, sizeof (gpointer));
+       
+       amd64_patch (empty_slot, code);
+       amd64_patch (tls_get_reg_done, code);
+
+       if (tmp_reg != -1)
+               amd64_pop_reg (code, tmp_reg);
+
+       return code;
+}
+
+#endif
+
 static guint8*
 emit_tls_get_reg (guint8* code, int dreg, int offset_reg)
 {
@@ -3804,6 +3871,8 @@ emit_tls_get_reg (guint8* code, int dreg, int offset_reg)
        amd64_mov_reg_memindex (code, dreg, dreg, 0, offset_reg, 0, 8);
        if (tmpreg != -1)
                amd64_mov_reg_membase (code, tmpreg, AMD64_RSP, -8, 8);
+#elif defined(TARGET_WIN32)
+       code = emit_tls_get_reg_windows (code, dreg, offset_reg);
 #else
        g_assert_not_reached ();
 #endif
index 1d04234c1785f7d5eb0045764bc3212a81cdfc42..9c2d7c0ec29db3066708e08936a9772262cbb4cc 100644 (file)
@@ -435,7 +435,7 @@ typedef struct {
 #define MONO_ARCH_HAVE_UNWIND_BACKTRACE 1
 #endif
 
-#if defined(TARGET_OSX) || defined(__linux__)
+#if defined(TARGET_OSX) || defined(__linux__) || defined(TARGET_WIN32)
 #define MONO_ARCH_HAVE_TLS_GET_REG 1
 #endif
 
index 621a7a5fc31970e22d3e75e4993623ae10dddece..e7764e91beb749709474b294aef00cb5c30932ce 100644 (file)
@@ -59,14 +59,12 @@ void
 mono_runtime_install_handlers (void)
 {
 #ifndef MONO_CROSS_COMPILE
-       if (!mono_aot_only) {
-               win32_seh_init();
-               win32_seh_set_handler(SIGFPE, mono_sigfpe_signal_handler);
-               win32_seh_set_handler(SIGILL, mono_sigill_signal_handler);
-               win32_seh_set_handler(SIGSEGV, mono_sigsegv_signal_handler);
-               if (mini_get_debug_options ()->handle_sigint)
-                       win32_seh_set_handler(SIGINT, mono_sigint_signal_handler);
-       }
+       win32_seh_init();
+       win32_seh_set_handler(SIGFPE, mono_sigfpe_signal_handler);
+       win32_seh_set_handler(SIGILL, mono_sigill_signal_handler);
+       win32_seh_set_handler(SIGSEGV, mono_sigsegv_signal_handler);
+       if (mini_get_debug_options ()->handle_sigint)
+               win32_seh_set_handler(SIGINT, mono_sigint_signal_handler);
 #endif
 }
 
@@ -74,9 +72,7 @@ void
 mono_runtime_cleanup_handlers (void)
 {
 #ifndef MONO_CROSS_COMPILE
-       if (!mono_aot_only) {
-               win32_seh_cleanup();
-       }
+       win32_seh_cleanup();
 #endif
 }