From 34293f2604a2b0785ccc88a57d242874577cc72f Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Wed, 15 Feb 2017 12:53:16 +0900 Subject: [PATCH 1/7] [x86/Linux] Initial patch for EH funclet Generate a simple EH funclet frame and Support SP-based stack unwinding for funclets. --- src/jit/codegencommon.cpp | 26 ++++++++++++++++++++++++++ src/vm/eetwain.cpp | 30 +++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 65cc6de47266..79c20326549e 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -10317,10 +10317,33 @@ void CodeGen::genFuncletProlog(BasicBlock* block) compiler->unwindBegProlog(); + inst_RV(INS_push, REG_FPBASE, TYP_REF); + compiler->unwindPush(REG_FPBASE); + // TODO Save callee-saved registers // This is the end of the OS-reported prolog for purposes of unwinding compiler->unwindEndProlog(); + + if (block->bbCatchTyp != BBCT_FINALLY && block->bbCatchTyp != BBCT_FAULT) + { + getEmitter()->emitIns_R_AR(INS_mov, EA_PTRSIZE, REG_EXCEPTION_OBJECT, REG_SPBASE, 12); + + getEmitter()->emitIns_R_AR(INS_mov, EA_PTRSIZE, REG_FPBASE, REG_SPBASE, 8); + getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_FPBASE, REG_FPBASE, -8); + } + else if (block->bbCatchTyp == BBCT_FAULT) + { + getEmitter()->emitIns_R_AR(INS_mov, EA_PTRSIZE, REG_FPBASE, REG_SPBASE, 8); + getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_FPBASE, REG_FPBASE, -8); + } + + // Add a padding (for 16-byte alignment) + // See #9439 for details + inst_RV_IV(INS_sub, REG_SPBASE, 8, EA_PTRSIZE); + + // We've modified EBP, but not really. Say that we haven't... + regSet.rsRemoveRegsModified(RBM_FPBASE); } /***************************************************************************** @@ -10341,6 +10364,9 @@ void CodeGen::genFuncletEpilog() // TODO Restore callee-saved registers + inst_RV_IV(INS_add, REG_SPBASE, 8, EA_PTRSIZE); + + inst_RV(INS_pop, REG_FPBASE, TYP_REF); instGen_Return(0); } diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp index bf6e1c7aa5d9..7a186c0f9ae9 100644 --- a/src/vm/eetwain.cpp +++ b/src/vm/eetwain.cpp @@ -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; @@ -3806,6 +3807,24 @@ bool UnwindEbpDoubleAlignFrame( { TADDR baseSP; +#ifdef WIN64EXCEPTIONS + if (pCodeInfo->IsFunclet()) + { + baseSP = curESP + 8; + + pContext->SetEbpLocation(PTR_DWORD(baseSP)); + + baseSP += sizeof(TADDR); + + 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); @@ -3860,6 +3879,7 @@ bool UnwindEbpDoubleAlignFrame( return true; } +#endif // !WIN64EXCEPTIONS } // @@ -3990,7 +4010,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; } From abe858015f87e244994c6baeb84f0dab3e7d7401 Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Thu, 16 Feb 2017 20:38:09 +0900 Subject: [PATCH 2/7] [x86/Linux] Fix FINALLY funclet --- src/jit/codegencommon.cpp | 33 ++++++++++++++++++++++----------- src/jit/codegenxarch.cpp | 4 ++++ src/vm/eetwain.cpp | 6 +++++- src/vm/exceptionhandling.h | 4 ++++ 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 79c20326549e..1f0569ae87e8 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -10302,6 +10302,21 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo() /***************************************************************************** * * Generates code for an EH funclet prolog. + * + * Funclets have the following incoming arguments: + * + * catch/filter-handler: ecx = CallerSP, edx = the exception object that was caught (see GT_CATCH_ARG) + * filter: ecx = CallerSP, edx = the exception object to filter (see GT_CATCH_ARG) + * finally/fault: ecx = CallerSP + * + * 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) @@ -10325,21 +10340,17 @@ void CodeGen::genFuncletProlog(BasicBlock* block) // This is the end of the OS-reported prolog for purposes of unwinding compiler->unwindEndProlog(); - if (block->bbCatchTyp != BBCT_FINALLY && block->bbCatchTyp != BBCT_FAULT) - { - getEmitter()->emitIns_R_AR(INS_mov, EA_PTRSIZE, REG_EXCEPTION_OBJECT, REG_SPBASE, 12); - - getEmitter()->emitIns_R_AR(INS_mov, EA_PTRSIZE, REG_FPBASE, REG_SPBASE, 8); - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_FPBASE, REG_FPBASE, -8); - } - else if (block->bbCatchTyp == BBCT_FAULT) + if (handlerGetsXcptnObj(block->bbCatchTyp)) { - getEmitter()->emitIns_R_AR(INS_mov, EA_PTRSIZE, REG_FPBASE, REG_SPBASE, 8); - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_FPBASE, REG_FPBASE, -8); + getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_EXCEPTION_OBJECT, REG_ARG_1); } + // TODO We will need changes here when we introduce PSPSym + getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_FPBASE, REG_ARG_0); + getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_FPBASE, REG_FPBASE, -8); + // Add a padding (for 16-byte alignment) - // See #9439 for details + // See https://github.com/dotnet/coreclr/issues/9439 for details inst_RV_IV(INS_sub, REG_SPBASE, 8, EA_PTRSIZE); // We've modified EBP, but not really. Say that we haven't... diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index e893da603584..b6ef454030fa 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -241,7 +241,11 @@ BasicBlock* CodeGen::genCallFinally(BasicBlock* block) if ((compiler->lvaPSPSym == BAD_VAR_NUM) || (!compiler->compLocallocUsed && (compiler->funCurrentFunc()->funKind == FUNC_ROOT))) { +#ifdef _TARGET_X86_ + getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_ARG_0, REG_FPBASE, 8); // caller-SP for x86 +#else inst_RV_RV(INS_mov, REG_ARG_0, REG_SPBASE, TYP_I_IMPL); +#endif } else { diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp index 7a186c0f9ae9..8ca381bfc446 100644 --- a/src/vm/eetwain.cpp +++ b/src/vm/eetwain.cpp @@ -3808,9 +3808,13 @@ 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. + // If funclet frame layout is changed from CodeGen::genFuncletProlog() and genFuncletEpilog(), + // we need to change here accordingly. if (pCodeInfo->IsFunclet()) { - baseSP = curESP + 8; + baseSP = curESP + 8; // padding for 16byte stack alignment allocated in genFuncletProlog() pContext->SetEbpLocation(PTR_DWORD(baseSP)); diff --git a/src/vm/exceptionhandling.h b/src/vm/exceptionhandling.h index c6fe35ae09f0..3643e21b1a10 100644 --- a/src/vm/exceptionhandling.h +++ b/src/vm/exceptionhandling.h @@ -32,7 +32,11 @@ ClrUnwindEx(EXCEPTION_RECORD* pExceptionRecord, UINT_PTR TargetIP, UINT_PTR TargetFrameSp); +#if defined(_TARGET_X86_) && defined(FEATURE_PAL) +typedef DWORD_PTR FASTCALL (HandlerFn)(UINT_PTR uStackFrame, Object* pExceptionObj); +#else typedef DWORD_PTR (HandlerFn)(UINT_PTR uStackFrame, Object* pExceptionObj); +#endif // _TARGET_X86_ && FEATURE_PAL enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; From 23fdd85c5478b5f8c96728b96955bec4fa50bed0 Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Mon, 20 Feb 2017 14:33:11 +0900 Subject: [PATCH 3/7] [x86/Linux] Call funclet with assembly helpers - Introduce assembly helpers : CallEHFunclet and CallEHFilterFunclet - FP as establisher frame pointer --- src/jit/codegencommon.cpp | 20 ++++---- src/jit/codegenxarch.cpp | 8 ++-- src/vm/CMakeLists.txt | 1 + src/vm/eetwain.cpp | 5 +- src/vm/exceptionhandling.cpp | 83 ++++++++++++++++++++++----------- src/vm/exceptionhandling.h | 5 +- src/vm/i386/ehhelpers.S | 90 ++++++++++++++++++++++++++++++++++++ 7 files changed, 163 insertions(+), 49 deletions(-) create mode 100644 src/vm/i386/ehhelpers.S diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 1f0569ae87e8..058b1997600a 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -10303,11 +10303,12 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo() * * Generates code for an EH funclet prolog. * + * * Funclets have the following incoming arguments: * - * catch/filter-handler: ecx = CallerSP, edx = the exception object that was caught (see GT_CATCH_ARG) - * filter: ecx = CallerSP, edx = the exception object to filter (see GT_CATCH_ARG) - * finally/fault: ecx = CallerSP + * catch/filter-handler: eax = the exception object that was caught (see GT_CATCH_ARG), ecx = FP + * filter: eax = the exception object that was caught (see GT_CATCH_ARG), ecx = FP + * finally/fault: ecx = FP * * Funclets set the following registers on exit: * @@ -10340,17 +10341,11 @@ void CodeGen::genFuncletProlog(BasicBlock* block) // This is the end of the OS-reported prolog for purposes of unwinding compiler->unwindEndProlog(); - if (handlerGetsXcptnObj(block->bbCatchTyp)) - { - getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_EXCEPTION_OBJECT, REG_ARG_1); - } - + // Restore frame pointer // TODO We will need changes here when we introduce PSPSym - getEmitter()->emitIns_R_R(INS_mov, EA_PTRSIZE, REG_FPBASE, REG_ARG_0); - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_FPBASE, REG_FPBASE, -8); + inst_RV_RV(INS_mov, REG_FPBASE, REG_ARG_0); - // Add a padding (for 16-byte alignment) - // See https://github.com/dotnet/coreclr/issues/9439 for details + // Add a padding for 16-byte alignment inst_RV_IV(INS_sub, REG_SPBASE, 8, EA_PTRSIZE); // We've modified EBP, but not really. Say that we haven't... @@ -10375,6 +10370,7 @@ void CodeGen::genFuncletEpilog() // TODO Restore callee-saved registers + // Revert a padding that was added for 16-byte alignment inst_RV_IV(INS_add, REG_SPBASE, 8, EA_PTRSIZE); inst_RV(INS_pop, REG_FPBASE, TYP_REF); diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index b6ef454030fa..01941c4abb8d 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -241,10 +241,10 @@ BasicBlock* CodeGen::genCallFinally(BasicBlock* block) if ((compiler->lvaPSPSym == BAD_VAR_NUM) || (!compiler->compLocallocUsed && (compiler->funCurrentFunc()->funKind == FUNC_ROOT))) { -#ifdef _TARGET_X86_ - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_ARG_0, REG_FPBASE, 8); // caller-SP for x86 -#else - inst_RV_RV(INS_mov, REG_ARG_0, REG_SPBASE, TYP_I_IMPL); +#ifdef UNIX_X86_ABI + inst_RV_RV(INS_mov, REG_ARG_0, REG_FPBASE, TYP_I_IMPL); // FP for UNIX_X86 +#else // UNIX_X86_ABI + inst_RV_RV(INS_mov, REG_ARG_0, REG_SPBASE, TYP_I_IMPL); // SP for all except UNIX_X86 #endif } else diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index cdf5a53d1cec..01537534edcc 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -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 diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp index 8ca381bfc446..b92843d0b85a 100644 --- a/src/vm/eetwain.cpp +++ b/src/vm/eetwain.cpp @@ -3810,8 +3810,9 @@ bool UnwindEbpDoubleAlignFrame( #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. - // If funclet frame layout is changed from CodeGen::genFuncletProlog() and genFuncletEpilog(), - // we need to change here accordingly. + // 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 + 8; // padding for 16byte stack alignment allocated in genFuncletProlog() diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp index 29906c5a9077..0369e234d01c 100644 --- a/src/vm/exceptionhandling.cpp +++ b/src/vm/exceptionhandling.cpp @@ -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 @@ -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 { @@ -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 } @@ -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 @@ -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 @@ -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(); @@ -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(pfnHandler); +#else + return (UINT_PTR)pfnHandler; +#endif +} + +static inline UINT_PTR *GetRegRestoreBase(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("GetRegRestoreBase"); + 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) ) @@ -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. @@ -3275,14 +3313,9 @@ DWORD_PTR ExceptionTracker::CallHandler( if (funcletType != EHFuncletType::Filter) { dwResumePC = CallEHFunclet((funcletType == EHFuncletType::Catch)?OBJECTREFToObject(throwable):(Object *)NULL, -#ifdef _TARGET_ARM_ - DataPointerToThumbCode(pfnHandler), - (UINT_PTR*)&(pContextRecord->R4), -#else - (UINT_PTR)pfnHandler, - &(pContextRecord->X19), -#endif // _TARGET_ARM_ - pFuncletCallerSP); + CastHandlerFn(pfnHandler), + GetRegRestoreBase(pContextRecord), + pFuncletCallerSP); } else { @@ -3290,20 +3323,16 @@ DWORD_PTR ExceptionTracker::CallHandler( // it will retrieve the framepointer for accessing the locals in the parent // method. dwResumePC = CallEHFilterFunclet(OBJECTREFToObject(throwable), - GetSP(pContextRecord), -#ifdef _TARGET_ARM_ - DataPointerToThumbCode(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) { diff --git a/src/vm/exceptionhandling.h b/src/vm/exceptionhandling.h index 3643e21b1a10..2c736d50cfcf 100644 --- a/src/vm/exceptionhandling.h +++ b/src/vm/exceptionhandling.h @@ -32,11 +32,7 @@ ClrUnwindEx(EXCEPTION_RECORD* pExceptionRecord, UINT_PTR TargetIP, UINT_PTR TargetFrameSp); -#if defined(_TARGET_X86_) && defined(FEATURE_PAL) -typedef DWORD_PTR FASTCALL (HandlerFn)(UINT_PTR uStackFrame, Object* pExceptionObj); -#else typedef DWORD_PTR (HandlerFn)(UINT_PTR uStackFrame, Object* pExceptionObj); -#endif // _TARGET_X86_ && FEATURE_PAL enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; @@ -406,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) ); diff --git a/src/vm/i386/ehhelpers.S b/src/vm/i386/ehhelpers.S new file mode 100644 index 000000000000..72def8f07969 --- /dev/null +++ b/src/vm/i386/ehhelpers.S @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +.intel_syntax noprefix +#include "unixasmmacros.inc" +#include "asmconstants.h" + +// DWORD_PTR STDCALL CallEHFunclet(Object *pThrowable, UINT_PTR pFuncletToInvoke, UINT_PTR *pFirstNonVolReg, UINT_PTR *pFuncletCallerSP); +NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler + + PROLOG_BEG + PROLOG_PUSH ebx + PROLOG_PUSH esi + PROLOG_PUSH edi + PROLOG_END + + // On entry: + // + // [ebp+ 8] = throwable + // [ebp+12] = PC to invoke + // [ebp+16] = address of EDI register in CONTEXT record // used to restore the non-volatile registers of CrawlFrame + // [ebp+20] = address of the location where the SP of funclet's caller (i.e. this helper) should be saved. + // + + // Save the SP of this function + mov eax, [ebp + 20] + mov [eax], esp + // Restore non-volatiles registers (except ebp) + mov eax, [ebp + 16] + mov edi, [eax] + mov esi, [eax + 4] + mov ebx, [eax + 8] + // Pass FP to funclet + // We don't restore EBP here or it would affect unwinding this helper + mov ecx, [eax + 24] + // Pass throwable object to funclet + mov eax, [ebp + 8] + // Invoke the funclet + mov edx, [ebp + 12] + call edx + + EPILOG_BEG + EPILOG_POP edi + EPILOG_POP esi + EPILOG_POP ebx + EPILOG_END + + ret 16 + +NESTED_END CallEHFunclet, _TEXT + +// DWORD_PTR STDCALL CallEHFilterFunclet(Object *pThrowable, TADDR CallerSP, UINT_PTR pFuncletToInvoke, UINT_PTR *pFuncletCallerSP); +NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler + + PROLOG_BEG + PROLOG_PUSH ebx + PROLOG_PUSH esi + PROLOG_PUSH edi + PROLOG_END + + // On entry: + // + // [ebp+ 8] = throwable + // [ebp+12] = FP to restore + // [ebp+16] = PC to invoke + // [ebp+20] = address of the location where the SP of funclet's caller (i.e. this helper) should be saved. + // + + // Save the SP of this function + mov eax, [ebp + 20] + mov [eax], esp + // Pass throwable object to funclet + mov eax, [ebp + 8] + // Pass FP to funclet + mov ecx, [ebp + 12] + // Invoke the funclet + mov edx, [ebp + 16] + call edx + + EPILOG_BEG + EPILOG_POP edi + EPILOG_POP esi + EPILOG_POP ebx + EPILOG_END + + ret 16 + +NESTED_END CallEHFunclet, _TEXT + From abcd2ffd7d90021511f272333e21e7d1eda8c6df Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Thu, 23 Feb 2017 14:11:57 +0900 Subject: [PATCH 4/7] [x86/Linux] Make EH helpers ESP based --- src/jit/codegencommon.cpp | 17 +++------- src/pal/inc/unixasmmacrosx86.inc | 15 +++++++++ src/pal/src/arch/i386/exceptionhelper.S | 7 ++--- src/pal/src/exception/seh.cpp | 4 +++ src/vm/i386/ehhelpers.S | 42 +++++++++++++------------ 5 files changed, 47 insertions(+), 38 deletions(-) diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 058b1997600a..102a90443709 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -10306,9 +10306,9 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo() * * Funclets have the following incoming arguments: * - * catch/filter-handler: eax = the exception object that was caught (see GT_CATCH_ARG), ecx = FP - * filter: eax = the exception object that was caught (see GT_CATCH_ARG), ecx = FP - * finally/fault: ecx = FP + * 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: * @@ -10336,20 +10336,13 @@ void CodeGen::genFuncletProlog(BasicBlock* block) inst_RV(INS_push, REG_FPBASE, TYP_REF); compiler->unwindPush(REG_FPBASE); - // TODO Save callee-saved registers - // This is the end of the OS-reported prolog for purposes of unwinding compiler->unwindEndProlog(); - // Restore frame pointer - // TODO We will need changes here when we introduce PSPSym - inst_RV_RV(INS_mov, REG_FPBASE, REG_ARG_0); + // 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, 8, EA_PTRSIZE); - - // We've modified EBP, but not really. Say that we haven't... - regSet.rsRemoveRegsModified(RBM_FPBASE); } /***************************************************************************** @@ -10368,8 +10361,6 @@ void CodeGen::genFuncletEpilog() ScopedSetVariable _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, 8, EA_PTRSIZE); diff --git a/src/pal/inc/unixasmmacrosx86.inc b/src/pal/inc/unixasmmacrosx86.inc index 77b3a6348406..ffbb4ffddea3 100644 --- a/src/pal/inc/unixasmmacrosx86.inc +++ b/src/pal/inc/unixasmmacrosx86.inc @@ -66,6 +66,21 @@ C_FUNC(\Name\()_End): pop ebp .endm +.macro ESP_PROLOG_BEG + push ebp + mov ebp, esp +.endm + +.macro ESP_PROLOG_END +.endm + +.macro ESP_EPILOG_BEG +.endm + +.macro ESP_EPILOG_END + pop ebp +.endm + .macro PREPARE_EXTERNAL_VAR Name, Reg .att_syntax call 0f diff --git a/src/pal/src/arch/i386/exceptionhelper.S b/src/pal/src/arch/i386/exceptionhelper.S index 4980b89451d1..f0bf8f8ef9e1 100644 --- a/src/pal/src/arch/i386/exceptionhelper.S +++ b/src/pal/src/arch/i386/exceptionhelper.S @@ -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] @@ -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 diff --git a/src/pal/src/exception/seh.cpp b/src/pal/src/exception/seh.cpp index ad09e028841c..de9a18771445 100644 --- a/src/pal/src/exception/seh.cpp +++ b/src/pal/src/exception/seh.cpp @@ -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); } diff --git a/src/vm/i386/ehhelpers.S b/src/vm/i386/ehhelpers.S index 72def8f07969..9eb8272e05ae 100644 --- a/src/vm/i386/ehhelpers.S +++ b/src/vm/i386/ehhelpers.S @@ -7,13 +7,14 @@ #include "asmconstants.h" // DWORD_PTR STDCALL CallEHFunclet(Object *pThrowable, UINT_PTR pFuncletToInvoke, UINT_PTR *pFirstNonVolReg, UINT_PTR *pFuncletCallerSP); +// ESP based frame NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler - PROLOG_BEG + ESP_PROLOG_BEG PROLOG_PUSH ebx PROLOG_PUSH esi PROLOG_PUSH edi - PROLOG_END + ESP_PROLOG_END // On entry: // @@ -26,38 +27,38 @@ NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler // Save the SP of this function mov eax, [ebp + 20] mov [eax], esp - // Restore non-volatiles registers (except ebp) - mov eax, [ebp + 16] - mov edi, [eax] - mov esi, [eax + 4] - mov ebx, [eax + 8] - // Pass FP to funclet - // We don't restore EBP here or it would affect unwinding this helper - mov ecx, [eax + 24] + // Save the funclet PC for later call + mov edx, [ebp + 12] // Pass throwable object to funclet mov eax, [ebp + 8] + // Restore non-volatiles registers + mov ecx, [ebp + 16] + mov edi, [ecx] + mov esi, [ecx + 4] + mov ebx, [ecx + 8] + mov ebp, [ecx + 24] // Invoke the funclet - mov edx, [ebp + 12] call edx - EPILOG_BEG + ESP_EPILOG_BEG EPILOG_POP edi EPILOG_POP esi EPILOG_POP ebx - EPILOG_END + ESP_EPILOG_END ret 16 NESTED_END CallEHFunclet, _TEXT // DWORD_PTR STDCALL CallEHFilterFunclet(Object *pThrowable, TADDR CallerSP, UINT_PTR pFuncletToInvoke, UINT_PTR *pFuncletCallerSP); +// ESP based frame NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler - PROLOG_BEG + ESP_PROLOG_BEG PROLOG_PUSH ebx PROLOG_PUSH esi PROLOG_PUSH edi - PROLOG_END + ESP_PROLOG_END // On entry: // @@ -70,19 +71,20 @@ NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler // Save the SP of this function mov eax, [ebp + 20] mov [eax], esp + // Save the funclet PC for later call + mov edx, [ebp + 16] // Pass throwable object to funclet mov eax, [ebp + 8] - // Pass FP to funclet - mov ecx, [ebp + 12] + // Restore FP + mov ebp, [ebp + 12] // Invoke the funclet - mov edx, [ebp + 16] call edx - EPILOG_BEG + ESP_EPILOG_BEG EPILOG_POP edi EPILOG_POP esi EPILOG_POP ebx - EPILOG_END + ESP_EPILOG_END ret 16 From 5c6a18cf34c32b46b20e205a9ed8f13d548a8975 Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Thu, 23 Feb 2017 22:53:49 +0900 Subject: [PATCH 5/7] [x86/Linux] Trivial fixes --- src/pal/inc/unixasmmacrosx86.inc | 3 --- src/vm/exceptionhandling.cpp | 6 +++--- src/vm/i386/ehhelpers.S | 6 ++++++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pal/inc/unixasmmacrosx86.inc b/src/pal/inc/unixasmmacrosx86.inc index ffbb4ffddea3..61ad946c7f09 100644 --- a/src/pal/inc/unixasmmacrosx86.inc +++ b/src/pal/inc/unixasmmacrosx86.inc @@ -67,8 +67,6 @@ C_FUNC(\Name\()_End): .endm .macro ESP_PROLOG_BEG - push ebp - mov ebp, esp .endm .macro ESP_PROLOG_END @@ -78,7 +76,6 @@ C_FUNC(\Name\()_End): .endm .macro ESP_EPILOG_END - pop ebp .endm .macro PREPARE_EXTERNAL_VAR Name, Reg diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp index 0369e234d01c..34bc69dc6345 100644 --- a/src/vm/exceptionhandling.cpp +++ b/src/vm/exceptionhandling.cpp @@ -3215,7 +3215,7 @@ static inline UINT_PTR CastHandlerFn(HandlerFn *pfnHandler) #endif } -static inline UINT_PTR *GetRegRestoreBase(PCONTEXT pContextRecord) +static inline UINT_PTR *GetFirstNonVolatileRegisterAddress(PCONTEXT pContextRecord) { #if defined(_TARGET_ARM_) return (UINT_PTR*)&(pContextRecord->R4); @@ -3224,7 +3224,7 @@ static inline UINT_PTR *GetRegRestoreBase(PCONTEXT pContextRecord) #elif defined(_TARGET_X86_) return (UINT_PTR*)&(pContextRecord->Edi); #else - PORTABILITY_ASSERT("GetRegRestoreBase"); + PORTABILITY_ASSERT("GetFirstNonVolatileRegisterAddress"); return NULL; #endif } @@ -3314,7 +3314,7 @@ DWORD_PTR ExceptionTracker::CallHandler( { dwResumePC = CallEHFunclet((funcletType == EHFuncletType::Catch)?OBJECTREFToObject(throwable):(Object *)NULL, CastHandlerFn(pfnHandler), - GetRegRestoreBase(pContextRecord), + GetFirstNonVolatileRegisterAddress(pContextRecord), pFuncletCallerSP); } else diff --git a/src/vm/i386/ehhelpers.S b/src/vm/i386/ehhelpers.S index 9eb8272e05ae..9f959b1bbdd0 100644 --- a/src/vm/i386/ehhelpers.S +++ b/src/vm/i386/ehhelpers.S @@ -11,6 +11,8 @@ NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler ESP_PROLOG_BEG + PROLOG_PUSH ebp + mov ebp, esp PROLOG_PUSH ebx PROLOG_PUSH esi PROLOG_PUSH edi @@ -44,6 +46,7 @@ NESTED_ENTRY CallEHFunclet, _TEXT, NoHandler EPILOG_POP edi EPILOG_POP esi EPILOG_POP ebx + EPILOG_POP ebp ESP_EPILOG_END ret 16 @@ -55,6 +58,8 @@ NESTED_END CallEHFunclet, _TEXT NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler ESP_PROLOG_BEG + PROLOG_PUSH ebp + mov ebp, esp PROLOG_PUSH ebx PROLOG_PUSH esi PROLOG_PUSH edi @@ -84,6 +89,7 @@ NESTED_ENTRY CallEHFilterFunclet, _TEXT, NoHandler EPILOG_POP edi EPILOG_POP esi EPILOG_POP ebx + EPILOG_POP ebp ESP_EPILOG_END ret 16 From d09ff846dac765d85e519c79f3a767b52e6a0dce Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Fri, 24 Feb 2017 11:25:29 +0900 Subject: [PATCH 6/7] [x86/Linux] EH ABI : remove unnecessary part --- src/jit/codegencommon.cpp | 4 ---- src/jit/codegenxarch.cpp | 8 +++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 102a90443709..a36bc9f93986 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -10333,9 +10333,6 @@ void CodeGen::genFuncletProlog(BasicBlock* block) compiler->unwindBegProlog(); - inst_RV(INS_push, REG_FPBASE, TYP_REF); - compiler->unwindPush(REG_FPBASE); - // This is the end of the OS-reported prolog for purposes of unwinding compiler->unwindEndProlog(); @@ -10364,7 +10361,6 @@ void CodeGen::genFuncletEpilog() // Revert a padding that was added for 16-byte alignment inst_RV_IV(INS_add, REG_SPBASE, 8, EA_PTRSIZE); - inst_RV(INS_pop, REG_FPBASE, TYP_REF); instGen_Return(0); } diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp index 01941c4abb8d..7a7d105e0ff8 100644 --- a/src/jit/codegenxarch.cpp +++ b/src/jit/codegenxarch.cpp @@ -241,11 +241,9 @@ BasicBlock* CodeGen::genCallFinally(BasicBlock* block) if ((compiler->lvaPSPSym == BAD_VAR_NUM) || (!compiler->compLocallocUsed && (compiler->funCurrentFunc()->funKind == FUNC_ROOT))) { -#ifdef UNIX_X86_ABI - inst_RV_RV(INS_mov, REG_ARG_0, REG_FPBASE, TYP_I_IMPL); // FP for UNIX_X86 -#else // UNIX_X86_ABI - inst_RV_RV(INS_mov, REG_ARG_0, REG_SPBASE, TYP_I_IMPL); // SP for all except UNIX_X86 -#endif +#ifndef UNIX_X86_ABI + inst_RV_RV(INS_mov, REG_ARG_0, REG_SPBASE, TYP_I_IMPL); +#endif // !UNIX_X86_ABI } else { From 696f6184391e284c82b9a58d175ea361e2d2eaa9 Mon Sep 17 00:00:00 2001 From: Hanjoung Lee Date: Fri, 24 Feb 2017 12:39:31 +0900 Subject: [PATCH 7/7] [x86/Linux] EH ABI : More fixes --- src/jit/codegencommon.cpp | 4 ++-- src/vm/eetwain.cpp | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index a36bc9f93986..7b6c80fda969 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -10339,7 +10339,7 @@ void CodeGen::genFuncletProlog(BasicBlock* block) // 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, 8, EA_PTRSIZE); + inst_RV_IV(INS_sub, REG_SPBASE, 12, EA_PTRSIZE); } /***************************************************************************** @@ -10359,7 +10359,7 @@ void CodeGen::genFuncletEpilog() ScopedSetVariable _setGeneratingEpilog(&compiler->compGeneratingEpilog, true); // Revert a padding that was added for 16-byte alignment - inst_RV_IV(INS_add, REG_SPBASE, 8, EA_PTRSIZE); + inst_RV_IV(INS_add, REG_SPBASE, 12, EA_PTRSIZE); instGen_Return(0); } diff --git a/src/vm/eetwain.cpp b/src/vm/eetwain.cpp index b92843d0b85a..989300d4685d 100644 --- a/src/vm/eetwain.cpp +++ b/src/vm/eetwain.cpp @@ -3815,11 +3815,7 @@ bool UnwindEbpDoubleAlignFrame( // TODO Currently we assume that ESP of funclet frames is always fixed but actually it could change. if (pCodeInfo->IsFunclet()) { - baseSP = curESP + 8; // padding for 16byte stack alignment allocated in genFuncletProlog() - - pContext->SetEbpLocation(PTR_DWORD(baseSP)); - - baseSP += sizeof(TADDR); + baseSP = curESP + 12; // padding for 16byte stack alignment allocated in genFuncletProlog() pContext->PCTAddr = baseSP; pContext->ControlPC = *PTR_PCODE(pContext->PCTAddr);