Enable YMM context support on Windows. (#45829)
authormonojenkins <jo.shields+jenkins@xamarin.com>
Wed, 9 Dec 2020 13:10:19 +0000 (08:10 -0500)
committerGitHub <noreply@github.com>
Wed, 9 Dec 2020 13:10:19 +0000 (14:10 +0100)
Controlled using MONO_HAVE_SIMD_REG_AVX define, disabled by default.

Co-authored-by: lateralusX <lateralusX@users.noreply.github.com>
src/mono/mono/mini/mini-windows.c
src/mono/mono/utils/mono-context.c
src/mono/mono/utils/mono-context.h
src/mono/mono/utils/mono-threads-windows.c
src/mono/mono/utils/win64.asm

index 24f1a8b..a250c96 100644 (file)
@@ -44,6 +44,7 @@
 #include <mono/utils/mono-logger-internals.h>
 #include <mono/utils/mono-mmap.h>
 #include <mono/utils/dtrace.h>
+#include <mono/utils/mono-context.h>
 #include <mono/utils/w32subset.h>
 
 #include "mini.h"
@@ -391,22 +392,31 @@ gboolean
 mono_setup_thread_context(DWORD thread_id, MonoContext *mono_context)
 {
        HANDLE handle;
-       CONTEXT context;
+#if defined(MONO_HAVE_SIMD_REG_AVX) && HAVE_API_SUPPORT_WIN32_CONTEXT_XSTATE
+       BYTE context_buffer [2048];
+       DWORD context_buffer_len = G_N_ELEMENTS (context_buffer);
+       PCONTEXT context = NULL;
+       BOOL success = InitializeContext (context_buffer, CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL | CONTEXT_XSTATE, &context, &context_buffer_len);
+       success &= SetXStateFeaturesMask (context, XSTATE_MASK_AVX);
+       g_assert (success == TRUE);
+#else
+       CONTEXT context_buffer;
+       PCONTEXT context = &context_buffer;
+       context->ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
+#endif
 
        g_assert (thread_id != GetCurrentThreadId ());
 
        handle = OpenThread (THREAD_ALL_ACCESS, FALSE, thread_id);
        g_assert (handle);
 
-       context.ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
-
-       if (!GetThreadContext (handle, &context)) {
+       if (!GetThreadContext (handle, context)) {
                CloseHandle (handle);
                return FALSE;
        }
 
        memset (mono_context, 0, sizeof (MonoContext));
-       mono_sigctx_to_monoctx (&context, mono_context);
+       mono_sigctx_to_monoctx (context, mono_context);
 
        CloseHandle (handle);
        return TRUE;
@@ -433,8 +443,18 @@ mono_thread_state_init_from_handle (MonoThreadUnwindState *tctx, MonoThreadInfo
                DWORD id = mono_thread_info_get_tid (info);
                mono_setup_thread_context (id, &tctx->ctx);
        } else {
+#ifdef ENABLE_CHECKED_BUILD
                g_assert (((CONTEXT *)sigctx)->ContextFlags & CONTEXT_INTEGER);
                g_assert (((CONTEXT *)sigctx)->ContextFlags & CONTEXT_CONTROL);
+               g_assert (((CONTEXT *)sigctx)->ContextFlags & CONTEXT_FLOATING_POINT);
+#if defined(MONO_HAVE_SIMD_REG_AVX) && HAVE_API_SUPPORT_WIN32_CONTEXT_XSTATE
+               DWORD64 features = 0;
+               g_assert (((CONTEXT *)sigctx)->ContextFlags & CONTEXT_XSTATE);
+               g_assert (GetXStateFeaturesMask (((CONTEXT *)sigctx), &features) == TRUE);
+               g_assert ((features & XSTATE_MASK_LEGACY_SSE) != 0);
+               g_assert ((features & XSTATE_MASK_AVX) != 0);
+#endif
+#endif
                mono_sigctx_to_monoctx (sigctx, &tctx->ctx);
        }
 
index 62b1feb..b77c4b2 100644 (file)
@@ -164,6 +164,7 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
 
 #ifdef HOST_WIN32
 #include <windows.h>
+#include <mono/utils/w32subset.h>
 #endif
 
 void
@@ -233,6 +234,7 @@ mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
        mctx->gregs [AMD64_R13] = context->R13;
        mctx->gregs [AMD64_R14] = context->R14;
        mctx->gregs [AMD64_R15] = context->R15;
+
        memcpy (&(mctx->fregs [AMD64_XMM0]), &(context->Xmm0), sizeof (MonoContextSimdReg));
        memcpy (&(mctx->fregs [AMD64_XMM1]), &(context->Xmm1), sizeof (MonoContextSimdReg));
        memcpy (&(mctx->fregs [AMD64_XMM2]), &(context->Xmm2), sizeof (MonoContextSimdReg));
@@ -249,6 +251,26 @@ mono_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
        memcpy (&(mctx->fregs [AMD64_XMM13]), &(context->Xmm13), sizeof (MonoContextSimdReg));
        memcpy (&(mctx->fregs [AMD64_XMM14]), &(context->Xmm14), sizeof (MonoContextSimdReg));
        memcpy (&(mctx->fregs [AMD64_XMM15]), &(context->Xmm15), sizeof (MonoContextSimdReg));
+
+#ifdef MONO_HAVE_SIMD_REG_AVX
+#if HAVE_API_SUPPORT_WIN32_CONTEXT_XSTATE
+       DWORD64 features = 0;
+       if (((context->ContextFlags & CONTEXT_XSTATE) != 0) && (GetXStateFeaturesMask (context, &features) == TRUE) && ((features & XSTATE_MASK_AVX) != 0)) {
+               DWORD feature_len = 0;
+               PM128A ymm = (PM128A)LocateXStateFeature (context, XSTATE_AVX, &feature_len);
+#ifdef ENABLE_CHECKED_BUILD
+               g_assert (ymm);
+               g_assert (feature_len == (sizeof (MonoContextSimdReg) * AMD64_XMM_NREG));
+#endif
+               memcpy (&(mctx->fregs [AMD64_XMM_NREG]), ymm, feature_len);
+       } else {
+               memset (&(mctx->fregs [AMD64_XMM_NREG]), 0, sizeof (MonoContextSimdReg) * AMD64_XMM_NREG);
+       }
+#else
+       memset (&(mctx->fregs [AMD64_XMM_NREG]), 0, sizeof (MonoContextSimdReg) * AMD64_XMM_NREG);
+#endif
+#endif
+
 #elif defined(__HAIKU__)
        // Haiku uses sigcontext because there's no ucontext
        struct sigcontext *ctx = (struct sigcontext *)sigctx;
@@ -342,6 +364,9 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
        context->R13 = mctx->gregs [AMD64_R13];
        context->R14 = mctx->gregs [AMD64_R14];
        context->R15 = mctx->gregs [AMD64_R15];
+
+       // When using MONO_HAVE_SIMD_REG_AVX, Mono won't change YMM (read only), so no need to
+       // write extended context state.
        memcpy (&(context->Xmm0), &(mctx->fregs [AMD64_XMM0]), sizeof (MonoContextSimdReg));
        memcpy (&(context->Xmm1), &(mctx->fregs [AMD64_XMM1]), sizeof (MonoContextSimdReg));
        memcpy (&(context->Xmm2), &(mctx->fregs [AMD64_XMM2]), sizeof (MonoContextSimdReg));
@@ -358,6 +383,7 @@ mono_monoctx_to_sigctx (MonoContext *mctx, void *sigctx)
        memcpy (&(context->Xmm13), &(mctx->fregs [AMD64_XMM13]), sizeof (MonoContextSimdReg));
        memcpy (&(context->Xmm14), &(mctx->fregs [AMD64_XMM14]), sizeof (MonoContextSimdReg));
        memcpy (&(context->Xmm15), &(mctx->fregs [AMD64_XMM15]), sizeof (MonoContextSimdReg));
+
 #elif defined(__HAIKU__)
        // Haiku uses sigcontext because there's no ucontext
        struct sigcontext *ctx = (struct sigcontext *)sigctx;
index 2dbf043..d89c5a4 100644 (file)
@@ -33,6 +33,7 @@ typedef struct __darwin_xmm_reg MonoContextSimdReg;
 typedef struct _libc_xmmreg MonoContextSimdReg;
 #elif defined(HOST_WIN32)
 #define MONO_HAVE_SIMD_REG
+//#define MONO_HAVE_SIMD_REG_AVX
 #include <emmintrin.h>
 typedef __m128d MonoContextSimdReg;
 #elif defined(HOST_ANDROID)
@@ -272,7 +273,10 @@ MONO_DISABLE_WARNING(4324) // 'struct_name' : structure was padded due to __decl
 
 typedef struct {
        host_mgreg_t gregs [AMD64_NREG];
-#if defined(MONO_HAVE_SIMD_REG)
+#if defined(MONO_HAVE_SIMD_REG_AVX)
+       // Lower AMD64_XMM_NREG fregs holds lower 128 bit YMM. Upper AMD64_XMM_NREG fregs holds upper 128-bit YMM.
+       MonoContextSimdReg fregs [AMD64_XMM_NREG * 2];
+#elif defined(MONO_HAVE_SIMD_REG)
        MonoContextSimdReg fregs [AMD64_XMM_NREG];
 #else
        double fregs [AMD64_XMM_NREG];
@@ -289,11 +293,29 @@ MONO_RESTORE_WARNING
 #define MONO_CONTEXT_GET_BP(ctx) ((gpointer)(gsize)((ctx)->gregs [AMD64_RBP]))
 #define MONO_CONTEXT_GET_SP(ctx) ((gpointer)(gsize)((ctx)->gregs [AMD64_RSP]))
 
-#if defined (HOST_WIN32) && !defined(__GNUC__)
+#if defined(HOST_WIN32) && !defined(__GNUC__)
 /* msvc doesn't support inline assembly, so have to use a separate .asm file */
 // G_EXTERN_C due to being written in assembly.
+#if defined(MONO_HAVE_SIMD_REG_AVX) && defined(__AVX__)
+G_EXTERN_C void mono_context_get_current_avx (void *);
+#define MONO_CONTEXT_GET_CURRENT(ctx) \
+do { \
+       mono_context_get_current_avx((void*)&(ctx)); \
+} while (0)
+#elif defined(MONO_HAVE_SIMD_REG_AVX)
 G_EXTERN_C void mono_context_get_current (void *);
-#define MONO_CONTEXT_GET_CURRENT(ctx) do { mono_context_get_current((void*)&(ctx)); } while (0)
+#define MONO_CONTEXT_GET_CURRENT(ctx) \
+do { \
+       mono_context_get_current((void*)&(ctx)); \
+       memset (&(ctx.fregs [AMD64_XMM_NREG]), 0, sizeof (MonoContextSimdReg) * AMD64_XMM_NREG); \
+} while (0)
+#else
+G_EXTERN_C void mono_context_get_current (void *);
+#define MONO_CONTEXT_GET_CURRENT(ctx) \
+do { \
+       mono_context_get_current((void*)&(ctx)); \
+} while (0)
+#endif
 
 #else
 
index 6523674..faeecbb 100644 (file)
@@ -17,8 +17,9 @@
 #include <mono/utils/mono-threads-coop.h>
 #include <mono/utils/mono-threads-debug.h>
 #include <mono/utils/mono-os-wait.h>
-#include <limits.h>
+#include <mono/utils/mono-context.h>
 #include <mono/utils/w32subset.h>
+#include <limits.h>
 
 enum Win32APCInfo {
        WIN32_APC_INFO_CLEARED = 0,
@@ -205,9 +206,19 @@ mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interru
        /* in mono's thread state machine implementation on Windows. By requesting a threads context after issuing a */
        /* suspended request, this will wait until thread is suspended and thread context has been collected */
        /* and returned. */
-       CONTEXT context;
-       context.ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
-       if (!GetThreadContext (handle, &context)) {
+#if defined(MONO_HAVE_SIMD_REG_AVX) && HAVE_API_SUPPORT_WIN32_CONTEXT_XSTATE
+       BYTE context_buffer [2048];
+       DWORD context_buffer_len = G_N_ELEMENTS (context_buffer);
+       PCONTEXT context = NULL;
+       BOOL success = InitializeContext (context_buffer, CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL | CONTEXT_XSTATE, &context, &context_buffer_len);
+       success &= SetXStateFeaturesMask (context, XSTATE_MASK_AVX);
+       g_assert (success == TRUE);
+#else
+       CONTEXT context_buffer;
+       PCONTEXT context = &context_buffer;
+       context->ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
+#endif
+       if (!GetThreadContext (handle, context)) {
                result = ResumeThread (handle);
                g_assert (result == 1);
                if (!mono_threads_transition_abort_async_suspend (info)) {
@@ -234,7 +245,7 @@ mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interru
                //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
                return TRUE;
        }
-       info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info, &context);
+       info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info, context);
        THREADS_SUSPEND_DEBUG ("thread state %p -> %u\n", GUINT_TO_POINTER (id), result);
        if (info->suspend_can_continue) {
                if (interrupt_kernel)
@@ -295,6 +306,8 @@ mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
                info->async_target = NULL;
                info->user_data = NULL;
 
+               // When using MONO_HAVE_SIMD_REG_AVX, Mono won't change YMM (read only), so no need to
+               // read extended context state.
                context.ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
 
                if (!GetThreadContext (handle, &context)) {
@@ -304,6 +317,8 @@ mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
 
                mono_monoctx_to_sigctx (&ctx, &context);
 
+               // When using MONO_HAVE_SIMD_REG_AVX, Mono won't change YMM (read only), so no need to
+               // write extended context state.
                context.ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
                res = SetThreadContext (handle, &context);
                if (!res) {
index 669e96a..590b2fb 100644 (file)
@@ -57,6 +57,70 @@ mono_context_get_current PROC
 
 mono_context_get_current endP
 
+PUBLIC mono_context_get_current_avx
+
+mono_context_get_current_avx PROC
+;rcx has the ctx ptr
+       mov [rcx + 00h], rax
+       mov [rcx + 08h], rcx
+       mov [rcx + 10h], rdx
+       mov [rcx + 18h], rbx
+       mov [rcx + 28h], rbp
+       mov [rcx + 30h], rsi
+       mov [rcx + 38h], rdi
+       mov [rcx + 40h], r8
+       mov [rcx + 48h], r9
+       mov [rcx + 50h], r10
+       mov [rcx + 58h], r11
+       mov [rcx + 60h], r12
+       mov [rcx + 68h], r13
+       mov [rcx + 70h], r14
+       mov [rcx + 78h], r15
+
+       lea rax, [rsp+8]
+       mov [rcx + 20h], rax
+
+       mov rax, qword ptr [rsp]
+       mov [rcx + 80h], rax
+
+       movaps xmmword ptr [rcx + 90h], xmm0
+       movaps xmmword ptr [rcx + 0A0h], xmm1
+       movaps xmmword ptr [rcx + 0B0h], xmm2
+       movaps xmmword ptr [rcx + 0C0h], xmm3
+       movaps xmmword ptr [rcx + 0D0h], xmm4
+       movaps xmmword ptr [rcx + 0E0h], xmm5
+       movaps xmmword ptr [rcx + 0F0h], xmm6
+       movaps xmmword ptr [rcx + 100h], xmm7
+       movaps xmmword ptr [rcx + 110h], xmm8
+       movaps xmmword ptr [rcx + 120h], xmm9
+       movaps xmmword ptr [rcx + 130h], xmm10
+       movaps xmmword ptr [rcx + 140h], xmm11
+       movaps xmmword ptr [rcx + 150h], xmm12
+       movaps xmmword ptr [rcx + 160h], xmm13
+       movaps xmmword ptr [rcx + 170h], xmm14
+       movaps xmmword ptr [rcx + 180h], xmm15
+
+       vextractf128 xmmword ptr [rcx + 190h],ymm0,1
+       vextractf128 xmmword ptr [rcx + 1A0h],ymm1,1
+       vextractf128 xmmword ptr [rcx + 1B0h],ymm2,1
+       vextractf128 xmmword ptr [rcx + 1C0h],ymm3,1
+       vextractf128 xmmword ptr [rcx + 1D0h],ymm4,1
+       vextractf128 xmmword ptr [rcx + 1E0h],ymm5,1
+       vextractf128 xmmword ptr [rcx + 1F0h],ymm6,1
+       vextractf128 xmmword ptr [rcx + 200h],ymm7,1
+       vextractf128 xmmword ptr [rcx + 210h],ymm8,1
+       vextractf128 xmmword ptr [rcx + 220h],ymm9,1
+       vextractf128 xmmword ptr [rcx + 230h],ymm10,1
+       vextractf128 xmmword ptr [rcx + 240h],ymm11,1
+       vextractf128 xmmword ptr [rcx + 250h],ymm12,1
+       vextractf128 xmmword ptr [rcx + 260h],ymm13,1
+       vextractf128 xmmword ptr [rcx + 270h],ymm14,1
+       vextractf128 xmmword ptr [rcx + 280h],ymm15,1
+
+       ret
+
+mono_context_get_current_avx endP
+
 ; Implementation of __builtin_unwind_init under MSVC, dumping
 ; nonvolatile registers into MonoBuiltinUnwindInfo *.