Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
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
26 changes: 23 additions & 3 deletions src/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10302,6 +10302,22 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()
/*****************************************************************************
*
* Generates code for an EH funclet prolog.
*
*
* Funclets have the following incoming arguments:
*
* catch/filter-handler: eax = the exception object that was caught (see GT_CATCH_ARG)
* filter: eax = the exception object that was caught (see GT_CATCH_ARG)
* finally/fault: none
*
* Funclets set the following registers on exit:
*
* catch/filter-handler: eax = the address at which execution should resume (see BBJ_EHCATCHRET)
* filter: eax = non-zero if the handler should handle the exception, zero otherwise (see GT_RETFILT)
* finally/fault: none
*
* Funclet prolog/epilog sequence and funclet frame layout are TBD.
*
*/

void CodeGen::genFuncletProlog(BasicBlock* block)
Expand All @@ -10317,10 +10333,13 @@ void CodeGen::genFuncletProlog(BasicBlock* block)

compiler->unwindBegProlog();

// TODO Save callee-saved registers

// This is the end of the OS-reported prolog for purposes of unwinding
compiler->unwindEndProlog();

// TODO We may need EBP restore sequence here if we introduce PSPSym

// Add a padding for 16-byte alignment
inst_RV_IV(INS_sub, REG_SPBASE, 12, EA_PTRSIZE);
}

/*****************************************************************************
Expand All @@ -10339,7 +10358,8 @@ void CodeGen::genFuncletEpilog()

ScopedSetVariable<bool> _setGeneratingEpilog(&compiler->compGeneratingEpilog, true);

// TODO Restore callee-saved registers
// Revert a padding that was added for 16-byte alignment
inst_RV_IV(INS_add, REG_SPBASE, 12, EA_PTRSIZE);

instGen_Return(0);
}
Expand Down
2 changes: 2 additions & 0 deletions src/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,9 @@ BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
if ((compiler->lvaPSPSym == BAD_VAR_NUM) ||
(!compiler->compLocallocUsed && (compiler->funCurrentFunc()->funKind == FUNC_ROOT)))
{
#ifndef UNIX_X86_ABI
inst_RV_RV(INS_mov, REG_ARG_0, REG_SPBASE, TYP_I_IMPL);
#endif // !UNIX_X86_ABI
}
else
{
Expand Down
12 changes: 12 additions & 0 deletions src/pal/inc/unixasmmacrosx86.inc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ C_FUNC(\Name\()_End):
pop ebp
.endm

.macro ESP_PROLOG_BEG
.endm

.macro ESP_PROLOG_END
.endm

.macro ESP_EPILOG_BEG
.endm

.macro ESP_EPILOG_END
.endm

.macro PREPARE_EXTERNAL_VAR Name, Reg
.att_syntax
call 0f
Expand Down
7 changes: 2 additions & 5 deletions src/pal/src/arch/i386/exceptionhelper.S
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
push ebp
mov eax, [esp + 12] // ebx: PAL_SEHException *
mov ebx, [esp + 8] // eax: CONTEXT *
mov ecx, [esp + 12] // ecx: PAL_SEHException * (first argument for ThrowExceptionHelper)
mov ebx, [esp + 8] // ebx: CONTEXT *

mov ebp, [ebx + CONTEXT_Ebp]
mov esp, [ebx + CONTEXT_ResumeEsp]
Expand All @@ -33,9 +33,6 @@ LEAF_ENTRY ThrowExceptionFromContextInternal, _TEXT
// the EBP is no longer saved in the current stack frame.
.cfi_restore ebp

// Store PAL_SEHException as the first argument
push eax

// Store return address to the stack
mov ebx, [ebx + CONTEXT_Eip]
push ebx
Expand Down
4 changes: 4 additions & 0 deletions src/pal/src/exception/seh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ PAL_ThrowExceptionFromContext(CONTEXT* context, PAL_SEHException* ex)
PAL_SEHException* ex - the exception to throw.
--*/
extern "C"
#ifdef _TARGET_X86_
void __fastcall ThrowExceptionHelper(PAL_SEHException* ex)
#else // _TARGET_X86_
void ThrowExceptionHelper(PAL_SEHException* ex)
#endif // !_TARGET_X86_
{
throw std::move(*ex);
}
Expand Down
1 change: 1 addition & 0 deletions src/vm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,7 @@ else(WIN32)
)
elseif(CLR_CMAKE_TARGET_ARCH_I386)
set(VM_SOURCES_WKS_ARCH_ASM
${ARCH_SOURCES_DIR}/ehhelpers.S
${ARCH_SOURCES_DIR}/asmhelpers.S
${ARCH_SOURCES_DIR}/jithelp.S
${ARCH_SOURCES_DIR}/gmsasm.S
Expand Down
31 changes: 26 additions & 5 deletions src/vm/eetwain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3786,10 +3786,11 @@ void UnwindEbpDoubleAlignFrameProlog(
/*****************************************************************************/

bool UnwindEbpDoubleAlignFrame(
PREGDISPLAY pContext,
hdrInfo * info,
PTR_CBYTE methodStart,
unsigned flags,
PREGDISPLAY pContext,
EECodeInfo *pCodeInfo,
hdrInfo *info,
PTR_CBYTE methodStart,
unsigned flags,
StackwalkCacheUnwindInfo *pUnwindInfo) // out-only, perf improvement
{
LIMITED_METHOD_CONTRACT;
Expand All @@ -3806,6 +3807,25 @@ bool UnwindEbpDoubleAlignFrame(
{
TADDR baseSP;

#ifdef WIN64EXCEPTIONS
// Funclets' frame pointers(EBP) are always restored so they can access to main function's local variables.
// Therefore the value of EBP is invalid for unwinder so we should use ESP instead.
// TODO If funclet frame layout is changed from CodeGen::genFuncletProlog() and genFuncletEpilog(),
// we need to change here accordingly. It is likely to have changes when introducing PSPSym.
// TODO Currently we assume that ESP of funclet frames is always fixed but actually it could change.
if (pCodeInfo->IsFunclet())
{
baseSP = curESP + 12; // padding for 16byte stack alignment allocated in genFuncletProlog()

pContext->PCTAddr = baseSP;
pContext->ControlPC = *PTR_PCODE(pContext->PCTAddr);

pContext->SP = (DWORD)(baseSP + sizeof(TADDR));

return true;
}
#else // WIN64EXCEPTIONS

FrameType frameType = GetHandlerFrameInfo(info, curEBP,
curESP, (DWORD) IGNORE_VAL,
&baseSP);
Expand Down Expand Up @@ -3860,6 +3880,7 @@ bool UnwindEbpDoubleAlignFrame(

return true;
}
#endif // !WIN64EXCEPTIONS
}

//
Expand Down Expand Up @@ -3990,7 +4011,7 @@ bool UnwindStackFrame(PREGDISPLAY pContext,
* Now we know that have an EBP frame
*/

if (!UnwindEbpDoubleAlignFrame(pContext, info, methodStart, flags, pUnwindInfo))
if (!UnwindEbpDoubleAlignFrame(pContext, pCodeInfo, info, methodStart, flags, pUnwindInfo))
return false;
}

Expand Down
83 changes: 56 additions & 27 deletions src/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_) || defined(_TARGET_X86_)
#define ADJUST_PC_UNWOUND_TO_CALL
#define USE_CALLER_SP_IN_FUNCLET
#endif // _TARGET_ARM_ || _TARGET_ARM64_ || _TARGET_X86_

#ifndef DACCESS_COMPILE
Expand Down Expand Up @@ -580,7 +581,7 @@ UINT_PTR ExceptionTracker::CallCatchHandler(CONTEXT* pContextRecord, bool* pfAbo
if (!fIntercepted)
{
_ASSERTE(m_uCatchToCallPC != 0 && m_pClauseForCatchToken != NULL);
uResumePC = CallHandler(m_uCatchToCallPC, sfStackFp, &m_ClauseForCatch, pMD, Catch ARM_ARG(pContextRecord) ARM64_ARG(pContextRecord));
uResumePC = CallHandler(m_uCatchToCallPC, sfStackFp, &m_ClauseForCatch, pMD, Catch X86_ARG(pContextRecord) ARM_ARG(pContextRecord) ARM64_ARG(pContextRecord));
}
else
{
Expand Down Expand Up @@ -1295,12 +1296,12 @@ void ExceptionTracker::InitializeCurrentContextForCrawlFrame(CrawlFrame* pcfThis
pRD->SP = sfEstablisherFrame.SP;
pRD->ControlPC = pDispatcherContext->ControlPc;

#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
#ifdef USE_CALLER_SP_IN_FUNCLET
pcfThisFrame->pRD->IsCallerSPValid = TRUE;

// Assert our first pass assumptions for the Arm/Arm64
_ASSERTE(sfEstablisherFrame.SP == GetSP(pDispatcherContext->ContextRecord));
#endif // defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
#endif // USE_CALLER_SP_IN_FUNCLET

}

Expand Down Expand Up @@ -2878,7 +2879,7 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
SetEnclosingClauseInfo(fIsFunclet,
pcfThisFrame->GetRelOffset(),
GetSP(pcfThisFrame->GetRegisterSet()->pCallerContext));
#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
#ifdef USE_CALLER_SP_IN_FUNCLET
// On ARM & ARM64, the OS passes us the CallerSP for the frame for which personality routine has been invoked.
// Since IL filters are invoked in the first pass, we pass this CallerSP to the filter funclet which will
// then lookup the actual frame pointer value using it since we dont have a frame pointer to pass to it
Expand All @@ -2893,11 +2894,11 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
_ASSERTE(pCurRegDisplay->IsCallerContextValid && pCurRegDisplay->IsCallerSPValid);
// 3) CallerSP is intact
_ASSERTE(GetSP(pCurRegDisplay->pCallerContext) == GetRegdisplaySP(pCurRegDisplay));
#endif // _TARGET_ARM_ || _TARGET_ARM64_
#endif // USE_CALLER_SP_IN_FUNCLET
{
// CallHandler expects to be in COOP mode.
GCX_COOP();
dwResult = CallHandler(dwFilterStartPC, sf, &EHClause, pMD, Filter ARM_ARG(pCurRegDisplay->pCallerContext) ARM64_ARG(pCurRegDisplay->pCallerContext));
dwResult = CallHandler(dwFilterStartPC, sf, &EHClause, pMD, Filter X86_ARG(pCurRegDisplay->pCallerContext) ARM_ARG(pCurRegDisplay->pCallerContext) ARM64_ARG(pCurRegDisplay->pCallerContext));
}
}
EX_CATCH
Expand Down Expand Up @@ -3130,7 +3131,7 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(
// Since we also forbid GC during second pass, disable it now since
// invocation of managed code can result in a GC.
ENDFORBIDGC();
dwStatus = CallHandler(dwHandlerStartPC, sf, &EHClause, pMD, FaultFinally ARM_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM64_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext));
dwStatus = CallHandler(dwHandlerStartPC, sf, &EHClause, pMD, FaultFinally X86_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext) ARM64_ARG(pcfThisFrame->GetRegisterSet()->pCurrentContext));

// Once we return from a funclet, forbid GC again (refer to comment before start of the loop for details)
BEGINFORBIDGC();
Expand Down Expand Up @@ -3198,20 +3199,57 @@ CLRUnwindStatus ExceptionTracker::ProcessManagedCallFrame(

#define OPTIONAL_SO_CLEANUP_UNWIND(pThread, pFrame) if (pThread->GetFrame() < pFrame) { UnwindFrameChain(pThread, pFrame); }

#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
#ifdef USE_CALLER_SP_IN_FUNCLET
// This is an assembly helper that enables us to call into EH funclets.
EXTERN_C DWORD_PTR STDCALL CallEHFunclet(Object *pThrowable, UINT_PTR pFuncletToInvoke, UINT_PTR *pFirstNonVolReg, UINT_PTR *pFuncletCallerSP);

// This is an assembly helper that enables us to call into EH filter funclets.
EXTERN_C DWORD_PTR STDCALL CallEHFilterFunclet(Object *pThrowable, TADDR CallerSP, UINT_PTR pFuncletToInvoke, UINT_PTR *pFuncletCallerSP);
#endif // _TARGET_ARM_ || _TARGET_ARM64_

static inline UINT_PTR CastHandlerFn(HandlerFn *pfnHandler)
{
#ifdef _TARGET_ARM_
return DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler);
#else
return (UINT_PTR)pfnHandler;
#endif
}

static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextRecord)
{
#if defined(_TARGET_ARM_)
return (UINT_PTR*)&(pContextRecord->R4);
#elif defined(_TARGET_ARM64_)
return (UINT_PTR*)&(pContextRecord->X19);
#elif defined(_TARGET_X86_)
return (UINT_PTR*)&(pContextRecord->Edi);
#else
PORTABILITY_ASSERT("GetFirstNonVolatileRegisterAddress");
return NULL;
#endif
}

static inline TADDR GetFrameRestoreBase(PCONTEXT pContextRecord)
{
#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
return GetSP(pContextRecord);
#elif defined(_TARGET_X86_)
return pContextRecord->Ebp;
#else
PORTABILITY_ASSERT("GetFrameRestoreBase");
return NULL;
#endif
}

#endif // USE_CALLER_SP_IN_FUNCLET

DWORD_PTR ExceptionTracker::CallHandler(
UINT_PTR uHandlerStartPC,
StackFrame sf,
EE_ILEXCEPTION_CLAUSE* pEHClause,
MethodDesc* pMD,
EHFuncletType funcletType
X86_ARG(PCONTEXT pContextRecord)
ARM_ARG(PCONTEXT pContextRecord)
ARM64_ARG(PCONTEXT pContextRecord)
)
Expand Down Expand Up @@ -3266,7 +3304,7 @@ DWORD_PTR ExceptionTracker::CallHandler(
break;
}

#if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
#ifdef USE_CALLER_SP_IN_FUNCLET
// Invoke the funclet. We pass throwable only when invoking the catch block.
// Since the actual caller of the funclet is the assembly helper, pass the reference
// to the CallerStackFrame instance so that it can be updated.
Expand All @@ -3275,35 +3313,26 @@ DWORD_PTR ExceptionTracker::CallHandler(
if (funcletType != EHFuncletType::Filter)
{
dwResumePC = CallEHFunclet((funcletType == EHFuncletType::Catch)?OBJECTREFToObject(throwable):(Object *)NULL,
#ifdef _TARGET_ARM_
DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler),
(UINT_PTR*)&(pContextRecord->R4),
#else
(UINT_PTR)pfnHandler,
&(pContextRecord->X19),
#endif // _TARGET_ARM_
pFuncletCallerSP);
CastHandlerFn(pfnHandler),
GetFirstNonVolatileRegisterAddress(pContextRecord),
pFuncletCallerSP);
}
else
{
// For invoking IL filter funclet, we pass the CallerSP to the funclet using which
// it will retrieve the framepointer for accessing the locals in the parent
// method.
dwResumePC = CallEHFilterFunclet(OBJECTREFToObject(throwable),
GetSP(pContextRecord),
#ifdef _TARGET_ARM_
DataPointerToThumbCode<UINT_PTR, HandlerFn *>(pfnHandler),
#else
(UINT_PTR)pfnHandler,
#endif // _TARGET_ARM_
pFuncletCallerSP);
GetFrameRestoreBase(pContextRecord),
CastHandlerFn(pfnHandler),
pFuncletCallerSP);
}
#else // defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
#else // USE_CALLER_SP_IN_FUNCLET
//
// Invoke the funclet.
//
dwResumePC = pfnHandler(sf.SP, OBJECTREFToObject(throwable));
#endif // _TARGET_ARM_
#endif // !USE_CALLER_SP_IN_FUNCLET

switch(funcletType)
{
Expand Down
1 change: 1 addition & 0 deletions src/vm/exceptionhandling.h
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ class ExceptionTracker
EE_ILEXCEPTION_CLAUSE* pEHClause,
MethodDesc* pMD,
EHFuncletType funcletType
X86_ARG(PT_CONTEXT pContextRecord)
ARM_ARG(PT_CONTEXT pContextRecord)
ARM64_ARG(PT_CONTEXT pContextRecord)
);
Expand Down
Loading