diff options
author | Koundinya Veluri <kouvel@users.noreply.github.com> | 2017-09-26 13:14:53 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-26 13:14:53 -0700 |
commit | 8f0ac5d2041d0c577607e2f778f2fbca1f7d732e (patch) | |
tree | 23e191f0f10287d162ef53b4a2b77d9170ac7615 /src/vm/jithelpers.cpp | |
parent | 296aaf12b40ddd7478b6a14d6099f48b4b049438 (diff) | |
download | coreclr-8f0ac5d2041d0c577607e2f778f2fbca1f7d732e.tar.gz coreclr-8f0ac5d2041d0c577607e2f778f2fbca1f7d732e.tar.bz2 coreclr-8f0ac5d2041d0c577607e2f778f2fbca1f7d732e.zip |
Remove Monitor asm helpers (#14146)
- Removed asm helpers on Windows and used portable C++ helpers instead
- Rearranged fast path code to improve them a bit and match the asm more closely
Perf:
- The asm helpers are a bit faster. The code generated for the portable helpers is almost the same now, the remaining differences are:
- There were some layout issues where hot paths were in the wrong place and return paths were not cloned. Instrumenting some of the tests below with PGO on x64 resolved all of the layout issues. I couldn't get PGO instrumentation to work on x86 but I imagine it would be the same there.
- Register usage
- x64: All of the Enter functions are using one or two (TryEnter is using two) callee-saved registers for no apparent reason, forcing them to be saved and restored. r10 and r11 seem to be available but they're not being used.
- x86: Similarly to x64, the compiled functions are pushing and popping 2-3 additional registers in the hottest fast paths.
- I believe this is the main remaining gap and PGO is not helping with this
- On Linux, perf is >= before for the most part
- Perf tests used for below are updated in PR https://github.com/dotnet/coreclr/pull/13670
Diffstat (limited to 'src/vm/jithelpers.cpp')
-rw-r--r-- | src/vm/jithelpers.cpp | 319 |
1 files changed, 16 insertions, 303 deletions
diff --git a/src/vm/jithelpers.cpp b/src/vm/jithelpers.cpp index 32be77823c..8cff03ae60 100644 --- a/src/vm/jithelpers.cpp +++ b/src/vm/jithelpers.cpp @@ -4472,19 +4472,18 @@ HCIMPL_MONHELPER(JIT_MonEnterWorker_Portable, Object* obj) result = obj->EnterObjMonitorHelper(pCurThread); if (result == AwareLock::EnterHelperResult_Entered) { - MONHELPER_STATE(*pbLockTaken = 1;) + MONHELPER_STATE(*pbLockTaken = 1); return; } - else if (result == AwareLock::EnterHelperResult_Contention) { - AwareLock::EnterHelperResult resultSpin = obj->EnterObjMonitorHelperSpin(pCurThread); - if (resultSpin == AwareLock::EnterHelperResult_Entered) + result = obj->EnterObjMonitorHelperSpin(pCurThread); + if (result == AwareLock::EnterHelperResult_Entered) { - MONHELPER_STATE(*pbLockTaken = 1;) + MONHELPER_STATE(*pbLockTaken = 1); return; } - if (resultSpin == AwareLock::EnterHelperResult_Contention) + if (result == AwareLock::EnterHelperResult_Contention) { FC_INNER_RETURN_VOID(JIT_MonContention_Helper(obj, MONHELPER_ARG, GetEEFuncEntryPointMacro(JIT_MonEnter))); } @@ -4519,15 +4518,14 @@ HCIMPL1(void, JIT_MonEnter_Portable, Object* obj) { return; } - else if (result == AwareLock::EnterHelperResult_Contention) { - AwareLock::EnterHelperResult resultSpin = obj->EnterObjMonitorHelperSpin(pCurThread); - if (resultSpin == AwareLock::EnterHelperResult_Entered) + result = obj->EnterObjMonitorHelperSpin(pCurThread); + if (result == AwareLock::EnterHelperResult_Entered) { return; } - if (resultSpin == AwareLock::EnterHelperResult_Contention) + if (result == AwareLock::EnterHelperResult_Contention) { FC_INNER_RETURN_VOID(JIT_MonContention_Helper(obj, NULL, GetEEFuncEntryPointMacro(JIT_MonEnter))); } @@ -4563,16 +4561,15 @@ HCIMPL2(void, JIT_MonReliableEnter_Portable, Object* obj, BYTE* pbLockTaken) *pbLockTaken = 1; return; } - else if (result == AwareLock::EnterHelperResult_Contention) { - AwareLock::EnterHelperResult resultSpin = obj->EnterObjMonitorHelperSpin(pCurThread); - if (resultSpin == AwareLock::EnterHelperResult_Entered) + result = obj->EnterObjMonitorHelperSpin(pCurThread); + if (result == AwareLock::EnterHelperResult_Entered) { *pbLockTaken = 1; return; } - if (resultSpin == AwareLock::EnterHelperResult_Contention) + if (result == AwareLock::EnterHelperResult_Contention) { FC_INNER_RETURN_VOID(JIT_MonContention_Helper(obj, pbLockTaken, GetEEFuncEntryPointMacro(JIT_MonReliableEnter))); } @@ -4649,14 +4646,15 @@ HCIMPL3(void, JIT_MonTryEnter_Portable, Object* obj, INT32 timeOut, BYTE* pbLock *pbLockTaken = 1; return; } - else if (result == AwareLock::EnterHelperResult_Contention) { if (timeOut == 0) + { return; + } - AwareLock::EnterHelperResult resultSpin = obj->EnterObjMonitorHelperSpin(pCurThread); - if (resultSpin == AwareLock::EnterHelperResult_Entered) + result = obj->EnterObjMonitorHelperSpin(pCurThread); + if (result == AwareLock::EnterHelperResult_Entered) { *pbLockTaken = 1; return; @@ -4741,7 +4739,6 @@ FCIMPL1(void, JIT_MonExit_Portable, Object* obj) { return; } - else if (action == AwareLock::LeaveHelperAction_Signal) { FC_INNER_RETURN_VOID(JIT_MonExit_Signal(obj)); @@ -4773,7 +4770,6 @@ HCIMPL_MONHELPER(JIT_MonExitWorker_Portable, Object* obj) MONHELPER_STATE(*pbLockTaken = 0;) return; } - else if (action == AwareLock::LeaveHelperAction_Signal) { MONHELPER_STATE(*pbLockTaken = 0;) @@ -4821,7 +4817,7 @@ HCIMPL_MONHELPER(JIT_MonEnterStatic_Portable, AwareLock *lock) goto FramedLockHelper; } - if (lock->EnterHelper(pCurThread) == AwareLock::EnterHelperResult_Entered) + if (lock->EnterHelper(pCurThread, true /* checkRecursiveCase */)) { #if defined(_DEBUG) && defined(TRACK_SYNC) // The best place to grab this is from the ECall frame @@ -4909,289 +4905,6 @@ HCIMPL_MONHELPER(JIT_MonExitStatic_Portable, AwareLock *lock) HCIMPLEND #include <optdefault.h> -/*********************************************************************/ -// JITutil_Mon* are helpers than handle slow paths for JIT_Mon* methods -// implemented in assembly. They are not doing any spinning compared -// to the full fledged portable implementations above. -/*********************************************************************/ - -/*********************************************************************/ -HCIMPL_MONHELPER(JITutil_MonEnterWorker, Object* obj) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - OBJECTREF objRef = ObjectToOBJECTREF(obj); - - // The following makes sure that Monitor.Enter shows up on thread abort - // stack walks (otherwise Monitor.Enter called within a CER can block a - // thread abort indefinitely). Setting the __me internal variable (normally - // only set for fcalls) will cause the helper frame below to be able to - // backtranslate into the method desc for the Monitor.Enter fcall. - // - // Note that we need explicitly initialize Monitor.Enter fcall in - // code:SystemDomain::LoadBaseSystemClasses to make this work in the case - // where the first call ever to Monitor.Enter is done as JIT helper - // for synchronized method. - __me = GetEEFuncEntryPointMacro(JIT_MonEnter); - - // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth. - HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH, objRef); - - if (objRef == NULL) - COMPlusThrow(kArgumentNullException); - - MONHELPER_STATE(GCPROTECT_BEGININTERIOR(pbLockTaken);) - -#ifdef _DEBUG - Thread *pThread = GetThread(); - DWORD lockCount = pThread->m_dwLockCount; -#endif - if (GET_THREAD()->CatchAtSafePointOpportunistic()) - { - GET_THREAD()->PulseGCMode(); - } - objRef->EnterObjMonitor(); - _ASSERTE ((objRef->GetSyncBlock()->GetMonitor()->m_Recursion == 1 && pThread->m_dwLockCount == lockCount + 1) || - pThread->m_dwLockCount == lockCount); - MONHELPER_STATE(if (pbLockTaken != 0) *pbLockTaken = 1;) - - MONHELPER_STATE(GCPROTECT_END();) - HELPER_METHOD_FRAME_END(); -} -HCIMPLEND - -/*********************************************************************/ - -// This helper is only ever used as part of FCall, but it is implemented using HCIMPL macro -// so that it can be tail called from assembly helper without triggering asserts in debug. -HCIMPL2(void, JITutil_MonReliableEnter, Object* obj, BYTE* pbLockTaken) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - OBJECTREF objRef = ObjectToOBJECTREF(obj); - - // The following makes sure that Monitor.Enter shows up on thread abort - // stack walks (otherwise Monitor.Enter called within a CER can block a - // thread abort indefinitely). Setting the __me internal variable (normally - // only set for fcalls) will cause the helper frame below to be able to - // backtranslate into the method desc for the Monitor.Enter fcall. - __me = GetEEFuncEntryPointMacro(JIT_MonReliableEnter); - - // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth. - HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH, objRef); - - if (objRef == NULL) - COMPlusThrow(kArgumentNullException); - - GCPROTECT_BEGININTERIOR(pbLockTaken); - -#ifdef _DEBUG - Thread *pThread = GetThread(); - DWORD lockCount = pThread->m_dwLockCount; -#endif - if (GET_THREAD()->CatchAtSafePointOpportunistic()) - { - GET_THREAD()->PulseGCMode(); - } - objRef->EnterObjMonitor(); - _ASSERTE ((objRef->GetSyncBlock()->GetMonitor()->m_Recursion == 1 && pThread->m_dwLockCount == lockCount + 1) || - pThread->m_dwLockCount == lockCount); - *pbLockTaken = 1; - - GCPROTECT_END(); - HELPER_METHOD_FRAME_END(); -} -HCIMPLEND - - -/*********************************************************************/ - -// This helper is only ever used as part of FCall, but it is implemented using HCIMPL macro -// so that it can be tail called from assembly helper without triggering asserts in debug. -HCIMPL3(void, JITutil_MonTryEnter, Object* obj, INT32 timeOut, BYTE* pbLockTaken) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - BOOL result = FALSE; - - OBJECTREF objRef = ObjectToOBJECTREF(obj); - - // The following makes sure that Monitor.TryEnter shows up on thread - // abort stack walks (otherwise Monitor.TryEnter called within a CER can - // block a thread abort for long periods of time). Setting the __me internal - // variable (normally only set for fcalls) will cause the helper frame below - // to be able to backtranslate into the method desc for the Monitor.TryEnter - // fcall. - __me = GetEEFuncEntryPointMacro(JIT_MonTryEnter); - - // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth. - HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH, objRef); - - if (objRef == NULL) - COMPlusThrow(kArgumentNullException); - - if (timeOut < -1) - COMPlusThrow(kArgumentOutOfRangeException); - - GCPROTECT_BEGININTERIOR(pbLockTaken); - - if (GET_THREAD()->CatchAtSafePointOpportunistic()) - { - GET_THREAD()->PulseGCMode(); - } - - result = objRef->TryEnterObjMonitor(timeOut); - *pbLockTaken = result != FALSE; - - GCPROTECT_END(); - HELPER_METHOD_FRAME_END(); -} -HCIMPLEND - -/*********************************************************************/ -HCIMPL_MONHELPER(JITutil_MonExitWorker, Object* obj) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - MONHELPER_STATE(if (pbLockTaken != NULL && *pbLockTaken == 0) return;) - - OBJECTREF objRef = ObjectToOBJECTREF(obj); - - // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth. - HELPER_METHOD_FRAME_BEGIN_ATTRIB_1(Frame::FRAME_ATTR_NO_THREAD_ABORT|Frame::FRAME_ATTR_EXACT_DEPTH, objRef); - - if (objRef == NULL) - COMPlusThrow(kArgumentNullException); - - if (!objRef->LeaveObjMonitor()) - COMPlusThrow(kSynchronizationLockException); - - MONHELPER_STATE(if (pbLockTaken != 0) *pbLockTaken = 0;) - - TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE)); - - if (GET_THREAD()->IsAbortRequested()) { - GET_THREAD()->HandleThreadAbort(); - } - - HELPER_METHOD_FRAME_END(); -} -HCIMPLEND - -/*********************************************************************/ -// A helper for JIT_MonEnter that is on the callee side of an ecall -// frame and handles the contention case. - -HCIMPL_MONHELPER(JITutil_MonContention, AwareLock* lock) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - // The following makes sure that Monitor.Enter shows up on thread abort - // stack walks (otherwise Monitor.Enter called within a CER can block a - // thread abort indefinitely). Setting the __me internal variable (normally - // only set for fcalls) will cause the helper frame below to be able to - // backtranslate into the method desc for the Monitor.Enter fcall. - __me = GetEEFuncEntryPointMacro(JIT_MonEnter); - - // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth. - HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH); - MONHELPER_STATE(GCPROTECT_BEGININTERIOR(pbLockTaken);) - -#ifdef _DEBUG - Thread *pThread = GetThread(); - DWORD lockCount = pThread->m_dwLockCount; -#endif - lock->Contention(); - _ASSERTE (pThread->m_dwLockCount == lockCount + 1); - MONHELPER_STATE(if (pbLockTaken != 0) *pbLockTaken = 1;) - - MONHELPER_STATE(GCPROTECT_END();) - HELPER_METHOD_FRAME_END(); -} -HCIMPLEND - -// This helper is only ever used as part of FCall, but it is implemented using HCIMPL macro -// so that it can be tail called from assembly helper without triggering asserts in debug. -HCIMPL2(void, JITutil_MonReliableContention, AwareLock* lock, BYTE* pbLockTaken) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - // The following makes sure that Monitor.Enter shows up on thread abort - // stack walks (otherwise Monitor.Enter called within a CER can block a - // thread abort indefinitely). Setting the __me internal variable (normally - // only set for fcalls) will cause the helper frame below to be able to - // backtranslate into the method desc for the Monitor.Enter fcall. - __me = GetEEFuncEntryPointMacro(JIT_MonReliableEnter); - - // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth. - HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH); - GCPROTECT_BEGININTERIOR(pbLockTaken); - -#ifdef _DEBUG - Thread *pThread = GetThread(); - DWORD lockCount = pThread->m_dwLockCount; -#endif - lock->Contention(); - _ASSERTE (pThread->m_dwLockCount == lockCount + 1); - *pbLockTaken = 1; - - GCPROTECT_END(); - HELPER_METHOD_FRAME_END(); -} -HCIMPLEND - -/*********************************************************************/ -// A helper for JIT_MonExit and JIT_MonExitStatic that is on the -// callee side of an ecall frame and handles cases that might allocate, -// throw or block. -HCIMPL_MONHELPER(JITutil_MonSignal, AwareLock* lock) -{ - CONTRACTL - { - FCALL_CHECK; - } - CONTRACTL_END; - - // Monitor helpers are used as both hcalls and fcalls, thus we need exact depth. - HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH | Frame::FRAME_ATTR_NO_THREAD_ABORT); - - lock->Signal(); - MONHELPER_STATE(if (pbLockTaken != 0) *pbLockTaken = 0;) - - TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE)); - - if (GET_THREAD()->IsAbortRequested()) { - GET_THREAD()->HandleThreadAbort(); - } - - HELPER_METHOD_FRAME_END(); -} -HCIMPLEND - HCIMPL1(void *, JIT_GetSyncFromClassHandle, CORINFO_CLASS_HANDLE typeHnd_) CONTRACTL { FCALL_CHECK; |