Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve software exception handling performance #108480

Merged
merged 5 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/coreclr/inc/vptr_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ VPTR_CLASS(DebuggerSecurityCodeMarkFrame)
VPTR_CLASS(DebuggerExitFrame)
VPTR_CLASS(DebuggerU2MCatchHandlerFrame)
VPTR_CLASS(FaultingExceptionFrame)
#ifdef FEATURE_EH_FUNCLETS
VPTR_CLASS(SoftwareExceptionFrame)
#endif // FEATURE_EH_FUNCLETS
VPTR_CLASS(FuncEvalFrame)
VPTR_CLASS(HelperMethodFrame)
VPTR_CLASS(HelperMethodFrame_1OBJ)
Expand Down
76 changes: 76 additions & 0 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11553,3 +11553,79 @@ MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDe
#endif // FEATURE_COMINTEROP
return pUserMD;
}


#ifdef FEATURE_EH_FUNCLETS

void SoftwareExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD, bool updateFloats)
{
LIMITED_METHOD_DAC_CONTRACT;

#define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContext->regname = *dac_cast<PTR_SIZE_T>((TADDR)m_ContextPointers.regname);
ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = m_ContextPointers.regname;
ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContext->regname = m_Context.regname;
ENUM_FP_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

SetIP(pRD->pCurrentContext, ::GetIP(&m_Context));
SetSP(pRD->pCurrentContext, ::GetSP(&m_Context));

pRD->ControlPC = ::GetIP(&m_Context);
pRD->SP = ::GetSP(&m_Context);

pRD->IsCallerContextValid = FALSE;
pRD->IsCallerSPValid = FALSE; // Don't add usage of this field. This is only temporary.
}

#ifndef DACCESS_COMPILE
//
// Init a new frame
//
void SoftwareExceptionFrame::Init()
{
WRAPPER_NO_CONTRACT;

#define CALLEE_SAVED_REGISTER(regname) m_ContextPointers.regname = NULL;
ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

#ifndef TARGET_UNIX
Thread::VirtualUnwindCallFrame(&m_Context, &m_ContextPointers);
#else // !TARGET_UNIX
BOOL success = PAL_VirtualUnwind(&m_Context, &m_ContextPointers);
if (!success)
{
_ASSERTE(!"SoftwareExceptionFrame::Init failed");
EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
}
#endif // !TARGET_UNIX

#define CALLEE_SAVED_REGISTER(regname) if (m_ContextPointers.regname == NULL) m_ContextPointers.regname = &m_Context.regname;
ENUM_CALLEE_SAVED_REGISTERS();
#undef CALLEE_SAVED_REGISTER

_ASSERTE(ExecutionManager::IsManagedCode(::GetIP(&m_Context)));

m_ReturnAddress = ::GetIP(&m_Context);
}

//
// Init and Link in a new frame
//
void SoftwareExceptionFrame::InitAndLink(Thread *pThread)
{
WRAPPER_NO_CONTRACT;

Init();

Push(pThread);
}

#endif // DACCESS_COMPILE
#endif // FEATURE_EH_FUNCLETS
67 changes: 67 additions & 0 deletions src/coreclr/vm/frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ FRAME_TYPE_NAME(ResumableFrame)
FRAME_TYPE_NAME(RedirectedThreadFrame)
#endif // FEATURE_HIJACK
FRAME_TYPE_NAME(FaultingExceptionFrame)
#ifdef FEATURE_EH_FUNCLETS
FRAME_TYPE_NAME(SoftwareExceptionFrame)
#endif // FEATURE_EH_FUNCLETS
#ifdef DEBUGGING_SUPPORTED
FRAME_TYPE_NAME(FuncEvalFrame)
#endif // DEBUGGING_SUPPORTED
Expand Down Expand Up @@ -1146,6 +1149,69 @@ class FaultingExceptionFrame : public Frame
DEFINE_VTABLE_GETTER_AND_DTOR(FaultingExceptionFrame)
};

#ifdef FEATURE_EH_FUNCLETS

class SoftwareExceptionFrame : public Frame
{
TADDR m_ReturnAddress;
T_CONTEXT m_Context;
T_KNONVOLATILE_CONTEXT_POINTERS m_ContextPointers;

VPTR_VTABLE_CLASS(SoftwareExceptionFrame, Frame)

public:
#ifndef DACCESS_COMPILE
SoftwareExceptionFrame() {
LIMITED_METHOD_CONTRACT;
}
#endif

virtual TADDR GetReturnAddressPtr()
{
LIMITED_METHOD_DAC_CONTRACT;
return PTR_HOST_MEMBER_TADDR(SoftwareExceptionFrame, this, m_ReturnAddress);
}

void Init();
void InitAndLink(Thread *pThread);

Interception GetInterception()
{
LIMITED_METHOD_DAC_CONTRACT;
return INTERCEPTION_EXCEPTION;
}

virtual ETransitionType GetTransitionType()
{
LIMITED_METHOD_DAC_CONTRACT;
return TT_InternalCall;
}

unsigned GetFrameAttribs()
{
LIMITED_METHOD_DAC_CONTRACT;
return FRAME_ATTR_EXCEPTION;
}

T_CONTEXT* GetContext()
{
LIMITED_METHOD_DAC_CONTRACT;
return &m_Context;
}

virtual BOOL NeedsUpdateRegDisplay()
{
return TRUE;
}

virtual void UpdateRegDisplay(const PREGDISPLAY, bool updateFloats = false);

// Keep as last entry in class
DEFINE_VTABLE_GETTER_AND_DTOR(SoftwareExceptionFrame)
};

#endif // FEATURE_EH_FUNCLETS

//-----------------------------------------------------------------------
// Frame for debugger function evaluation
//
Expand Down Expand Up @@ -3190,6 +3256,7 @@ class FrameWithCookie
void Poll() { WRAPPER_NO_CONTRACT; m_frame.Poll(); }
void SetStackPointerPtr(TADDR sp) { WRAPPER_NO_CONTRACT; m_frame.SetStackPointerPtr(sp); }
void InitAndLink(T_CONTEXT *pContext) { WRAPPER_NO_CONTRACT; m_frame.InitAndLink(pContext); }
void InitAndLink(Thread *pThread) { WRAPPER_NO_CONTRACT; m_frame.InitAndLink(pThread); }
void Init(Thread *pThread, OBJECTREF *pObjRefs, UINT numObjRefs, BOOL maybeInterior)
{ WRAPPER_NO_CONTRACT; m_frame.Init(pThread, pObjRefs, numObjRefs, maybeInterior); }
ValueClassInfo ** GetValueClassInfoList() { WRAPPER_NO_CONTRACT; return m_frame.GetValueClassInfoList(); }
Expand Down
136 changes: 69 additions & 67 deletions src/coreclr/vm/jithelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2675,65 +2675,68 @@ HCIMPLEND

/*************************************************************/

#ifdef FEATURE_EH_FUNCLETS
void ThrowNew(OBJECTREF oref)
HCIMPL1(void, IL_Throw, Object* obj)
{
if (oref == 0)
DispatchManagedException(kNullReferenceException);
else
if (!IsException(oref->GetMethodTable()))
FCALL_CONTRACT;

/* Make no assumptions about the current machine state */
ResetCurrentContext();

FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC

OBJECTREF oref = ObjectToOBJECTREF(obj);

#ifdef FEATURE_EH_FUNCLETS
if (g_isNewExceptionHandlingEnabled)
{
GCPROTECT_BEGIN(oref);
Thread *pThread = GetThread();

WrapNonCompliantException(&oref);
FrameWithCookie<SoftwareExceptionFrame> exceptionFrame;
*(&exceptionFrame)->GetGSCookiePtr() = GetProcessGSCookie();
RtlCaptureContext(exceptionFrame.GetContext());
exceptionFrame.InitAndLink(pThread);

GCPROTECT_END();
}
else
{ // We know that the object derives from System.Exception
FC_CAN_TRIGGER_GC();

// If the flag indicating ForeignExceptionRaise has been set,
// then do not clear the "_stackTrace" field of the exception object.
if (GetThread()->GetExceptionState()->IsRaisingForeignException())
{
((EXCEPTIONREF)oref)->SetStackTraceString(NULL);
}
if (oref == 0)
DispatchManagedException(kNullReferenceException);
else
if (!IsException(oref->GetMethodTable()))
{
((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace();
}
}
GCPROTECT_BEGIN(oref);

DispatchManagedException(oref);
}
#endif // FEATURE_EH_FUNCLETS
WrapNonCompliantException(&oref);

HCIMPL1(void, IL_Throw, Object* obj)
{
FCALL_CONTRACT;
GCPROTECT_END();
}
else
{ // We know that the object derives from System.Exception

/* Make no assumptions about the current machine state */
ResetCurrentContext();
// If the flag indicating ForeignExceptionRaise has been set,
// then do not clear the "_stackTrace" field of the exception object.
if (pThread->GetExceptionState()->IsRaisingForeignException())
{
((EXCEPTIONREF)oref)->SetStackTraceString(NULL);
}
else
{
((EXCEPTIONREF)oref)->ClearStackTracePreservingRemoteStackTrace();
}
}

FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
DispatchManagedException(oref, exceptionFrame.GetContext());
FC_CAN_TRIGGER_GC_END();
UNREACHABLE();
}
#endif // FEATURE_EH_FUNCLETS

HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame

OBJECTREF oref = ObjectToOBJECTREF(obj);

#if defined(_DEBUG) && defined(TARGET_X86)
__helperframe.InsureInit(NULL);
g_ExceptionEIP = (LPVOID)__helperframe.GetReturnAddress();
#endif // defined(_DEBUG) && defined(TARGET_X86)

#ifdef FEATURE_EH_FUNCLETS
if (g_isNewExceptionHandlingEnabled)
{
ThrowNew(oref);
UNREACHABLE();
}
#endif

if (oref == 0)
COMPlusThrow(kNullReferenceException);
else
Expand Down Expand Up @@ -2768,49 +2771,48 @@ HCIMPLEND

/*************************************************************/

#ifdef FEATURE_EH_FUNCLETS
void RethrowNew()
HCIMPL0(void, IL_Rethrow)
{
Thread *pThread = GetThread();
FCALL_CONTRACT;

ExInfo *pActiveExInfo = (ExInfo*)pThread->GetExceptionState()->GetCurrentExceptionTracker();
FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC

CONTEXT exceptionContext;
RtlCaptureContext(&exceptionContext);
#ifdef FEATURE_EH_FUNCLETS
if (g_isNewExceptionHandlingEnabled)
{
Thread *pThread = GetThread();

ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, &exceptionContext, ExKind::None);
FrameWithCookie<SoftwareExceptionFrame> exceptionFrame;
*(&exceptionFrame)->GetGSCookiePtr() = GetProcessGSCookie();
RtlCaptureContext(exceptionFrame.GetContext());
exceptionFrame.InitAndLink(pThread);

GCPROTECT_BEGIN(exInfo.m_exception);
PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW);
DECLARE_ARGHOLDER_ARRAY(args, 2);
ExInfo *pActiveExInfo = (ExInfo*)pThread->GetExceptionState()->GetCurrentExceptionTracker();

args[ARGNUM_0] = PTR_TO_ARGHOLDER(pActiveExInfo);
args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo);
ExInfo exInfo(pThread, pActiveExInfo->m_ptrs.ExceptionRecord, exceptionFrame.GetContext(), ExKind::None);

pThread->IncPreventAbort();
FC_CAN_TRIGGER_GC();

//Ex.RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo)
CALL_MANAGED_METHOD_NORET(args)
GCPROTECT_END();
}
#endif // FEATURE_EH_FUNCLETS
GCPROTECT_BEGIN(exInfo.m_exception);
PREPARE_NONVIRTUAL_CALLSITE(METHOD__EH__RH_RETHROW);
DECLARE_ARGHOLDER_ARRAY(args, 2);

HCIMPL0(void, IL_Rethrow)
{
FCALL_CONTRACT;
args[ARGNUM_0] = PTR_TO_ARGHOLDER(pActiveExInfo);
args[ARGNUM_1] = PTR_TO_ARGHOLDER(&exInfo);

FC_GC_POLL_NOT_NEEDED(); // throws always open up for GC
pThread->IncPreventAbort();

HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame
//Ex.RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo)
CALL_MANAGED_METHOD_NORET(args)
GCPROTECT_END();

#ifdef FEATURE_EH_FUNCLETS
if (g_isNewExceptionHandlingEnabled)
{
RethrowNew();
FC_CAN_TRIGGER_GC_END();
UNREACHABLE();
}
#endif

HELPER_METHOD_FRAME_BEGIN_ATTRIB_NOPOLL(Frame::FRAME_ATTR_EXCEPTION); // Set up a frame

OBJECTREF throwable = GetThread()->GetThrowable();
if (throwable != NULL)
{
Expand Down