diff --git a/src/coreclr/interpreter/inc/interpretershared.h b/src/coreclr/interpreter/inc/interpretershared.h index d9e79f3bffea57..cae71c625da081 100644 --- a/src/coreclr/interpreter/inc/interpretershared.h +++ b/src/coreclr/interpreter/inc/interpretershared.h @@ -22,6 +22,10 @@ struct InterpHelperData { uint32_t accessType : 3; }; +#ifndef INTERPRETER_COMPILER_INTERNAL +class MethodDesc; +#endif + struct CallStubHeader; struct InterpMethod @@ -29,7 +33,11 @@ struct InterpMethod #if DEBUG InterpMethod *self; #endif +#ifdef INTERPRETER_COMPILER_INTERNAL CORINFO_METHOD_HANDLE methodHnd; +#else + DPTR(MethodDesc) methodDesc; +#endif int32_t argsSize; int32_t allocaSize; void** pDataItems; @@ -38,6 +46,7 @@ struct InterpMethod bool initLocals; bool unmanagedCallersOnly; +#ifdef INTERPRETER_COMPILER_INTERNAL InterpMethod( CORINFO_METHOD_HANDLE methodHnd, int32_t argsSize, int32_t allocaSize, void** pDataItems, bool initLocals, bool unmanagedCallersOnly @@ -54,6 +63,7 @@ struct InterpMethod this->unmanagedCallersOnly = unmanagedCallersOnly; pCallStub = NULL; } +#endif bool CheckIntegrity() { diff --git a/src/coreclr/interpreter/interpreter.h b/src/coreclr/interpreter/interpreter.h index bf5fc6bd1a94ea..e7f85bbef96c94 100644 --- a/src/coreclr/interpreter/interpreter.h +++ b/src/coreclr/interpreter/interpreter.h @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#define INTERPRETER_COMPILER_INTERNAL #include #include #include diff --git a/src/coreclr/vm/amd64/AsmHelpers.asm b/src/coreclr/vm/amd64/AsmHelpers.asm index 83d5ae3903598b..772fe72643da8e 100644 --- a/src/coreclr/vm/amd64/AsmHelpers.asm +++ b/src/coreclr/vm/amd64/AsmHelpers.asm @@ -13,7 +13,7 @@ extern OnHijackWorker:proc extern JIT_RareDisableHelperWorker:proc ifdef FEATURE_INTERPRETER extern ExecuteInterpretedMethod:proc -extern GetInterpThreadContextWithPossiblyMissingThread:proc +extern GetInterpThreadContextWithPossiblyMissingThreadOrCallStub:proc endif extern g_pPollGC:QWORD @@ -563,15 +563,16 @@ NESTED_ENTRY InterpreterStub, _TEXT INLINE_GETTHREAD r10; thrashes rax and r11 test r10, r10 - jz NoManagedThread + jz NoManagedThreadOrCallStub mov rax, qword ptr [r10 + OFFSETOF__Thread__m_pInterpThreadContext] test rax, rax jnz HaveInterpThreadContext -NoManagedThread: - mov rcx, r10 - call GetInterpThreadContextWithPossiblyMissingThread +NoManagedThreadOrCallStub: + lea rcx, [rsp + __PWTB_TransitionBlock] ; pTransitionBlock* + mov rdx, rbx + call GetInterpThreadContextWithPossiblyMissingThreadOrCallStub RESTORE_ARGUMENT_REGISTERS __PWTB_ArgumentRegisters RESTORE_FLOAT_ARGUMENT_REGISTERS __PWTB_FloatArgumentRegisters @@ -580,6 +581,8 @@ HaveInterpThreadContext: ; Load the InterpMethod pointer from the IR bytecode mov rax, qword ptr [rbx] mov rax, qword ptr [rax + OFFSETOF__InterpMethod__pCallStub] + test rax, rax + jz NoManagedThreadOrCallStub lea r11, qword ptr [rax + OFFSETOF__CallStubHeader__Routines] lea rax, [rsp + __PWTB_TransitionBlock] ; Copy the arguments to the interpreter stack, invoke the InterpExecMethod and load the return value diff --git a/src/coreclr/vm/amd64/asmhelpers.S b/src/coreclr/vm/amd64/asmhelpers.S index 0c657bcc13a9b5..7231750b6698b0 100644 --- a/src/coreclr/vm/amd64/asmhelpers.S +++ b/src/coreclr/vm/amd64/asmhelpers.S @@ -444,21 +444,24 @@ NESTED_ENTRY InterpreterStub, _TEXT, NoHandler INLINE_GETTHREAD // result in rax, it can thrash all argument registers as it can call a helper mov r10, rax test rax, rax - jz LOCAL_LABEL(NoManagedThread) + jz LOCAL_LABEL(NoManagedThreadOrCallStub) mov rax, qword ptr [r10 + OFFSETOF__Thread__m_pInterpThreadContext] test rax, rax jnz LOCAL_LABEL(HaveInterpThreadContext) -LOCAL_LABEL(NoManagedThread): - mov rdi, r10 - call C_FUNC(GetInterpThreadContextWithPossiblyMissingThread) +LOCAL_LABEL(NoManagedThreadOrCallStub): + lea rdi, [rsp + __PWTB_TransitionBlock] + mov rsi, rbx + call C_FUNC(GetInterpThreadContextWithPossiblyMissingThreadOrCallStub) LOCAL_LABEL(HaveInterpThreadContext): mov r10, qword ptr [rax + OFFSETOF__InterpThreadContext__pStackPointer] // Load the InterpMethod pointer from the IR bytecode mov rax, qword ptr [rbx] mov rax, qword ptr [rax + OFFSETOF__InterpMethod__pCallStub] + test rax, rax + jz LOCAL_LABEL(NoManagedThreadOrCallStub) // Reload the argument registers, the macro to get the thread have likely overwritten them mov rdi, qword ptr [rsp + __InterpreterStubArgumentRegistersOffset] mov rsi, qword ptr [rsp + __InterpreterStubArgumentRegistersOffset + 8] diff --git a/src/coreclr/vm/arm64/asmhelpers.S b/src/coreclr/vm/arm64/asmhelpers.S index 6ba589ed8d73c7..4375a04c9ec7e7 100644 --- a/src/coreclr/vm/arm64/asmhelpers.S +++ b/src/coreclr/vm/arm64/asmhelpers.S @@ -698,14 +698,14 @@ NESTED_ENTRY InterpreterStub, _TEXT, NoHandler mov x21, x0 #endif INLINE_GETTHREAD x20 // thrashes x0 on Apple OSes (and possibly other arg registers on other Unixes) - cbz x20, LOCAL_LABEL(NoManagedThread) + cbz x20, LOCAL_LABEL(NoManagedThreadOrCallStub) ldr x11, [x20, #OFFSETOF__Thread__m_pInterpThreadContext] cbnz x11, LOCAL_LABEL(HaveInterpThreadContext) -LOCAL_LABEL(NoManagedThread): +LOCAL_LABEL(NoManagedThreadOrCallStub): #ifdef TARGET_APPLE - // GetInterpThreadContextWithPossiblyMissingThread can destroy all argument registers, so we + // GetInterpThreadContextWithPossiblyMissingThreadOrCallStub can destroy all argument registers, so we // need to save them. For non-Apple, they have been already saved in the PROLOG_WITH_TRANSITION_BLOCK // Restore x0 thrashed by the INLINE_GETTHREAD mov x0, x21 @@ -713,8 +713,9 @@ LOCAL_LABEL(NoManagedThread): SAVE_FLOAT_ARGUMENT_REGISTERS sp, __PWTB_FloatArgumentRegisters #endif - mov x0, x20 - bl C_FUNC(GetInterpThreadContextWithPossiblyMissingThread) + add x0, sp, #__PWTB_TransitionBlock + 16 + mov x1, x19 + bl C_FUNC(GetInterpThreadContextWithPossiblyMissingThreadOrCallStub) mov x11, x0 #ifndef TARGET_APPLE @@ -733,6 +734,7 @@ LOCAL_LABEL(HaveInterpThreadContext): ldr x9, [x19] // InterpMethod* ldr x9, [x9, #OFFSETOF__InterpMethod__pCallStub] + cbz x9, LOCAL_LABEL(NoManagedThreadOrCallStub) add x10, x9, #OFFSETOF__CallStubHeader__Routines ldr x9, [x11, #OFFSETOF__InterpThreadContext__pStackPointer] // x19 contains IR bytecode address diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index c7fb9b8651c2f8..75bbcff6fd3b5f 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -24,7 +24,7 @@ IMPORT HijackHandler IMPORT ThrowControlForThread #ifdef FEATURE_INTERPRETER - IMPORT GetInterpThreadContextWithPossiblyMissingThread + IMPORT GetInterpThreadContextWithPossiblyMissingThreadOrCallStub IMPORT ExecuteInterpretedMethod #endif @@ -1064,23 +1064,25 @@ JIT_PollGCRarePath PROLOG_WITH_TRANSITION_BLOCK INLINE_GETTHREAD x20, x19 - cbz x20, NoManagedThread + cbz x20, NoManagedThreadOrCallStub + mov x19, METHODDESC_REGISTER ; x19 contains IR bytecode address ldr x11, [x20, #OFFSETOF__Thread__m_pInterpThreadContext] cbnz x11, HaveInterpThreadContext -NoManagedThread - mov x0, x20 - bl GetInterpThreadContextWithPossiblyMissingThread +NoManagedThreadOrCallStub + add x0, sp, #__PWTB_TransitionBlock + 16 + mov x1, x19 + bl GetInterpThreadContextWithPossiblyMissingThreadOrCallStub mov x11, x0 RESTORE_ARGUMENT_REGISTERS sp, __PWTB_ArgumentRegisters RESTORE_FLOAT_ARGUMENT_REGISTERS sp, __PWTB_FloatArgumentRegisters HaveInterpThreadContext ; IR bytecode address - mov x19, METHODDESC_REGISTER - ldr x9, [METHODDESC_REGISTER] + ldr x9, [x19] ldr x9, [x9, #OFFSETOF__InterpMethod__pCallStub] + cbz x9, NoManagedThreadOrCallStub add x10, x9, #OFFSETOF__CallStubHeader__Routines ldr x9, [x11, #OFFSETOF__InterpThreadContext__pStackPointer] ; x19 contains IR bytecode address diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index e97a68245cc381..fdddc3221fdb92 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -43,8 +43,6 @@ EXTERN_C BOOL CallRtlUnwind(EXCEPTION_REGISTRATION_RECORD *pEstablisherFrame, PV #endif #endif // !TARGET_UNIX -bool IsCallDescrWorkerInternalReturnAddress(PCODE pCode); - #ifdef USE_CURRENT_CONTEXT_IN_FILTER inline void CaptureNonvolatileRegisters(PKNONVOLATILE_CONTEXT pNonvolatileContext, PCONTEXT pContext) { diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index ad2d3e8f6bd961..472074fa3e5325 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -38,6 +38,8 @@ VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionConte void DECLSPEC_NORETURN DispatchExSecondPass(ExInfo *pExInfo); +bool IsCallDescrWorkerInternalReturnAddress(PCODE pCode); + enum CLRUnwindStatus { UnwindPending, FirstPassComplete, SecondPassComplete }; enum TrackerMemoryType diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 59e5aac882b7f1..bf7881c85e1ad0 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -298,30 +298,24 @@ CallStubHeader *CreateNativeToInterpreterCallStub(InterpMethod* pInterpMethod) { CallStubGenerator callStubGenerator; CallStubHeader *pHeader = VolatileLoadWithoutBarrier(&pInterpMethod->pCallStub); - GCX_PREEMP(); - - if (pHeader == NULL) + if (pHeader != NULL) { - // Ensure that there is an interpreter thread context instance and thus an interpreter stack - // allocated for this thread. This allows us to not to have to check and allocate it from - // the interpreter stub right after this call. - GetThread()->GetInterpThreadContext(); - - GCX_PREEMP(); + return pHeader; + } + GCX_PREEMP(); - AllocMemTracker amTracker; - pHeader = callStubGenerator.GenerateCallStub((MethodDesc*)pInterpMethod->methodHnd, &amTracker, false /* interpreterToNative */); + AllocMemTracker amTracker; + pHeader = callStubGenerator.GenerateCallStub(pInterpMethod->methodDesc, &amTracker, false /* interpreterToNative */); - if (InterlockedCompareExchangeT(&pInterpMethod->pCallStub, pHeader, NULL) == NULL) - { - amTracker.SuppressRelease(); - } - else - { - // We have lost the race for generating the header, use the one that was generated by another thread - // and let the amTracker release the memory of the one we generated. - pHeader = VolatileLoadWithoutBarrier(&pInterpMethod->pCallStub); - } + if (InterlockedCompareExchangeT(&pInterpMethod->pCallStub, pHeader, NULL) == NULL) + { + amTracker.SuppressRelease(); + } + else + { + // We have lost the race for generating the header, use the one that was generated by another thread + // and let the amTracker release the memory of the one we generated. + pHeader = VolatileLoadWithoutBarrier(&pInterpMethod->pCallStub); } return pHeader; @@ -352,7 +346,7 @@ void DBG_PrintInterpreterStack() InterpMethodContextFrame* cxtFrame = pInterpFrame->GetTopInterpMethodContextFrame(); while (cxtFrame != NULL) { - MethodDesc* currentMD = ((MethodDesc*)cxtFrame->startIp->Method->methodHnd); + MethodDesc* currentMD = cxtFrame->startIp->Method->methodDesc; size_t irOffset = ((size_t)cxtFrame->ip - (size_t)(&cxtFrame->startIp[1])) / sizeof(size_t); fprintf(stderr, "%4d) %s::%s, IR_%04zx\n", diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index c063464443450b..aaf3d1ee485edf 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -190,7 +190,7 @@ TADDR InterpreterPrecode::GetMethodDesc() LIMITED_METHOD_DAC_CONTRACT; InterpByteCodeStart* pInterpreterCode = dac_cast(GetData()->ByteCodeAddr); - return (TADDR)pInterpreterCode->Method->methodHnd; + return dac_cast(pInterpreterCode->Method->methodDesc); } #endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index fa63cb58f5da53..3193129948340a 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2433,14 +2433,6 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo pCode = DoBackpatch(pMT, pDispatchingMT, false /* doFullBackpatch */); Return: -// Interpreter-FIXME: Call stubs are not yet supported on WASM -#if defined(FEATURE_INTERPRETER) && !defined(TARGET_WASM) - InterpByteCodeStart *pInterpreterCode = GetInterpreterCode(); - if (pInterpreterCode != NULL) - { - CreateNativeToInterpreterCallStub(pInterpreterCode->Method); - } -#endif // FEATURE_INTERPRETER && !TARGET_WASM RETURN pCode; } diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index 45cb8ff328ef05..8f601c14e627bf 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -7701,21 +7701,136 @@ InterpThreadContext* Thread::GetInterpThreadContext() return m_pInterpThreadContext; } -extern "C" InterpThreadContext* STDCALL GetInterpThreadContextWithPossiblyMissingThread(Thread *pThread) +InterpThreadContext* GetInterpThreadContextWithPossiblyMissingThreadOrCallStub_Worker(Thread* currentThread, InterpByteCodeStart* pByteCodeStart) { CONTRACTL { THROWS; + GC_TRIGGERS; + MODE_ANY; } CONTRACTL_END; - if (pThread == nullptr) + InterpThreadContext *pThreadContext = currentThread->GetInterpThreadContext(); + if (pThreadContext == NULL) + COMPlusThrowOM(); + CreateNativeToInterpreterCallStub(pByteCodeStart->Method); + + return pThreadContext; +} + +//============================================================================= +// This function is used when starting from Preemptive mode. +// It is specifically designed to work with the UnmanagedCallersOnlyAttribute. +//============================================================================= +static InterpThreadContext* GetInterpThreadContextWithPossiblyMissingThreadOrCallStub_Preemptive( + _In_ TransitionBlock* pTransitionBlock, + Thread* currentThread, + InterpByteCodeStart* pByteCodeStart) +{ + _ASSERTE(pByteCodeStart->Method->methodDesc->HasUnmanagedCallersOnlyAttribute()); + + InterpThreadContext *pThreadContext = NULL; + + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_PREEMPTIVE; + + // Starting from preemptive mode means the possibility exists + // that the thread is new to the runtime so we might have to + // create one. + if (currentThread == NULL) { - pThread = SetupThread(); + // If our attempt to create a thread fails, there is nothing + // more we can do except fail fast. The reverse P/Invoke isn't + // going to work. + CREATETHREAD_IF_NULL_FAILFAST(currentThread, W("Failed to setup new thread during reverse P/Invoke")); + } + + MAKE_CURRENT_THREAD_AVAILABLE_EX(currentThread); + + // No GC frame is needed here since there should be no OBJECTREFs involved + // in this call due to UnmanagedCallersOnlyAttribute semantics. + + INSTALL_MANAGED_EXCEPTION_DISPATCHER; + INSTALL_UNWIND_AND_CONTINUE_HANDLER; + + pThreadContext = GetInterpThreadContextWithPossiblyMissingThreadOrCallStub_Worker(currentThread, pByteCodeStart); + + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER; + + return pThreadContext; +} + +//============================================================================= +// This function ensures that the interpreter thread context is initialized, and +// the CallStub for calling from jit calling convention to interpreter convention +// is in place. Usually ***BUT NOT ALWAYS***, this function runs only once +// per methoddesc. In addition to installing the new code, this function +// returns a pointer to the interpreter thread context. +//============================================================================= +extern "C" InterpThreadContext* STDCALL GetInterpThreadContextWithPossiblyMissingThreadOrCallStub(TransitionBlock* pTransitionBlock, InterpByteCodeStart* pByteCodeStart) +{ + InterpThreadContext *pThreadContext = NULL; + + PreserveLastErrorHolder preserveLastError; + + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_ENTRY_POINT; + + MAKE_CURRENT_THREAD_AVAILABLE_EX(GetThreadNULLOk()); + + // Attempt to check what GC mode we are running under. + if (CURRENT_THREAD == NULL + || !CURRENT_THREAD->PreemptiveGCDisabled()) + { + pThreadContext = GetInterpThreadContextWithPossiblyMissingThreadOrCallStub_Preemptive(pTransitionBlock, CURRENT_THREAD, pByteCodeStart); } + else + { + // This is the typical case (i.e. COOP mode). + +#ifdef _DEBUG + Thread::ObjectRefFlush(CURRENT_THREAD); +#endif + + PrestubMethodFrame frame(pTransitionBlock, pByteCodeStart->Method->methodDesc); + PrestubMethodFrame* pPFrame = &frame; + + pPFrame->Push(CURRENT_THREAD); + + EX_TRY + { + bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress); - return pThread->GetInterpThreadContext(); + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; + INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; + + pThreadContext = GetInterpThreadContextWithPossiblyMissingThreadOrCallStub_Worker(CURRENT_THREAD, pByteCodeStart); + + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode); + UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + } + EX_CATCH + { + OBJECTHANDLE ohThrowable = CURRENT_THREAD->LastThrownObjectHandle(); + _ASSERTE(ohThrowable); + StackTraceInfo::AppendElement(ohThrowable, 0, (UINT_PTR)pTransitionBlock, pByteCodeStart->Method->methodDesc, NULL); + EX_RETHROW; + } + EX_END_CATCH + + pPFrame->Pop(CURRENT_THREAD); + } + + _ASSERTE(pThreadContext != NULL); + + return pThreadContext; } + #endif // FEATURE_INTERPRETER /* static */ diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index a7498525c98ee6..db08933e87793b 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1645,7 +1645,6 @@ extern "C" PCODE CID_ResolveWorker(TransitionBlock * pTransitionBlock, return target; } #endif // FEATURE_CACHED_INTERFACE_DISPATCH -bool IsCallDescrWorkerInternalReturnAddress(PCODE pCode); /* Resolve to a method and return its address or NULL if there is none. Our return value is the target address that control should continue to. Our caller will